《互联网大厂面试:Java核心知识、多线程、框架及中间件大考验》
第一轮面试:Java 基础与集合框架
面试官:你好,欢迎参加本次面试。首先问你几个基础问题,Java 中基本数据类型有哪些?王铁牛:这个我知道,有 byte、short、int、long、float、double、char、boolean。面试官:回答得不错。那 HashMap 是如何解决哈希冲突的?王铁牛:它采用链地址法,也就是当发生哈希冲突时,会在对应的数组位置形成一个链表,把冲突的元素都放到这个链表上。面试官:很好,看来你对基础掌握得挺扎实。那 ArrayList 是线程安全的吗?王铁牛:不是,ArrayList 不是线程安全的。如果多个线程同时对它进行操作,可能会出现数据不一致等问题。面试官:非常棒,你的回答很准确。
第二轮面试:多线程与 JUC
面试官:接下来我们聊聊多线程。创建线程有几种方式?王铁牛:有三种,继承 Thread 类、实现 Runnable 接口、实现 Callable 接口。面试官:不错。那什么是线程池,为什么要使用线程池?王铁牛:线程池就是管理一组线程的容器。使用线程池可以减少线程创建和销毁的开销,提高系统的性能,还能控制线程的数量,避免资源过度占用。面试官:回答得很好。那 JUC 包中常用的类有哪些,比如用于线程同步的?王铁牛:这个……我想想,有 ReentrantLock,它可以实现线程同步,比 synchronized 更灵活。还有 CountDownLatch 也可以用来控制线程的执行顺序。面试官:可以,你对多线程和 JUC 有一定的了解。
第三轮面试:框架与中间件
面试官:现在谈谈框架方面的问题。Spring 框架的核心特性有哪些?王铁牛:Spring 的核心特性有 IoC(控制反转)和 AOP(面向切面编程)。IoC 就是把对象的创建和管理交给 Spring 容器,AOP 可以在不修改原有代码的基础上增加额外的功能。面试官:还不错。那 Spring Boot 相比 Spring 有什么优势?王铁牛:这个嘛,Spring Boot 简化了 Spring 项目的配置,有很多自动配置,能快速搭建项目,提高开发效率。面试官:有点意思。那 MyBatis 中 #{} 和 ${} 的区别是什么?王铁牛:呃……这个我好像有点记不清了,好像一个是预编译,一个不是,但具体哪个我有点乱了。面试官:没关系。那 Dubbo 是做什么的,它的核心组件有哪些?王铁牛:Dubbo 是一个分布式服务框架,核心组件嘛……我只记得有注册中心,其他的不太清楚了。面试官:好的。那 RabbitMq 的工作模式有哪些?王铁牛:这个我不太会,只知道它是消息队列。面试官:行,还有最后一个问题,xxl - job 和 Redis 你了解多少?王铁牛:xxl - job 好像是个分布式任务调度平台,Redis 是个缓存数据库,但具体的使用和原理我不太熟悉。
面试官总结道:“从今天的面试来看,你对 Java 基础和部分核心知识掌握得还可以,像基本数据类型、集合框架、多线程的一些基础概念都能准确回答,这很不错。在 Spring 和 Spring Boot 方面也有一定的理解,对它们的核心特性和优势有清晰的认知。然而,在面对一些复杂的框架和中间件问题时,比如 MyBatis 的细节、Dubbo 的核心组件、RabbitMq 的工作模式以及 xxl - job 和 Redis 的深入知识,你回答得不够理想,存在知识的欠缺。我们需要综合考虑你的整体表现,你先回家等通知吧,后续会有工作人员和你联系。”
问题答案
- Java 中基本数据类型有哪些?
- Java 中有 8 种基本数据类型,可分为 4 类:
- 整数类型:byte(1 字节,-128 到 127)、short(2 字节,-32768 到 32767)、int(4 字节,-2147483648 到 2147483647)、long(8 字节,-9223372036854775808 到 9223372036854775807)。
- 浮点类型:float(4 字节,单精度浮点数)、double(8 字节,双精度浮点数)。
- 字符类型:char(2 字节,用于表示单个字符,采用 Unicode 编码)。
- 布尔类型:boolean(只有两个值,true 和 false)。
- Java 中有 8 种基本数据类型,可分为 4 类:
- HashMap 是如何解决哈希冲突的?
- HashMap 采用链地址法(拉链法)解决哈希冲突。当多个元素通过哈希函数计算得到相同的数组索引位置时,会在该位置形成一个链表。JDK 8 及以后,当链表长度达到 8 且数组长度达到 64 时,链表会转换为红黑树,以提高查找效率。当红黑树节点数小于 6 时,又会转换回链表。
- ArrayList 是线程安全的吗?
- ArrayList 不是线程安全的。因为它的方法没有进行同步处理,在多线程环境下,如果多个线程同时对 ArrayList 进行读写操作,可能会导致数据不一致、数组越界等问题。如果需要线程安全的列表,可以使用 Vector 或 CopyOnWriteArrayList。
- 创建线程有几种方式?
- 有三种方式:
- 继承 Thread 类:创建一个类继承 Thread 类,重写 run 方法,然后创建该类的实例并调用 start 方法启动线程。
class MyThread extends Thread { @Override public void run() { System.out.println(\"Thread is running\"); }}public class Main { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); }}
- 实现 Runnable 接口:创建一个类实现 Runnable 接口,实现 run 方法,然后将该类的实例作为参数传递给 Thread 类的构造函数,再调用 start 方法启动线程。
class MyRunnable implements Runnable { @Override public void run() { System.out.println(\"Runnable is running\"); }}public class Main { public static void main(String[] args) { MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start(); }}
- 实现 Callable 接口:创建一个类实现 Callable 接口,实现 call 方法,该方法有返回值。使用 FutureTask 包装 Callable 实例,再将 FutureTask 作为参数传递给 Thread 类的构造函数,调用 start 方法启动线程,最后可以通过 FutureTask 的 get 方法获取返回值。
import java.util.concurrent.*;class MyCallable implements Callable { @Override public Integer call() throws Exception { return 1 + 2; }}public class Main { public static void main(String[] args) throws ExecutionException, InterruptedException { MyCallable callable = new MyCallable(); FutureTask futureTask = new FutureTask(callable); Thread thread = new Thread(futureTask); thread.start(); Integer result = futureTask.get(); System.out.println(\"Result: \" + result); }}
- 有三种方式:
- 什么是线程池,为什么要使用线程池?
- 线程池是一种线程使用模式,它管理着一组线程,负责线程的创建、分配和销毁。使用线程池的原因如下:
- 减少线程创建和销毁的开销:线程的创建和销毁需要消耗系统资源,使用线程池可以复用已创建的线程,避免频繁创建和销毁线程带来的性能损耗。
- 提高响应速度:当有任务提交时,线程池中如果有空闲线程,可以立即执行任务,无需等待线程的创建。
- 控制线程数量:可以根据系统的资源和需求,设置线程池的大小,避免创建过多线程导致系统资源耗尽。
- 提供线程管理功能:线程池可以对线程进行统一的管理,如监控线程的状态、设置线程的优先级等。
- 线程池是一种线程使用模式,它管理着一组线程,负责线程的创建、分配和销毁。使用线程池的原因如下:
- JUC 包中常用的类有哪些,比如用于线程同步的?
- JUC(java.util.concurrent)包提供了很多用于并发编程的类和接口。用于线程同步的常用类有:
- ReentrantLock:可重入锁,与 synchronized 类似,但比 synchronized 更灵活。它可以实现公平锁和非公平锁,还可以通过 lock 和 unlock 方法手动控制锁的获取和释放。
import java.util.concurrent.locks.ReentrantLock;class MyTask implements Runnable { private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { lock.lock(); try { // 临界区代码 System.out.println(\"Thread is in critical section\"); } finally { lock.unlock(); } }}
- CountDownLatch:用于控制线程的执行顺序。它可以让一个或多个线程等待其他线程完成操作后再继续执行。
import java.util.concurrent.CountDownLatch;public class Main { public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(3); for (int i = 0; i { System.out.println(\"Thread is working\"); latch.countDown(); }).start(); } latch.await(); System.out.println(\"All threads have finished\"); }}
- JUC(java.util.concurrent)包提供了很多用于并发编程的类和接口。用于线程同步的常用类有:
- Spring 框架的核心特性有哪些?
- Spring 框架的核心特性主要有 IoC(控制反转)和 AOP(面向切面编程):
- IoC(控制反转):也称为依赖注入(DI),是指将对象的创建和管理交给 Spring 容器,而不是由对象本身负责。通过 IoC,对象之间的依赖关系由容器来维护,降低了代码的耦合度。例如,在 Spring 中可以通过 XML 配置文件、注解等方式来定义和管理对象。
public class UserService { private UserDao userDao; public UserService(UserDao userDao) { this.userDao = userDao; } // 其他方法}
- AOP(面向切面编程):允许在不修改原有代码的基础上,对程序的功能进行增强。它将一些通用的功能(如日志记录、事务管理等)封装成切面,在程序运行时动态地将这些切面织入到目标对象的方法中。Spring AOP 支持基于代理的 AOP 实现,使用 JDK 动态代理或 CGLIB 代理。
- Spring 框架的核心特性主要有 IoC(控制反转)和 AOP(面向切面编程):
- Spring Boot 相比 Spring 有什么优势?
- Spring Boot 相比 Spring 具有以下优势:
- 简化配置:Spring Boot 提供了大量的自动配置,减少了繁琐的 XML 配置文件,开发者只需要添加少量的配置或使用默认配置即可快速搭建项目。
- 快速搭建项目:Spring Boot 提供了 Spring Initializr 工具,可以通过网页或命令行快速生成项目骨架,包含所需的依赖和基本配置。
- 嵌入式服务器:Spring Boot 内置了 Tomcat、Jetty 等嵌入式服务器,无需额外配置和部署服务器,直接运行项目即可。
- 依赖管理:Spring Boot 的 Starter 依赖简化了依赖管理,开发者只需要添加相应的 Starter 依赖,Spring Boot 会自动处理相关的依赖版本和冲突。
- 监控和管理:Spring Boot Actuator 提供了对应用程序的监控和管理功能,如健康检查、性能指标监控等。
- Spring Boot 相比 Spring 具有以下优势:
- MyBatis 中 #{} 和 ${} 的区别是什么?
- #{} 是预编译处理,${} 是字符串替换。
- #{}:在 SQL 语句中使用 #{} 时,MyBatis 会将其替换为占位符(?),然后使用 PreparedStatement 进行预编译。这样可以防止 SQL 注入攻击,因为参数会经过类型检查和转义处理。例如:
SELECT * FROM users WHERE id = #{id}
- ${}:使用 ${} 时,MyBatis 会直接将其替换为传入的参数值,不会进行预编译。这种方式可能会导致 SQL 注入攻击,通常用于需要动态传入表名、列名等情况。例如:
SELECT * FROM users ORDER BY ${column}
- Dubbo 是做什么的,它的核心组件有哪些?
- Dubbo 是一个高性能、轻量级的分布式服务框架,用于解决分布式系统中服务之间的调用和管理问题。它提供了服务注册与发现、远程调用、集群容错、负载均衡等功能。
- Dubbo 的核心组件有:
- 服务提供者(Provider):暴露服务的一方,将服务注册到注册中心。
- 服务消费者(Consumer):调用服务的一方,从注册中心获取服务提供者的地址,然后远程调用服务。
- 注册中心(Registry):负责服务的注册和发现,服务提供者将服务信息注册到注册中心,服务消费者从注册中心订阅服务。常用的注册中心有 Zookeeper、Redis 等。
- 监控中心(Monitor):统计服务的调用次数、调用时间等信息,为服务的性能优化提供依据。
- 调用协议(Protocol):定义了服务之间的通信协议,如 Dubbo 协议、HTTP 协议等。
- RabbitMq 的工作模式有哪些?
- RabbitMQ 有以下几种常见的工作模式:
- 简单模式(Simple Mode):一个生产者对应一个消费者,生产者将消息发送到队列,消费者从队列中获取消息。
- 工作队列模式(Work Queues):一个生产者对应多个消费者,多个消费者竞争消费队列中的消息,用于实现任务的负载均衡。
- 发布/订阅模式(Publish/Subscribe):生产者将消息发送到交换机,交换机将消息广播到所有绑定的队列,每个绑定的队列都有一个消费者接收消息。
- 路由模式(Routing):生产者将消息发送到交换机,交换机根据消息的路由键将消息路由到指定的队列,消费者从相应的队列中获取消息。
- 主题模式(Topics):与路由模式类似,但路由键可以使用通配符,更灵活地进行消息的匹配和路由。
- RabbitMQ 有以下几种常见的工作模式:
- xxl - job 和 Redis 你了解多少?
- xxl - job:是一个分布式任务调度平台,具有以下特点:
- 简单易用:提供了可视化的任务管理界面,方便任务的配置和管理。
- 分布式支持:支持分布式部署,多个执行器可以同时处理任务。
- 弹性扩容:可以根据任务的负载情况动态增加或减少执行器的数量。
- 故障转移:当某个执行器出现故障时,任务可以自动转移到其他执行器上执行。
- Redis:是一个开源的高性能键值对数据库,具有以下特点:
- 数据类型丰富:支持字符串、哈希、列表、集合、有序集合等多种数据类型。
- 高速读写:数据存储在内存中,读写速度非常快。
- 持久化:支持 RDB 和 AOF 两种持久化方式,保证数据的安全性。
- 分布式支持:可以通过集群和主从复制实现分布式部署。
- 应用场景广泛:常用于缓存、消息队列、分布式锁、计数器等场景。
- xxl - job:是一个分布式任务调度平台,具有以下特点: