Java部分面经整理

HashMap专题

1.什么情况下HashMap会进行链表转化为红黑树?
当链表的长度大于8同时数组的长度需要大于64 的时候才能转化。(引入红黑树的目的就是为了提高查找效率)。
2.HashMap的简单特性?
(1)当我们默认创建一个新的HashMap的时候其长度是16(初始化变成比输入大小大的最小的2的n次幂);
(2)在JDK8之前是有一个默认的结构叫Entry,JDK8之后叫Node,两个都实现了Map.Entry这样的一个接口,实际上我们存放的东西也就是这个东西,而不是别的(alt+7查看目录结构,crtl + n查看源源码)。
(3)首先计算一个key的hashcode,之后去计算该键值对的位置,使用的是无符号右移,异或以及与运算,这样就达到了一个效率极高的情况。还可以进行伪随机数法。
(4)HashMap的数组大小只要达到了阈值,就会进行括容,而不是满了括容。
3.如何对key进行比较?
(1)计算hashcode,没有值直接存。
(2)有值的话就比较equals,要是相等就替换,不相等就添加。
4.为什么HashMap的大小都必须是2的n次幂?
(1)因为计算数组中索引的函数是key&(length - 1),如果是2的n次幂,减一生成的东西全都是1,在后续进行&操作的时候不容易产生冲突。
(2)提高计算速度。
在这里插入图片描述
在这个过程中,将我们传递过来的无论是多少,都会最终变成16,这个cap就是传递过来的值,-1目的是为了将其变成大于等于传递值的最小的2的次幂,要是不-1的话就会变成32了(一共右移了32位)就算完事了。
// 这个是边界值,代表大于这个值就进行数组括容)。
在这里插入图片描述
这一行就是把我们的容量给到了边界值,这个边界值还不是容量,
5.为什么链表的长度大于等于8的时候,需要变成红黑树;6再转化回去。
(1)树节点的大小是普通节点的两倍,变成红黑树所占用的空间较大。
(2)每个空间存储的概率到达8的可能性实际是很小的,0.0000006这种级别的概率,很低的可能才会变成红黑树。
(3)如果7就转化回去,那么会产生抖动。
(4)这个在源码里面有一段注释,就是当我们的HashCode分配均匀的情况下,是很难达到一个节点下面串的个数超过8的,越大概率越小。
(5)基本上到8的时候就是快要百万分之6了,所以就足够了。
(6)但是不妨碍有些人使用的时候会造成这种可能。
(7)退化是6就是为了防止频繁转化,影响性能,中间加一个7缓冲一下。
6.为什么负载因子是0.75而不是0.5?
(1)太大了造成数据非常密,链表非常多,降低性能。
(2)太小了浪费空间。
7.put的流程?
(1)首先计算出来哈希值,之后异或了它向右移16位,之后按位与table的长度(实际上就是相当于哈希值对table长度取余,这样计算效率是很高的)。
(2)之后看看我们的table是不是空,是的话就默认创建一个大小为16,边界值为12的表。
(3)看看数组的对应位置有没有数据,要是没有数据的话,那么就直接将其存放。
(4)首先比较哈希值,哈希值相等了,只能说明位置相同;之后判断key是不是相等,或者key是equals相等的,要是都满足,那么就覆盖。
8.HashMap的扩容时机?
(1)第一个,是第一次put的时候进行括容。
(2)第二个,是当我们一个位置的链表长度大于8的时候,同时数组长度小于64 的时候,进行括容。
(3)第三个,存放的数据总数是大于容量*填充因子。
(HashMap第一次括容是先括容再插入数据,但是后续都是先插入数据在进行括容)。
9.HashMap的在JDK1.8中如何避免rehash?
在这里插入图片描述
我们的Hash值是在扩容前后没有变化的,但是我们容量括容之后是有变化的,假设原来是16,那就是10000,15为01111;扩容后为32,10 0000 ,31为
01 1111,我们只需要看最高位是不是1,是1的话,就是原来的哈希值+原来表的长度,是0的话就是原来的哈希值不变。
10.HashMap的哈希函数是怎么设计的?
其是使用正常对象调用hashcode方法得到的-2147483648~2147483647区间的数字作为值得,之后用其高16位和低16位进行异或,得到作为其哈希值。这样算是保留了高位特征同时又保证了散列函数得均匀性。
11.JDK1.8和1.7比得改动以及目的?
(1)数组+链表+红黑树。
防止Hash冲突导致得链表长度过长性能下降。
(2)尾插法替换头插法。
多线程得条件下容易产生欢。
(3)使用最高位1/0得判断替代rehash。
上面说过了。
(4)先判断括容再插入改为先插入再判断括容。
12.Hash Map是线程安全的吗?
不是,1.7中会出现死循环的问题;1.8中会出现数据覆盖,首先A线程插入,B线程插入获取资源A线程挂起,B插完了A回头插,就会出现丢失数据,同时++size本身就是线程不安全的。
13.为什么HashMap使用红黑树存储数据呢?
(1)使用树状结构主要是为了保证查询的时候更快一点,因为查询的次数只与层次有关。
(2)树状结构并且带有顺序的有三种(常见),二叉排序树,平衡二叉树和红黑树。
(3)二叉排序树极端的情况下就插成链表了
(4)平衡二叉树的话,在添加的时候还好,和红黑树一样,一般两次旋转就搞定了,但是删除的时候,可能需要旋转多次,而红黑树无论什么情况最多旋转三次就能搞定。
14.HashMap是线程安全的吗?
(1)两个线程都需要向里面塞值,A拿到头比较equals,B也拿到,比较equals之后将值塞进去,A又拿到了CPU之后将值塞进去了。
(2)在进行塞入值得时候,需要将当前容量++。
15.为什么JDK1.7会产生并发死链?
https://blog.csdn.net/weixin_41814742/article/details/110739935
16.为什么是HashMap是线程不安全的?
(1)两个线程操作同一个key会造成覆盖的情况
(2)size++本身就是线程不安全的

