互联网大厂面试:Java 核心、JUC、JVM 等知识大考验
在互联网大厂的一间明亮的面试室内,严肃的面试官坐在桌子的一侧,而求职者王铁牛则略显紧张地坐在另一侧。一场关于 Java 技术知识的面试即将拉开帷幕。
第一轮提问 面试官:首先问你几个 Java 核心知识的问题。Java 中基本数据类型有哪些? 王铁牛:这个我知道,Java 基本数据类型有 byte、short、int、long、float、double、char、boolean。 面试官:不错,回答得很准确。那 String 是基本数据类型吗? 王铁牛:不是,String 是引用数据类型,它是一个类。 面试官:很好。那 Java 中重载和重写的区别是什么? 王铁牛:重载是指在同一个类中,方法名相同但参数列表不同。重写是子类对父类中允许访问的方法进行重新实现,方法名、参数列表和返回值类型都要相同。 面试官:非常棒,看来你对 Java 核心知识掌握得很扎实。
第二轮提问 面试官:接下来聊聊 JUC、多线程和线程池相关的内容。JUC 包是什么? 王铁牛:JUC 就是 java.util.concurrent 包,它提供了一些用于并发编程的工具类。 面试官:那多线程有什么好处呢? 王铁牛:多线程可以提高程序的执行效率,充分利用 CPU 的多核资源,还能提高用户体验,比如在界面程序中可以避免界面卡顿。 面试官:线程池有哪些常见的创建方式? 王铁牛:这个……好像有通过 Executors 工具类创建,还有手动通过 ThreadPoolExecutor 来创建。 面试官:回答得还行。那线程池的核心参数有哪些呢? 王铁牛:嗯……好像有最大线程数、核心线程数,其他的我有点记不清了。 面试官:这块你掌握得不够全面,后续要加强学习。
第三轮提问 面试官:现在来问你关于 Spring、Spring Boot、MyBatis、Dubbo、RabbitMq、xxl - job、Redis 的问题。Spring 的核心特性有哪些? 王铁牛:Spring 的核心特性有依赖注入和面向切面编程。 面试官:那 Spring Boot 相比 Spring 有什么优势呢? 王铁牛:Spring Boot 简化了 Spring 的配置,有自动配置功能,还能快速搭建项目。 面试官:MyBatis 中 #{} 和 ${} 的区别是什么? 王铁牛:这个……好像一个是预编译,一个不是,但具体我不太清楚。 面试官:Dubbo 是什么,有什么作用? 王铁牛:Dubbo 好像是一个分布式服务框架,能实现服务的调用,但具体原理我说不上来。 面试官:看来你对这些框架的了解还停留在表面,没有深入掌握。
面试结束后,面试官严肃地说:“王铁牛,今天的面试就到这里。你的表现有好的地方,比如对 Java 核心知识掌握得比较扎实,但在一些复杂的技术点上,像线程池的核心参数、MyBatis 和 Dubbo 的深入原理等方面回答得不够清晰。我们会综合评估你的表现,你回家等通知吧。”
问题答案
- Java 中基本数据类型有哪些?
Java 中的基本数据类型分为四大类八种:
- 整数类型:byte(1 字节)、short(2 字节)、int(4 字节)、long(8 字节)。
- 浮点类型:float(4 字节)、double(8 字节)。
- 字符类型:char(2 字节)。
- 布尔类型:boolean(理论上 1 位,但实际实现可能是 1 字节)。
- String 是基本数据类型吗? String 不是基本数据类型,而是引用数据类型。它是 Java 中的一个类,位于 java.lang 包下,用于表示字符串。基本数据类型是 Java 语言中最基础的数据类型,而引用数据类型是对对象的引用。
- 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");
}
}
- JUC 包是什么? JUC 即 java.util.concurrent 包,是 Java 5 引入的用于并发编程的工具包。它提供了很多实用的类和接口,如线程池相关的类(ThreadPoolExecutor、Executors)、并发集合(ConcurrentHashMap、CopyOnWriteArrayList)、锁机制(ReentrantLock、ReadWriteLock)、同步工具类(CountDownLatch、CyclicBarrier、Semaphore)等,方便开发者进行高效的并发编程。
- 多线程有什么好处呢?
- 提高程序执行效率:在多核 CPU 环境下,多线程可以让程序同时利用多个 CPU 核心,并行执行多个任务,从而减少程序的整体执行时间。
- 充分利用系统资源:当一个线程因为等待 I/O 操作(如读取文件、网络请求)而阻塞时,其他线程可以继续执行,避免 CPU 资源的闲置。
- 提高用户体验:在图形界面程序中,使用多线程可以避免主线程被耗时操作阻塞,保证界面的流畅性和响应性。
- 线程池有哪些常见的创建方式?
- 通过 Executors 工具类创建:
- newFixedThreadPool:创建一个固定大小的线程池,线程池中的线程数量固定,当有新任务提交时,如果线程池中有空闲线程则立即执行,否则将任务放入任务队列等待。
- newCachedThreadPool:创建一个可缓存的线程池,线程池中的线程数量根据需要动态调整。如果有新任务提交且线程池中有空闲线程则立即执行,否则创建新线程执行任务。当线程空闲时间超过 60 秒时,会被回收。
- newSingleThreadExecutor:创建一个单线程的线程池,线程池中只有一个线程,所有任务按照提交的顺序依次执行。
- newScheduledThreadPool:创建一个可定时执行任务的线程池,可用于执行定时任务或周期性任务。
- 手动通过 ThreadPoolExecutor 创建:通过 ThreadPoolExecutor 的构造函数可以更灵活地配置线程池的参数,如核心线程数、最大线程数、线程空闲时间、任务队列等。例如:
- 通过 Executors 工具类创建:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
60, TimeUnit.SECONDS, // 线程空闲时间
new LinkedBlockingQueue<>(10) // 任务队列
);
- 线程池的核心参数有哪些呢?
ThreadPoolExecutor 的核心参数有:
- corePoolSize:核心线程数,线程池在初始化时会创建的线程数量。当有新任务提交时,会优先使用核心线程执行任务。
- maximumPoolSize:最大线程数,线程池允许创建的最大线程数量。当任务队列已满且核心线程都在执行任务时,会创建新线程直到达到最大线程数。
- keepAliveTime:线程空闲时间,当线程空闲时间超过该值时,会被回收(核心线程默认不会被回收,可通过 allowCoreThreadTimeOut 方法设置为可回收)。
- unit:keepAliveTime 的时间单位,如 TimeUnit.SECONDS、TimeUnit.MILLISECONDS 等。
- workQueue:任务队列,用于存储提交的任务。常见的任务队列有 ArrayBlockingQueue(有界队列)、LinkedBlockingQueue(无界队列或有界队列)、SynchronousQueue(同步队列)等。
- threadFactory:线程工厂,用于创建线程。可以通过自定义线程工厂来设置线程的名称、优先级等属性。
- handler:任务拒绝策略,当任务队列已满且线程池中的线程数量达到最大线程数时,新提交的任务会被拒绝。常见的拒绝策略有 AbortPolicy(直接抛出异常)、CallerRunsPolicy(由提交任务的线程执行该任务)、DiscardPolicy(直接丢弃任务)、DiscardOldestPolicy(丢弃任务队列中最老的任务)。
- Spring 的核心特性有哪些?
Spring 的核心特性主要有:
- 依赖注入(Dependency Injection,DI):是一种设计模式,通过该模式可以将对象之间的依赖关系从代码中解耦。在 Spring 中,对象的依赖关系由 Spring 容器负责管理,通过配置文件或注解的方式将依赖对象注入到需要的对象中。例如,通过 @Autowired 注解可以实现自动注入。
- 面向切面编程(Aspect - Oriented Programming,AOP):允许开发者在不修改原有业务逻辑的基础上,对程序进行增强。AOP 将横切关注点(如日志记录、事务管理、权限验证等)从业务逻辑中分离出来,以切面的形式进行统一管理。在 Spring 中,通过 @Aspect 注解可以定义切面,通过 @Before、@After、@Around 等注解可以定义通知。
- Spring Boot 相比 Spring 有什么优势呢?
- 简化配置:Spring Boot 提供了自动配置功能,通过引入相应的 Starter 依赖,Spring Boot 会根据类路径下的依赖自动进行配置,减少了大量的 XML 配置文件或 Java 配置类。
- 快速搭建项目:Spring Boot 提供了 Spring Initializr 工具,可以快速生成项目骨架,开发者只需选择所需的依赖即可快速搭建项目。
- 嵌入式服务器:Spring Boot 内置了 Tomcat、Jetty 等嵌入式服务器,无需手动部署到外部服务器,直接运行主类即可启动项目。
- 生产级特性:Spring Boot 提供了一些生产级特性,如健康检查、监控、日志管理等,方便开发者在生产环境中对应用进行管理和维护。
- MyBatis 中 #{} 和 ${} 的区别是什么?
- #{}:是预编译处理,MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为占位符 ?,然后使用 PreparedStatement 进行参数绑定,这样可以防止 SQL 注入攻击。例如:
<select id="getUserById" parameterType="int" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
- **${}**:是字符串替换,MyBatis 在处理 ${} 时,会将 ${} 直接替换为传入的参数值。这种方式可能会导致 SQL 注入攻击,一般用于动态表名、列名等场景。例如:
<select id="getUserByColumnName" parameterType="map" resultType="User">
SELECT * FROM users WHERE ${columnName} = #{value}
</select>
- Dubbo 是什么,有什么作用?
Dubbo 是阿里巴巴开源的一个高性能、轻量级的分布式服务框架,用于实现分布式系统中的服务调用。其主要作用有:
- 服务注册与发现:Dubbo 提供了服务注册中心(如 ZooKeeper、Nacos 等),服务提供者将自己提供的服务注册到注册中心,服务消费者从注册中心获取服务提供者的地址信息,实现服务的发现和调用。
- 远程调用:Dubbo 支持多种远程调用协议(如 Dubbo 协议、HTTP 协议等),可以实现不同服务之间的远程调用,屏蔽了网络通信的细节。
- 负载均衡:Dubbo 提供了多种负载均衡策略(如随机、轮询、最少活跃调用数等),可以根据服务提供者的性能和负载情况,将请求均衡地分发到不同的服务提供者上。
- 服务治理:Dubbo 提供了服务监控、服务降级、服务熔断等服务治理功能,提高了分布式系统的可靠性和稳定性。