Skip to content

Java 高级特性笔记 ​

1. Lambda 表达式 ​

1.1 什么是 Lambda 表达式? ​

Lambda 表达式是 Java 8 引入的一种新的语法,允许将函数作为方法参数,或者将代码作为数据处理。

1.2 Lambda 表达式的语法 ​

(参数列表) -> { 代码块 }
(参数列表) -> { 代码块 }

简化规则:

  • 如果参数列表只有一个参数,可以省略括号
  • 如果代码块只有一条语句,可以省略大括号和分号
  • 如果代码块只有一条 return 语句,可以省略 return 关键字

1.3 Lambda 表达式的使用 ​

1.3.1 替代匿名内部类 ​

java
// 传统匿名内部类
Runnable runnable1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Traditional Runnable");
    }
};

// Lambda 表达式
Runnable runnable2 = () -> System.out.println("Lambda Runnable");

// 启动线程
new Thread(runnable1).start();
new Thread(runnable2).start();
new Thread(() -> System.out.println("Inline Lambda")).start();
// 传统匿名内部类
Runnable runnable1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Traditional Runnable");
    }
};

// Lambda 表达式
Runnable runnable2 = () -> System.out.println("Lambda Runnable");

// 启动线程
new Thread(runnable1).start();
new Thread(runnable2).start();
new Thread(() -> System.out.println("Inline Lambda")).start();

1.3.2 函数式接口 ​

Lambda 表达式只能用于函数式接口(只有一个抽象方法的接口)。

内置函数式接口:

  • Predicate<T>:接收一个参数,返回布尔值
  • Consumer<T>:接收一个参数,无返回值
  • Function<T, R>:接收一个参数,返回一个结果
  • Supplier<T>:无参数,返回一个结果
  • BiFunction<T, U, R>:接收两个参数,返回一个结果

示例:

java
// Predicate:用于过滤
Predicate<String> isLongerThan5 = str -> str.length() > 5;
boolean result = isLongerThan5.test("Java"); // false

// Consumer:用于消费数据
Consumer<String> printUpperCase = str -> System.out.println(str.toUpperCase());
printUpperCase.accept("Java"); // "JAVA"

// Function:用于转换数据
Function<String, Integer> stringToLength = str -> str.length();
int length = stringToLength.apply("Java"); // 4

// Supplier:用于提供数据
Supplier<Double> randomDouble = () -> Math.random();
double random = randomDouble.get();

// BiFunction:用于处理两个数据
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
int sum = add.apply(5, 3); // 8
// Predicate:用于过滤
Predicate<String> isLongerThan5 = str -> str.length() > 5;
boolean result = isLongerThan5.test("Java"); // false

// Consumer:用于消费数据
Consumer<String> printUpperCase = str -> System.out.println(str.toUpperCase());
printUpperCase.accept("Java"); // "JAVA"

// Function:用于转换数据
Function<String, Integer> stringToLength = str -> str.length();
int length = stringToLength.apply("Java"); // 4

// Supplier:用于提供数据
Supplier<Double> randomDouble = () -> Math.random();
double random = randomDouble.get();

// BiFunction:用于处理两个数据
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
int sum = add.apply(5, 3); // 8

1.3.3 自定义函数式接口 ​

java
// 自定义函数式接口
@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);
}

// 使用
Calculator add = (a, b) -> a + b;
Calculator subtract = (a, b) -> a - b;
Calculator multiply = (a, b) -> a * b;

int result1 = add.calculate(5, 3); // 8
int result2 = subtract.calculate(5, 3); // 2
int result3 = multiply.calculate(5, 3); // 15
// 自定义函数式接口
@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);
}

// 使用
Calculator add = (a, b) -> a + b;
Calculator subtract = (a, b) -> a - b;
Calculator multiply = (a, b) -> a * b;

int result1 = add.calculate(5, 3); // 8
int result2 = subtract.calculate(5, 3); // 2
int result3 = multiply.calculate(5, 3); // 15

2. Stream API ​

2.1 什么是 Stream? ​

Stream 是 Java 8 引入的一种新的抽象,用于处理集合数据。Stream 不是数据结构,而是一个计算流程。

2.2 Stream 的特点 ​

  • 无存储:Stream 不存储数据,只是通过数据源的操作管道进行计算
  • 函数式编程:Stream 操作是函数式的,不会修改原始数据源
  • 惰性求值:中间操作是惰性的,只有当终端操作执行时才会执行
  • 可消费:Stream 只能被消费一次,一旦被消费,就不能再使用

2.3 Stream 的操作流程 ​

  1. 创建 Stream:从数据源创建 Stream
  2. 中间操作:对 Stream 进行转换、过滤、排序等操作
  3. 终端操作:触发计算,生成结果