ConcurrentHashMap-JDK1.8专题

1.ConcurrentHashMap中的重要属性?
(1)sizeCtl:默认是0,初始化的时候是-1,括容的时候是-(1+括容线程数),括容完成之后为下一次括容的阈值大小。
(2)table:就是哈希表。
(3)nextTable:也是一个node的数组。
(4)子类,ForwardingNode,也是Node的子类,当在搬迁的时候,一个节点下搬迁完成,就将这里置一个FordingNode,表示已经处理完成。凡是搬迁完成,都放一个。第二个用途,当get的时候,一旦发现是ForwardingNode,那么就到新的表里面去拿,否则就在这个表里面拿。
(5)子类,TreeBin(作为红黑树的头节点),TreeNode(作为红黑树的节点)。
2.ConcurrentHashMap中重要方法?
(1)tabAt:就是获取Node数组中第i个Node。
(2)casTabAt:Cas的方式修改第i个Node的值。
(3)setTabAt:不使用CAS的方式修改。
3.ConcurrentHashMap的构造方法?
其在1.8之中是使用懒惰初始化的,最开始只是获取了我们需要创建的数组的大小而不是真正的初始化出来,比较节省空间,(1 + 容量 / 负载因子)。
4.Concurrent Hash Map的get方法?
首先通过spread,将我们的key获取的hashCode变成一个正数,右移16位之后和0111 1111 1111 1111 1111 1111 1111 1111进行与操作(正数是因为我们的负数是有特殊用途的)。
在这里插入图片描述
(2)table的大小是0就返回null,获取表长度n,用n-1 和 得到的hash值&操作,得到桶下标,桶下标是对应的节点是null也返回null。
(3)之后看看这个节点的hash是不是负数,-1就是forwardingNode,就去新的表,是-2就是树,都是调用find。
(4)While进行依次比较。
5.ConcurrentHashMap的put方法?
(1)首先,使用spread得到一个正数的值。
(2)之后看看表是不是空的,是的话,那么就使用CAS去创建新的表,之后计算出来对应的位置,如果这个位置是null得话CAS在这个位置添加新的节点。
(3)看看头节点是不是ForwardingNode(e.hash = -1),是的话,就进行帮忙括容。
(4)最后这种情况,那就是我们发生了冲突,就只锁这个头节点,之后去遍历链表,如果有key相同,那么就替换掉;如果找了一圈没找到,那么就创建新的节点,之后将尾部指向我们新建的节点。如果是红黑树的TreeBin得话,那么我们就需要使用这个逻辑了。
6.ConcurrentHashMap的initTable?
(1)如果sizeCtl<0的时候,就先让出使用权,让其他的线程来创建这个表。
(2)否则的话,就尝试使用CAS来将sizeCtl修改成-1,也就是创建。
7.ConcurrentHashMap的transfer方法?
(1)首先判断新链表是不是空,是空的话,就创建一个新的链表,长度是原来链表大小的二倍。
(2)进行搬迁。
(3)如果搬迁完成,就CAS将头节点替换成ForwardingNode。
(4)如果这个位是是null,也将其CAS替换成ForwardingNode。
(5)如果其余线程过来发现是ForwardingNode的话,那么就直接下一轮。
(6)之后使用Synchronized去保证搬迁的线程安全性,根据不同类型去使用不同的方法进行搬迁。

ConcurrentHashMap-JDK1.7专题

1.原理?
实际上是维护了一个segment数组,每个segment数组对应一把锁,继承自ReentrantLock。每个setment下标有一个HashEntry,会在这个里面存放真正的数组+链表。
优点:多个线程访问不同的segment,实际上是没有冲突的,与JDK8中类似。
缺点:setment数组大小默认是16,容量初始化了之后就不会改变了,同时,这个东西不是懒惰初始化的,对内存的占用比较大。
(1)维护了一个segment数组,每个segment数组对应一把锁。
(2)多个线程访问不同的segment,实际是没有冲突的。
(3)默认的segment数组大小是16,容量初始化之后就不能改变了,并且不是懒惰初始化。
(4)每个Segment里面对应一个HashEntry,就是一个小的哈希表。
(5)他在操作的时候,实际上是有两个属性,一个是segmentShift,就是移位,之后还有一个sigmentMask,就是掩码,将我们得到的哈希码移位之后得到的值和掩码进行与运算,最终的结果就是我们的想要进行操作的segment下标。
2.区别?
(1)1.7里面是基于ReentrantLock + Segment + HashEntry.
-Segment里面包含了一个HashEntry数组,数组下面挂载的是链表结构。
-元素查询的时候需要经过两次哈希,第一次定位segment,第二次定位链表头部,之后再去选用合适的方法遍历。
(2)1.8里面是基于CAS + Synchronized + Node + 红黑树
-Node的val和next都是使用volatile修饰,保证了可见性。
-查找,查询和替换都使用CAS来保证效率而不是全部上锁。
-锁链表的头节点,不影响其他的元素的读写。

设计模式专题

1.静态代理模式?
实际上就是面向接口的代理模式,需要被代理的对象和代理对象需要实现同一个接口,在代理对象中聚合被代理的对象,在实现方法的时候调用被代理对象方法的前后分别调用自定义的方法实现功能的增强。
在这里插入图片描述
在这里插入图片描述
优点:对客户隐藏了被代理接口的具体实现类,在一定程度上实现了解耦合,提高了安全性。
缺点:需要实现目标类的接口,并实现其方法,大量代码冗余;只能对某个固定的接口实现类进行代理服务,灵活性不强。
在这里插入图片描述
2.JDK动态代理?
在这里插入图片描述
优点:只需将被代理对象传入到代理类中就可以拿到代理对象;不需要在每一个代理类中去写增强的代码,只需要在代理类里面写一次就可以了。
缺点:仍需要实现接口。使用反射创建代理类效率低;并且需要为被代理对象创建一个实现的接口。
3.cgLib动态代理?
在这里插入图片描述
优点:可以直接进行类代理,而不是必须进行接口代理。
缺点:第一次调用生成多个class对象较jdk方式慢一些。
4.两种动态代理的优缺点对比?
(1)jdk方式只能代理实现了接口的类,不能代理没实现接口的类;cglib可以
(2)Jdk方式实际上是相当于利用反射生成一个实现了接口的匿名实现类,使用invoke方法进行面向切面的处理;cglib是将代理对象的字节码进行加载,修改其字节码生成子类,回调其父类的方法(要求父类不能final修饰,也不能有final方法)。
5.Spring中使用什么实现AOP?
(1)如果类有实现接口,那么就使用JDK动态代理。
(2)如果类没有实现接口,那么就使用cgLib实现动态代理。
6.为什么懒汉式要有两个if?
要因为可能同时有多个线程过了第一个if但是还在排队等待Synchronized之后里面的创建完成走了,外面的进来又创建了。

Spring专题

1.AOP中关键术语?
(1)Advice:就是增强/通知,即在切入点执行的增强处理。
(2)Target:就是目标对象,被增强的对象。
(3)Proxy:将通知应用到目标对象之后,被动态创建的对象。
(4)Weaving(织入):将切面代码插入到目标对象上,生成代理对象的过程。
2.Spring中自带的通知类型?
(1)前置通知;
(2)后置通知;
(3)环绕通知;
(4)异常通知;

Java基础知识

