简介:《JDK 11 API中文手册》为Java开发者提供了JDK 11中所有类、接口、方法和异常的详尽记录和使用指南。JDK 11作为长期支持版本,引入了模块化系统、HTTP客户端、动态类型语言支持等新特性,并增强了安全性和性能。本手册深入介绍这些关键更新,提供示例代码帮助开发者理解和应用,旨在提升开发效率和代码质量。
1. JDK 11 API详尽文档
1.1 Java 11 API概述
Java开发工具包(JDK)的每个新版本都会引入对API的更新和改进。JDK 11作为Java平台的一个重要更新,也不例外。它提供了一系列新的API,旨在提高开发者的生产力和系统的性能。这些更新不仅包括对现有功能的改进,还引入了一些全新的功能,例如HTTP客户端API、新的字符串处理方法、以及对G1垃圾收集器的一些增强。
1.2 新API的价值和影响
新API的价值在于它能够减少开发者在编写代码时的工作量,提高效率。例如,引入的 var
关键字允许开发者在局部变量声明中省略类型,使代码更加简洁。同时,JDK 11的HTTP客户端API提供了一种更现代化、更有效的方式来处理HTTP请求和响应,相比旧的 HttpURLConnection
有显著的优势。JDK 11的API更新强调了Java作为现代编程语言的能力,以及对现代开发需求的响应。
1.3 开始使用新API
要开始使用JDK 11的新API,开发者首先需要确保他们使用的是JDK 11或更高版本的环境。然后,可以通过查阅 官方文档 来了解新API的详细信息和使用方法。例如,使用 var
关键字时,可以简单地在变量声明中指定初始值来让编译器自动推断其类型:
var message = "Hello, JDK 11!";
接下来,可以在实际项目中尝试使用新的HTTP客户端API进行网络请求:
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
通过这样的实践,开发者可以逐步熟悉并利用JDK 11的新API来优化和提升现有的Java应用。
2. 模块化系统(Project Jigsaw)
2.1 模块化系统简介
2.1.1 模块化的历史背景
在软件工程领域中,模块化是一种设计原则,旨在将复杂的系统分解为易于管理和理解的组件。自从软件工程概念诞生之初,模块化就一直扮演着至关重要的角色。随着应用程序变得越来越复杂,需要一种机制来组织代码并提供更严格的封装边界。这种需求催生了模块化系统的出现。
Java平台自其诞生以来就一直采用包(package)来组织类和接口,但随着项目规模的扩大,传统的包结构开始暴露出若干局限性。例如,包之间往往存在难以控制的依赖关系,使得项目难以维护和重构。这些问题促使Java社区寻找更为先进的模块化解决方案。
2.1.2 JDK 11中模块化的目标和设计
为了解决上述问题,Project Jigsaw项目应运而生,旨在为Java平台引入正式的模块系统。JDK 9的发布标志着这一长期项目的重要里程碑。JDK 11进一步强化了模块化系统的稳定性和可用性。
JDK 11的模块化系统主要有以下几个设计目标: - 明确的模块边界:每个模块可以明确声明它提供的API和它需要依赖的模块。 - 更好的封装:模块可以隐藏内部实现细节,只公开必要的接口。 - 增强的性能:通过减少不必要的类加载和更高效的依赖解析,提高运行时性能。
JDK 11的模块系统设计基于Jigsaw项目,每个模块由模块声明(module-info.java文件)和一系列Java包组成。这种设计既提供了向后兼容性,也支持面向未来的模块化编程范式。
2.2 模块化编程实践
2.2.1 模块的定义和声明
在JDK 11中定义模块需要创建一个名为module-info.java的文件,该文件位于模块的根目录下。module-info.java文件使用module关键字开始,后跟模块的名称。模块声明可以包含模块依赖声明、导出包声明以及使用服务的声明。
例如,定义一个名为"my.module"的基本模块,可以这样写:
module my.module {
// 导出特定的包到其他模块
exports com.my.module.package1;
// 依赖其他模块
requires some.other.module;
// 使用服务接口和实现
uses com.my.service.ServiceInterface;
provides com.my.service.ServiceInterface with com.my.service.ServiceImpl;
}
2.2.2 模块间的依赖管理
模块化系统的核心是管理模块间的依赖关系。在JDK 11中,可以通过module-info.java文件中的requires指令来声明模块需要依赖的其他模块。依赖管理有助于避免循环依赖,提供清晰的模块间交互和边界。
如果模块A需要使用模块B中定义的类,那么模块A应该在module-info.java文件中声明:
requires moduleB;
2.2.3 模块化开发的工具链和最佳实践
随着模块化系统的引入,Java开发工具链也进行了更新,以支持模块化开发。开发人员通常使用JDK自带的jmod和jlink工具来构建模块化应用和分发模块化应用。
JDK 11推荐的最佳实践包括: - 使用jmod命令创建模块化JAR包。 - 使用jlink命令创建自定义的运行时映像,只包含应用所需模块。 - 使用jshell进行模块化代码的交互式测试和验证。
2.3 模块化系统的挑战与展望
2.3.1 面临的问题和解决方案
模块化系统的引入带来了诸如迁移成本高、学习曲线陡峭以及对现有系统的破坏性更新等问题。为了解决这些问题,JDK 11提供了过渡选项,允许模块化和非模块化代码同时运行,并逐步引导开发者向模块化迁移。
例如,JDK提供了--add-modules和--limit-modules参数来在运行时指定模块依赖,以支持混合模式的应用。
2.3.2 模块化对Java生态系统的影响
模块化对Java生态系统有深远的影响。首先,模块化推动了API的清晰定义和封装,增强了Java平台的健壮性。其次,模块化为构建更小、更高效的运行时映像提供了可能,这在微服务和容器化环境中尤为重要。
此外,模块化也鼓励了社区采用更好的代码组织实践,提高了大型Java项目的可维护性。随着时间的推移,预计Java生态中会涌现出更多以模块为中心的项目和库,从而推动整个平台向前发展。
3. HTTP客户端API
3.1 HTTP客户端API概述
3.1.1 新旧HTTP客户端API对比
在Java 11中,引入了一个全新的HTTP客户端API,取代了早期版本中基于HttpURLConnection以及Apache HttpClient和OkHttp等第三方库的使用。新旧API在设计理念、性能以及易用性方面有着本质的区别。旧的HttpURLConnection提供了同步和基于线程池的异步请求处理能力,但由于其API设计较为陈旧,使用起来显得笨拙且不易于调试。另一方面,虽然Apache HttpClient和OkHttp在社区中被广泛使用,并提供丰富的特性和更好的性能,但它们并不是Java标准库的一部分,这导致在不同环境下可能出现不一致的行为。
新引入的HTTP客户端API基于Java 9中实验性引入的JEP 110,旨在提供一个现代、高效且功能丰富的HTTP客户端实现。它支持异步非阻塞操作,改进了性能,并且提供了对HTTP/2的原生支持。此外,新API还简化了API的使用,提供了一致的编程模型,从而使得HTTP通信更加方便。
3.1.2 异步处理与响应式编程支持
新HTTP客户端API的一大亮点是它对异步操作和响应式编程模式的原生支持。在现代的网络应用中,异步处理是提高效率和系统吞吐量的关键技术之一。新API使用CompletableFuture来提供异步操作,这使得开发者能够以声明式的方式编写非阻塞代码,而无需担心底层线程管理的复杂性。
响应式编程则是一种声明式的编程范式,专注于数据流和变化的传播。它允许开发者将代码编写为一系列的异步数据流操作,这些操作会自动响应底层数据源的变化。在JDK 11中,HTTP客户端API通过与Java的响应式流规范(Reactive Streams)的整合,使得开发者可以将HTTP通信逻辑无缝地融入到响应式应用架构中。这不但提升了网络应用的响应性和伸缩性,还降低了错误处理的复杂性。
3.2 HTTP客户端编程实战
3.2.1 创建和管理HTTP请求
要创建和管理HTTP请求,可以使用 HttpClient
类及其相关方法。以下是一个创建基本HTTP GET请求的示例代码:
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
public class SimpleHttpClientExample {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com"))
.build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println(response.body());
}
}
在这个例子中,首先通过 HttpClient.newHttpClient()
创建了一个HTTP客户端实例。然后,使用 HttpRequest.newBuilder()
构建了一个HTTP请求,指定了目标URI。 HttpRequest
对象通过调用 build()
方法来完成构建。之后,使用 client.send()
方法发送请求,并通过 BodyHandlers.ofString()
指定了如何处理响应体,这里将响应体作为字符串处理。
3.2.2 处理HTTP响应和错误
处理HTTP响应时,首先应该检查响应状态码来确定请求是否成功。下面的代码段展示了如何获取状态码并根据状态码进行适当的错误处理:
if (response.statusCode() == 200) {
// 请求成功
System.out.println("Response Status: " + response.statusCode());
} else {
// 请求失败,处理错误
System.err.println("Request failed with status code: " + response.statusCode());
throw new RuntimeException("Failed to fetch resource");
}
如果响应状态码指示请求未成功,则代码抛出一个异常。异常处理机制在这里可以用于触发重试逻辑,或者在更复杂的场景中,可以用于触发错误恢复过程。
3.2.3 客户端与服务器端的交互示例
与服务器端的交互不仅包括基本的GET请求,还包括发送POST请求以及处理JSON数据。以下是一个发送POST请求并发送JSON数据的示例代码:
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpRequest.BodyPublishers;
import org.json.JSONObject;
public class HttpPostExample {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
JSONObject jsonInput = new JSONObject();
jsonInput.put("key1", "value1");
jsonInput.put("key2", "value2");
StringEntity entity = new StringEntity(jsonInput.toString(), ContentType.APPLICATION_JSON);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com/post"))
.header("Content-type", "application/json")
.POST(BodyPublishers.ofEntity(entity))
.build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println("Response Status: " + response.statusCode());
System.out.println(response.body());
}
}
在上面的代码中,我们首先创建了一个 JSONObject
并填充了数据,然后将其转换为字符串。我们使用 StringEntity
和 BodyPublishers.ofEntity
将字符串包装成一个实体,通过POST方法发送到服务器端。最后,打印出服务器的响应状态和响应体。
通过以上示例,可以看出,JDK 11中引入的HTTP客户端API不仅提供了现代的异步处理和响应式编程支持,还使得客户端与服务器端的交互变得更加灵活和高效。
4. 动态类型语言支持(var关键字)
4.1 var关键字介绍
4.1.1 var的引入背景
Java一直被看作是一个强类型的语言,要求在代码中明确地声明变量的类型。然而,这样的要求有时会导致代码冗长且难以阅读。JDK 10中引入的 var
关键字正是为了简化局部变量的声明而设计的。通过允许开发者在声明局部变量时不必显式指定类型,从而让代码更加简洁、易于阅读。
4.1.2 var与局部变量类型推断
在引入 var
之前,Java开发者需要重复书写变量类型,特别是在使用泛型时。这不仅增加了代码的冗余度,也影响了代码的可读性。 var
关键字允许编译器推断变量的具体类型,它不是类型声明的一部分,只是让编译器知道变量的类型必须从声明的右侧推断出来。重要的是要注意, var
仅适用于局部变量,且变量的声明必须初始化。
4.2 var在实际开发中的应用
4.2.1 使用var简化代码
使用 var
可以减少代码中的类型声明,使代码更加简洁。例如,传统的for循环:
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
String item = iterator.next();
// do something with item
}
可以简化为使用 var
的版本:
for (var iterator = list.iterator(); iterator.hasNext();) {
var item = iterator.next();
// do something with item
}
这里,编译器能够从 list.iterator()
的返回类型推断出 iterator
和 item
的类型。
4.2.2 var在复杂场景下的表现
在更复杂的场景下, var
仍然可以保持代码的简洁性。例如,使用lambda表达式时,可以省略具体的函数式接口类型:
var result = list.stream()
.filter(s -> s.contains("example"))
.map(String::toUpperCase)
.collect(Collectors.toList());
在这个例子中, result
的类型会被编译器推断为 List<String>
,这使得代码更加清晰易读。
4.2.3 var与传统类型的性能比较
var
关键字的引入并没有改变Java的类型系统,它只是在编译时提供了一个语法糖。使用 var
和使用传统类型声明在性能上并没有区别,因为 var
声明的变量在编译后的字节码中仍然具有明确的类型信息。在运行时,JVM处理局部变量的方式没有改变,因此开发者可以放心使用 var
而不用担心性能上的损失。
// 这个代码片段展示var声明的变量在字节码中的类型
var number = 42;
System.out.println(number.getClass().getName()); // 输出: java.lang.Integer
上面的例子说明 number
变量在运行时是 Integer
类型,就像它被明确声明一样。
通过以上几个小节的介绍,我们深入了解了 var
关键字的背景、使用方法以及在实际开发中的应用。在接下来的章节中,我们将继续探索 var
在其他复杂场景下的表现和与传统类型变量的性能比较。
5. 文本块(Text Blocks)
文本块是JDK 11中引入的一项新特性,它允许开发者在Java代码中编写多行字符串,并自动处理字符串中的转义序列。随着编程实践中JSON、XML等多行格式数据的广泛应用,文本块的出现极大地简化了代码的可读性和维护性。
5.1 文本块概念解析
5.1.1 文本块的定义和用途
文本块是一个多行的字符串字面量,它允许开发者在编写代码时直接包含多行文本,而无需手动转义换行符和其他特殊字符。文本块以三个双引号( """
)作为开头和结尾,这种语法上的简洁性使得它们特别适用于编写配置文件、SQL查询、JSON或XML数据,以及其他需要多行文本的场景。
文本块的主要用途包括但不限于:
- 配置文件 : 在应用程序中嵌入配置数据,例如数据库连接字符串、API密钥等。
- 模板文本 : 创建HTML、XML、JSON或CSV等格式的模板字符串。
- SQL查询 : 插入复杂的SQL查询语句,无需为了换行进行特殊处理。
5.1.2 文本块的多行字符串处理优势
在传统的Java代码中,如果需要多行字符串,开发者必须使用特殊的转义序列来包含换行和特殊字符。例如,一个简单的HTML段落可能需要写成如下形式:
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, World!</p>\n" +
" </body>\n" +
"</html>";
这种编码方式不仅繁琐,而且可读性差。而使用文本块,同样的内容可以这样写:
String html = """
<html>
<body>
<p>Hello, World!</p>
</body>
</html>
""";
文本块的使用提高了代码的可读性,并且使得编辑多行文本数据成为一种更加自然的体验。
5.2 文本块的使用与实践
5.2.1 文本块的声明和转义规则
文本块的声明非常简单,只需以三个双引号开头和结尾即可。转义规则是文本块的一个重要组成部分,特别是在需要在文本块中包含双引号字符时。
为了在文本块内嵌入双引号,开发者可以使用反斜杠( \
)转义双引号,如:
String json = """
{
"name": "John",
"age": 30,
"city": "New York"
}
""";
如果需要在文本块中包含反斜杠本身,就需要使用两个反斜杠进行转义。
5.2.2 文本块与现有字符串处理的兼容性
文本块的引入不会影响现有的字符串处理逻辑。对于现有的API,文本块会自动转换为普通的字符串对象。例如,调用 .length()
方法时,它返回的是处理后的字符串长度。
尽管文本块在语义上与普通的字符串相同,但它们之间有一个关键的区别:文本块会保留其格式,包括换行和空格。这使得文本块非常适合于那些格式非常重要的场景。
5.2.3 文本块在JSON、XML处理中的应用
在处理JSON或XML数据时,文本块可以显著提高代码的清晰度和可维护性。例如,考虑下面这个JSON字符串:
String json = """
{
"name": "John",
"age": 30,
"city": "New York"
}
""";
文本块允许直接在Java代码中嵌入格式化的JSON,并保持良好的可读性。对于XML数据也有类似的效果,使得数据的处理和调试变得更加直观。
通过这样的实践,开发者可以轻松地将文本块用作数据模板,甚至可以在开发过程中直接使用它们来测试JSON或XML解析器。
import javax.json.Json;
import javax.json.JsonReader;
try (JsonReader reader = Json.createReader(new StringReader(json))) {
JsonObject jsonObject = reader.readObject();
// 处理jsonObject...
}
在上述代码中,文本块被用作JSON数据源,传递给 JsonReader
以解析JSON对象。
以上各节展示了文本块作为JDK 11中的一个重大更新,对多行字符串的处理提供了革命性的改进。其直观的语法和增强的可读性为处理复杂的文本数据提供了便利。随着Java社区对这一特性的熟悉和采用,预期文本块将会在各种应用和框架中扮演越来越重要的角色。
6. 新日期时间API增强
6.1 Java 8日期时间API回顾
Java 8日期时间API的组成和特性
Java 8 引入了一套全新的日期和时间 API,通过 java.time
包下的类来处理日期和时间,弥补了旧的 java.util.Date
和 Calendar
API 的不足。新 API 的设计更符合人的直觉,并且是不可变的,因此线程安全。它解决了诸如时区不一致、日期时间运算不方便等问题。
核心组件包括:
-
LocalDate
:仅包含日期的不可变对象,表示年月日。 -
LocalTime
:仅包含时间的不可变对象,表示时分秒。 -
LocalDateTime
:包含日期和时间的不可变对象。 -
ZonedDateTime
:包含时区信息的LocalDateTime
。 -
Duration
:表示两个时间点之间的时间差。 -
Period
:表示两个日期之间的时间长度。
Java 8日期时间API的常见用途
新 API 的使用场景非常广泛:
- 日期计算 :可以方便地添加或减去天数、月数,易于处理闰年等问题。
- 格式化和解析 :支持自定义日期时间格式化和解析。
- 时区处理 :可以很容易地处理不同时区的数据,不必担心夏令时问题。
- 国际化 :考虑到了不同地区的日期时间习惯。
6.2 JDK 11对日期时间API的扩展
新增的日期时间类和方法
JDK 11 为 java.time
包添加了一些新特性和改进:
- 新增
LocalDateTime::from
和ZonedDateTime::from
方法,可以接受 ISO-8601 格式的字符串。 - 新增
DateTimeFormatter::ofPattern
方法允许使用单引号括起来的文本作为模式的一部分,增强了对日期时间格式的控制。 - 新增
ZonedId::of
方法可以接受更多的标识符,如缩写时区名称,使时区处理更灵活。
日期时间API的性能提升和修正
JDK 11 还对日期时间API的性能进行了优化:
- 减少了在某些操作中的内存占用。
- 修复了一些在特定条件下可能引发的异常。
- 对于使用线程局部存储的日期时间格式化器进行了性能优化。
通过这些扩展,JDK 11 提升了 java.time
API 的功能性和健壮性,使得处理日期和时间变得更加高效和方便。
示例代码与分析
以下是一个使用新日期时间 API 的示例代码,展示如何创建和操作日期时间对象:
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateTimeExample {
public static void main(String[] args) {
// 创建 LocalDate 对象
LocalDate date = LocalDate.now();
System.out.println("Current date: " + date);
// 使用 DateTimeFormatter 格式化日期
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String formattedDate = date.format(formatter);
System.out.println("Formatted date: " + formattedDate);
// 创建 LocalDateTime 对象
LocalDateTime dateTime = LocalDateTime.of(2023, 04, 01, 13, 30);
System.out.println("Specific date and time: " + dateTime);
// 计算 100天后的时间
LocalDateTime futureDateTime = dateTime.plusDays(100);
System.out.println("100 days later: " + futureDateTime);
}
}
输出结果:
Current date: 2023-03-15
Formatted date: 15/03/2023
Specific date and time: 2023-04-01T13:30
100 days later: 2023-07-13T13:30
在这个例子中,我们首先创建了一个 LocalDate
对象来获取当前日期,并使用 DateTimeFormatter
对象将其格式化为特定格式。然后我们创建了一个 LocalDateTime
对象表示一个具体的时间点,并演示了如何使用 plusDays
方法计算100天后的时间。
代码注释已经提供了逻辑上的解释,而参数说明则隐含在方法名和变量命名中,以便于读者理解每个步骤的作用。
7. 强引用集合工厂方法
7.1 强引用集合简介
7.1.1 强引用的概念和特点
在Java编程语言中,强引用是最常见的引用类型。当一个对象通过强引用被引用时,垃圾收集器将不会回收这个对象,即使内存不足。这是因为在Java虚拟机(JVM)中,一个对象只要还有强引用指向它,它就始终被认为是"活着的",不会被垃圾回收。
强引用的主要特点可以概括为以下几点:
- 强引用是Java默认的引用类型,无需显式声明。
- 有强引用指向的对象,不会被垃圾收集器回收。
- 当最后一个强引用消失时,对象变得不可达,随后可能会被垃圾收集器回收。
7.1.2 常用强引用集合类介绍
Java集合框架提供了多种集合类来处理强引用,包括但不限于:
- List :有序的集合,可以包含重复的元素。
- Set :不允许重复元素的集合,通常用于表示一组唯一的数据。
- Map :存储键值对,允许快速检索值。
以上每种集合类型都有多个实现类,例如 ArrayList
, HashSet
, HashMap
等。在实际开发中,开发者会根据需求选择合适的集合类型和实现。
7.2 集合工厂方法的使用和优势
7.2.1 集合工厂方法的定义和使用场景
JDK 9引入了集合工厂方法,使得创建不可变集合变得更为便捷。这些工厂方法定义在 List
, Set
, Map
接口中。工厂方法返回一个不可修改的集合视图,即对集合内容的任何修改都会抛出异常。
下面是一个使用 List.of
工厂方法创建不可变列表的示例:
List<String> immutableList = List.of("apple", "banana", "cherry");
工厂方法的主要使用场景包括:
- 作为方法返回的不可变集合。
- 在多线程环境中共享集合时提供线程安全的引用。
- 用于简单的数据传递,无需担心数据被外部修改。
7.2.2 集合工厂方法与传统构造器的对比
与传统使用构造器创建集合相比,工厂方法有以下优势:
- 简洁性 :直接使用
List.of
等方法,而不需要额外创建对象。 - 不可变性 :工厂方法返回的集合是不可变的,天然线程安全,减少了使用时的错误。
- 性能 :由于不可变性,JVM可以对这些集合进行内部优化。
然而,工厂方法也有局限性,例如:
- 无法直接创建空集合(但可以使用
Set.of()
、List.of()
与Map.of()
传入无元素的参数)。 - 不支持包含
null
值,尝试传入null
会抛出NullPointerException
。
7.2.3 集合工厂方法在项目中的最佳实践
在项目中,集合工厂方法应被用于以下最佳实践:
- 作为方法返回类型 :返回常量集合,例如返回一个静态配置的错误代码列表。
- 数据初始化 :在初始化代码中快速创建小型集合。
- API设计 :设计出易于使用、不易出错的API,例如使用不可变集合来确保输入参数的不变性。
通过使用工厂方法,可以有效减少样板代码,提升代码的可读性和可维护性。考虑到这些优势,推荐在合适的场景中优先采用集合工厂方法。
简介:《JDK 11 API中文手册》为Java开发者提供了JDK 11中所有类、接口、方法和异常的详尽记录和使用指南。JDK 11作为长期支持版本,引入了模块化系统、HTTP客户端、动态类型语言支持等新特性,并增强了安全性和性能。本手册深入介绍这些关键更新,提供示例代码帮助开发者理解和应用,旨在提升开发效率和代码质量。