互联网大厂 Java 面试:核心知识、框架与中间件的全方位考察
在互联网大厂宽敞明亮的面试室里,严肃的面试官正襟危坐,对面坐着略显紧张的求职者王铁牛。一场对 Java 核心知识、JUC、JVM、多线程等众多技术领域的考察即将展开。
第一轮提问 面试官:首先问你几个 Java 基础核心知识的问题。Java 中基本数据类型有哪些? 王铁牛:有 byte、short、int、long、float、double、char、boolean。 面试官:回答得不错。那说说 Java 中重载和重写的区别。 王铁牛:重载是在一个类中,方法名相同但参数列表不同;重写是子类对父类方法的重新实现,方法名、参数列表和返回值类型都要一样。 面试官:很好,理解得很清晰。那 final、finally 和 finalize 的区别是什么? 王铁牛:final 可以修饰类、方法和变量,修饰类时类不能被继承,修饰方法时方法不能被重写,修饰变量时变量成为常量;finally 是在异常处理时配合 try-catch 使用,里面的代码无论是否发生异常都会执行;finalize 是 Object 类的一个方法,在对象被垃圾回收之前会调用这个方法。 面试官:非常棒,基础很扎实。
第二轮提问 面试官:接下来聊聊 JUC 和多线程相关的。什么是线程安全? 王铁牛:线程安全就是在多线程环境下,对共享资源的访问不会出现数据不一致或者其他错误的情况。 面试官:不错。那说说 ThreadLocal 的作用和使用场景。 王铁牛:ThreadLocal 可以为每个使用它的线程都提供一个独立的变量副本,每个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。使用场景比如在多线程环境下,每个线程需要有自己独立的数据库连接,就可以用 ThreadLocal 来实现。 面试官:有一定理解。那线程池的核心参数有哪些,分别有什么作用? 王铁牛:呃……有……好像有核心线程数,还有……最大线程数,其他的我有点记不清了。 面试官:这里你回答得不够完整,线程池核心参数还有阻塞队列、线程存活时间、时间单位、任务拒绝策略等,你后续需要再加强这方面的学习。
第三轮提问 面试官:现在考察下框架和中间件的知识。Spring 的核心特性有哪些? 王铁牛:Spring 的核心特性有 IoC(控制反转)和 AOP(面向切面编程)。 面试官:对的。那 Spring Boot 是如何实现自动配置的? 王铁牛:这个……好像是通过一些配置文件,具体的我不太清楚了。 面试官:那 MyBatis 中 #{} 和 ${} 的区别是什么? 王铁牛:我只知道它们都可以用来传参数,具体区别我也说不太好。 面试官:看来你对框架这块的知识掌握得不够深入。后续要好好去研究下。今天的面试就到这里,你回家等通知吧。
问题答案
- Java 中基本数据类型有哪些:
- Java 中有 8 种基本数据类型,分为 4 类:
- 整数类型:byte(1 字节,范围 -128 到 127)、short(2 字节)、int(4 字节)、long(8 字节)。
- 浮点类型:float(4 字节)、double(8 字节)。
- 字符类型:char(2 字节,用于存储单个字符)。
- 布尔类型:boolean(只有 true 和 false 两个值)。
- Java 中有 8 种基本数据类型,分为 4 类:
- Java 中重载和重写的区别:
- 重载(Overloading):发生在同一个类中,方法名相同但参数列表不同(参数的类型、个数、顺序不同)。重载与返回值类型无关,主要是为了提供更多的调用方式,方便使用。例如:
public class OverloadExample {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
- 重写(Overriding):发生在子类和父类之间,子类重写父类的方法。方法名、参数列表和返回值类型必须相同(Java 5 开始返回值类型可以是父类方法返回值类型的子类,称为协变返回类型)。重写主要是为了实现多态,子类可以根据自身的需求对父类的方法进行重新实现。例如:
class Parent {
public void print() {
System.out.println("Parent");
}
}
class Child extends Parent {
@Override
public void print() {
System.out.println("Child");
}
}
- final、finally 和 finalize 的区别:
- final:是一个关键字,可以修饰类、方法和变量。
- 修饰类时,该类不能被继承,例如
final class FinalClass {}。 - 修饰方法时,该方法不能被重写,例如
public final void finalMethod() {}。 - 修饰变量时,该变量成为常量,一旦赋值就不能再修改,例如
final int NUM = 10;。
- 修饰类时,该类不能被继承,例如
- finally:是异常处理机制中的一部分,通常与 try-catch 块一起使用。无论 try 块中是否发生异常,finally 块中的代码都会执行,一般用于释放资源,例如:
- final:是一个关键字,可以修饰类、方法和变量。
try {
// 可能抛出异常的代码
} catch (Exception e) {
// 异常处理代码
} finally {
// 无论是否发生异常都会执行的代码
}
- finalize:是 Object 类的一个方法,在对象被垃圾回收之前,垃圾回收器会调用该对象的 finalize 方法。该方法可以用来在对象被销毁前做一些清理工作,但不建议依赖它来释放资源,因为垃圾回收的时间是不确定的。
4. 什么是线程安全:
- 线程安全是指在多线程环境下,对共享资源的访问不会出现数据不一致或者其他错误的情况。当多个线程同时访问共享资源时,如果没有进行适当的同步控制,可能会导致数据竞争、脏读、不可重复读等问题。例如,多个线程同时对一个计数器进行自增操作,如果没有进行同步,可能会出现计数不准确的情况。为了保证线程安全,可以使用同步机制,如 synchronized 关键字、Lock 接口等。
5. ThreadLocal 的作用和使用场景:
- 作用:ThreadLocal 可以为每个使用它的线程都提供一个独立的变量副本,每个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。它通过一个 ThreadLocalMap 来存储每个线程的变量副本,ThreadLocalMap 的键是 ThreadLocal 对象,值是线程的变量副本。
- 使用场景:
- 数据库连接管理:在多线程环境下,每个线程需要有自己独立的数据库连接,避免多个线程共享一个连接导致数据混乱。可以使用 ThreadLocal 来为每个线程管理自己的数据库连接。
- 会话管理:在 Web 应用中,每个用户的会话信息可以存储在 ThreadLocal 中,方便在同一个线程的不同方法中获取和使用会话信息。
6. 线程池的核心参数有哪些,分别有什么作用:
- 线程池的核心参数主要有以下几个:
- corePoolSize(核心线程数):线程池的基本大小,当提交的任务数小于 corePoolSize 时,线程池会创建新的线程来执行任务。
- maximumPoolSize(最大线程数):线程池允许创建的最大线程数。当阻塞队列满了,且提交的任务数大于 corePoolSize 时,线程池会创建新的线程,直到线程数达到 maximumPoolSize。
- keepAliveTime(线程存活时间):当线程池中的线程数量超过 corePoolSize 时,多余的空闲线程在等待新任务的最长时间,超过这个时间线程会被销毁。
- unit(时间单位):keepAliveTime 的时间单位,如 TimeUnit.SECONDS、TimeUnit.MILLISECONDS 等。
- workQueue(阻塞队列):用于存储等待执行的任务的队列。常见的阻塞队列有 ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue 等。
- threadFactory(线程工厂):用于创建线程的工厂,可以自定义线程的名称、优先级等属性。
- handler(任务拒绝策略):当线程池的线程数达到 maximumPoolSize 且阻塞队列已满时,新提交的任务会被拒绝,此时会调用任务拒绝策略来处理这些任务。常见的任务拒绝策略有 AbortPolicy(直接抛出异常)、CallerRunsPolicy(由调用线程来执行任务)、DiscardPolicy(直接丢弃任务)、DiscardOldestPolicy(丢弃队列中最老的任务)。
7. Spring 的核心特性有哪些:
- Spring 的核心特性主要有 IoC(控制反转)和 AOP(面向切面编程)。
- IoC(控制反转):也称为依赖注入(DI),是一种设计模式,它将对象的创建和依赖关系的管理从代码中转移到 Spring 容器中。通过 Spring 容器来创建和管理对象,对象之间的依赖关系由 Spring 容器自动注入,降低了对象之间的耦合度。例如,在 Spring 中可以通过 XML 配置文件、注解等方式来定义对象和它们之间的依赖关系。
- AOP(面向切面编程):是一种编程范式,它允许开发者在不修改原有代码的基础上,对程序的某些功能进行增强。AOP 通过将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,以切面的形式进行统一管理。在 Spring 中,可以使用注解或 XML 配置来定义切面、切点和通知,实现 AOP 功能。
8. Spring Boot 是如何实现自动配置的:
- Spring Boot 的自动配置是基于 Spring 的条件注解和类路径扫描机制实现的。主要步骤如下:
- 启动类注解:Spring Boot 应用的启动类上通常会有 @SpringBootApplication 注解,它是一个组合注解,包含了 @EnableAutoConfiguration 注解,这个注解开启了自动配置功能。
- 自动配置类:Spring Boot 在 spring-boot-autoconfigure 模块中提供了大量的自动配置类,这些类位于 org.springframework.boot.autoconfigure 包下。每个自动配置类都有相应的条件注解,如 @ConditionalOnClass、@ConditionalOnMissingBean 等,用于判断是否满足自动配置的条件。
- 类路径扫描:Spring Boot 在启动时会扫描类路径下的依赖,根据依赖的存在情况和条件注解来决定是否启用相应的自动配置类。例如,如果类路径下存在 Tomcat 的依赖,Spring Boot 会自动配置 Tomcat 作为嵌入式服务器。
- 属性配置:用户可以通过 application.properties 或 application.yml 文件来配置自动配置的参数,覆盖默认的配置。
9. **MyBatis 中 #{} 和 {} 的区别是什么**:
- `#{}` 和 `{}都是 MyBatis 中用于传参的占位符,但它们有以下区别: - 语法和处理方式: -#{}是预编译处理,MyBatis 会将#{}替换为占位符?`,然后使用 PreparedStatement 来执行 SQL 语句,这样可以防止 SQL 注入攻击。例如:
<select id="getUserById" parameterType="int" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
- `${}` 是字符串替换,MyBatis 会直接将 `${}` 替换为传入的参数值,不进行预编译处理。例如:
<select id="getUserByTableName" parameterType="String" resultType="User">
SELECT * FROM ${tableName}
</select>
- 使用场景:
- `#{}` 适用于大部分的参数传递场景,特别是需要防止 SQL 注入的情况。
- `${}` 主要用于动态传入表名、列名等情况,但使用时需要注意 SQL 注入的风险,因为它会直接将参数值拼接到 SQL 语句中。