1. CompletableFuture中thenApply()
与thenCompose()
的区别
考察点 :组合式异步编程
解析:
- **
thenApply()
**:接收前序任务结果,返回普通对象(同步转换),适用简单数据处理。 - **
thenCompose()
**:接收前序任务结果,返回新的CompletableFuture
(异步嵌套),用于链式调用异步任务。
示例:
java
CompletableFuture.supplyAsync(() -> "Hello")
.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World")); // 链式异步
2. Java模块化系统(JPMS)如何解决"自动模块"问题?
考察点 :模块化依赖管理
解析:
-
自动模块:非模块化JAR包会被视为自动模块,导出所有包并读取所有其他模块。
-
解决方案 :在
module-info.java
中显式声明依赖:java
requires transitive com.example.lib; // 传递依赖
3. 如何用GraalVM将Spring Boot应用编译为原生镜像?
考察点 :AOT编译与云原生优化
解析:
-
添加依赖 :
xml
<dependency> <groupId>org.springframework.experimental</groupId> <artifactId>spring-native</artifactId> </dependency>
-
编译命令 :
bash
mvn spring-boot:build-image
优势:启动时间<50ms,内存占用降低60%。
4. 记录类型(Records)能否实现Builder模式?
考察点 :不可变数据结构设计
解析:
-
限制 :Records默认生成final类,字段不可变,需通过静态内部类模拟Builder:
java
public record User(Long id, String name) { public static class Builder { private Long id; private String name; // Setter方法 public User build() { return new User(id, name); } } }
5. 模式匹配在instanceof
和switch
中的性能差异
考察点 :新特性底层优化
解析:
- **
instanceof
模式匹配**:编译器生成类型检查代码,性能与传统instanceof
+强制转型相当。 - **
switch
模式匹配**(Java 21预览):生成跳表优化,适合多分支场景,性能优于链式if-else
。
6. ZGC的"染色指针"如何实现并发标记?
考察点 :低延迟GC原理
解析:
- 指针元数据:在64位指针中存储标记位、重定位状态等,无需STW即可更新对象状态。
- 读屏障:在访问对象时动态修正指针,保证并发标记期间线程安全。
7. JFR(Java Flight Recorder)如何定位线程阻塞问题?
考察点 :性能分析工具
步骤:
-
录制JFR数据:
bash
jcmd <pid> JFR.start duration=60s filename=blocking.jfr
-
分析
jdk.JavaMonitorWait
事件,查看持有锁的线程栈。
8. 如何用JMH测试StringBuffer
与StringBuilder
的性能差异?
考察点 :基准测试实践
代码:
java
@BenchmarkMode(Mode.Throughput)
public class StringBenchmark {
@Benchmark
public void testStringBuffer() {
StringBuffer sb = new StringBuffer();
for (int i=0; i<1000; i++) sb.append(i);
}
// 类似实现StringBuilder测试
}
结论 :单线程下StringBuilder
快30%,多线程需考虑锁竞争。
9. 密封类(Sealed Classes)在领域驱动设计(DDD)中的应用
考察点 :领域模型限制
场景:定义核心领域对象,限制子类扩展:
java
public sealed interface PaymentMethod permits CreditCard, PayPal {}
public final class CreditCard implements PaymentMethod { /* 字段校验逻辑 */ }
优势:强制业务规则,避免模型污染。
10. 虚拟线程(Project Loom)与传统线程池的资源消耗对比
考察点 :轻量级并发模型
数据:
- 传统线程:1个线程 ≈ 1MB栈内存,万级线程消耗GB级内存。
- 虚拟线程 :1个虚拟线程 ≈ 1KB内存,支持百万级并发。
适用场景:IO密集型服务(如API网关、爬虫)。
11. 静态类初始化陷阱:静态代码块与构造函数的执行顺序
考察点 :类加载机制
示例:
java
class Parent {
static { System.out.println("Parent静态块"); }
Parent() { System.out.println("Parent构造器"); }
}
class Child extends Parent {
static { System.out.println("Child静态块"); }
Child() { System.out.println("Child构造器"); }
}
// 输出顺序:Parent静态块 → Child静态块 → Parent构造器 → Child构造器
12. Optional.orElse()
与orElseGet()
的性能差异
考察点 :延迟计算优化
解析:
- **
orElse()
**:无论Optional是否为空,都会执行参数表达式。 - **
orElseGet()
**:仅在Optional为空时执行Supplier
逻辑,适合高开销操作。
13. 方法句柄(MethodHandle)与反射的性能对比
考察点 :底层API优化
数据:
- 反射调用:每次调用检查访问权限,性能较差。
- 方法句柄 :JVM内联优化,性能接近直接方法调用(快5-10倍)。
示例:
java
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findVirtual(String.class, "length", MethodType.methodType(int.class));
int len = (int) mh.invokeExact("test"); // len=4
14. JNI调用的内存泄漏风险及解决方案
考察点 :本地方法安全
风险点:
- 未正确释放
NewGlobalRef
创建的全局引用。 - 本地代码中直接修改Java对象未同步回JVM。
解决 :使用try-finally
块确保释放资源,或换用JNA/JNR等安全框架。
15. 动态代理在Spring AOP中的性能瓶颈
考察点 :代理机制优化
问题 :JDK动态代理基于接口,CGLIB基于类,后者生成子类可能导致方法final修饰符冲突。
优化 :Spring 5默认使用CGLIB,可通过@EnableAspectJAutoProxy(proxyTargetClass=true)
强制使用。
16. 弱引用(WeakReference)在缓存设计中的误用场景
考察点 :引用类型特性
陷阱 :若缓存键通过弱引用持有,可能被GC提前回收,导致缓存失效。
方案 :使用WeakHashMap
或搭配ReferenceQueue
手动清理。
17. JVM退出时的钩子函数(ShutdownHook)执行限制
考察点 :JVM生命周期
规则:
- 钩子函数必须快速执行,超时会被强制终止。
- 已执行
System.exit()
后注册的钩子无效。
18. 常量折叠(Constant Folding)对程序逻辑的潜在影响
考察点 :编译器优化
示例:
java
final int a = 1;
final int b = 2;
System.out.println(a + b); // 编译时优化为3,字节码中无加法指令
注意:依赖编译时常量的计算可能隐藏逻辑错误。
19. 伪共享(False Sharing)在并发计数器的解决方案
考察点 :CPU缓存优化
问题 :多个线程修改同一缓存行的不同变量,导致缓存失效。
解决:
java
@Contended // JVM参数需加-XX:-RestrictContended
public class Counter {
public volatile long value1;
public volatile long value2;
}
20. Java模块化系统中如何允许第三方库反射访问私有字段?
考察点 :模块权限控制
配置 :在module-info.java
中开放包:
java
open module com.example.app {
opens com.example.internal to spring.core; // 对特定模块开放
}
风险:过度开放可能导致封装性破坏。