秋招备战——Java基础知识

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了秋招备战——Java基础知识相关的知识,希望对你有一定的参考价值。


垃圾回收,JVM常用参数

将内存中不再被使用的对象进行回收,GC中用于回收的方法称为收集器,由于GC需要消耗一些资源和时间,Java在对对象的生命周期特征进行分析后,按照新生代、老年代的方式来对对象进行收集,以尽可能的缩短GC对应用造成的暂停

对新生代的对象收集称为minor GC

对老生代的对象收集称为full GC

程序中主动调用System.gc()强制执行的GC为full GC。

Xms堆内存的最小大小,默认为物理内存的1/64

Xmx堆内存的最大大小,默认为物理内存的1/4

Xmn堆内新生代的大小,通过这个值也可以得到老生代的大小:Xmx减去Xmn。

char和byte的区别

一个字符类型,无符号型的,占2个字节(Unicode码),Java用char来表示一个字符;一个是字节类型,有符号型,占1个字节。

int和byte能否相互转换

可以相互转换,byte占1字节,而int占用4个字节,不会照成精度损失。

数组和链表的区别

数组是连续的内存空间,适合通过索引查找,查找和修改比较方便;链表不一定是连续的内存空间,添加和删除比较方便。

红黑树的特点

解决平衡二叉查找树的缺点:不适合需要大量插入、删除和查找的场景,需要左旋和右旋来保证平衡,引入红黑树,每条路径的黑色节点数相同,红色节点不能相同,时间复杂度O(logn)。红黑树的特性:红黑树是近似平衡的二叉查找树,支持高效的查找、插入和删除元素,查找删除和插入的效率稳定。红黑树更适合应对实际开发过程中的复杂场景。

父节点为红,子节点还能是红色吗

不可以,需要把红色节点先变成黑色。

排序算法哪些是稳定的

稳定性体现在相同数字在排序之后后面的不会出现到前面

稳定算法:插入排序、冒泡排序、归并排序、基数排序;不稳定算法:希尔排序、选择排序、堆排序、快速排序。

设计模式有哪些?单例模式、工厂模式,单例模式怎么实现?spring中的单例模式和普通单例模式有什么区别?

单例模式:枚举实现、静态内部类实现、利用Spring的依赖注入能力实现单例、双重检查锁。Spring中的单例模式和普通单例模式的区别:spring中的单例是相对于容器,既在ApplicationContext中是单例的。而平常所说的单例是相对于JVM的。另一个JVM可以有多个Spring容器,而且Spring中的单例也只是按bean的id来区分的。

collection 和 collections

collection是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,实现该接口的类主要有List和Set。

collections是针对集合类的一个包裹类,它提供了一系列静态方法实现对各种集合的搜索、排序以及线程安全化等操作。如sort()对集合进行排序,min(),max()求集合的最大值和最小值。

synchronized和reentranLock的区别

  1. Reentrant Lock显示地获得释放锁,synchronized隐式获得释放锁;
  2. ReentrantLock可响应中断,可轮回,synchronized是不可以响应中断的
  3. reentranLock是API级别的,synchronized是JVM级别的
  4. ReentrantLock可以实现公平锁;
  5. ReentrantLock通过Condition可以绑定多个条件;
  6. 底层实现不一样,synchronized是同步阻塞,使用的是悲观并发策略,lock是同步非阻塞,采用的是乐观并发策略;
  7. Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现
  8. synchronized在发生异常时,会自动释放线程中的锁,因此不会导致死锁现象发生;而lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁。

synchronized底层如何实现,锁升级的过程

synchronized底层通过不同的锁实现。线程获取共享资源时,
第一步:检查mark world里面是不是放的自己的Thread,如果是,表示当前线程是处于“偏向锁”。
第二步:如果mark world不是自己的threadID,锁升级,这时候,用CAS来执行切换,新的线程根据MarkWorld里现有的ThreadId,通知之前线程暂停,之前线程将MarkWOrld的内容置空。
第三步:两个线程都把锁对象的hashcode复制到自己新建的用于存储锁的记录空间,接着开始通过cas操作,把锁对象的MarkWorld的内容修改为自己新建的记录空间的地址的方式竞争MarkWorld。
第四步:第三步中成功执行的CAS获得资源,失败的则进入自旋。
第五步:自旋的线程在自旋过程中,成功获得资源,则整个状态依然处于轻量级锁的状态,如果自旋失败。
第六步:进入重量级锁的状态,这个时候,自旋的线程进行阻塞,等待之前线程完成并唤醒自己。

List、Set、Map的区别

List:一个有序容器,元素存入集合的顺序和取出顺序一致,,元素可以重复,可以出入null元素,元素都有索引。常用的有ArrayList、LinkedList和Vector。

Set:无序容器,存入和取出顺序不一致,不可以存储重复元素,只允许存入一个null元素,必须保证元素的唯一性。set接口常用的实现类是HashSet、LinkedHashSet、TreeSet

Map:是一个键值对集合,存储键、值和之间的映射。key无序,唯一;value不要求有序,允许重复。Map常用的有HashMap、Tree Map、Hash Table、LinkedHashMap、ConcurrentHashMap.

集合框架底层的数据结构

List集合:Arraylist和Vector使用的是 Object 数组, LinkedList使用双向循环链表

set集合:HashSet(无序,唯一):基于 HashMap 实现的,HashSet的值作为key,value是Object类型的常量
LinkedHashSet继承HashSet,并且通过 LinkedHashMap 来实现的
TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树。)

Map集合:HashMap由数组+链表+红黑树组成,数组是是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,当链表长度大于阈值(默认为8)并且数组长度大于64时,将链表转化为红黑树

LinkedHashMap(有序) 继承自 HashMap,底层仍然是数组+链表+红黑树组成。另外,LinkedHashMap 在此基础上,节点之间增加了一条双向链表,使得可以保持键值对的插入顺序

HashTable无序,数组+链表组成的,数组是 HashTable的主体,链表则是主要为了解决哈希冲突而存在的

TreeMap有序,红黑树

复合框架的扩容

ArrayList和Vector默认初始容量为10,当元素个数超过容量长度时都进行进行扩容,ArrayList扩容为原来的1.5倍,而Vector扩容为原来的2倍

HashSet和HashMap默认初始容量为16,加载因子为0.75:即当元素个数超过容量长度的0.75倍时,进行扩容,扩容为原来的2倍。HashSet基于 HashMap 实现的,因此两者相同

HashTable:默认初始容量为11,加载因子为0.75,扩容策略是2倍+1,如 初始的容量为11,一次扩容后是容量为23

HashMap的实现原理以及JDK1.7和JDK1.8的区别

JDK1.7是数组+链表,无冲突时,存放数组;冲突时,存放链表;采用头插法。

JDK1.8是数组 + 链表 + 红黑树,无冲突时,存放数组;有冲突存放链表或者红黑树,当链表长度大于阈值(默认为8)并且数组长度大于64时,将链表转化为红黑树;树元素小于等于6时,树结构还原成链表形式。

HashMap是怎么解决哈希冲突的?

(1)使用链地址法(链表)来链接拥有相同hash值的数据;
(2)使用2次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均;
(3)引入红黑树进一步降低遍历的时间复杂度,使得遍历更快。

扰动函数的解释:

如果只使用hashCode取余,那么相当于参与运算的只有hashCode的低位,高位是没有起到任何作用的,所以我们的思路就是让hashCode的高位也参与进行运算,来获取hash值,进一步降低hash碰撞的概率,使得数据分布更平均,我们把这样的操作称为扰动。

ConcurrentHashMap底层的具体实现

