互联网大厂Java求职者面试大揭秘:核心知识深度考察
面试官:好了,开始今天的面试。第一轮问题,先问几个基础的。Java 中的多线程有哪些实现方式?
王铁牛:嗯,有继承Thread类和实现Runnable接口这两种方式。
面试官:不错,回答正确。那说说线程池的核心参数都有哪些?
王铁牛:有corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。
面试官:很好,看来基础还不错。再问一个,HashMap 在JDK 1.7和JDK 1.8中有哪些主要的区别?
王铁牛:呃……这个嘛,1.8好像是改成链表和红黑树结合了?
面试官:回答得不太准确。1.7中HashMap是数组+链表结构,1.8中在链表长度大于8时会转换为红黑树,以提高查询效率。而且哈希值的计算方式等也有一些变化。这一轮就先到这里。
第二轮问题,难度加深。说说Spring框架中的IoC和AOP的概念和作用。
王铁牛:IoC就是控制反转,把对象的创建和依赖注入交给Spring容器管理。AOP是面向切面编程,能在不修改原有代码的基础上增强功能。
面试官:还可以。那Spring Boot的自动配置原理是什么?
王铁牛:呃……就是自动配置一些默认的配置类啥的?
面试官:不太清晰。Spring Boot通过条件注解来实现自动配置,根据类路径下的依赖和配置来自动装配相关的组件。再问一个,MyBatis的#{}和${}的区别是什么?
王铁牛:#{}是预编译处理,能防止SQL注入,${}是字符串替换。
面试官:这轮回答得有好有坏。进入第三轮。Dubbo的集群容错策略有哪些?
王铁牛:好像有failover、failfast这些?
面试官:具体说说failover和failfast的特点。
王铁牛:嗯……failover就是失败自动重试,failfast就是快速失败,只调用一次。
面试官:还算基本正确。最后一个问题,RabbitMq的消息确认机制是怎样的?
王铁牛:这个……不太清楚。
面试官:好了,面试就到这里。回去等通知吧。
答案解析
Java中的多线程实现方式:
- 继承Thread类:通过继承Thread类并重写run方法来定义线程的执行逻辑。每个Thread类的实例代表一个线程。例如:
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行");
}
}
MyThread thread = new MyThread();
thread.start();
- 实现Runnable接口:实现Runnable接口的类需要实现run方法,然后将该类的实例作为参数传递给Thread类的构造函数来创建线程。这样可以方便地实现多个线程共享同一个资源。例如:
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程执行");
}
}
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
线程池的核心参数:
- corePoolSize:线程池的核心线程数,当提交的任务数小于corePoolSize时,会创建新的线程来执行任务。
- maximumPoolSize:线程池允许的最大线程数,当任务数超过corePoolSize且workQueue已满时,会创建新线程直到线程数达到maximumPoolSize。
- keepAliveTime:线程池中非核心线程的存活时间,当线程数大于corePoolSize时,非核心线程在空闲一段时间后会被销毁,这个空闲时间就是keepAliveTime。
- unit:keepAliveTime的时间单位。
- workQueue:任务队列,用于存放提交的任务,当线程数达到corePoolSize时,新提交的任务会放入workQueue中。
- threadFactory:线程工厂,用于创建线程,可自定义线程的名称、优先级等属性。
- handler:拒绝策略,当线程数达到maximumPoolSize且workQueue已满时,会调用handler来处理新提交的任务。
HashMap在JDK 1.7和JDK 1.8中的区别:
- 数据结构:
- JDK 1.7:数组 + 链表结构。
- JDK 1.8:数组 + 链表 + 红黑树结构。当链表长度大于8时,链表会转换为红黑树,以提高查询效率。
- 哈希值计算方式:
- JDK 1.7:采用扰动函数4次位运算来计算哈希值。
- JDK 1.8:先通过key的hashCode()方法得到哈希值,然后进行高位运算和低位运算来优化哈希分布。
Spring框架中的IoC和AOP:
- IoC(控制反转):
- 概念:将对象的创建和依赖注入交给Spring容器管理,而不是在对象内部自行创建和管理依赖。
- 作用:降低对象之间的耦合度,提高代码的可维护性和可测试性。例如,一个Service类依赖于一个Dao类,通过IoC容器可以方便地将Dao类注入到Service类中,而不需要在Service类中直接new Dao对象。
- AOP(面向切面编程):
- 概念:在不修改原有代码的基础上,对业务逻辑进行横向切割,将通用的功能(如日志记录、事务管理等)封装成切面,织入到目标对象中。
- 作用:提高代码的复用性,减少重复代码。比如在多个业务方法中都需要进行日志记录,通过AOP可以将日志记录功能统一封装在一个切面中,在方法执行前后进行日志记录。
Spring Boot的自动配置原理: Spring Boot通过条件注解来实现自动配置。它会根据类路径下的依赖和配置来自动装配相关的组件。例如,当项目中引入了Spring Data JPA的依赖时,Spring Boot会自动配置JPA相关的组件,如EntityManagerFactory、JpaRepository等。它通过@Conditional注解来判断是否满足特定的条件,如果满足条件则自动配置相应的组件。比如@ConditionalOnClass注解会判断某个类是否在类路径下,如果在则进行相关配置。
MyBatis的#{}和${}的区别:
- #{}:是预编译处理,它会将传入的值作为参数,用?占位符代替,能防止SQL注入。例如:
<select id="selectUser" parameterType="int" resultType="User">
select * from user where id = #{id}
</select>
在执行SQL时,会将#{id}替换为?,然后通过PreparedStatement设置参数值,这样可以有效防止SQL注入。
- ${}:是字符串替换,它会将传入的值直接替换到SQL语句中。例如:
<select id="selectUser" parameterType="string" resultType="User">
select * from user where name = '${name}'
</select>
如果传入的name值包含恶意SQL语句,可能会导致SQL注入问题,所以使用时要特别小心。
Dubbo的集群容错策略:
- failover:失败自动重试,当调用失败时,会自动重试其他服务器,默认重试次数是2次。适用于读操作等对数据一致性要求不高的场景。例如,在查询商品信息时,如果某个服务节点调用失败,会自动重试其他节点。
- failfast:快速失败,只调用一次,若调用失败立即返回错误。适用于幂等操作等不允许重复调用的场景。比如下单操作,若第一次调用失败,就不再重试,直接返回错误,避免重复下单。
RabbitMq的消息确认机制:
- 生产者确认机制:
- 事务机制:通过channel.txSelect()开启事务,然后发送消息,若发送成功则通过channel.txCommit()提交事务,若失败则通过channel.txRollback()回滚事务。
- 发送方确认机制:通过channel.confirmSelect()开启发送方确认模式,发送消息后通过addConfirmListener监听确认结果。可以监听消息发送成功和失败的回调。
- 消费者确认机制:
- 自动确认:消费者接收到消息后,自动认为消息已被成功消费。
- 手动确认:消费者接收到消息后,需要通过channel.basicAck()方法手动确认消息已被成功消费,若处理失败可通过channel.basicNack()或channel.basicReject()拒绝消息。