2.4 Stream 的创建 ​

java
// 从集合创建
List<String> list = Arrays.asList("Java", "Python", "C++", "JavaScript");
Stream<String> stream1 = list.stream(); // 顺序流
Stream<String> stream2 = list.parallelStream(); // 并行流

// 从数组创建
String[] array = {"Java", "Python", "C++"};
Stream<String> stream3 = Arrays.stream(array);

// 创建空 Stream
Stream<String> emptyStream = Stream.empty();

// 创建有限 Stream
Stream<String> finiteStream = Stream.of("Java", "Python", "C++");

// 创建无限 Stream
Stream<Double> infiniteStream1 = Stream.generate(Math::random); // 生成随机数
Stream<Integer> infiniteStream2 = Stream.iterate(0, n -> n + 2); // 生成偶数

// 限制无限 Stream 的大小
Stream<Double> limitedStream = infiniteStream1.limit(5); // 只取前 5 个元素
// 从集合创建
List<String> list = Arrays.asList("Java", "Python", "C++", "JavaScript");
Stream<String> stream1 = list.stream(); // 顺序流
Stream<String> stream2 = list.parallelStream(); // 并行流

// 从数组创建
String[] array = {"Java", "Python", "C++"};
Stream<String> stream3 = Arrays.stream(array);

// 创建空 Stream
Stream<String> emptyStream = Stream.empty();

// 创建有限 Stream
Stream<String> finiteStream = Stream.of("Java", "Python", "C++");

// 创建无限 Stream
Stream<Double> infiniteStream1 = Stream.generate(Math::random); // 生成随机数
Stream<Integer> infiniteStream2 = Stream.iterate(0, n -> n + 2); // 生成偶数

// 限制无限 Stream 的大小
Stream<Double> limitedStream = infiniteStream1.limit(5); // 只取前 5 个元素

2.5 Stream 的中间操作 ​

过滤:

java
Stream<String> filteredStream = list.stream()
    .filter(str -> str.length() > 4); // 过滤长度大于 4 的字符串
Stream<String> filteredStream = list.stream()
    .filter(str -> str.length() > 4); // 过滤长度大于 4 的字符串

映射:

java
Stream<Integer> mappedStream = list.stream()
    .map(String::length); // 将字符串映射为其长度
Stream<Integer> mappedStream = list.stream()
    .map(String::length); // 将字符串映射为其长度

排序:

java
Stream<String> sortedStream1 = list.stream()
    .sorted(); // 自然排序

Stream<String> sortedStream2 = list.stream()
    .sorted((a, b) -> b.length() - a.length()); // 自定义排序(按长度降序)
Stream<String> sortedStream1 = list.stream()
    .sorted(); // 自然排序

Stream<String> sortedStream2 = list.stream()
    .sorted((a, b) -> b.length() - a.length()); // 自定义排序(按长度降序)

去重:

java
Stream<String> distinctStream = Stream.of("Java", "Python", "Java", "C++")
    .distinct(); // 去重
Stream<String> distinctStream = Stream.of("Java", "Python", "Java", "C++")
    .distinct(); // 去重

截断:

java
Stream<String> limitedStream = list.stream()
    .limit(2); // 只取前 2 个元素
Stream<String> limitedStream = list.stream()
    .limit(2); // 只取前 2 个元素

跳过:

java
Stream<String> skippedStream = list.stream()
    .skip(2); // 跳过前 2 个元素
Stream<String> skippedStream = list.stream()
    .skip(2); // 跳过前 2 个元素

2.6 Stream 的终端操作 ​

收集:

java
// 收集到 List
List<String> collectedList = list.stream()
    .filter(str -> str.startsWith("J"))
    .collect(Collectors.toList());

// 收集到 Set
Set<String> collectedSet = list.stream()
    .collect(Collectors.toSet());

// 收集到 Map
Map<String, Integer> collectedMap = list.stream()
    .collect(Collectors.toMap(str -> str, String::length));
// 收集到 List
List<String> collectedList = list.stream()
    .filter(str -> str.startsWith("J"))
    .collect(Collectors.toList());

// 收集到 Set
Set<String> collectedSet = list.stream()
    .collect(Collectors.toSet());

// 收集到 Map
Map<String, Integer> collectedMap = list.stream()
    .collect(Collectors.toMap(str -> str, String::length));

聚合:

java
// 计数
long count = list.stream()
    .count();

// 最大值
Optional<String> max = list.stream()
    .max(Comparator.comparing(String::length));

// 最小值
Optional<String> min = list.stream()
    .min(Comparator.comparing(String::length));

// 求和
int sum = Stream.of(1, 2, 3, 4, 5)
    .mapToInt(Integer::intValue)
    .sum();

// 平均值
OptionalDouble average = Stream.of(1, 2, 3, 4, 5)
    .mapToInt(Integer::intValue)
    .average();
// 计数
long count = list.stream()
    .count();

// 最大值
Optional<String> max = list.stream()
    .max(Comparator.comparing(String::length));

// 最小值
Optional<String> min = list.stream()
    .min(Comparator.comparing(String::length));

// 求和
int sum = Stream.of(1, 2, 3, 4, 5)
    .mapToInt(Integer::intValue)
    .sum();

// 平均值
OptionalDouble average = Stream.of(1, 2, 3, 4, 5)
    .mapToInt(Integer::intValue)
    .average();

匹配:

java
// 全部匹配
boolean allMatch = list.stream()
    .allMatch(str -> str.length() > 0);

// 任意匹配
boolean anyMatch = list.stream()
    .anyMatch(str -> str.startsWith("J"));

// 没有匹配
boolean noneMatch = list.stream()
    .noneMatch(str -> str.startsWith("X"));
// 全部匹配
boolean allMatch = list.stream()
    .allMatch(str -> str.length() > 0);

// 任意匹配
boolean anyMatch = list.stream()
    .anyMatch(str -> str.startsWith("J"));

// 没有匹配
boolean noneMatch = list.stream()
    .noneMatch(str -> str.startsWith("X"));

遍历:

java
list.stream()
    .forEach(System.out::println);
list.stream()
    .forEach(System.out::println);

2.7 Stream 示例 ​

java
// 示例:计算列表中所有长度大于 4 的字符串的平均长度
List<String> languages = Arrays.asList("Java", "Python", "C++", "JavaScript", "Ruby", "Go");

double averageLength = languages.stream()
    .filter(str -> str.length() > 4) // 过滤长度大于 4 的字符串
    .mapToInt(String::length) // 转换为 int 流
    .average() // 计算平均值
    .orElse(0.0); // 如果没有元素,返回 0.0

System.out.println("Average length: " + averageLength); // 输出:7.0
// 示例:计算列表中所有长度大于 4 的字符串的平均长度
List<String> languages = Arrays.asList("Java", "Python", "C++", "JavaScript", "Ruby", "Go");

double averageLength = languages.stream()
    .filter(str -> str.length() > 4) // 过滤长度大于 4 的字符串
    .mapToInt(String::length) // 转换为 int 流
    .average() // 计算平均值
    .orElse(0.0); // 如果没有元素,返回 0.0

System.out.println("Average length: " + averageLength); // 输出:7.0

3. Optional 类 ​

3.1 什么是 Optional? ​

Optional 是 Java 8 引入的一个容器类,用于表示可能为 null 的值。Optional 类的目的是避免 NullPointerException。

3.2 Optional 的使用 ​

3.2.1 创建 Optional 对象 ​

java
// 创建空 Optional
Optional<String> emptyOptional = Optional.empty();

// 创建非空 Optional
Optional<String> nonEmptyOptional1 = Optional.of("Java"); // 如果传入 null,会抛出 NullPointerException
Optional<String> nonEmptyOptional2 = Optional.ofNullable("Java"); // 如果传入 null,返回空 Optional
Optional<String> nullOptional = Optional.ofNullable(null); // 空 Optional
// 创建空 Optional
Optional<String> emptyOptional = Optional.empty();

// 创建非空 Optional
Optional<String> nonEmptyOptional1 = Optional.of("Java"); // 如果传入 null,会抛出 NullPointerException
Optional<String> nonEmptyOptional2 = Optional.ofNullable("Java"); // 如果传入 null,返回空 Optional
Optional<String> nullOptional = Optional.ofNullable(null); // 空 Optional

3.2.2 获取 Optional 的值 ​

java
// get():如果 Optional 为空,抛出 NoSuchElementException
String value1 = nonEmptyOptional1.get();

// orElse():如果 Optional 为空,返回默认值
String value2 = nullOptional.orElse("Default"); // "Default"

// orElseGet():如果 Optional 为空,通过 Supplier 获取默认值
String value3 = nullOptional.orElseGet(() -> "Generated Default"); // "Generated Default"

// orElseThrow():如果 Optional 为空,抛出指定异常
String value4 = nullOptional.orElseThrow(() -> new IllegalArgumentException("Value is null"));
// get():如果 Optional 为空,抛出 NoSuchElementException
String value1 = nonEmptyOptional1.get();

// orElse():如果 Optional 为空,返回默认值
String value2 = nullOptional.orElse("Default"); // "Default"

