互联网大厂面试:Java 核心、并发、框架与中间件知识大考验
在互联网大厂的一间明亮的面试室内,严肃的面试官正襟危坐,对面坐着略显紧张的求职者王铁牛。一场关于 Java 核心知识的面试即将拉开帷幕。
第一轮面试 面试官:首先,我们来聊聊 Java 核心知识。Java 中基本数据类型有哪些? 王铁牛:Java 的基本数据类型有 byte、short、int、long、float、double、char、boolean。 面试官:回答得不错。那你说说 Java 中自动装箱和拆箱是怎么回事? 王铁牛:自动装箱就是把基本数据类型转换为对应的包装类类型,比如把 int 转换成 Integer;拆箱就是反过来,把包装类类型转换成基本数据类型。 面试官:很好。那在 Java 中,final 关键字有什么作用呢? 王铁牛:final 修饰类的时候,这个类不能被继承;修饰方法,方法不能被重写;修饰变量,变量就变成常量,不能再被赋值。 面试官:非常棒,你的基础很扎实。
第二轮面试 面试官:接下来我们谈谈 JUC(Java 并发工具包)相关的内容。说说 CountDownLatch 和 CyclicBarrier 的区别。 王铁牛:嗯……这个嘛,好像都是跟线程同步有关的,具体区别我有点不太清楚了。 面试官:那再问你,Executors 提供了几种创建线程池的方法? 王铁牛:好像有好几种,什么固定大小线程池、单线程线程池,具体几种我记不太准了。 面试官:在 JUC 中,ReentrantLock 和 synchronized 有什么不同呢? 王铁牛:它们都是用来实现线程同步的,但是具体不同点我表达得可能不太清晰。ReentrantLock 好像更灵活点。
第三轮面试 面试官:现在聊聊框架相关的。Spring 中的 IoC(控制反转)和 DI(依赖注入)是什么关系? 王铁牛:IoC 就是把对象的创建和管理交给 Spring 容器,DI 是具体的实现方式,但是具体怎么联系的我说不好。 面试官:Spring Boot 相对于 Spring 有什么优势呢? 王铁牛:Spring Boot 好像简化了配置,能快速搭建项目,具体优势我讲不详细。 面试官:MyBatis 中 #{} 和 {} 的区别是什么? **王铁牛**:我知道 #{} 是预编译处理,{} 是字符串替换,但是在业务场景中的区别我说不出来。
面试结束,面试官放下手中的笔,说道:“今天的面试就到这里,你对于一些基础的 Java 核心知识掌握得还不错,但在 JUC、框架等一些复杂知识点上,回答得不够清晰和深入。你先回家等通知吧,后续我们会综合评估后给你答复。”
问题答案
-
Java 中基本数据类型有哪些? Java 的基本数据类型分为四类八种:
- 整数类型:byte(1 字节)、short(2 字节)、int(4 字节)、long(8 字节)。
- 浮点类型:float(4 字节)、double(8 字节)。
- 字符类型:char(2 字节)。
- 布尔类型:boolean(理论上 1 位,但 JVM 实现通常为 1 字节)。
-
Java 中自动装箱和拆箱是怎么回事? 自动装箱是 Java 编译器在基本数据类型和对应的包装类之间做的一个自动转换。例如,把 int 类型自动转换为 Integer 类型,编译器会自动调用 Integer 的 valueOf() 方法。
Integer i = 10; // 自动装箱,相当于 Integer i = Integer.valueOf(10);
自动拆箱则是把包装类对象转换为基本数据类型,编译器会自动调用对应的 xxxValue() 方法。
Integer i = 10;
int j = i; // 自动拆箱,相当于 int j = i.intValue();
-
在 Java 中,final 关键字有什么作用呢?
- 修饰类:当一个类被 final 修饰时,这个类不能被继承,即不能有子类。例如,
String类就是被 final 修饰的,所以不能有类继承自String类。 - 修饰方法:被 final 修饰的方法不能被子类重写。这样可以防止子类修改该方法的实现。
- 修饰变量:如果是基本数据类型的变量,被 final 修饰后,其值不能再被改变,成为常量;如果是引用类型的变量,被 final 修饰后,该变量不能再指向其他对象,但对象本身的属性可以改变。
- 修饰类:当一个类被 final 修饰时,这个类不能被继承,即不能有子类。例如,
-
说说 CountDownLatch 和 CyclicBarrier 的区别。
- CountDownLatch:是一个计数器,它的构造函数接收一个整数作为计数器的初始值。线程可以调用
countDown()方法将计数器减 1,当计数器的值变为 0 时,在await()方法上等待的线程会被唤醒继续执行。它主要用于一个或多个线程等待其他线程完成操作后再继续执行,计数器不能重置。 - CyclicBarrier:是一个栅栏,它的构造函数接收一个整数表示需要等待的线程数量。当调用
await()方法的线程数量达到指定值时,所有等待的线程会同时被释放继续执行。而且它可以重复使用,当一轮线程通过栅栏后,栅栏可以重置等待下一轮线程。
- CountDownLatch:是一个计数器,它的构造函数接收一个整数作为计数器的初始值。线程可以调用
-
Executors 提供了几种创建线程池的方法?
Executors是一个线程池的工厂类,它提供了以下几种创建线程池的方法:- newFixedThreadPool(int nThreads):创建一个固定大小的线程池,线程池中的线程数量始终保持为 nThreads。
- newSingleThreadExecutor():创建一个单线程的线程池,这个线程池只有一个线程在工作,相当于单线程串行执行所有任务。
- newCachedThreadPool():创建一个可缓存的线程池,如果线程池中的线程数量超过了处理任务所需的数量,空闲线程会在一定时间后被回收;当有新任务提交时,如果有空闲线程则复用,没有则创建新线程。
- newScheduledThreadPool(int corePoolSize):创建一个可以执行定时任务和周期性任务的线程池。
-
在 JUC 中,ReentrantLock 和 synchronized 有什么不同呢?
- 语法层面:
synchronized是 Java 中的关键字,是内置的语言实现;ReentrantLock是一个类,需要手动创建对象来使用。 - 锁的获取和释放:
synchronized是自动获取和释放锁,当代码块或方法执行完毕,锁会自动释放;ReentrantLock需要手动调用lock()方法获取锁,调用unlock()方法释放锁,通常需要在finally块中释放锁以确保锁一定会被释放。 - 锁的特性:
ReentrantLock比synchronized更灵活,它支持可中断锁、公平锁等特性。例如,ReentrantLock可以通过构造函数指定是否为公平锁,而synchronized是非公平锁。
- 语法层面:
-
Spring 中的 IoC(控制反转)和 DI(依赖注入)是什么关系? IoC(控制反转)是一种设计思想,它将对象的创建和管理的控制权从程序代码中转移到了外部容器(如 Spring 容器)。而 DI(依赖注入)是 IoC 的一种具体实现方式,通过 DI 可以将对象所依赖的其他对象自动注入到对象中。例如,一个类 A 依赖于类 B,在传统的方式中,类 A 自己创建类 B 的对象;而在 Spring 中,通过 DI,Spring 容器会自动创建类 B 的对象并注入到类 A 中。
-
Spring Boot 相对于 Spring 有什么优势呢?
- 简化配置:Spring Boot 提供了大量的自动配置,减少了繁琐的 XML 配置文件,通过注解和约定大于配置的原则,让开发者可以快速搭建项目。
- 快速开发:Spring Boot 内置了嵌入式服务器(如 Tomcat、Jetty 等),可以直接运行项目,无需额外的服务器配置和部署。
- 依赖管理:Spring Boot 的 Starter 依赖简化了依赖管理,开发者只需要引入对应的 Starter 依赖,Spring Boot 会自动管理相关的依赖版本。
- 监控和管理:Spring Boot Actuator 提供了丰富的监控和管理功能,如健康检查、性能指标监控等。
-
MyBatis 中 #{} 和 ${} 的区别是什么?
- #{}:是预编译处理,MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为
?占位符,然后使用PreparedStatement进行查询,这样可以防止 SQL 注入攻击。例如:
- #{}:是预编译处理,MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为
<select id="getUserById" parameterType="int" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
- **${}**:是字符串替换,MyBatis 在处理 ${} 时,会直接将 ${} 替换为传入的参数值。例如:
<select id="getUserByColumnName" parameterType="String" resultType="User">
SELECT * FROM users WHERE ${columnName} = 'value'
</select>
由于 ${} 是直接进行字符串替换,可能会导致 SQL 注入问题,所以在使用时要谨慎,通常用于动态表名、列名等场景。