1.递归调用的问题所在?
(1)层次过深的话容易造成栈溢出;
(2)有很多重复计算。
2.说一说重载和重写的区别?
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求,不能根据返回类型进行区分。
3.接口和抽象类的区别?
(1)接口实际是对类进行一个约束,约束了行为的有无,但是无法约束具体的操作;抽象类是当我们很多类具有相同的方法,代码复用的作用。
(2)抽象类中可以有普通的成员函数,但是接口中只有抽象的函数。
(3)抽象类继承一个,但是接口可以实现多个。
4.为什么Java的内存对其是8byte?
一般我们都是64位的机器,一次性读取的都是8字节的数据,所以用8byte作为一个读取单元比较方便,对于压缩指针也很方便。
5.System.exit和Runtime.hauf的区别?
第一个稍微安全一点,未执行完的执行,钩子函数完事了之后退出;第二个不咋样,直接强行结束所有的进程。
6.对象实例化的方法?
(1)new出来一个对象。
(2)Class的newInstance方法(需要配置空参构造器,且必须为public)
(3)Constructor的newInstance方法,可以是空参也可以是带参。
(4)Clone方法
(5)反序列化。
7.对象创建的步骤?
(1)判断对象的类是不是已经加载链接初始化了。
(2)为对象分配内存。
(3)设置对象的对象头。
(4)执行init进行初始化。(这一步和第二步可能会被重排序,所以我们需要使用volatile保证指令不重排序)
8.对象头中的内存布局?
(1)首地址值Hash码;
(2)线程持有的锁
(3)偏向锁的ID
(4)锁状态
(5)分代年龄
(6)类型指针,指向自己的class。
9.说一说String?
(1)String其实是一个final的类型,是不可以被继承的存放在字符串常量池中。
(2)1.8中使用的是一个char形数组,但是在1.9中就是一个byte形数组。
(3)字符串的加法两侧有一侧是变量的时候就会导致这个对象是存放在堆中。
(4)Intern就是将这个对象存放在常量池中一份并返回其在常量池中的地址。
(5)new String(“ab”)会创建两个对象,其中一个会放在堆空间中,另一个会放在字符串常量池中。
10.为什么匿名内部类只能用final修饰?
(1)编译后将会生成两个.class文件。
(2)内部类和外部类为同一个等级,不会因为外部类被回收了,内部类的变量就被回收了。
(3)如果内部类没用完,但是外部类用完了,要回收,怎么办?
(4)考虑去复制一份。
(5)但是复制的东西变了会不会影响外面?
(6)想要不影响,使用final修饰即可。
11.说一说Array List?
(1)动态数组,需要连续的存储空间,能根据下标进行随机访问。
(2)在jdk1.7中初始的大小是10,但是在jdk1.8中采用的是懒加载,初始大小是0,只用第一次向里面添加了才会变成10,之后每次扩大1.5倍数。
12.JDK1.8的新特性?
(1)lambda表达式:允许将接口作为函数的参数,但是这个接口内只有一个方法,我们将其做出实现即可。简化操作。
(2)流式编程:就是简化函数书写过程,通过将数据处理为流数据之后进行封装或者是处理。
(3)链式编程。
13.HashCode和Equals的区别?
(1)hashCode就是计算出一个对象对应的int类型的数字,相当于其坐标;equals则是比对两个对象相等与否的方法,如果不重写,等同于==,比较地址。
(2)如果我们使用过程中不涉及HashSet,HashMap等哈希函数类,那么这两个东西不产生联系,如果涉及了,那么就有关系。
(3)但凡是重写了equals,就需要重写hashCode,要是不重写,可能我们规定什么情况下是equals,但是在插入哈希表的时候先比较的是hashcode,仍然不相等,导致表中存在着我们认为相同的两个对象。
14.servelet/filter/interceptor/listener的区别和联系?
15.为什么ArrayList是1.5括容不是2.0括容?
(1)首先扩容得时候这个1.5倍是原容量右移1未之后加上原容量,得到的一个大概得数字。
(2)其次,在我们使用1.5倍扩容的情况下,我们是很容易去复用之前的空间的,比如最开始是4,扩容6,在扩容9,然后14,这样就可以把之前的覆盖了;如果两倍实际也可以,再大了的话就有点浪费了,每次扩容之后空闲太多的空间了。
(3)太小了的话,前面积压的实在是太多了。
16.ArrayList里面有100个元素,我们如何删除里面的20-30号元素?
(1)遍历,按照index删除。
(2)subList(startIndex,endIndex).clear()就行了。
17.多线程下访问double/long会怎么样?
(1)double是非volatile的,所以是线程不安全的,他将操作变成操作两次32位的数据。
(2)所有基本数据类型都被要求是原子的,所以这个可能不是原子的,读到低32或者是高32.
18.红黑树,B树,B+树有什么区别?
(1)红黑树其实就是优化版本的平衡二叉树,只不过其在插入和删除的时候性能稍微好一些,但是查询的时候效率略逊色于AVL。
(2)B树将元素存放在每一个节点上面,当我们需要查询范围之类的时候,是需要去将其进行中序遍历的,
(3)B+树在非叶子节点中存放的只是索引,只有在叶子节点上存放的才是数据,这样就保证了在使用的时候在顺序查询过程中在一块区域内就可以进行查询,因为其叶子节点每一个元素之间会有指针相连。
19.Java一个对象占用多大内存?
(1)首先是对象头,在64位机器上是占用12字节的,但是要是不能压缩占用16字节。
(2)之后引用类型是占用4字节,不能压缩就是8字节。
(3)其余的,char 2 short 2 long 8 double 8 int 4 boolean 1 float 4 byte 1
20.什么是反射?
Java语言在运行状态中对于任意一个类,都能够获取这个类的所有属性和方法;对于任意一个对象都能够调用他的任意方法和属性,也能改变他的属性,这就是Java被视作动态语言的一个关键性质。
他能在我们运行过程中获取任何一个已知名称的class的内部信息,包括修饰符,属性方法等,并可用于更加灵活的调用代码。
21.什么是动态代理?
动态代理就是在程序运行阶段创建目标对象的代理对象,并对目标对象增强的一种手段(JDK和cjlib)。
22.filter和inteceptot的区别?
(1)filter是在javax包下的,intercepter是在spring下的。
(2)Filter是servlet规定的,intercepter是spring规定的。
(3)过滤器是基于函数回调的,拦截器是基于反射机制的。
(4)Filter会对所有进入servlet中的请求进行过滤,intercepter只会对controller进行拦截。
23.说一说几种基本的数据结构?
(1)队列,树,堆。
(2)数组,链表,栈,哈希表,图。
(3)无向图可以表示北京地铁。
24.面向对象的基本特征?
(1)封装:将我们要实现的功能包装成一个黑盒子,外部只能调用但是不能知道里面的具体实现细节。
(2)继承:继承就是可以实现父类的所有功能,同时无需编写原来的类的情况下进行扩展。
(3)多态:同一个接口具有多种不同的表现能力。实现方式为重载和重写。
25.抽象类和接口的区别?
(1)抽象类中既可以有方法的定义,又可以有实现;接口中只有定义,没有实现(default修饰符在1.8之后是可以有实现的)。
(2)一个类可以实现多个接口,但是只能继承一个类。
(3)接口强调的是功能的实现,抽象类强调的是所属关系。
(4)抽象类中可以有成员变量,接口中的变量默认为public static final修饰。
26.CPU负载过高如何排查问题?
(1)先top一下,看看哪些进程占用cpu最多;
(2)之后ps -mp pid -o THREAD,tid,time查看进程里面的线程。
(3)使用jstack 线程号 >> ****.log打印日志。
(4)把线程号转成16进制,之后去日志里面找。
27.简述Servlet的生命周期?
(1)首先是加载和实例化,当请求到来的时候,会首先查看里面有没有Servlet的实例,要是有的话,就使用,没有的话就创建。
(2)实例化之后,调用init方法进行初始化,对资源等信息进行初始化。
(3)初始化之后,就能工作了,来了请求,调用service方法,之后会被分发给合适的doGet和doPost方法去使用。
(4)非线程安全。
(5)销毁:容器销毁他才被销毁。
28.如何解决其线程不安全的问题?
(1)可以设置SingleThreadModel接口,这样就会使同一时刻这个Servlet的service不会被两个线程使用,系统很慢。
(2)同步对共享数据的操作。
(3)尽量不适用实例变量,使用局部变量就可以了。
29.序列化和反序列化?
(1)序列化就是将对象转化成可以传输保存的形式。
(2)反序列化就是将字节等转化成对象的过程。
(3)序列化的最终目的就是让对象可以跨平台存储,进行网络传输。
RabbitMQ就是序列化。
30.Integer常量池?
Integer中默认保存了-128-127的数据,在比较相等的时候需要使用==,当然数值介于二者之间的时候是可以用==的。
31.Java中的异常体系?
(1)Throwable是所有错误或者异常的超类,其有两个子类,Error和Exception。
(2)Error是一个合理的应用程序不应该捕获的严重问题,写程序的时候不应该捕获这种错误,OutOfMemoryError,NoSushMethodError这种。
(3)Exception就是子类的运行时异常和检查异常,这种异常是允许在代码里面出现的。
-检查异常:NoSushMethodException,CloneNotSupportException
-运行时异常:NullPointerException,ArrayIndexOutOfBoundsException等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值