// orElseGet():如果 Optional 为空,通过 Supplier 获取默认值
String value3 = nullOptional.orElseGet(() -> "Generated Default"); // "Generated Default"

// orElseThrow():如果 Optional 为空,抛出指定异常
String value4 = nullOptional.orElseThrow(() -> new IllegalArgumentException("Value is null"));

3.2.3 检查 Optional 是否包含值 ​

java
// isPresent():如果 Optional 包含值,返回 true
boolean hasValue = nonEmptyOptional1.isPresent(); // true

// ifPresent():如果 Optional 包含值,执行指定操作
nonEmptyOptional1.ifPresent(System.out::println); // 输出:Java

// ifPresentOrElse():如果 Optional 包含值,执行第一个操作,否则执行第二个操作
nullOptional.ifPresentOrElse(
    System.out::println,
    () -> System.out.println("Value is not present")
); // 输出:Value is not present
// isPresent():如果 Optional 包含值,返回 true
boolean hasValue = nonEmptyOptional1.isPresent(); // true

// ifPresent():如果 Optional 包含值,执行指定操作
nonEmptyOptional1.ifPresent(System.out::println); // 输出:Java

// ifPresentOrElse():如果 Optional 包含值,执行第一个操作,否则执行第二个操作
nullOptional.ifPresentOrElse(
    System.out::println,
    () -> System.out.println("Value is not present")
); // 输出:Value is not present

3.2.4 Optional 的转换和过滤 ​

java
// map():如果 Optional 包含值,对其进行转换
Optional<Integer> lengthOptional = nonEmptyOptional1.map(String::length); // Optional[4]

// flatMap():如果 Optional 包含值,对其进行转换,返回另一个 Optional
Optional<String> flatMappedOptional = nonEmptyOptional1.flatMap(str -> Optional.of(str.toUpperCase())); // Optional[JAVA]

// filter():如果 Optional 包含值且满足条件,返回该 Optional,否则返回空 Optional
Optional<String> filteredOptional = nonEmptyOptional1.filter(str -> str.length() > 5); // Optional.empty
// map():如果 Optional 包含值,对其进行转换
Optional<Integer> lengthOptional = nonEmptyOptional1.map(String::length); // Optional[4]

// flatMap():如果 Optional 包含值,对其进行转换,返回另一个 Optional
Optional<String> flatMappedOptional = nonEmptyOptional1.flatMap(str -> Optional.of(str.toUpperCase())); // Optional[JAVA]

// filter():如果 Optional 包含值且满足条件,返回该 Optional,否则返回空 Optional
Optional<String> filteredOptional = nonEmptyOptional1.filter(str -> str.length() > 5); // Optional.empty

3.3 Optional 示例 ​

java
// 示例:使用 Optional 避免 NullPointerException
class Person {
    private String name;
    private Integer age;
    private Address address;
    // 构造函数、getter、setter
}

class Address {
    private String city;
    // 构造函数、getter、setter
}

// 传统方式,容易出现 NullPointerException
String city1 = null;
if (person != null) {
    Address address = person.getAddress();
    if (address != null) {
        city1 = address.getCity();
    }
}

// 使用 Optional,避免 NullPointerException
String city2 = Optional.ofNullable(person)
    .map(Person::getAddress)
    .map(Address::getCity)
    .orElse("Unknown");
// 示例:使用 Optional 避免 NullPointerException
class Person {
    private String name;
    private Integer age;
    private Address address;
    // 构造函数、getter、setter
}

class Address {
    private String city;
    // 构造函数、getter、setter
}

// 传统方式,容易出现 NullPointerException
String city1 = null;
if (person != null) {
    Address address = person.getAddress();
    if (address != null) {
        city1 = address.getCity();
    }
}

// 使用 Optional,避免 NullPointerException
String city2 = Optional.ofNullable(person)
    .map(Person::getAddress)
    .map(Address::getCity)
    .orElse("Unknown");

4. 日期时间 API ​

4.1 为什么需要新的日期时间 API? ​

Java 8 之前的日期时间 API 存在以下问题:

  • Date 类设计不佳,月份从 0 开始
  • Calendar 类也存在类似问题,且不是线程安全的
  • 没有统一的日期时间处理标准

4.2 新的日期时间 API 核心类 ​

  • LocalDate:表示日期(年、月、日)
  • LocalTime:表示时间(时、分、秒、纳秒)
  • LocalDateTime:表示日期和时间
  • ZonedDateTime:表示带时区的日期和时间
  • Instant:表示时间戳
  • Duration:表示两个时间之间的间隔
  • Period:表示两个日期之间的间隔
  • DateTimeFormatter:用于日期时间的格式化和解析

