第一轮面试 面试官:先从基础的Java核心知识问起。Java中多态是如何实现的? 王铁牛:多态就是子类重写父类的方法,然后通过父类引用指向子类对象来实现不同的行为。 面试官:回答得不错。那HashMap在JDK1.7和JDK1.8中有什么主要区别? 王铁牛:1.8好像是用红黑树优化了,1.7是链表,1.8解决了链表过长性能问题。 面试官:嗯,基本要点抓住了。ArrayList在扩容时具体是怎样的机制? 王铁牛:当元素个数达到容量时,会扩容为原来的1.5倍,然后把旧数组元素复制到新数组。 面试官:这轮回答得不错,对基础掌握得还行。
第二轮面试 面试官:接下来聊聊JUC和多线程。线程池的核心参数有哪些,分别有什么作用? 王铁牛:有核心线程数、最大线程数,还有个队列啥的,核心线程数就是一开始的线程数,最大就是最多能有多少线程。 面试官:那Callable和Runnable有什么区别? 王铁牛:嗯……Callable好像能有返回值,Runnable没有,其他的不太清楚了。 面试官:JVM的垃圾回收机制,常见的垃圾回收算法有哪些? 王铁牛:有标记清除,还有复制算法,标记清除就是标记垃圾然后清除,复制就是把存活对象复制到另一块空间。 面试官:这轮有些回答不太清晰,对JUC和JVM的理解还可以更深入。
第三轮面试 面试官:说说Spring框架中IOC和AOP的原理。 王铁牛:IOC就是控制反转,把对象创建交给Spring容器,AOP是面向切面编程,能在方法前后加功能。 面试官:Spring Boot自动配置的原理是什么? 王铁牛:好像是根据依赖和配置文件自动配置一些组件,具体不太明白。 面试官:MyBatis的一级缓存和二级缓存有什么区别? 王铁牛:一级缓存是SqlSession级别的,二级缓存是namespace级别的,二级缓存能跨SqlSession。 面试官:Dubbo的服务暴露和引用流程是怎样的? 王铁牛:呃,就是服务提供者暴露服务,消费者引用,具体细节不太记得了。 面试官:RabbitMQ的消息确认机制是怎样的? 王铁牛:有生产者确认,还有消费者确认,生产者确认就是确认消息发送到了Broker,消费者确认就是确认消费了消息。 面试官:xxl - job的调度原理是什么? 王铁牛:不太清楚,好像是有调度中心啥的。 面试官:Redis的持久化机制有哪些? 王铁牛:有RDB和AOF,RDB是快照,AOF是记录写操作。
面试总结:通过这三轮面试,能看出你对一些基础的Java知识掌握得还可以,像Java核心知识、集合类的一些特性回答得比较准确。但在JUC、JVM以及一些框架和中间件的深入原理方面,回答得不是特别清晰和全面。对于线程池核心参数的作用阐述不够详细,Spring Boot自动配置原理没有深入理解,Dubbo、xxl - job等的原理回答得比较模糊。回去等通知吧,我们会综合评估所有候选人后,再做决定。
答案:
- Java中多态的实现:多态通过继承、重写和向上转型来实现。子类继承父类,重写父类方法,然后使用父类引用指向子类对象,在运行时根据实际对象类型调用相应的方法。例如,父类Animal有eat方法,子类Dog继承Animal并重写eat方法,当Animal animal = new Dog(); animal.eat();时,实际调用的是Dog的eat方法。
- HashMap在JDK1.7和JDK1.8的区别:JDK1.7中HashMap采用数组 + 链表的数据结构,在哈希冲突严重时,链表长度过长,查找性能下降。JDK1.8引入了红黑树,当链表长度大于8且数组容量大于64时,链表会转换为红黑树,提高了查找性能。同时,JDK1.8的HashMap在插入元素时采用尾插法(1.7是头插法),避免了多线程环境下的环形链表问题。
- ArrayList扩容机制:ArrayList有一个默认初始容量,当添加元素使得元素个数达到当前容量时,会进行扩容。新容量为原来容量的1.5倍(oldCapacity + (oldCapacity >> 1))。然后创建一个新的更大的数组,将旧数组中的元素复制到新数组中。
- 线程池核心参数:
- corePoolSize:核心线程数,线程池初始化时创建的线程数,这些线程会一直存活,即使处于空闲状态也不会被销毁(除非设置了allowCoreThreadTimeOut为true)。
- maximumPoolSize:最大线程数,线程池允许创建的最大线程数。当任务队列已满且核心线程都在忙碌时,会创建新线程直到达到最大线程数。
- keepAliveTime:线程存活时间,当线程数大于核心线程数时,多余的空闲线程在存活时间内没有任务,就会被销毁。
- unit:keepAliveTime的时间单位,如TimeUnit.SECONDS等。
- workQueue:任务队列,用于存放等待执行的任务。常见的有ArrayBlockingQueue、LinkedBlockingQueue等。
- Callable和Runnable区别:Runnable是一个接口,实现该接口的类的run方法没有返回值,也不能抛出受检异常。而Callable也是一个接口,实现该接口的类的call方法有返回值,并且可以抛出受检异常。通常Callable配合Future或FutureTask使用来获取任务执行结果。
- JVM常见垃圾回收算法:
- 标记 - 清除算法:分为标记和清除两个阶段。首先标记出所有需要回收的对象,然后统一回收所有被标记的对象。缺点是会产生大量不连续的内存碎片,空间利用率低。
- 复制算法:将内存分为大小相等的两块,每次只使用其中一块。当这一块内存用完,就将还存活的对象复制到另一块上面,然后把已使用过的内存空间一次性清理掉。适用于新生代,因为新生代对象存活率低。缺点是内存使用率只有一半。
- 标记 - 整理算法:标记阶段和标记 - 清除算法一样,但是在清除阶段,它不是直接清理对象,而是将所有存活的对象向一端移动,然后直接清理掉边界以外的内存。解决了标记 - 清除算法的碎片问题,适用于老年代。
- Spring中IOC原理:IOC(控制反转)是Spring框架的核心思想之一。通过容器来管理对象的创建、生命周期和依赖关系。Spring使用BeanFactory或ApplicationContext作为IOC容器。在配置文件(XML或注解配置)中定义Bean,容器根据配置创建Bean实例,并注入其依赖的其他Bean。例如,一个Service类依赖一个Dao类,通过IOC容器可以自动将Dao实例注入到Service中,而不是在Service中手动创建Dao实例。
- Spring中AOP原理:AOP(面向切面编程)是在不改变原有代码的基础上,对程序的横切关注点(如日志记录、事务管理等)进行统一处理。Spring AOP基于动态代理实现,对于实现了接口的类,使用JDK动态代理;对于没有实现接口的类,使用CGLIB代理。通过切点表达式定义切入点,通过通知(Advice,如前置通知、后置通知等)定义在切入点处执行的逻辑,将通知织入到目标方法中。
- Spring Boot自动配置原理:Spring Boot通过@EnableAutoConfiguration注解开启自动配置。它会根据classpath下的依赖和配置文件(如application.properties或application.yml),自动配置一些常用的组件。Spring Boot有一个自动配置类库,里面有大量的条件配置类,这些类根据条件(如某个类是否存在、某个属性是否配置等)来决定是否创建相应的Bean。例如,如果classpath下有Tomcat依赖,就会自动配置Tomcat作为Web服务器。
- MyBatis一级缓存和二级缓存区别:
- 一级缓存:是SqlSession级别的缓存,在同一个SqlSession中,执行相同的SQL查询时,会先从一级缓存中获取数据,不会再次查询数据库。当SqlSession执行commit、rollback等操作,会清空一级缓存。
- 二级缓存:是namespace级别的缓存,多个SqlSession可以共享二级缓存。二级缓存需要手动开启,并且需要实现Serializable接口。当一个SqlSession对某一namespace下的SQL进行操作并提交后,会清空该namespace下的二级缓存。
- Dubbo服务暴露和引用流程:
- 服务暴露:服务提供者在启动时,通过Dubbo的配置文件或注解,将服务接口和实现类注册到注册中心(如Zookeeper)。Dubbo会根据配置生成服务的代理类,并将服务发布到指定的协议(如Dubbo协议、HTTP协议等)的端口上。
- 服务引用:服务消费者在启动时,从注册中心获取服务提供者的地址列表。Dubbo根据配置生成服务的代理类,消费者通过代理类调用服务,代理类会根据负载均衡算法选择一个服务提供者地址,然后通过网络通信协议(如Dubbo协议)向服务提供者发起调用。
- RabbitMQ消息确认机制:
- 生产者确认:生产者将消息发送到RabbitMQ Broker后,Broker会给生产者发送确认消息,告知消息已成功接收。生产者可以通过设置confirm模式,使用Channel.waitForConfirms()方法等待确认,或者通过异步回调的方式处理确认结果。这样可以确保消息成功发送到Broker,避免消息丢失。
- 消费者确认:消费者从RabbitMQ获取消息并处理完成后,需要向Broker发送确认消息,告知Broker该消息已被成功处理。RabbitMQ有两种确认模式,自动确认(auto - ack)和手动确认(manual - ack)。自动确认模式下,消费者一旦接收到消息,RabbitMQ就认为消息已被处理;手动确认模式下,消费者需要调用Channel.basicAck()方法手动确认消息,这样可以确保消息被正确处理,否则RabbitMQ会重新将消息发送给其他消费者。
- xxl - job调度原理:xxl - job有一个调度中心,负责管理任务的调度配置,如任务执行周期、执行时间等。调度中心通过数据库存储任务信息和执行日志。任务执行器(Executor)启动时会向调度中心注册自己,并保持心跳连接。调度中心根据任务配置,定时触发任务,通过网络通信将任务发送给对应的任务执行器。任务执行器接收到任务后,执行具体的业务逻辑,并将执行结果返回给调度中心。
- Redis持久化机制:
- RDB(Redis Database):RDB是一种快照持久化方式,Redis会在指定的时间间隔内,将内存中的数据以快照的形式写入到磁盘文件(dump.rdb)中。优点是恢复速度快,适合大规模数据恢复;缺点是可能会丢失最后一次快照后的部分数据,因为快照是定期执行的。
- AOF(Append - Only - File):AOF是一种追加式持久化方式,Redis会将每一个写操作都追加到一个文件(appendonly.aof)中。当Redis重启时,会重新执行AOF文件中的命令来恢复数据。优点是数据完整性好,基本不会丢失数据;缺点是AOF文件会不断增大,需要定期进行重写(rewrite)操作,以压缩文件大小。