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); // 81.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); // 152. Stream API
2.1 什么是 Stream?
Stream 是 Java 8 引入的一种新的抽象,用于处理集合数据。Stream 不是数据结构,而是一个计算流程。
2.2 Stream 的特点
- 无存储:Stream 不存储数据,只是通过数据源的操作管道进行计算
- 函数式编程:Stream 操作是函数式的,不会修改原始数据源
- 惰性求值:中间操作是惰性的,只有当终端操作执行时才会执行
- 可消费:Stream 只能被消费一次,一旦被消费,就不能再使用
2.3 Stream 的操作流程
- 创建 Stream:从数据源创建 Stream
- 中间操作:对 Stream 进行转换、过滤、排序等操作
- 终端操作:触发计算,生成结果
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.03. 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); // 空 Optional3.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 present3.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.empty3.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(); // 转换为 LocalTime4.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.1234567894.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(); // 305. 模块化系统
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 编程的重要组成部分,包括:
- Lambda 表达式:简化函数式编程
- Stream API:用于处理集合数据
- Optional 类:避免 NullPointerException
- 新的日期时间 API:更易用、更安全的日期时间处理
- 模块化系统:更好的封装和依赖管理
- 虚拟线程:轻量级线程,简化异步编程
- 其他高级特性:类型推断、文本块、记录类、密封类等
这些高级特性使得 Java 编程更加简洁、高效、安全。掌握这些特性,将有助于编写更现代、更高效的 Java 代码。
Cao Note