4.3 LocalDate、LocalTime、LocalDateTime ​

java
// LocalDate:日期
LocalDate today = LocalDate.now(); // 当前日期
LocalDate specificDate = LocalDate.of(2023, 12, 25); // 指定日期(2023-12-25)
LocalDate parsedDate = LocalDate.parse("2023-12-25"); // 解析日期字符串

// 获取日期组件
int year = today.getYear(); // 年份
Month month = today.getMonth(); // 月份(枚举类型)
int dayOfMonth = today.getDayOfMonth(); // 日
DayOfWeek dayOfWeek = today.getDayOfWeek(); // 星期几(枚举类型)
int dayOfYear = today.getDayOfYear(); // 一年中的第几天

// 修改日期
LocalDate tomorrow = today.plusDays(1); // 明天
LocalDate lastMonth = today.minusMonths(1); // 上个月
LocalDate firstDayOfMonth = today.withDayOfMonth(1); // 本月第一天

// LocalTime:时间
LocalTime nowTime = LocalTime.now(); // 当前时间
LocalTime specificTime = LocalTime.of(14, 30, 45); // 指定时间(14:30:45)
LocalTime parsedTime = LocalTime.parse("14:30:45"); // 解析时间字符串

// 获取时间组件
int hour = nowTime.getHour(); // 时
int minute = nowTime.getMinute(); // 分
int second = nowTime.getSecond(); // 秒
int nano = nowTime.getNano(); // 纳秒

// LocalDateTime:日期和时间
LocalDateTime nowDateTime = LocalDateTime.now(); // 当前日期时间
LocalDateTime specificDateTime = LocalDateTime.of(2023, 12, 25, 14, 30, 45); // 指定日期时间
LocalDateTime parsedDateTime = LocalDateTime.parse("2023-12-25T14:30:45"); // 解析日期时间字符串

// 转换
LocalDate date = nowDateTime.toLocalDate(); // 转换为 LocalDate
LocalTime time = nowDateTime.toLocalTime(); // 转换为 LocalTime
// LocalDate:日期
LocalDate today = LocalDate.now(); // 当前日期
LocalDate specificDate = LocalDate.of(2023, 12, 25); // 指定日期(2023-12-25)
LocalDate parsedDate = LocalDate.parse("2023-12-25"); // 解析日期字符串

// 获取日期组件
int year = today.getYear(); // 年份
Month month = today.getMonth(); // 月份(枚举类型)
int dayOfMonth = today.getDayOfMonth(); // 日
DayOfWeek dayOfWeek = today.getDayOfWeek(); // 星期几(枚举类型)
int dayOfYear = today.getDayOfYear(); // 一年中的第几天

// 修改日期
LocalDate tomorrow = today.plusDays(1); // 明天
LocalDate lastMonth = today.minusMonths(1); // 上个月
LocalDate firstDayOfMonth = today.withDayOfMonth(1); // 本月第一天

// LocalTime:时间
LocalTime nowTime = LocalTime.now(); // 当前时间
LocalTime specificTime = LocalTime.of(14, 30, 45); // 指定时间(14:30:45)
LocalTime parsedTime = LocalTime.parse("14:30:45"); // 解析时间字符串

// 获取时间组件
int hour = nowTime.getHour(); // 时
int minute = nowTime.getMinute(); // 分
int second = nowTime.getSecond(); // 秒
int nano = nowTime.getNano(); // 纳秒

// LocalDateTime:日期和时间
LocalDateTime nowDateTime = LocalDateTime.now(); // 当前日期时间
LocalDateTime specificDateTime = LocalDateTime.of(2023, 12, 25, 14, 30, 45); // 指定日期时间
LocalDateTime parsedDateTime = LocalDateTime.parse("2023-12-25T14:30:45"); // 解析日期时间字符串

// 转换
LocalDate date = nowDateTime.toLocalDate(); // 转换为 LocalDate
LocalTime time = nowDateTime.toLocalTime(); // 转换为 LocalTime

4.4 DateTimeFormatter ​

java
// 格式化日期时间
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = now.format(formatter); // 例如:2023-12-25 14:30:45

// 解析日期时间字符串
String dateTimeStr = "2023-12-25 14:30:45";
LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeStr, formatter);

// 预定义的格式化器
String isoDate = now.format(DateTimeFormatter.ISO_DATE); // 2023-12-25
String isoTime = now.format(DateTimeFormatter.ISO_TIME); // 14:30:45.123456789
String isoDateTime = now.format(DateTimeFormatter.ISO_DATE_TIME); // 2023-12-25T14:30:45.123456789
// 格式化日期时间
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = now.format(formatter); // 例如:2023-12-25 14:30:45

