面试之【葵花宝典】

165 阅读10分钟

欢迎关注WX公众号:“程序猿补课班”,分享Java相关技术知识,学习经验,面试经验等。小伙伴快来补课吧!

正文开始

1.ConcurrentHashMap了解吗?

在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争。

在JDK1.8的时候ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构类似,数组+链表/红黑二叉树。

Java 8在链表长度超过一定阈值(8)时将链表(寻址时间复杂度为O(N))转换为红黑树(寻址时间复杂度为O(log(N)))synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。

2.自定义异常用过吗?

自定义异常的步骤

(1)继承 Exception 或 RuntimeException

(2)定义构造方法

(3)使用异常

3.JDK1.8有哪些新特性

Lambda表达式:Lambda允许函数作为参数传递到方法中。

Date Time API:加强对日期与时间的处理。

Optional类:用来解决空指针异常。

Stream API:可以把函数式编程风格引入到Java中。

4.Bean的生命周期了解吗?

bean的生命周期就是一个创建bean的过程,主要分为4个步骤,实例化,属性注入,初始化,销毁。

因此spring的bean的生命周期总共有以下几步:

1、实现了BeanFactoryPostProcessor接口的bean,在加载其他的bean的时候,也会调用这个bean的postProcessBeanFactory方法,可以在这个步骤去对bean中的属性去赋值。设置年龄初始化18等等。

2、实现了InstantiationAwareBeanPostProcessor接口的bean,会在实例化bean之前调用postProcessBeforeInstantiation方法

3、然后在对bean进行实例化

4、对bean进行属性注入

5、对bean进行初始化,在初始化中,包含了以下几个步骤:


1)实现了BeanFactoryAware接口,会先调用setBeanFactory方法

2)实现了BeanNameAware接口,会先调用setBeanName方法

3)实现了BeanPostProcessor接口,会先调用postProcessBeforeInitialization方法

3)实现了InitializingBean接口,会调用afterPropertiesSet方法

4)然后在进行aop后置处理,通过实现BeanPostProcessor接口,在postProcessAfterInitialization方法中进行动态代理

6、销毁

5.说一下Spring的事务传播行为

spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。

① PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。

② PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。

③ PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

④ PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。

⑤ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

⑥ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

⑦ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。

6.说一下 spring 的事务隔离?

使用数据库默认的事务隔离级别

读未提交

读已提交

可重复读

串行化

7.什么是嵌套事务?

    嵌套是子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫save point,然后执行子事务,这个子事务的执行也算是父事务的一部分,然后子事务执行结束,父事务继续执行。重点就在于那个save point。看几个问题就明了了:

1)如果子事务回滚,会发生什么?

    父事务会回滚到进入子事务前建立的save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。

2)如果父事务回滚,会发生什么?

    父事务回滚,子事务也会跟着回滚!为什么呢,因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理。

3)事务的提交,是什么情况?

    是父事务先提交,然后子事务提交,还是子事务先提交,父事务再提交?答案是第二种情况,还是那句话,子事务是父事务的一部分,由父事务统一提交。

8.事务失效的几种原因

1、spring的事务注解@Transactional只能放在public修饰的方法上才起作用,如果放在其他非public(private,protected)方法上,虽然不报错,但是事务不起作用

2、在业务代码中如果抛出RuntimeException异常,事务回滚;但是抛出Exception,事务不回滚;

3、如果在加有事务的方法内,使用了try...catch..语句块对异常进行了捕获,而catch语句块没有throw  new RuntimeExecption异常,事务也不会回滚。

4、在类A里面有方法a 和方法b, 然后方法b上面用 @Transactional加了方法级别的事务,在方法a里面 调用了方法b, 方法b里面的事务不会生效。

原因是在同一个类之中,方法互相调用,切面无效 ,而不仅仅是事务。这里事务之所以无效,是因为spring的事务是通过aop实现的。

9.Java虚拟机内存的各个区域包括:

程序计数器

Java虚拟机栈

本地方法栈

Java堆

方法区


10. 什么是程序计数器呢?

程序计数器是一块较小的内存空间,它可以是当前程序所执行的字节码的行号指示器。

程序计数器的作用

字节码解释器工作时,就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,从而分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖此计数器来完成。

在多线程的情况下,在任何一个确定的时刻,一个处理器都只会执行一条线程中的指令。线程切换后,能恢复到正确的执行位置。

11.什么是Java虚拟机栈?

当方法在运行过程中需要创建局部变量时,就将局部变量的值存入栈帧的局部变量表中,当这个方法执行完毕后,这个方法所对应的栈帧将会出栈,并释放内存空间。

栈中存放局部变量,堆中存放对象

12.什么是本地方法栈?

本地方法栈与虚拟机栈所发挥的作用是非常相似的,他们的区别是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到 的Native方法服务。

13.什么是堆?

存放对象实例,几乎所有对象实例都在这里分配内存。

整个java虚拟机只有一个堆,所有的线程都访问同一个堆。而程序计数器、java虚拟机栈、本地方法栈都是一个线程对应一个。

Java堆中还可以细分为:新生代和老年代,再细致一点有:Eden空间、From Survivor空间、To Survivor空间。不同的区域存放具有不同声明周期的对象。这样可以根据不同的区域使用不同的垃圾回收算法,从而更具有针对性,从而更加高效。

14.什么是方法区?

描述为堆的一个逻辑分区,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

15.双亲委派模型了解吗

简单点说,所谓的双亲委派模型,就是加载类的时候,先请求其父类加载器去加载,如果父类加载器无法加载类,再尝试自己去加载类。如果都没加载到,就抛出异常。

16.jvm怎么判断对象无效

1、引用计数法

给对象添加一个引用计数器,每当有一个地方引用该对象时,计数器+1,当引用失效时,计数器-1,任何时候当计数器为0的时候,该对象不再被引用。

2、可达性分析算法

这个算法的基本思想就是通过一系列的称为 “GC Roots”的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。

17.GC是什么?为什么要有GC?

 GC是JVM虚拟机的垃圾回收机制,JVM中的垃圾回收器实现了自动内存管理,无需开发人员手动参与内存的分配与回收,这样降低内存泄漏和内存溢出的风险。在程序运行的过程中会不断的创建对象,占用内存空间,如果不进行垃圾回收,随着程序的运行,迟早会发生内存溢出。除了释放没用的对象,垃圾回收也可以清除内存里的记录碎片。碎片整理将所占用的堆内存移到堆的一端,以便JVM 将整理出的内存分配给新的对象。

18.垃圾收集算法

标记--清除算法

该算法分为“标记”和“清除”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。它是最基础的收集算法,后续的算法都是对其不足进行改进得到。

复制算法

为了解决效率问题,“复制”收集算法出现了。它将可用内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。实现简单,运行高效。

标记--整理算法

根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。

新生代采用复制算法,老年代采用标记-整理算法。

19.CMS与G1的不同?

(1)CMS使用标记清除法,G1采用复制法,G1不会产生内存碎片

(2)G1提供更为可控的垃圾回收时间,可以根据项目的需要进行设置

(3)CMS和G1处理跨代引用的方式不同(主要是老年代引用年轻带):

20.CMS回收停顿了几次,为什么要停顿两次?

停顿两次,第一次停顿为初始化标记,标记所有的GC root,在该阶段需要进行一次STW。初始化标记后要将进行并发标记,在该阶段不需要STW,

但是随着用户线程的运行,已标记的对象的状态可能会发生变化,以确保GC过程的一致性,需要对已标记的对象状态进行修正,且必须在STW状态下进行,否则无法保证GC的一致性。

如有错漏之处,敬请指正