互联网大厂 Java 面试:核心知识、框架与组件大考验
在互联网大厂的一间安静的面试室内,严肃的面试官坐在桌前,对面坐着紧张的求职者王铁牛。面试开始,一场对 Java 知识的全面考察拉开帷幕。
第一轮提问 面试官:首先问你几个基础的 Java 核心知识问题。Java 中基本数据类型有哪些? 王铁牛:Java 的基本数据类型有 byte、short、int、long、float、double、char、boolean。 面试官:不错,回答得很准确。那 String 类为什么是不可变的? 王铁牛:因为 String 类被 final 修饰,它的成员变量也是 final 的,一旦创建,其值不能被改变。 面试官:很好,看来基础很扎实。那在多线程环境下,使用 String 进行拼接操作,用什么比较好? 王铁牛:用 StringBuilder 效率高,但是它是非线程安全的,在多线程环境下要用 StringBuffer,它是线程安全的。 面试官:非常棒,基础掌握得很牢固。
第二轮提问 面试官:接下来聊聊 JUC 和多线程相关的。JUC 包是什么,有什么作用? 王铁牛:JUC 是 java.util.concurrent 包,它提供了一些在并发编程中常用的工具类,能帮助我们更方便地进行多线程编程。 面试官:那说一下 CountDownLatch 和 CyclicBarrier 的区别。 王铁牛:嗯……它们好像都是用来协调线程的,具体区别……我有点记不清了。 面试官:再问你,线程池有哪些创建方式,各自有什么特点? 王铁牛:可以通过 Executors 工具类创建,像 newFixedThreadPool 能创建固定大小的线程池,newCachedThreadPool 能根据需要创建线程。不过具体特点我一下子说不全。
第三轮提问 面试官:现在谈谈框架相关的。Spring 框架的核心特性有哪些? 王铁牛:Spring 的核心特性有 IoC(控制反转)和 AOP(面向切面编程)。IoC 是把对象的创建和管理交给 Spring 容器,AOP 是在不修改原有代码的基础上增加额外功能。 面试官:Spring Boot 是如何实现自动配置的? 王铁牛:这个……好像是通过一些配置文件和注解,具体我不太清楚了。 面试官:MyBatis 中 #{} 和 {} 的区别是什么? **王铁牛**:好像 #{} 是预编译的,能防止 SQL 注入,{} 是直接替换,可能有 SQL 注入风险,但具体原理我讲不明白。
面试官总结:王铁牛,通过这次面试,我能看出你对 Java 的一些基础知识掌握得还不错,像 Java 基本数据类型、String 类的特性以及多线程环境下字符串拼接的选择等问题都回答得很好,说明你有一定的知识储备。但在一些稍微复杂的知识点上,比如 JUC 中 CountDownLatch 和 CyclicBarrier 的区别、线程池创建方式的详细特点,以及框架相关的 Spring Boot 自动配置原理、MyBatis 中 #{} 和 ${} 的具体原理等问题,回答得不够清晰或者不太完整。这可能反映出你对这些知识的理解还不够深入,缺乏系统性的学习。我们公司对技术人员的要求比较高,希望能招到对各项知识都有深入理解和掌握的人才。你先回家等通知吧,后续如果有进一步的消息,我们会及时联系你。
问题答案
-
Java 中基本数据类型有哪些? Java 的基本数据类型分为四类八种:
- 整数类型:byte(1 字节)、short(2 字节)、int(4 字节)、long(8 字节)。
- 浮点类型:float(4 字节)、double(8 字节)。
- 字符类型:char(2 字节)。
- 布尔类型:boolean(理论上 1 位,但 JVM 实现通常按 1 字节处理)。
-
String 类为什么是不可变的? String 类被 final 修饰,这意味着它不能被继承。同时,String 类内部使用一个 final 的 char 数组来存储字符串内容,一旦这个数组被初始化,其引用不能再指向其他数组,而且没有提供修改数组内容的公共方法,所以 String 对象一旦创建,其值不能被改变。不可变的好处有:线程安全、可以作为 HashMap 的键、便于缓存哈希码等。
-
在多线程环境下,使用 String 进行拼接操作,用什么比较好? 在多线程环境下,使用 StringBuffer 比较好。String 是不可变的,每次进行拼接操作都会创建一个新的 String 对象,效率较低。StringBuilder 和 StringBuffer 都是可变的,但是 StringBuilder 是非线程安全的,而 StringBuffer 的方法都使用了 synchronized 关键字修饰,是线程安全的,所以在多线程环境下选择 StringBuffer。
-
JUC 包是什么,有什么作用? JUC 是 java.util.concurrent 包的简称,它是 Java 5 引入的一个用于并发编程的工具包。该包提供了一系列高级的并发工具类,如线程池(ExecutorService)、锁(Lock)、同步器(如 CountDownLatch、CyclicBarrier、Semaphore 等)、并发集合(如 ConcurrentHashMap、CopyOnWriteArrayList 等),能帮助开发者更方便、高效地进行多线程编程,提高程序的并发性能和可靠性。
-
CountDownLatch 和 CyclicBarrier 的区别
- 作用不同:CountDownLatch 是让一个或多个线程等待其他线程完成一组操作后再继续执行;CyclicBarrier 是让一组线程相互等待,直到所有线程都到达某个屏障点后再一起继续执行。
- 计数器是否可重用:CountDownLatch 的计数器只能使用一次,一旦计数器减到 0 就不能再使用;CyclicBarrier 的计数器可以重用,当所有线程到达屏障点后,计数器会重置,可以继续新一轮的等待。
- 使用场景不同:CountDownLatch 适用于一个线程等待多个线程完成任务的场景,如主线程等待多个子线程完成数据加载;CyclicBarrier 适用于多个线程之间相互等待,共同到达某个状态后再继续执行的场景,如多个运动员同时起跑。
-
线程池有哪些创建方式,各自有什么特点? 可以通过 Executors 工具类创建,也可以通过 ThreadPoolExecutor 直接创建。
- Executors 工具类创建方式:
- newFixedThreadPool:创建一个固定大小的线程池,线程池中的线程数量固定,当有新任务提交时,如果线程池中有空闲线程则立即执行,否则将任务放入队列等待。适用于需要控制并发线程数量的场景。
- newCachedThreadPool:创建一个可缓存的线程池,线程池中的线程数量会根据需要动态调整。如果有新任务提交,且线程池中有空闲线程则立即执行,否则创建新线程执行任务。当线程空闲时间超过 60 秒会被回收。适用于执行大量短期异步任务的场景。
- newSingleThreadExecutor:创建一个单线程的线程池,线程池只有一个线程,所有任务按顺序执行。适用于需要保证任务顺序执行的场景。
- newScheduledThreadPool:创建一个可定时执行任务的线程池,可用于定时执行任务或周期性执行任务。
- ThreadPoolExecutor 直接创建:可以自定义线程池的核心线程数、最大线程数、线程空闲时间、任务队列等参数,更加灵活,能满足不同的业务需求。
- Executors 工具类创建方式:
-
Spring 框架的核心特性有哪些?
- IoC(控制反转):也称为依赖注入(DI),是一种设计模式,它将对象的创建和管理交给 Spring 容器,而不是由对象本身负责创建和管理依赖对象。通过 IoC,降低了对象之间的耦合度,提高了代码的可维护性和可测试性。
- AOP(面向切面编程):是一种编程范式,它允许开发者在不修改原有代码的基础上,将横切关注点(如日志记录、事务管理、权限验证等)从业务逻辑中分离出来,以切面的形式进行统一管理。AOP 可以提高代码的复用性和可维护性。
-
Spring Boot 是如何实现自动配置的? Spring Boot 的自动配置主要基于以下几个关键技术:
- @EnableAutoConfiguration 注解:该注解是 Spring Boot 自动配置的入口,它会触发 Spring Boot 的自动配置机制。
- SpringFactoriesLoader:Spring Boot 在启动时会通过 SpringFactoriesLoader 加载 META - INF/spring.factories 文件中的自动配置类。这些自动配置类根据类路径下的依赖和配置属性,为应用程序提供默认的配置。
- 条件注解:如 @ConditionalOnClass、@ConditionalOnMissingBean 等,自动配置类会使用这些条件注解来判断是否应该应用该配置。例如,@ConditionalOnClass 表示只有当类路径下存在指定的类时才会应用该配置。
- 配置属性绑定:Spring Boot 可以将 application.properties 或 application.yml 中的配置属性绑定到自动配置类的属性上,从而实现定制化配置。
-
MyBatis 中 #{} 和 ${} 的区别是什么?
- 语法和处理方式:#{} 是预编译处理,MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为占位符?,然后使用 PreparedStatement 进行参数设置,能有效防止 SQL 注入;{} 时,会直接将 ${} 替换为实际的参数值,可能会导致 SQL 注入问题。
- 使用场景:#{} 适用于绝大多数的参数传递场景,如查询条件、插入值等;${} 通常用于动态表名、动态列名等场景,因为这些场景不能使用占位符。