互联网大厂Java求职者面试:核心知识大考验
面试官:好了,面试开始。第一轮第一个问题,简述一下Java中的多线程实现方式,以及它们的优缺点。
王铁牛:多线程实现方式有继承Thread类和实现Runnable接口,还有使用Callable和Future。继承Thread类简单但无法再继承其他类,实现Runnable接口更灵活,Callable有返回值。
面试官:回答得不错。那再问,如何创建一个线程池?线程池有哪些参数?
王铁牛:通过ThreadPoolExecutor来创建线程池。参数有corePoolSize核心线程数、maximumPoolSize最大线程数、keepAliveTime线程存活时间、unit时间单位、workQueue任务队列、threadFactory线程工厂、handler拒绝策略。
面试官:可以。最后一个问题,说说HashMap的底层数据结构以及扩容机制。
王铁牛:HashMap底层是数组+链表+红黑树。扩容机制是当容量达到阈值(容量*加载因子)时会扩容,扩容后容量变为原来的2倍,链表节点会重新计算哈希值分配到新位置。
面试官:第一轮表现还行,接下来第二轮。说说JVM的内存结构,以及各个区域的作用。
王铁牛:JVM内存结构有堆、栈、方法区等。堆存放对象实例,栈存放局部变量等,方法区存放类信息、常量等。
面试官:那类加载机制分哪几个步骤?
王铁牛:类加载机制分加载、验证、准备、解析、初始化。
面试官:讲讲Spring框架中IoC和AOP的概念及作用。
王铁牛:IoC是控制反转,把对象创建和依赖注入交给Spring容器。AOP是面向切面编程,用于处理横切关注点,比如日志、事务。
面试官:第二轮就到这里,最后一轮。简述一下MyBatis的工作原理。
王铁牛:MyBatis通过读取配置文件,创建SqlSessionFactory,然后通过SqlSession执行SQL语句,它会根据映射文件把SQL和Java对象进行映射。
面试官:Dubbo的集群容错有哪些策略?
王铁牛:Dubbo集群容错策略有Failover Cluster(失败自动切换)、Failfast Cluster(快速失败)、Failsafe Cluster(失败安全)、Failback Cluster(失败自动恢复)等。
面试官:说说RabbitMq的消息确认机制。
王铁牛:RabbitMq消息确认机制有发布确认、消费者确认。发布确认又分同步确认和异步确认,消费者确认有自动确认和手动确认。
面试官:面试就到这里,回去等通知吧。
答案详解
- Java多线程实现方式:
- 继承Thread类:创建一个类继承Thread类,重写run方法,在run方法中编写线程执行的代码。优点是简单直接,缺点是Java是单继承,继承了Thread类后无法再继承其他类。
- 实现Runnable接口:创建一个类实现Runnable接口,实现run方法。优点是更灵活,一个类可以同时实现多个接口,缺点是没有返回值。
- 使用Callable和Future:创建一个类实现Callable接口,实现call方法,call方法有返回值。通过FutureTask来包装Callable对象,再通过Thread来执行FutureTask。
- 创建线程池及参数:
- 通过ThreadPoolExecutor来创建线程池。
- corePoolSize核心线程数:线程池创建时初始化的线程数,当提交的任务数小于corePoolSize时,会创建新线程执行任务。
- maximumPoolSize最大线程数:线程池允许的最大线程数,当提交的任务数大于corePoolSize且任务队列已满时,会创建新线程直到线程数达到maximumPoolSize。
- keepAliveTime线程存活时间:当线程数大于corePoolSize时,多余的线程在空闲多长时间后会被销毁。
- unit时间单位:keepAliveTime的时间单位。
- workQueue任务队列:用于存放提交的任务,当线程数小于corePoolSize时,任务会优先放入任务队列。
- threadFactory线程工厂:用于创建线程,可自定义线程的名称、优先级等。
- handler拒绝策略:当线程数达到maximumPoolSize且任务队列已满时,如何处理新提交的任务,有AbortPolicy(抛出异常)、CallerRunsPolicy(调用者运行)、DiscardPolicy(丢弃新任务)、DiscardOldestPolicy(丢弃队列中最旧的任务)等。
- HashMap底层数据结构及扩容机制:
- 底层数据结构:HashMap底层是数组+链表+红黑树。初始时数组长度为16,每个元素是一个Node节点,当链表长度大于8且数组长度大于64时,链表会转换为红黑树。
- 扩容机制:当HashMap的容量达到阈值(容量*加载因子,默认加载因子为0.75)时会进行扩容。扩容后容量变为原来的2倍,扩容时会重新计算每个元素的哈希值,然后重新分配到新的数组位置。
- JVM内存结构及各区域作用:
- 堆:存放对象实例,是JVM中最大的一块内存区域,被所有线程共享。
- 栈:存放局部变量、方法调用等信息,每个线程都有自己独立的栈空间。
- 方法区:存放类信息、常量、静态变量等,被所有线程共享。
- 程序计数器:记录当前线程执行的字节码指令地址,是线程私有的。
- 本地方法栈:用于执行本地方法,是线程私有的。
- 类加载机制步骤:
- 加载:通过类的全限定名来获取定义此类的二进制字节流,将字节流所代表的静态存储结构转化为方法区的运行时数据结构,在内存中生成一个代表这个类的Class对象。
- 验证:确保加载的类信息符合JVM规范,不会危害虚拟机安全。
- 准备:为类的静态变量分配内存,并设置默认初始值。
- 解析:将常量池内的符号引用替换为直接引用。
- 初始化:执行类构造器方法,为类的静态变量赋予正确的初始值。
- Spring框架中IoC和AOP的概念及作用:
- IoC(控制反转):把对象的创建和对象之间的依赖关系交给Spring容器来管理,而不是由应用程序自己来控制。这样可以降低对象之间的耦合度,提高程序的可维护性和可扩展性。
- AOP(面向切面编程):用于处理横切关注点,比如日志、事务、权限控制等。通过将这些横切关注点从业务逻辑中分离出来,以切面的形式织入到业务逻辑中,使业务逻辑更加清晰和简洁。
- MyBatis工作原理:
- 首先读取MyBatis的配置文件,通过配置文件创建SqlSessionFactory。
- SqlSessionFactory是MyBatis的关键对象,它负责创建SqlSession。
- SqlSession是MyBatis执行SQL语句的入口,通过它可以执行各种SQL操作,如查询、插入、更新、删除等。
- MyBatis通过映射文件(XML文件或注解)来定义SQL语句和Java对象之间的映射关系,根据映射关系将SQL执行结果封装成Java对象返回给调用者。
- Dubbo集群容错策略:
- Failover Cluster(失败自动切换):失败后自动切换到其他服务器,默认策略。
- Failfast Cluster(快速失败):调用失败立即报错,适用于幂等操作。
- Failsafe Cluster(失败安全):调用失败不报错,直接忽略,适用于写审计日志等操作。
- Failback Cluster(失败自动恢复):失败后会在后台定时重试,适用于消息通知等操作。
- RabbitMq消息确认机制:
- 发布确认:
- 同步确认:生产者发送消息后等待服务器确认,确认后才继续发送下一条消息。优点是可靠性高,缺点是性能低。
- 异步确认:生产者发送消息后不需要等待服务器确认,通过回调函数来处理确认结果。优点是性能高,缺点是实现复杂。
- 消费者确认:
- 自动确认:消费者接收到消息后自动确认,消息一旦被接收就从队列中删除。优点是简单,缺点是可能会导致消息丢失。
- 手动确认:消费者接收到消息后不自动确认,需要手动调用方法确认,这样可以确保消息被正确处理后才删除。优点是可靠性高,缺点是实现复杂。
- 发布确认: