JDK 8 相对于 JDK 7 引入的主要特性:
- Lambda 表达式
- 函数式接口(Functional Interface)
- Stream API
- 方法引用(Method Reference)
- 默认方法(Default Method)
- Optional
- 新的日期时间 API(java.time)
- Nashorn JavaScript 引擎
- Base64
- 并发增强(CompletableFuture、StampedLock、LongAdder)
- 重复注解(Repeating Annotations)
- 类型注解(Type Annotations)
- PermGen 移除,改用 Metaspace
- HashMap 内部实现优化:链表转红黑树
- GC 组合废弃(JEP 173)
Lambda 表达式
Lambda 来自数学中的 λ 演算(Lambda Calculus),是函数式编程的理论基础。函数式编程的核心思想是:函数是"一等公民",可以像普通值一样被传递、赋值、返回。JDK 8 引入 Lambda,本质上是给 Java 这门面向对象语言加入了函数式编程的能力。
Lambda 表达式允许将函数(即 Lambda 表达式本身)作为参数传递,简化匿名内部类的写法。
语法结构
(参数列表) -> 方法体
(参数列表):传入的参数,无参数时写空括号()->:箭头操作符,读作 “goes to”,分隔参数列表和方法体方法体:要执行的逻辑
Runnable 是一个接口(java.lang.Runnable),只有一个无参方法 void run(),因此参数列表为空 ()。
// JDK 7:匿名内部类写法
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
};
// JDK 8:Lambda 写法,箭头右边等价于 run() 的方法体
Runnable r = () -> System.out.println("Hello");
将函数作为参数传递
JDK 8 之前,Java 无法直接传递"一段逻辑",只能把逻辑包装成匿名对象再传递。Lambda 表达式让你可以直接把逻辑作为参数传入方法,这就是"将函数作为参数传递"的含义。
以 Thread 为例,它的构造方法接受一个 Runnable 参数:
// JDK 7:先创建匿名类对象,再传入
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
};
new Thread(r).start();
// JDK 8:直接把 Lambda 表达式作为参数传入
new Thread(() -> System.out.println("Hello")).start();
单行与多行
方法体只有一行时可省略花括号;多行时需要用 {} 包裹:
// 单行:省略花括号
Runnable r = () -> System.out.println("Hello");
// 多行:使用花括号
Runnable r = () -> {
System.out.println("Hello");
System.out.println("World");
};
返回值
单行时返回值隐式返回,不需要写 return;多行时必须显式写 return:
// 单行:隐式返回
Function<Integer, Integer> f = x -> x * 2;
// 多行:必须显式 return
Function<Integer, Integer> f = x -> {
int result = x * 2;
return result;
};
函数式接口(Functional Interface)
只有一个抽象方法的接口称为函数式接口——更准确地说,须通过 SAM(Single Abstract Method) 判定;定义与判定流程见 Java 8 函数式接口。可用 @FunctionalInterface 注解标记(可选,用于文档与编译期约束,不是判定本身)。
@FunctionalInterface
public interface MyFunction {
int apply(int x, int y); // the one SAM
}
Lambda 与方法引用的目标类型必须是 SAM 成立的函数式接口,例如上面的 MyFunction 或 Runnable。
Stream API
Stream API 提供对集合进行函数式风格操作(过滤、映射、归约等)的能力。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 过滤 + 映射 + 收集
List<String> result = names.stream()
.filter(name -> name.startsWith("A"))
.map(String::toUpperCase)
.collect(Collectors.toList());
方法引用(Method Reference)
方法引用是 Lambda 的简写形式,直接引用已有方法。
// 静态方法引用
Function<String, Integer> parseInt = Integer::parseInt;
// 实例方法引用
Consumer<String> printer = System.out::println;
// 构造方法引用
Supplier<List<String>> listFactory = ArrayList::new;
默认方法(Default Method)
接口可以定义带有默认实现的方法,不破坏已有实现类。
public interface Greeting {
default String greet(String name) {
return "Hello, " + name;
}
}
Optional
Optional<T> 用于包装可能为 null 的值,减少 NullPointerException。
Optional<String> opt = Optional.ofNullable(getValue());
String result = opt.orElse("default");
新的日期时间 API(java.time)
取代旧的 java.util.Date 和 Calendar,提供不可变、线程安全的日期时间类。
LocalDate date = LocalDate.now();
LocalTime time = LocalTime.of(10, 30);
LocalDateTime dateTime = LocalDateTime.now();
ZonedDateTime zoned = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
// 格式化
String formatted = dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Nashorn JavaScript 引擎
内置 Nashorn 引擎,可在 JVM 上运行 JavaScript 代码(JDK 11 已废弃)。
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval("print('Hello from JavaScript')");
Base64
标准库新增 java.util.Base64,提供 Base64 编解码支持。
String encoded = Base64.getEncoder().encodeToString("hello".getBytes());
byte[] decoded = Base64.getDecoder().decode(encoded);
并发增强
CompletableFuture:支持异步编程和链式组合StampedLock:读写锁的改进版,支持乐观读LongAdder/LongAccumulator:高并发计数器
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenApply(String::toUpperCase);
System.out.println(future.get()); // HELLO WORLD
重复注解(Repeating Annotations)
同一位置可以多次使用同一个注解。
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Schedules.class)
public @interface Schedule {
String day();
}
@Schedule(day = "Mon")
@Schedule(day = "Wed")
public void meeting() {}
类型注解(Type Annotations)
注解可以应用到类型使用处,支持更强的静态分析。
@NonNull String name = "Alice";
List<@NonNull String> items = new ArrayList<>();
PermGen 移除
移除永久代(PermGen),改用本地内存中的元空间(Metaspace),解决 OutOfMemoryError: PermGen space 问题。
HashMap 内部实现优化:链表转红黑树
这是 java.util.HashMap 内部实现的优化,不涉及语言语法或公开 API 的变化,因此通常不会出现在"Java 8 新特性"这类面向开发者的概览清单里。
当某个桶(bucket)里的链表长度超过 TREEIFY_THRESHOLD(默认 8),且数组容量已达到 MIN_TREEIFY_CAPACITY(默认 64)时,JDK 8 会把该桶的链表转换成红黑树,将该桶最坏情况下的查找复杂度从 O(n) 降到 O(log n)。详细实现见 HashMap。
GC 组合废弃(JEP 173)
JDK 8 通过 JEP 173(Retire Some Rarely-Used GC Combinations) 把几种冷门的垃圾收集器组合标记为过时:ParNew + SerialOld、DefNew + CMS,以及 CMS 的增量模式(Incremental CMS,iCMS)。这一步本身不移除任何功能,继续使用这些组合只会在启动时打印废弃警告,为后续正式移除做铺垫——这些组合在 JDK 9(JEP 214)就被直接移除,此后再传对应参数 JVM 会拒绝启动。完整的 GC 收集器废弃/移除时间线见 Java GC。
JDK 8 可用收集器速查
JDK 8 仍是传统分代模型的时代,G1 之后的 ZGC、Shenandoah、Epsilon 都还没出现(它们是 JDK 11/12 才引入的)。JDK 8 可用的收集器有:
| 收集器 | 启用参数 | 说明 |
|---|---|---|
| Serial | -XX:+UseSerialGC |
单线程,小数据量/单核场景 |
| Parallel(默认) | -XX:+UseParallelGC |
新生代 Parallel Scavenge + 老年代 Parallel Old(随开关自动一起启用),吞吐量优先;JDK 8 在无特殊配置的服务端机器上默认使用它 |
| CMS | -XX:+UseConcMarkSweepGC |
ParNew + CMS 组合,低停顿;此时 CMS 尚未被标记废弃(废弃是 JDK 9 的 JEP 291 才发生的事) |
| G1 | -XX:+UseG1GC |
自 JDK 7u4 起已转为正式支持,但在 JDK 8 里仍需显式开启,不是默认项 |
上文提到的 ParNew+SerialOld、DefNew+CMS、CMS 增量模式这几个冷门组合在 JDK 8 里技术上仍可用,只是会打印废弃警告。JDK 9 及之后各版本的收集器变化(G1 转默认、CMS 移除、ZGC/Shenandoah 加入等),见 Java GC。
参考
维护记录
| 时间 | 修改内容 | 原因 |
|---|---|---|
| 2026-06-20 | 开篇「主要新特性」改为「主要特性」 | 全系列统一命名 |
| 2026-06-20 | 重命名为 jdk-8.md;title 改为「JDK 8」;url 改为 jdk-8;移至 language/java/ |
全系列统一简洁命名 |
| 2026-06-20 | 函数式接口小节补充 SAM 术语并链到 functional-interface | 与 lambda 等文统一术语出处 |
| 2026-07-13 | 补充「HashMap 内部实现优化:链表转红黑树」一节,内链至 hashmap.md | 该特性是 JDK 8 的真实变化,但属于内部实现优化而非公开 API,之前遗漏 |
| 2026-07-14 | 补充「GC 组合废弃(JEP 173)」一节,内链至 gc.md 的收集器发展时间线 | 原文未提及 JEP 173,属于 JDK 8 的真实 GC 相关变化,之前遗漏 |