// 解析日期时间字符串
String dateTimeStr = "2023-12-25 14:30:45";
LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeStr, formatter);

// 预定义的格式化器
String isoDate = now.format(DateTimeFormatter.ISO_DATE); // 2023-12-25
String isoTime = now.format(DateTimeFormatter.ISO_TIME); // 14:30:45.123456789
String isoDateTime = now.format(DateTimeFormatter.ISO_DATE_TIME); // 2023-12-25T14:30:45.123456789

4.5 Duration 和 Period ​

java
// Duration:两个时间之间的间隔
LocalTime time1 = LocalTime.of(10, 0, 0);
LocalTime time2 = LocalTime.of(12, 30, 0);
Duration duration = Duration.between(time1, time2);
long hours = duration.toHours(); // 2
long minutes = duration.toMinutes(); // 150
long seconds = duration.getSeconds(); // 9000

// Period:两个日期之间的间隔
LocalDate date1 = LocalDate.of(2023, 1, 1);
LocalDate date2 = LocalDate.of(2023, 12, 31);
Period period = Period.between(date1, date2);
int years = period.getYears(); // 0
int months = period.getMonths(); // 11
int days = period.getDays(); // 30
// Duration:两个时间之间的间隔
LocalTime time1 = LocalTime.of(10, 0, 0);
LocalTime time2 = LocalTime.of(12, 30, 0);
Duration duration = Duration.between(time1, time2);
long hours = duration.toHours(); // 2
long minutes = duration.toMinutes(); // 150
long seconds = duration.getSeconds(); // 9000

// Period:两个日期之间的间隔
LocalDate date1 = LocalDate.of(2023, 1, 1);
LocalDate date2 = LocalDate.of(2023, 12, 31);
Period period = Period.between(date1, date2);
int years = period.getYears(); // 0
int months = period.getMonths(); // 11
int days = period.getDays(); // 30

5. 模块化系统 ​

5.1 什么是模块化? ​

模块化是 Java 9 引入的一种新的特性,允许将代码组织成模块,每个模块包含一组相关的包和资源。

5.2 模块化的好处 ​

  • 更好的封装:可以控制模块之间的依赖关系
  • 减少内存占用:只加载需要的模块
  • 提高安全性:可以限制模块之间的访问
  • 更好的可维护性:模块之间的依赖关系清晰

5.3 模块的定义 ​

模块声明文件:module-info.java

java
// 模块声明
module com.example.myapp {
    // 导出包:允许其他模块访问该包
    exports com.example.myapp.api;
    exports com.example.myapp.service;
    
    // 依赖其他模块
    requires java.base; // 隐式依赖,不需要显式声明
    requires java.sql; // 依赖 java.sql 模块
    requires transitive java.xml; // 传递依赖,依赖该模块的模块也会依赖 java.xml
    
    // 提供服务:实现某个服务接口
    provides com.example.myapp.Service with com.example.myapp.impl.ServiceImpl;
    
    // 使用服务:声明使用某个服务
    uses com.example.myapp.Service;
    
    // 打开包:允许其他模块通过反射访问该包
    opens com.example.myapp.internal to java.base;
}
// 模块声明
module com.example.myapp {
    // 导出包:允许其他模块访问该包
    exports com.example.myapp.api;
    exports com.example.myapp.service;
    
    // 依赖其他模块
    requires java.base; // 隐式依赖,不需要显式声明
    requires java.sql; // 依赖 java.sql 模块
    requires transitive java.xml; // 传递依赖,依赖该模块的模块也会依赖 java.xml
    
    // 提供服务:实现某个服务接口
    provides com.example.myapp.Service with com.example.myapp.impl.ServiceImpl;
    
    // 使用服务:声明使用某个服务
    uses com.example.myapp.Service;
    
    // 打开包:允许其他模块通过反射访问该包
    opens com.example.myapp.internal to java.base;
}

5.4 模块化示例 ​

模块 1:com.example.api

java
// module-info.java
module com.example.api {
    exports com.example.api;
}

// Service.java
package com.example.api;

public interface Service {
    void doSomething();
}
// module-info.java
module com.example.api {
    exports com.example.api;
}

// Service.java
package com.example.api;

public interface Service {
    void doSomething();
}

模块 2:com.example.impl

java
// module-info.java
module com.example.impl {
    requires com.example.api;
    provides com.example.api.Service with com.example.impl.ServiceImpl;
}

// ServiceImpl.java
package com.example.impl;

import com.example.api.Service;

public class ServiceImpl implements Service {
    @Override
    public void doSomething() {
        System.out.println("Service implementation");
    }
}
// module-info.java
module com.example.impl {
    requires com.example.api;
    provides com.example.api.Service with com.example.impl.ServiceImpl;
}