在JDK1.7中,concurrenthashMap是由Segment数组结构和Hash Entry数据结构组成。segment继承了ReentrantLock,是一种可重入锁。Hash Entry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组,一个Segment里包含一个HashEntry数组,每个HashEnrty是一个链表结构的元素,因此JDK1.7的concurrentHashMap是一种数组+链表结构。当对HashEnrty数组的数据进行修改时,必须首先获得与它对应的锁,这样才能保证线程安全,也就实现了全局的线程安全。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4z2vmVc2-1662292004425)(en-resource://database/1127:1)]

而在JDK1.8中采用Node + CAS+synchronized来保证并发安全进行实现,synchronized只锁定当前链表或红黑二叉树的首节点。如果相应位置上的Node没有初始化,则调用CAS插入相应的数据;如果相应位置的Node不为空,如果该节点的
如果该节点的first.hash!=-1,则对该节点加synchronized锁,更新节点或插入新节点; 如果该节点的first.hash=-1,则扩容。读操作无锁,Node节点的val和next使用volatile修饰,数组也用volatile修饰。

双亲委派机制

当一个类加载器收到了类加载请求的时候,他不会直接去加载指定的类,而是把这个请求委托给自己的父加载器去加载。只有父加载器无法加载这个类的时候,才会由当前这个加载器来负责类的加载。

Java中提供四种类型的加载器,具体如下:

Bootstrap ClassLoader类加载器:主要负责加载Java核心类库,
%JRE_HOME%\\lib下的rt.jar、resources.jar、charsets.jar和class等。

Extention CLassLoader扩展类加载器:主要负责加载目录%JRE_HOME%\\lib\\ext目录下的jar包和class文件。

Application ClassLoader(应用程序类加载器):主要负责加载当前应用的classpath下的所有类

User ClassLoader(用户自定义类加载器):用户自定义的类加载器可加载指定路径的class文件。

这四种类加载器存在如下关系,当进行类加载的时候,虽然用户自定义类不会由bootstrap classloader或是extension classloader加载(由类加载器的加载范围决定),但是代码实现还是会一直委托到bootstrap classloader, 上层无法加载,再由下层是否可以加载,如果都无法加载,就会触发findclass,抛出classNotFoundException.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ophNl0Cp-1662292004427)(en-resource://database/1129:1)]

加载器之间的层级关系并不是以继承的方式存在的,而是以组合的方式处理的。

双亲委派机制的意义:

  1. 通过委派的方式,可以避免类的重复加载,当父加载器已经加载某一个类时,子加载器就不会再重新加载这个类。
  2. 通过双亲委派的方式,还保证了安全性。因为启动加载器在加载的时候,只会加载Java-home中的jar包里面的类,那么这个类是不会被随意替换的,除非有人跑到你的机器上,破坏你的jdk。那么就可以避免有人自定义一个有破坏功能的jdk里面的类被加载。这样可以有效防止核心Java API被篡改。

双亲委派机制有他存在的意义,不过也存在许多场景是需要破坏这个机制的,所以双亲委派机制也非必然。比如 tomcat web容器里面部署了很多的应用程序,但是这些应用程序对于第三方类库的依赖版本却不一样,但这些第三方类库的路径又是一样的,如果采用默认的双亲委派类加载机制,那么是无法加载多个相同的类。所以,Tomcat破坏双亲委派原则,提供隔离的机制,为每个web容器单独提供一个WebAppClassLoader加载器。

Tomcat的类加载机制:为了实现隔离性,优先加载 Web 应用自己定义的类,所以没有遵照双亲委派的约定,每一个应用自己的类加载器——WebAppClassLoader负责加载本身的目录下的class文件,加载不到时再交给CommonClassLoader加载,这和双亲委派刚好相反。

面试大礼包分享

‍下面再给大家分享一个超级棒的Java后端面试的大礼包,是某华为大师兄当时呕心沥血整理的,希望会对大家有用。

秋招备战——Java基础知识_加载


里面涵盖的内容非常的完整,且非常有用。

以上是关于秋招备战——Java基础知识的主要内容,如果未能解决你的问题,请参考以下文章

备战秋招冲击大厂Java面试题系列—Java基础

秋招备战计划第二弹最后俩月能提升的知识清单

秋招备战计划第二弹最后俩月能提升的知识清单

服务端开发之Java备战秋招面试篇2-HashMap底层原理篇

备战秋招之八大排序——O(n^2)级排序算法

2021 备战秋招新鲜出炉的美团字节阿里腾讯等大厂综合 Java 岗面试题