java的演进节奏从jdk9开始显著加快,每半年一个新版本的发布节奏为java带来了大量的新特性。
其中包括令人瞩目的虚拟线程、记录类型等"明星特性",也有很多不太引人注意但同样实用的小功能。
本文整理了29个jdk9到jdk21中值得掌握的实用特性,帮助你编写更简洁、高效、安全的java代码。
jdk 9 模块化与api增强
1. 集合工厂方法:一行代码创建不可变集合
在jdk9之前,创建小型不可变集合相当繁琐,现在只需要一行代码:
// 旧方式 list<string> list = collections.unmodifiablelist(arrays.aslist("java", "kotlin", "scala")); map<string, integer> map = collections.unmodifiablemap(new hashmap<string, integer>() {{ put("java", 1995); put("kotlin", 2011); put("scala", 2004); }}); // jdk9方式 list<string> list = list.of("java", "kotlin", "scala"); set<string> set = set.of("java", "kotlin", "scala"); map<string, integer> map = map.of( "java", 1995, "kotlin", 2011, "scala", 2004 );
对于更多键值对,可以使用map.ofentries()
:
map<string, integer> largemap = map.ofentries( map.entry("java", 1995), map.entry("kotlin", 2011), map.entry("scala", 2004), map.entry("groovy", 2003) // ...可以有更多entries );
实用场景:常量定义、配置集合、测试数据准备、api返回不可变结果。
2. 私有接口方法:接口代码复用不再尴尬
jdk8引入了接口默认方法,jdk9进一步允许接口拥有私有方法,便于在接口内部复用代码:
public interface fileprocessor { // 公共抽象方法 void process(path path); // 默认方法 default void processfile(string filename) { validatefilename(filename); // 复用私有方法进行验证 process(path.of(filename)); log("processed file: " + filename); } default void processdirectory(string dirname) { validatefilename(dirname); // 复用相同的验证逻辑 try (stream<path> paths = files.list(path.of(dirname))) { paths.foreach(this::process); } catch (ioexception e) { handleexception(e); // 复用私有方法处理异常 } log("processed directory: " + dirname); } // 私有方法 - 供默认方法使用 private void validatefilename(string filename) { if (filename == null || filename.isempty()) { throw new illegalargumentexception("file name cannot be empty"); } } // 私有静态方法 private static void log(string message) { system.out.println("[" + localdatetime.now() + "] " + message); } private void handleexception(exception e) { log("error: " + e.getmessage()); } }
实用场景:api设计、接口默认方法逻辑复用、框架开发。
3. stream api增强:流操作更灵活
jdk9为stream api增加了几个实用方法:
// 1. takewhile - 从头开始获取元素,直到条件不满足 stream.of(2, 4, 6, 8, 9, 10, 12) .takewhile(n -> n % 2 == 0) // 结果: [2, 4, 6, 8] .foreach(system.out::println); // 2. dropwhile - 从头开始丢弃元素,直到条件不满足 stream.of(2, 4, 6, 8, 9, 10, 12) .dropwhile(n -> n % 2 == 0) // 结果: [9, 10, 12] .foreach(system.out::println); // 3. ofnullable - 安全创建单元素流,处理null值 stream.ofnullable(null).count(); // 0 stream.ofnullable("java").count(); // 1 // 4. iterate方法重载 - 带终止条件的迭代 // 旧方式需要使用limit或filter来限制 stream.iterate(1, n -> n * 2) .limit(5) .foreach(system.out::println); // 新方式更直接 stream.iterate(1, n -> n < 100, n -> n * 2) .foreach(system.out::println); // 1, 2, 4, 8, 16, 32, 64
实用场景:数据处理管道、复杂条件过滤、有界数据生成。
4. inputstream.transferto():流复制不再繁琐
在jdk9之前,在流之间复制数据需要手动处理缓冲区,现在只需要一行代码:
// 旧方式 - 冗长且易错 try (inputstream is = new fileinputstream("source.txt"); outputstream os = new fileoutputstream("target.txt")) { byte[] buffer = new byte[8192]; int length; while ((length = is.read(buffer)) > 0) { os.write(buffer, 0, length); } } // jdk9方式 - 简洁明了 try (inputstream is = new fileinputstream("source.txt"); outputstream os = new fileoutputstream("target.txt")) { is.transferto(os); // 一行代码搞定 }
实用场景:文件复制、网络数据传输、流处理。
5. 改进的process api:管理系统进程更容易
jdk9大幅增强了process api,让java程序与系统进程的交互更加强大:
// 获取当前进程 processhandle current = processhandle.current(); system.out.println("current pid: " + current.pid()); // 获取进程信息 current.info().user().ifpresent(user -> system.out.println("user: " + user)); current.info().commandline().ifpresent(cmd -> system.out.println("command: " + cmd)); current.info().startinstant().ifpresent(start -> system.out.println("start time: " + start)); current.info().totalcpuduration().ifpresent(cpu -> system.out.println("cpu time: " + cpu)); // 列出所有子进程 current.children().foreach(child -> system.out.println("child pid: " + child.pid())); // 列出所有进程 processhandle.allprocesses() .filter(ph -> ph.info().command().ispresent()) .foreach(ph -> system.out.println(ph.pid() + ": " + ph.info().command().get())); // 启动并等待进程完成 processbuilder pb = new processbuilder("ls", "-l"); process process = pb.start(); processhandle handle = process.tohandle(); boolean terminated = handle.onexit().thenaccept(p -> system.out.println("process " + p.pid() + " terminated") ).isdone();
实用场景:系统管理工具、守护进程、执行外部命令、监控应用。
jdk 10 局部变量推断
6. 局部变量类型推断(var):告别冗长的变量声明
jdk10引入了局部变量类型推断,使用var
关键字让编译器推断变量类型:
// 旧方式 - 类型重复且冗长 hashmap<string, list<customer>> customersbycity = new hashmap<>(); bufferedreader reader = new bufferedreader(new filereader("data.txt")); urlconnection connection = new url("https://example.com").openconnection(); // jdk10方式 - 简洁明了 var customersbycity = new hashmap<string, list<customer>>(); var reader = new bufferedreader(new filereader("data.txt")); var connection = new url("https://example.com").openconnection(); // 在for循环中特别有用 for (var entry : customersbycity.entryset()) { var city = entry.getkey(); var customers = entry.getvalue(); // ... }
注意事项:
var
只能用于局部变量,不能用于字段、方法参数或返回类型- 声明时必须初始化变量
- 不要过度使用,当类型不明显时应该明确声明类型
最佳实践:
// 好的用法 - 类型明确 var customers = new arraylist<customer>(); var entry = map.entry("key", "value"); // 避免的用法 - 类型不明确 var result = getresult(); // 返回类型不明显 var x = 1; // 基本类型推荐显式声明
实用场景:复杂泛型类型、匿名类、lambda表达式中的变量。
7. 不可修改集合的复制方法:集合转换更安全
jdk10为集合框架增加了copyof
方法,创建不可修改的集合副本:
// 原始集合 list<string> original = new arraylist<>(list.of("java", "kotlin", "scala")); set<integer> originalset = new hashset<>(set.of(1, 2, 3)); map<string, integer> originalmap = new hashmap<>(map.of("one", 1, "two", 2)); // 创建不可修改的副本 list<string> copy = list.copyof(original); set<integer> copiedset = set.copyof(originalset); map<string, integer> copiedmap = map.copyof(originalmap); // 修改原集合不影响副本 original.add("groovy"); system.out.println(original); // [java, kotlin, scala, groovy] system.out.println(copy); // [java, kotlin, scala] // 尝试修改副本会抛出异常 try { copy.add("clojure"); // 抛出 unsupportedoperationexception } catch (unsupportedoperationexception e) { system.out.println("cannot modify immutable copy"); }
与collections.unmodifiablelist()
不同,list.copyof()
会创建一个全新的集合,如果原集合已经是不可修改的,则可能直接返回原集合而不是副本。
实用场景:防御性编程、返回安全的集合副本、创建常量集合。
jdk 11 长期支持版功能增强
8. string新方法:文本处理得心应手
jdk11为string类增加了几个实用方法:
// 1. lines() - 按行分割字符串 string multiline = "java\nkotlin\nscala"; multiline.lines() .map(string::touppercase) .foreach(system.out::println); // 2. strip(), stripleading(), striptrailing() - 去除空白字符 string text = " hello world "; system.out.println(">" + text.strip() + "<"); // >hello world< system.out.println(">" + text.stripleading() + "<"); // >hello world < system.out.println(">" + text.striptrailing() + "<"); // > hello world< // strip()与trim()的区别: strip()识别更多的unicode空白字符 string unicodewhitespace = "\u2005hello\u2005"; system.out.println(">" + unicodewhitespace.trim() + "<"); // >⠀hello⠀< system.out.println(">" + unicodewhitespace.strip() + "<"); // >hello< // 3. isblank() - 检查字符串是否为空白 system.out.println(" ".isblank()); // true system.out.println("".isblank()); // true system.out.println(" a ".isblank()); // false // 4. repeat() - 重复字符串 string star = "*"; system.out.println(star.repeat(10)); // ********** system.out.println("=".repeat(20)); // ====================
实用场景:处理用户输入、解析文本文件、构建格式化输出。
9. files新方法:文件读写一步到位
jdk11为files类添加了几个便捷方法:
// 读取文件为string string content = files.readstring(path.of("config.json")); // 写入string到文件 files.writestring(path.of("output.txt"), "hello java 11!"); // 使用指定编码 string content = files.readstring(path.of("data.txt"), standardcharsets.utf_8); files.writestring(path.of("log.txt"), "logged at: " + localdatetime.now(), standardcharsets.utf_8); // 写入字符串集合 list<string> lines = list.of("line 1", "line 2", "line 3"); files.write(path.of("lines.txt"), lines);
实用场景:读取配置文件、生成报告、日志记录、快速文件i/o。
10. 标准http客户端:现代化网络请求
jdk11将http client从孵化模块升级为标准api,提供了现代化的http客户端:
// 创建http客户端 httpclient client = httpclient.newbuilder() .connecttimeout(duration.ofseconds(10)) .build(); // 构建get请求 httprequest request = httprequest.newbuilder() .uri(uri.create("https://api.github.com/users/octocat")) .header("user-agent", "java 11 httpclient") .get() .build(); // 同步发送请求,接收json响应 httpresponse<string> response = client.send(request, httpresponse.bodyhandlers.ofstring()); system.out.println("status code: " + response.statuscode()); system.out.println("body: " + response.body()); // post请求示例 httprequest postrequest = httprequest.newbuilder() .uri(uri.create("https://httpbin.org/post")) .header("content-type", "application/json") .post(httprequest.bodypublishers.ofstring("{"name": "java"}")) .build(); // 异步发送请求 client.sendasync(postrequest, httpresponse.bodyhandlers.ofstring()) .thenapply(httpresponse::body) .thenaccept(system.out::println) .join(); // 处理json响应(需要json库) client.sendasync(request, httpresponse.bodyhandlers.ofstring()) .thenapply(httpresponse::body) .thenapply(body -> { // 使用jackson或gson解析json return body; }) .thenaccept(system.out::println) .join();
实用场景:restful api调用、微服务通信、网络爬虫、数据集成。
jdk 12 语言和库的改进
11. string.transform():链式字符串处理
jdk12为string类添加了transform
方法,支持链式函数转换:
// 传统方式 string original = "hello, world!"; string result = original.touppercase(); result = result.substring(0, 5); result = result + "..."; // 使用transform方式 string result = "hello, world!" .transform(string::touppercase) .transform(s -> s.substring(0, 5)) .transform(s -> s + "..."); system.out.println(result); // hello... // 复杂转换 string parsed = "{ "name": "john", "age": 30 }" .transform(json -> { // 解析json // 此处简化,实际应使用jackson等 return json.substring(json.indexof("name") + 7, json.indexof("age") - 3); }) .transform(string::trim) .transform(string::touppercase); system.out.println(parsed); // john
对于多步转换特别有用,提高了代码可读性。
实用场景:数据转换链、复杂字符串处理、函数式数据处理管道。
12. compact number formatting:数字的可读性表示
jdk12引入了紧凑数字格式化功能,可以将大数字格式化为更易读的形式:
// 创建简短格式的格式化器 numberformat shortformatter = numberformat.getcompactnumberinstance( locale.us, numberformat.style.short); // 格式化数字 system.out.println(shortformatter.format(1000)); // 1k system.out.println(shortformatter.format(1500)); // 2k (四舍五入) system.out.println(shortformatter.format(1000000)); // 1m system.out.println(shortformatter.format(1000000000)); // 1b // 长格式 numberformat longformatter = numberformat.getcompactnumberinstance( locale.us, numberformat.style.long); system.out.println(longformatter.format(1000)); // 1 thousand system.out.println(longformatter.format(1000000)); // 1 million // 其他语言的格式化 numberformat germanformatter = numberformat.getcompactnumberinstance( locale.germany, numberformat.style.short); system.out.println(germanformatter.format(1000)); // 1.000 numberformat chineseformatter = numberformat.getcompactnumberinstance( locale.china, numberformat.style.short); system.out.println(chineseformatter.format(1000)); // 1千 system.out.println(chineseformatter.format(1000000)); // 100万 // 自定义精度 shortformatter.setmaximumfractiondigits(1); system.out.println(shortformatter.format(1234)); // 1.2k system.out.println(shortformatter.format(1500)); // 1.5k
实用场景:用户界面显示、仪表盘开发、数据可视化、国际化应用。
jdk 14 友好错误信息与语言改进
13. 友好的nullpointerexception:告别空指针调试噩梦
jdk14增强了nullpointerexception,异常消息中会准确指出哪个变量是null:
// 假设有这样的代码 user user = null; string city = user.getaddress().getcity();
在jdk14之前,你会得到一个简单的消息:
exception in thread "main" java.lang.nullpointerexception at main.main(main.java:5)
在jdk14及之后,异常消息变得非常具体:
exception in thread "main" java.lang.nullpointerexception: cannot invoke "user.getaddress()" because "user" is null at main.main(main.java:5)
对于更复杂的表达式:
map.get("key").process().getnestedvalue();
增强的npe消息会明确指出哪一部分是null:
exception in thread "main" java.lang.nullpointerexception: cannot invoke "result.getnestedvalue()" because the return value of "processeddata.process()" is null
实用场景:调试复杂对象链、排查第三方库错误、缩短问题定位时间。
14. switch表达式:更简洁的分支处理
jdk14正式发布了switch表达式(最初在jdk12引入为预览特性):
// 传统switch语句 string result; dayofweek day = localdate.now().getdayofweek(); switch (day) { case monday: case tuesday: case wednesday: case thursday: case friday: result = "weekday"; break; case saturday: case sunday: result = "weekend"; break; default: result = "invalid day"; break; } // 新的switch表达式 string result = switch (day) { case monday, tuesday, wednesday, thursday, friday -> "weekday"; case saturday, sunday -> "weekend"; default -> "invalid day"; }; // 复杂表达式,带代码块 int numletters = switch (day) { case monday, friday, sunday -> { system.out.println("six letters day"); yield 6; } case tuesday -> { system.out.println("seven letters day"); yield 7; } case thursday, saturday -> { system.out.println("eight letters day"); yield 8; } case wednesday -> { system.out.println("nine letters day"); yield 9; } default -> { throw new illegalstateexception("invalid day: " + day); } };
主要优点:
- 可以作为表达式返回值
- 箭头语法更简洁
- 使用逗号可以合并多个case
- 不需要break语句,消除了常见的错误源
- 穷尽性检查,确保所有情况都被处理
实用场景:状态机实现、命令处理、配置解析、业务逻辑分派。
15. 记录类(records):数据类不再冗长
jdk14引入了records作为预览特性,在jdk16正式发布,为不可变数据类提供了简洁的语法:
// 传统pojo类 public final class employee { private final string name; private final int id; private final department department; public employee(string name, int id, department department) { this.name = name; this.id = id; this.department = department; } public string getname() { return name; } public int getid() { return id; } public department getdepartment() { return department; } @override public boolean equals(object o) { if (this == o) return true; if (o == null || getclass() != o.getclass()) return false; employee employee = (employee) o; return id == employee.id && objects.equals(name, employee.name) && objects.equals(department, employee.department); } @override public int hashcode() { return objects.hash(name, id, department); } @override public string tostring() { return "employee{" + "name='" + name + ''' + ", id=" + id + ", department=" + department + '}'; } } // 使用record public record employee(string name, int id, department department) { }
records自动生成构造器、访问器、equals/hashcode和tostring方法。
你也可以向record添加额外的构造器、方法和静态成员:
public record point(int x, int y) { // 自定义紧凑构造器 public point { if (x < 0 || y < 0) { throw new illegalargumentexception("coordinates cannot be negative"); } } // 重载构造器 public point() { this(0, 0); } // 实例方法 public double distance(point other) { return math.sqrt(math.pow(this.x - other.x, 2) + math.pow(this.y - other.y, 2)); } // 静态成员 public static final point origin = new point(0, 0); // 静态方法 public static point of(int x, int y) { return new point(x, y); } }
实用场景:dto对象、api响应模型、消息体、不可变数据容器、值对象。
jdk 15-16 文本和类型检查优化
16. 文本块:多行字符串不再痛苦
jdk15正式发布了文本块功能(在jdk13首次预览),让多行字符串变得简单优雅:
// 传统多行字符串 string json = "{\n" + " "name": "john doe",\n" + " "age": 30,\n" + " "address": {\n" + " "street": "123 main st",\n" + " "city": "anytown"\n" + " }\n" + "}"; // 使用文本块 string json = """ { "name": "john doe", "age": 30, "address": { "street": "123 main st", "city": "anytown" } } """; // html示例 string html = """ <html> <body> <h1>hello, world!</h1> </body> </html> """; // sql查询 string query = """ select id, first_name, last_name from employees where department_id = ? order by last_name, first_name """;
文本块还支持字符串插值和格式控制:
// 使用\避免行尾换行 string compact = """ <html>\ <body>\ <p>hello</p>\ </body>\ </html>\ """; // 与string::formatted配合使用 string template = """ dear %s, your order #%d has been shipped on %s. thank you, customer service """; string message = template.formatted("john", 12345, "2023-05-15");
实用场景:sql查询、html/json/xml模板、代码生成、多行文本配置。
17. instanceof模式匹配:类型检查与转换合二为一
jdk16正式发布的instanceof模式匹配简化了类型检查和转换:
// 传统方式 if (obj instanceof string) { string s = (string) obj; if (s.length() > 5) { system.out.println(s.touppercase()); } } // 使用模式匹配 if (obj instanceof string s && s.length() > 5) { system.out.println(s.touppercase()); } // 在复杂条件中使用 if (obj instanceof string s && s.length() > 10 || obj instanceof list<?> list && list.size() > 5) { // 使用s或list } // 与switch配合使用(jdk17预览特性) object value = getvalue(); switch (value) { case string s when s.length() > 5 -> system.out.println("long string: " + s); case string s -> system.out.println("short string: " + s); case list<?> l -> system.out.println("list with " + l.size() + " elements"); default -> system.out.println("unknown type"); }
实用场景:多态对象处理、类型安全转换、空检查简化。
18. 外部内存访问api (foreign memory access):安全高效的本地内存操作
jdk16引入了foreign memory access api(孵化器阶段),为java提供了安全高效的本地内存访问能力:
// 分配堆外内存 try (arena arena = arena.ofconfined()) { // 分配100字节的本地内存 memorysegment segment = arena.allocate(100); // 写入数据 memorysegment.copy(new byte[] {1, 2, 3, 4, 5}, 0, segment, 0, 5); // 读取数据 byte value = segment.get(valuelayout.java_byte, 2); // 读取索引2的值 system.out.println("value at index 2: " + value); // 输出 3 // 填充内存段 memorysegment.fill(segment, (byte) 10); // 使用varhandle操作内存 varhandle inthandle = valuelayout.java_int.varhandle(); inthandle.set(segment, 0, 42); int result = (int) inthandle.get(segment, 0); system.out.println("integer value: " + result); // 输出 42 // 内存地址操作 long address = segment.address().torawlongvalue(); system.out.println("memory address: 0x" + long.tohexstring(address)); }
这个api在jdk17中得到了改进,在jdk21中正式发布。它为需要处理大量数据的应用程序提供了比bytebuffer更强大的替代方案。
实用场景:高性能计算、大数据处理、网络应用、与本地库集成。
jdk 17 长期支持版的强大特性
19. 密封类(sealed classes):精确控制继承关系
jdk17正式发布的密封类允许更精确地控制哪些类可以继承一个类:
// 声明一个密封接口,只允许特定的类实现它 public sealed interface shape permits circle, rectangle, triangle { double area(); } // 最终实现类,不允许进一步继承 public final class circle implements shape { private final double radius; public circle(double radius) { this.radius = radius; } @override public double area() { return math.pi * radius * radius; } } // 允许进一步继承的类 public non-sealed class rectangle implements shape { private final double width; private final double height; public rectangle(double width, double height) { this.width = width; this.height = height; } @override public double area() { return width * height; } } // 限制继承的类 public sealed class triangle implements shape permits equilateraltriangle, righttriangle { // ...实现... } // 允许的子类 public final class equilateraltriangle extends triangle { // ...实现... } public final class righttriangle extends triangle { // ...实现... }
密封类与switch模式匹配和记录类结合使用时特别强大,编译器可以进行穷尽性检查:
double calculatearea(shape shape) { return switch (shape) { case circle c -> math.pi * c.radius() * c.radius(); case rectangle r -> r.width() * r.height(); case triangle t -> t.base() * t.height() / 2; // 不需要default分支,因为编译器知道这些是shape的所有可能实现 }; }
实用场景:领域模型设计、类型安全的api、状态机实现、编译器检查增强。
20. 增强的伪随机数生成器:更灵活、可预测的随机数
jdk17引入了增强的伪随机数生成器(prng)框架,提供了更多算法和更好的接口:
// 获取默认的随机数生成器 randomgenerator random = randomgenerator.getdefault(); system.out.println(random.nextint(100)); // 0-99之间的随机数 // 使用特定算法的生成器 randomgenerator xoroshiro = randomgenerator.of("xoroshiro128plusplus"); system.out.println(xoroshiro.nextlong()); // 使用l32x64mixrandom - 平衡了速度和质量的算法 randomgenerator fastrandom = randomgenerator.of("l32x64mixrandom"); for (int i = 0; i < 5; i++) { system.out.println(fastrandom.nextint(1000)); } // 创建可复现的随机数序列 (使用相同的种子) randomgenerator seeded = randomgenerator.of("xoshiro256plusplus"); ((splittablerandomgenerator) seeded).setseed(42); for (int i = 0; i < 5; i++) { system.out.println(seeded.nextint(100)); } // 生成随机流 doublestream randomdoubles = randomgenerator.getdefault().doubles(1000); randomdoubles.foreach(system.out::println); // 查看所有可用的算法 randomgenerator.all() .map(provider -> provider.name() + ": " + provider.group()) .sorted() .foreach(system.out::println);
实用场景:科学计算、模拟、游戏开发、测试数据生成、加密应用。
21. 向量api (incubator):性能密集型计算
jdk17引入了孵化器阶段的向量api,支持simd(单指令多数据)风格的操作,显著加速特定类型的计算:
// 使用intvector加速数组求和 static int sumarrayvectorized(int[] a) { var species = intvector.species_preferred; var sum = intvector.zero(species); var i = 0; // 处理可以向量化的部分 for (; i <= a.length - species.length(); i += species.length()) { var v = intvector.fromarray(species, a, i); sum = sum.add(v); } // 处理剩余元素 var result = sum.reducelanes(vectoroperators.add); for (; i < a.length; i++) { result += a[i]; } return result; } // 向量化矩阵乘法 static void multiplymatricesvectorized(float[] a, float[] b, float[] c, int n) { var species = floatvector.species_preferred; int limit = species.length(); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { var sum = floatvector.zero(species); int k = 0; // 使用向量计算 for (; k <= n - limit; k += limit) { var va = floatvector.fromarray(species, a, i * n + k); var vb = floatvector.fromarray(species, b, k * n + j); sum = sum.add(va.mul(vb)); } // 累加向量结果 float dotproduct = sum.reducelanes(vectoroperators.add); // 处理剩余元素 for (; k < n; k++) { dotproduct += a[i * n + k] * b[k * n + j]; } c[i * n + j] = dotproduct; } } }
jdk19和jdk21继续改进了向量api,但截至jdk21仍处于孵化器阶段。
实用场景:科学计算、图像处理、机器学习、信号处理、金融模拟。
jdk 18-19 工具链和api增强
22. 简单web服务器:快速启动静态文件服务
jdk18引入了一个简单的命令行http服务器,可以快速启动静态文件服务:
// 命令行用法 // jwebserver -p 8000 -d /path/to/directory // 在代码中使用: import com.sun.net.httpserver.*; import java.io.ioexception; import java.net.inetsocketaddress; import java.nio.file.path; public class simplefileserver { public static void main(string[] args) throws ioexception { var server = simplefileserver.createfileserver( new inetsocketaddress(8000), path.of("/path/to/directory"), simplefileserver.outputlevel.verbose ); server.start(); system.out.println("server started at http://localhost:8000"); } }
这个功能特别适合开发和测试环境,比如快速预览静态网站或测试api调用。
实用场景:前端开发、静态网站预览、本地测试环境、原型开发。
23. 代码片段in javadoc:更好的api文档
jdk18引入了@snippet
标签,允许在javadoc中添加带有语法高亮的代码示例:
/** * this class provides utility methods for string operations. * * <p>example usage:</p> * {@snippet : * string result = stringutils.capitalize("hello"); // returns "hello" * boolean isempty = stringutils.isblank(" "); // returns true * } */ public class stringutils { // 类实现省略 }
增强的文档还支持高亮、区域标记和错误标记:
/** * example of snippet with highlighting: * {@snippet : * // @highlight region="important" * string encoded = base64.getencoder().encodetostring(data); * // @end * * // @highlight regex="data" type="bold" * byte[] decoded = base64.getdecoder().decode(encoded); * * // @replace regex="badpractice()" replacement="goodpractice()" type="error" * result = badpractice(); * } */
实用场景:api文档、开源项目、技术指南、教程编写。
24. 外部函数接口 (foreign function & memory api)
jdk19改进了孵化器中的外部函数接口(在jdk21中正式发布),让java与本地代码交互更加简单:
// 定义c库函数的接口 import java.lang.foreign.*; import static java.lang.foreign.valuelayout.*; public class libcdemo { public static void main(string[] args) { // 获取c标准库的链接器 linker linker = linker.nativelinker(); // 查找printf函数 symbollookup stdlib = linker.defaultlookup(); methodhandle printf = stdlib.find("printf") .map(addr -> linker.downcallhandle( addr, functiondescriptor.of(java_int, address), linker.option.firstvariadicarg(1) )) .orelsethrow(); // 准备字符串参数 try (arena arena = arena.ofconfined()) { memorysegment cstring = arena.allocateutf8string("hello from java! count: %d\n"); // 调用printf try { printf.invoke(cstring, 42); } catch (throwable e) { e.printstacktrace(); } } } }
这个api消除了jni的大部分复杂性,提供了更安全、更简洁的方式来调用本地代码。
实用场景:与c/c++库集成、系统编程、性能关键应用、多语言项目。
jdk 20-21 现代并发和语言增强
25. 虚拟线程(virtual threads):并发革命
jdk21正式发布了虚拟线程,这是java并发编程的重大变革:
// 创建和启动单个虚拟线程 thread.startvirtualthread(() -> { system.out.println("running in virtual thread"); }); // 使用虚拟线程运行多个任务 try (var executor = executors.newvirtualthreadpertaskexecutor()) { // 提交1000个任务,每个在独立的虚拟线程中运行 for (int i = 0; i < 1000; i++) { int taskid = i; executor.submit(() -> { system.out.println("task " + taskid + " running on " + thread.currentthread()); // 模拟io操作 try { thread.sleep(duration.ofmillis(100)); } catch (interruptedexception e) { thread.currentthread().interrupt(); } return taskid; }); } } // 自动关闭executor // 以构建器方式创建虚拟线程 threadfactory factory = thread.ofvirtual().name("worker-", 0).factory(); thread worker = factory.newthread(() -> { // 任务代码 }); worker.start(); // 使用虚拟线程改写传统的阻塞式io代码 void processfile(path file) throws ioexception { // 这段代码在虚拟线程中运行时不会阻塞平台线程 try (var reader = files.newbufferedreader(file)) { string line; while ((line = reader.readline()) != null) { processline(line); } } }
虚拟线程的主要优势是可以创建数百万个轻量级线程,而不会耗尽系统资源。它们特别适合io密集型应用,使同步代码可以获得与异步代码相当的扩展性。
实用场景:高并发web服务器、微服务、数据处理管道、爬虫程序。
26. 结构化并发(structured concurrency)
可以管理异步任务的生命周期
jdk21引入了结构化并发api(预览特性),简化了多线程代码的错误处理和资源管理:
// 并行获取用户及其订单 record user(int id, string name) {} record order(int id, double amount) {} record userwithorders(user user, list<order> orders) {} userwithorders getuserwithorders(int userid) { try (var scope = new structuredtaskscope.shutdownonfailure()) { // 并行执行两个子任务 future<user> userfuture = scope.fork(() -> fetchuser(userid)); future<list<order>> ordersfuture = scope.fork(() -> fetchorders(userid)); // 等待所有任务完成 scope.join(); // 检查子任务是否有异常 scope.throwiffailed(e -> new runtimeexception("failed to fetch data", e)); // 获取结果 return new userwithorders(userfuture.resultnow(), ordersfuture.resultnow()); } catch (exception e) { throw new runtimeexception(e); } } // 使用结构化并发处理多个api调用 try (var scope = new structuredtaskscope.shutdownonfailure()) { var weatherfuture = scope.fork(() -> callweatherapi()); var trafficfuture = scope.fork(() -> calltrafficapi()); var newsfuture = scope.fork(() -> callnewsapi()); try { scope.join(); scope.throwiffailed(); // 所有api调用成功,处理结果 var dashboard = createdashboard( weatherfuture.resultnow(), trafficfuture.resultnow(), newsfuture.resultnow() ); return dashboard; } catch (exception e) { // 有一个api调用失败,所有任务都被取消 return createfallbackdashboard(); } }
结构化并发确保所有子任务在父任务退出前要么完成,要么被取消,避免了资源泄漏和"遗忘"的后台任务。
实用场景:api聚合、微服务通信、并行数据处理、复杂异步工作流。
27. record模式(record patterns):解构数据更简单
jdk21正式发布的record模式允许在模式匹配中解构记录:
// 定义一些记录 record point(int x, int y) {} record rectangle(point topleft, point bottomright) {} record circle(point center, int radius) {} // 使用传统方式处理记录 object shape = new rectangle(new point(1, 2), new point(5, 6)); if (shape instanceof rectangle) { rectangle r = (rectangle) shape; point topleft = r.topleft(); point bottomright = r.bottomright(); int width = bottomright.x() - topleft.x(); int height = bottomright.y() - topleft.y(); system.out.println("rectangle with width " + width + " and height " + height); } // 使用record模式解构 if (shape instanceof rectangle(point(var x1, var y1), point(var x2, var y2))) { int width = x2 - x1; int height = y2 - y1; system.out.println("rectangle with width " + width + " and height " + height); } // 结合switch使用 string getdescription(object shape) { return switch (shape) { case rectangle(point(var x1, var y1), point(var x2, var y2)) -> "rectangle from (%d,%d) to (%d,%d)".formatted(x1, y1, x2, y2); case circle(point(var x, var y), var r) -> "circle at (%d,%d) with radius %d".formatted(x, y, r); default -> "unknown shape"; }; }
record模式与嵌套模式结合使用时特别强大,可以轻松处理复杂的数据结构。
实用场景:数据转换、json/xml解析结果处理、领域模型操作、事件处理。
28. 字符串模板(string templates):安全高效的字符串插值
jdk21引入了字符串模板作为预览特性,提供比字符串连接更简洁、更安全的方式:
// 传统方式 string name = "alice"; int age = 30; string message = "hello, " + name + "! next year, you'll be " + (age + 1) + "."; // 使用字符串模板 string message = str."hello, {name}! next year, you'll be {age + 1}."; // 带表达式的模板 string status = "active"; string message = str."user status: {status.touppercase()} (set {localdate.now()})"; // 格式化数字 double value = 1234.56789; string formatted = str."the value is {value%.2f}"; // "the value is 1234.57" // 多行json string json = str.""" { "name": "{name}", "age": {age}, "isadult": {age >= 18}, "contacts": [ {generatecontactsjson()} ] } """;
字符串模板不仅语法简洁,还提供了类型安全和对表达式的编译时检查。
实用场景:生成json/xml/html、日志记录、消息格式化、sql语句构建。
29. 序列集合 (sequenced collections):统一的集合操作
jdk21引入了sequencedcollection
、sequencedset
和sequencedmap
接口,为java集合框架添加了方向性和顺序性操作:
// 序列化集合基本用法 sequencedcollection<string> names = new arraylist<>(list.of("alice", "bob", "charlie")); // 获取第一个和最后一个元素 string first = names.getfirst(); // "alice" string last = names.getlast(); // "charlie" // 添加元素到两端 names.addfirst("zoe"); names.addlast("david"); system.out.println(names); // [zoe, alice, bob, charlie, david] // 创建反向视图 sequencedcollection<string> reversed = names.reversed(); system.out.println(reversed); // [david, charlie, bob, alice, zoe] // 序列化map sequencedmap<string, integer> scores = new linkedhashmap<>(); scores.put("alice", 95); scores.put("bob", 85); scores.put("charlie", 90); // 获取第一个和最后一个条目 map.entry<string, integer> firstentry = scores.firstentry(); // alice=95 map.entry<string, integer> lastentry = scores.lastentry(); // charlie=90 // 添加条目到两端 scores.putfirst("zoe", 100); scores.putlast("david", 80); // 获取键或值的序列化视图 sequencedcollection<string> keys = scores.sequencedkeyset(); sequencedcollection<integer> values = scores.sequencedvalues();
这些接口统一了java集合框架中的顺序操作,使api更加一致。现有的有序集合类如arraylist
、linkedhashset
和linkedhashmap
都实现了这些新接口。
实用场景:维持插入顺序的集合、fifo/lifo队列操作、双向迭代、有序数据处理。
总结
java语言在保持向后兼容性的同时,不断引入新特性以提高开发效率和代码质量。掌握这些新特性不仅能提高开发效率,还能编写出更加简洁、健壮的代码。
到此这篇关于jdk9到jdk21中值得掌握的29个实用特性分享的文章就介绍到这了,更多相关jdk实用特性内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论