// ServiceImpl.java
package com.example.impl;

import com.example.api.Service;

public class ServiceImpl implements Service {
    @Override
    public void doSomething() {
        System.out.println("Service implementation");
    }
}

模块 3:com.example.app

java
// module-info.java
module com.example.app {
    requires com.example.api;
    uses com.example.api.Service;
}

// Application.java
package com.example.app;

import com.example.api.Service;

import java.util.ServiceLoader;

public class Application {
    public static void main(String[] args) {
        // 加载服务实现
        ServiceLoader<Service> services = ServiceLoader.load(Service.class);
        for (Service service : services) {
            service.doSomething(); // 输出:Service implementation
        }
    }
}
// module-info.java
module com.example.app {
    requires com.example.api;
    uses com.example.api.Service;
}

// Application.java
package com.example.app;

import com.example.api.Service;

import java.util.ServiceLoader;

public class Application {
    public static void main(String[] args) {
        // 加载服务实现
        ServiceLoader<Service> services = ServiceLoader.load(Service.class);
        for (Service service : services) {
            service.doSomething(); // 输出:Service implementation
        }
    }
}

6. 虚拟线程 ​

6.1 什么是虚拟线程? ​

虚拟线程是 Java 19 引入的一种轻量级线程,是 JVM 级别的线程,不是操作系统级别的线程。虚拟线程的创建和切换成本很低,可以创建数百万个虚拟线程。

6.2 虚拟线程的特点 ​

  • 轻量级:创建和切换成本低
  • 大量创建:可以创建数百万个虚拟线程
  • 简化异步编程:允许使用同步编程模型编写异步代码
  • 兼容现有 API:可以使用现有的 Thread API

6.3 虚拟线程的使用 ​

java
// 创建虚拟线程
Runnable task = () -> {
    System.out.println("Virtual thread: " + Thread.currentThread().getName());
    try {
        Thread.sleep(Duration.ofSeconds(1));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
};

// 方式 1:使用 Thread.ofVirtual()
Thread virtualThread1 = Thread.ofVirtual()
    .name("my-virtual-thread")
    .start(task);

// 方式 2:使用 Executors.newVirtualThreadPerTaskExecutor()
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 1000; i++) {
        final int taskId = i;
        executor.submit(() -> {
            System.out.println("Task " + taskId + " running on " + Thread.currentThread().getName());
            Thread.sleep(Duration.ofMillis(500));
            return taskId;
        });
    }
}

// 方式 3:使用 Thread.Builder
Thread.Builder.OfVirtual builder = Thread.ofVirtual();
Thread virtualThread2 = builder.name("another-virtual-thread").unstarted(task);
virtualThread2.start();

// 等待虚拟线程完成
try {
    virtualThread1.join();
    virtualThread2.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}
// 创建虚拟线程
Runnable task = () -> {
    System.out.println("Virtual thread: " + Thread.currentThread().getName());
    try {
        Thread.sleep(Duration.ofSeconds(1));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
};

// 方式 1:使用 Thread.ofVirtual()
Thread virtualThread1 = Thread.ofVirtual()
    .name("my-virtual-thread")
    .start(task);

// 方式 2:使用 Executors.newVirtualThreadPerTaskExecutor()
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 1000; i++) {
        final int taskId = i;
        executor.submit(() -> {
            System.out.println("Task " + taskId + " running on " + Thread.currentThread().getName());
            Thread.sleep(Duration.ofMillis(500));
            return taskId;
        });
    }
}

// 方式 3:使用 Thread.Builder
Thread.Builder.OfVirtual builder = Thread.ofVirtual();
Thread virtualThread2 = builder.name("another-virtual-thread").unstarted(task);
virtualThread2.start();

// 等待虚拟线程完成
try {
    virtualThread1.join();
    virtualThread2.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}

6.4 虚拟线程与平台线程的比较 ​

特性平台线程虚拟线程
实现操作系统线程JVM 线程
成本高低
数量限制数千个数百万个
切换开销高低
内存占用几 MB几 KB
API 兼容性100%100%

7. 其他高级特性 ​

7.1 类型推断 ​

Java 10 引入了 var 关键字,允许编译器推断变量的类型。

java
// 传统方式
List<String> list1 = new ArrayList<String>();
Map<String, Integer> map1 = new HashMap<String, Integer>();

// 使用 var
var list2 = new ArrayList<String>();
var map2 = new HashMap<String, Integer>();
var stream = list2.stream();
var optional = Optional.of("Java");
// 传统方式
List<String> list1 = new ArrayList<String>();
Map<String, Integer> map1 = new HashMap<String, Integer>();

