互联网大厂面试:Java 核心、JUC、JVM 等知识大考验
在互联网大厂的一间安静面试室内,严肃的面试官坐在桌前,对面是略显紧张的求职者王铁牛。面试开始了。
第一轮提问 面试官:首先问几个 Java 核心知识的问题。Java 中基本数据类型有哪些? 王铁牛:Java 的基本数据类型有 byte、short、int、long、float、double、char、boolean。 面试官:回答得不错。那说说 Java 中重载和重写的区别。 王铁牛:重载是在一个类中,方法名相同但参数列表不同;重写是子类重写父类的方法,方法名、参数列表和返回值类型都要相同。 面试官:很好,看来基础很扎实。那 Java 中的异常处理机制是怎样的? 王铁牛:Java 中通过 try、catch、finally 来进行异常处理。try 块中放可能出现异常的代码,catch 块捕获并处理异常,finally 块不管是否发生异常都会执行。
第二轮提问 面试官:接下来聊聊 JUC 和多线程的问题。什么是 JUC? 王铁牛:JUC 是 java.util.concurrent 包,提供了很多并发编程的工具类。 面试官:不错。那说说线程的几种状态。 王铁牛:线程有新建、就绪、运行、阻塞、死亡这几种状态。 面试官:很好。那在多线程环境下,如何保证数据的一致性? 王铁牛:可以使用 synchronized 关键字或者 Lock 接口来实现同步,保证同一时间只有一个线程访问共享资源。 面试官:回答得很准确。那什么是线程池,为什么要使用线程池? 王铁牛:线程池就是预先创建好一定数量的线程,当有任务提交时,从线程池中获取线程来执行任务。使用线程池可以减少线程创建和销毁的开销,提高系统的性能。
第三轮提问 面试官:现在问一些框架相关的问题。说说 Spring 的核心特性有哪些? 王铁牛:Spring 的核心特性有依赖注入和面向切面编程。 面试官:还可以。那 Spring Boot 相对于 Spring 有什么优势? 王铁牛:Spring Boot 简化了 Spring 的配置,有自动配置功能,能快速搭建项目。 面试官:那 MyBatis 中 #{} 和 ${} 的区别是什么? 王铁牛:呃……这个我有点不太清楚,好像一个是预编译,一个不是,但具体我不太确定。 面试官:Dubbo 是什么,有什么作用? 王铁牛:Dubbo 是一个分布式服务框架,能实现服务的注册和发现,但是具体怎么实现我不太能说清楚。 面试官:RabbitMQ 有哪些应用场景? 王铁牛:好像可以用于消息队列,但是具体场景我不太知道。
面试接近尾声,面试官放下手中的记录,严肃地对王铁牛说:“今天的面试就到这里了,你在 Java 核心知识和一些基础的多线程方面回答得还不错,表现出了一定的知识储备。但在一些框架的细节和应用场景方面,回答得不够清晰和完整。我们需要综合评估一下,你先回家等通知吧。”
问题答案
- Java 中基本数据类型有哪些?
- Java 有 8 种基本数据类型,可分为 4 类:
- 整数类型:byte(1 字节,-128 到 127)、short(2 字节,-32768 到 32767)、int(4 字节,-2147483648 到 2147483647)、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):发生在子类和父类之间,子类重写父类的方法,方法名、参数列表和返回值类型必须相同(子类返回值类型可以是父类返回值类型的子类),访问修饰符不能比父类的更严格。例如:
class Parent {
public void print() {
System.out.println("Parent");
}
}
class Child extends Parent {
@Override
public void print() {
System.out.println("Child");
}
}
- Java 中的异常处理机制是怎样的?
- Java 通过 try、catch、finally 块来处理异常。
- try 块:包含可能抛出异常的代码。
- catch 块:用于捕获并处理 try 块中抛出的异常,可以有多个 catch 块,按顺序匹配异常类型。
- finally 块:无论 try 块中是否发生异常,finally 块中的代码都会执行,常用于释放资源等操作。 示例代码:
- Java 通过 try、catch、finally 块来处理异常。
try {
int result = 10 / 0; // 会抛出 ArithmeticException
} catch (ArithmeticException e) {
System.out.println("除数不能为 0: " + e.getMessage());
} finally {
System.out.println("finally 块执行");
}
- 什么是 JUC?
- JUC 即 java.util.concurrent 包,是 Java 提供的用于并发编程的工具包,包含了很多用于处理并发问题的类和接口,如线程池(ExecutorService)、锁(Lock)、并发集合(ConcurrentHashMap 等)、原子类(AtomicInteger 等)等,能帮助开发者更方便地进行多线程编程。
- 线程的几种状态
- 新建(New):线程对象被创建,但还未调用 start() 方法。
- 就绪(Runnable):线程调用了 start() 方法,等待获取 CPU 时间片。
- 运行(Running):线程获取了 CPU 时间片,正在执行。
- 阻塞(Blocked):线程因为某些原因放弃 CPU 时间片,暂时停止执行,如等待锁、等待 I/O 操作等。
- 死亡(Terminated):线程执行完毕或者因为异常退出。
- 在多线程环境下,如何保证数据的一致性?
- 可以使用以下几种方式:
- synchronized 关键字:可以修饰方法或代码块,保证同一时间只有一个线程能访问被修饰的方法或代码块。例如:
- 可以使用以下几种方式:
public class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
}
- Lock 接口:如 ReentrantLock,通过 lock() 和 unlock() 方法来实现同步。例如:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private int count = 0;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
- 原子类:如 AtomicInteger,使用 CAS(Compare-And-Swap)算法实现原子操作,保证数据的一致性。例如:
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
}
- 什么是线程池,为什么要使用线程池?
- 线程池是一种线程管理机制,预先创建一定数量的线程,当有任务提交时,从线程池中获取线程来执行任务。任务执行完毕后,线程不会销毁,而是返回线程池等待下一个任务。
- 使用线程池的好处:
- 减少线程创建和销毁的开销:频繁创建和销毁线程会消耗大量的系统资源,线程池可以复用线程,提高效率。
- 提高响应速度:任务提交后可以立即从线程池中获取线程执行,无需等待线程创建。
- 便于管理:可以控制线程的数量、执行顺序等,避免线程过多导致系统资源耗尽。
- Spring 的核心特性有哪些?
- 依赖注入(Dependency Injection,DI):对象之间的依赖关系由容器来负责注入,而不是在对象内部创建依赖对象。例如:
public class UserService {
private UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}
- 面向切面编程(Aspect-Oriented Programming,AOP):将与业务逻辑无关的通用功能(如日志记录、事务管理等)从业务逻辑中分离出来,以切面的形式进行统一管理。
9. Spring Boot 相对于 Spring 有什么优势? - 简化配置:Spring Boot 提供了自动配置功能,根据项目中引入的依赖自动配置 Spring 应用,减少了大量的 XML 配置文件。 - 快速搭建项目:通过 Spring Initializr 可以快速生成项目骨架,包含所需的依赖和基本配置。 - 嵌入式服务器:Spring Boot 内置了 Tomcat、Jetty 等服务器,无需额外配置服务器即可运行项目。 - 生产就绪:提供了一系列生产环境的特性,如监控、健康检查等。 10. MyBatis 中 #{} 和 ${} 的区别是什么? - #{} 是预编译处理,MyBatis 会将 #{} 替换为 ? 占位符,然后使用 PreparedStatement 进行 SQL 语句的执行,能防止 SQL 注入攻击。例如:
<select id="getUserById" parameterType="int" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
- ${} 是字符串替换,MyBatis 会将 ${} 直接替换为参数的值,可能会导致 SQL 注入攻击。例如:
<select id="getUserByColumnName" parameterType="String" resultType="User">
SELECT * FROM users WHERE ${columnName} = 'value'
</select>
- Dubbo 是什么,有什么作用?
- Dubbo 是阿里巴巴开源的分布式服务框架,用于实现服务的远程调用和服务治理。
- 作用:
- 服务注册与发现:通过注册中心(如 ZooKeeper),服务提供者将自己的服务信息注册到注册中心,服务消费者从注册中心获取服务提供者的地址信息,实现服务的自动发现。
- 远程调用:Dubbo 提供了多种远程调用协议(如 Dubbo 协议、HTTP 协议等),可以实现不同服务之间的远程调用。
- 负载均衡:Dubbo 提供了多种负载均衡策略(如随机、轮询等),可以将请求均匀地分发到多个服务提供者上。
- 服务治理:Dubbo 提供了服务监控、服务降级、服务熔断等功能,保证服务的高可用性和稳定性。
- RabbitMQ 有哪些应用场景?
- 异步处理:将一些耗时的操作(如发送邮件、生成报表等)放入消息队列中异步处理,提高系统的响应速度。例如,用户注册时,将发送注册邮件的任务放入 RabbitMQ 队列,由专门的消费者线程处理。
- 解耦系统:不同的系统之间通过消息队列进行通信,降低系统之间的耦合度。例如,订单系统和库存系统之间通过 RabbitMQ 进行消息传递,订单系统创建订单后发送消息到队列,库存系统从队列中获取消息并处理库存扣减。
- 流量削峰:在高并发场景下,将请求放入消息队列中,消费者按照自己的处理能力从队列中获取请求进行处理,避免系统因瞬间高流量而崩溃。例如,电商系统在促销活动时,将大量的订单请求放入 RabbitMQ 队列,消费者慢慢处理订单。
- 消息分发:可以实现一对多的消息分发,一个生产者发送的消息可以被多个消费者接收。例如,系统监控信息可以发送到 RabbitMQ 队列,多个监控系统从队列中获取信息进行处理。