// 使用 var
var list2 = new ArrayList<String>();
var map2 = new HashMap<String, Integer>();
var stream = list2.stream();
var optional = Optional.of("Java");

7.2 文本块 ​

Java 15 引入了文本块,允许更方便地编写多行字符串。

java
// 传统方式
String html1 = "<html>\n" +
              "  <body>\n" +
              "    <h1>Hello, Java!</h1>\n" +
              "  </body>\n" +
              "</html>";

// 使用文本块
String html2 = """
              <html>
                <body>
                  <h1>Hello, Java!</h1>
                </body>
              </html>
              """;

// 文本块可以包含表达式
String name = "Java";
String greeting = """
                 Hello, %s!
                 Welcome to Java programming.
                 """
                 .formatted(name);
// 传统方式
String html1 = "<html>\n" +
              "  <body>\n" +
              "    <h1>Hello, Java!</h1>\n" +
              "  </body>\n" +
              "</html>";

// 使用文本块
String html2 = """
              <html>
                <body>
                  <h1>Hello, Java!</h1>
                </body>
              </html>
              """;

// 文本块可以包含表达式
String name = "Java";
String greeting = """
                 Hello, %s!
                 Welcome to Java programming.
                 """
                 .formatted(name);

7.3 记录类 ​

Java 16 引入了记录类,用于创建不可变的数据类。

java
// 传统方式
class Person {
    private final String name;
    private final int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    
    @Override
    public String toString() {
        return "Person{" +
               "name='" + name + '\'' +
               ", age=" + age +
               '}';
    }
}

// 使用记录类
record Person(String name, int age) {
    // 可以添加自定义构造函数
    public Person {
        if (age < 0) {
            throw new IllegalArgumentException("Age cannot be negative");
        }
    }
    
    // 可以添加自定义方法
    public Person withName(String newName) {
        return new Person(newName, this.age);
    }
}

// 使用记录类
Person person = new Person("Java", 25);
String name = person.name(); // 获取 name
int age = person.age(); // 获取 age
Person newPerson = person.withName("Java 17"); // 创建新的记录对象
// 传统方式
class Person {
    private final String name;
    private final int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    
    @Override
    public String toString() {
        return "Person{" +
               "name='" + name + '\'' +
               ", age=" + age +
               '}';
    }
}

// 使用记录类
record Person(String name, int age) {
    // 可以添加自定义构造函数
    public Person {
        if (age < 0) {
            throw new IllegalArgumentException("Age cannot be negative");
        }
    }
    
    // 可以添加自定义方法
    public Person withName(String newName) {
        return new Person(newName, this.age);
    }
}

// 使用记录类
Person person = new Person("Java", 25);
String name = person.name(); // 获取 name
int age = person.age(); // 获取 age
Person newPerson = person.withName("Java 17"); // 创建新的记录对象

7.4 密封类 ​

Java 17 引入了密封类,用于限制类的继承关系。

java
// 密封类
public sealed class Shape permits Circle, Rectangle, Triangle {
    // 抽象方法
    public abstract double getArea();
}

// 允许继承的子类
public final class Circle extends Shape {
    private final double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

public non-sealed class Rectangle extends Shape {
    private final double width;
    private final double height;
    
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    public double getArea() {
        return width * height;
    }
}

public final class Triangle extends Shape {
    private final double base;
    private final double height;
    
    public Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }
    
    @Override
    public double getArea() {
        return 0.5 * base * height;
    }
}
// 密封类
public sealed class Shape permits Circle, Rectangle, Triangle {
    // 抽象方法
    public abstract double getArea();
}

// 允许继承的子类
public final class Circle extends Shape {
    private final double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

public non-sealed class Rectangle extends Shape {
    private final double width;
    private final double height;
    
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    public double getArea() {
        return width * height;
    }
}

public final class Triangle extends Shape {
    private final double base;
    private final double height;
    
    public Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }
    
    @Override
    public double getArea() {
        return 0.5 * base * height;
    }
}

8. 总结 ​

Java 高级特性是 Java 编程的重要组成部分,包括:

  1. Lambda 表达式:简化函数式编程
  2. Stream API:用于处理集合数据
  3. Optional 类:避免 NullPointerException
  4. 新的日期时间 API:更易用、更安全的日期时间处理
  5. 模块化系统:更好的封装和依赖管理
  6. 虚拟线程:轻量级线程,简化异步编程
  7. 其他高级特性:类型推断、文本块、记录类、密封类等

这些高级特性使得 Java 编程更加简洁、高效、安全。掌握这些特性,将有助于编写更现代、更高效的 Java 代码。