java 6-21 重要的变动
kotlin 成为了Android的首选语言,但是很遗憾,有的项目不能更换语言开发或者混合开发,但是在Android 开发中很容易忽略JDK的版本特性,因为它不像Android的版本特性一样总是被使用。JDK 这些年都增加了哪些新的功能和语法呢?
Java 6(2006年12月)
- 改进的 JIT 编译器: 增强了 Java 程序的性能。
- 脚本语言支持: 集成了对脚本语言的支持,特别是 JavaScript(通过 Rhino 引擎)。
- Java 编译 API: 提供了一组 API 用于编译 Java 源文件。
- 对 Web 服务的增强: 包括对 JAX-WS 2.0, JAXB 2.0 的支持。
- 改进的 Swing: 引入了桌面外观(SystemTray API)、桌面集成(Desktop API)和对 Windows Vista 的更好支持。
- Pluggable Annotation Processing API: 提供了一个新的 API 用于注解处理。
- 增强的监控和管理: 增加了对 JMX 和 JVM 的增强监控和管理能力。
- 垃圾收集改进: 包括并发标记扫描垃圾收集器(CMS)的改进。
Java 7(2011年7月)
- 语言增强(Project Coin):
- Switch 支持字符串:
switch
语句可以使用字符串。 - 多捕获块: 可以在一个
catch
块中捕获多个异常。 - Try-with-resources 语句: 自动管理资源关闭。
- 钻石操作符: 推断泛型类型以简化代码。
- 二进制字面量: 使用前缀
0b
或0B
表示二进制字面量。 - 下划线分隔符: 数字字面量中可以使用下划线分隔符以提高可读性,例如
1_000_000
。
- Switch 支持字符串:
- Fork/Join 框架: 用于并行执行任务的新框架,旨在充分利用多处理器系统。
- NIO.2 文件 I/O: 新的文件 I/O API,提供更丰富的文件系统操作和改进的异常处理。
- 异步 I/O: 提供了异步文件通道和异步套接字通道。
- 增强的并发实用工具: 包括
Phaser
、TransferQueue
和ConcurrentLinkedDeque
。 - 动态语言支持(InvokeDynamic): 改进 JVM 以更好地支持动态语言(如 JRuby 和 Groovy)。
- 安全增强: 提高了密码学和安全包的性能和灵活性。
- Java 核心类库的改进: 增强了基本库,如
java.util.Objects
,提供了一些实用方法。
7以前基本没什么需要注意的,除了NIO这块。
Java 8(2014年3月)
- Lambda 表达式: 支持函数式编程。
- 函数式接口: 使用
@FunctionalInterface
注解。 - Stream API: 处理集合的序列化操作。
- 默认方法: 接口可以包含默认实现的方法。
- 静态方法: 接口可以包含静态方法。
- Optional 类: 避免空指针异常的容器类。
- 新的日期和时间 API: 包括
LocalDate
,LocalTime
,LocalDateTime
,ZonedDateTime
等。 - Nashorn JavaScript 引擎: 更快的 JavaScript 引擎替代 Rhino。
- 重复注解: 支持在同一位置重复使用注解。
8就不一样了,lambda、Stream API、Optional 开发中使用频率还是比较高的
Java 9(2017年9月)
- 模块系统: 提供更好的封装性和依赖管理。
- JShell: 交互式 Java REPL 工具。
- 改进的 Javadoc: 包括搜索功能和 HTML5 支持。
- 多版本 JAR: 支持在一个 JAR 文件中包含多个版本的类文件。
- 增强的 Stream API: 添加了
takeWhile
,dropWhile
,iterate
方法。 - 工厂方法: 用于创建不可变集合的快捷方法,如
List.of()
,Set.of()
,Map.of()
。
Java 10(2018年3月)
- 局部变量类型推断: 使用
var
关键字。 - 应用数据类共享(AppCDS): 改进的类加载性能。
- 并行 Full GC for G1: G1 垃圾收集器的 Full GC 并行化。
- Root Certificates: 默认包含根证书,简化 SSL 连接。
局部变量的类型推断, 这里需要和kotlin 做个对比
Java 10 的局部变量类型推断和 Kotlin 的类型推断在功能和实现上有许多相似之处,但也有一些显著的区别。以下是两者的详细对比:
Java 10 的局部变量类型推断
-
使用
var
关键字:- 只能在局部变量声明中使用,如方法内的局部变量、增强的
for
循环变量、try-with-resources
语句中的变量。 - 语法示例:
var list = new ArrayList<String>(); for (var item : list) { System.out.println(item); }
- 只能在局部变量声明中使用,如方法内的局部变量、增强的
-
类型推断的范围:
- 只能在初始化时推断类型,必须在声明时赋值。
- 不能用于类成员变量、方法参数或返回类型。
-
限制:
- 无法改变变量的类型,推断类型是编译时确定的,不能使用
var
关键字来声明未初始化的变量。 var
不能用于 lambda 表达式和方法引用的参数。
- 无法改变变量的类型,推断类型是编译时确定的,不能使用
-
目标:
- 主要目的是简化代码的编写,减少样板代码,提高代码的可读性,同时保持 Java 的静态类型系统。
Kotlin 的类型推断
-
使用
val
和var
关键字:val
表示不可变变量,var
表示可变变量。Kotlin 可以在更多场景中使用类型推断,包括类成员变量、方法参数和返回类型。- 语法示例:
val list = ArrayList<String>() for (item in list) { println(item) }
-
类型推断的范围:
- 可以在很多地方使用类型推断,包括局部变量、类属性、函数返回类型等。
- 不要求在声明时初始化,可以在函数参数、返回类型和类属性中使用类型推断。
-
灵活性:
- Kotlin 的类型推断更为广泛和灵活,可以根据上下文推断类型。
- 支持更复杂的类型推断,例如 lambda 表达式和返回类型推断。
-
目标:
- 提供更简洁的语法,提高开发效率,减少样板代码,同时保持 Kotlin 的静态类型系统。
对比总结
-
使用场景和灵活性:
- Java 10: 类型推断只能用于局部变量声明,且必须在声明时初始化。它不能用于类成员变量、方法参数和返回类型。
- Kotlin: 类型推断的应用范围更广,几乎可以在任何地方使用类型推断,包括类成员变量、方法参数和返回类型。
-
关键字:
- Java 10: 使用
var
关键字来表示局部变量的类型推断。 - Kotlin: 使用
val
和var
来分别表示不可变和可变变量,并在更多场景中使用类型推断。
- Java 10: 使用
-
静态类型系统:
- 两者皆有: Java 和 Kotlin 都保持了静态类型系统,通过类型推断来简化代码,同时保持类型安全。
-
限制和特点:
- Java 10: 局部变量类型推断主要是为了减少样板代码,限制较多,使用范围较窄。
- Kotlin: 更加灵活和广泛的类型推断,允许在更多上下文中使用,并且支持更复杂的类型推断逻辑。
示例对比
Java 10 示例:
public class Main {
public static void main(String[] args) {
var list = new ArrayList<String>();
list.add("Java");
for (var item : list) {
System.out.println(item);
}
}
}
Kotlin 示例:
fun main() {
val list = ArrayList<String>()
list.add("Kotlin")
for (item in list) {
println(item)
}
}
总结
Java 10 的局部变量类型推断和 Kotlin 的类型推断都旨在简化代码编写,提高开发效率,但 Kotlin 的类型推断更为广泛和灵活。Java 的局部变量类型推断则更为保守和有限,主要聚焦于局部变量声明,确保不改变 Java 语言的静态类型特性和向后兼容性。
Java 11(2018年9月)
- 新的字符串方法:
isBlank
,lines
,strip
,stripLeading
,stripTrailing
,repeat
。 - 运行 Java 文件: 使用
java
命令直接运行.java
文件。 - 局部变量语法的 lambda 参数: lambda 参数支持
var
。 - HttpClient: 新的
HttpClient
API 用于 HTTP/2 和 WebSocket 支持。 - ZGC(Z Garbage Collector): 低延迟垃圾收集器。
Java 11 引入了一些新的字符串方法,包括 isBlank
, lines
, strip
, stripLeading
, stripTrailing
, repeat
。下面是这些方法的具体用法:
-
isBlank(): 判断字符串是否为空白(只包含空格、制表符、换行符等空白字符)。
String str = " "; System.out.println(str.isBlank()); // 输出 true
-
lines(): 将字符串拆分为行,返回一个流(Stream)。 可以拆分\n \r 或者\n\r这种
String str = "Hello\nWorld\nJava"; str.lines().forEach(System.out::println); // 输出: // Hello // World // Java
-
strip(): 去除字符串两端的空白字符(空格、制表符、换行符等)。
String str = " Hello World "; System.out.println(str.strip()); // 输出 "Hello World"
-
stripLeading(): 去除字符串开头的空白字符。
String str = " Hello World"; System.out.println(str.stripLeading()); // 输出 "Hello World"
-
stripTrailing(): 去除字符串末尾的空白字符。
String str = "Hello World "; System.out.println(str.stripTrailing()); // 输出 "Hello World"
-
repeat(int count): 将字符串重复指定次数。
String str = "Java "; System.out.println(str.repeat(3)); // 输出 "Java Java Java "
这些新的字符串方法可以使字符串操作更加方便和灵活,特别是在处理文本数据时,可以更容易地进行格式化、清理和处理。
Java 12(2019年3月)
- 增强的 switch 语句(预览):
switch
语句可以返回值,并简化语法。 - JVM 常量 API: 新的 API 用于描述常量池中的常量。
- G1 垃圾收集器改进: 改进的暂停时间。
Java 12 引入了增强的 switch
语句,允许 switch
语句返回一个值,并简化了语法。这个功能提供了一种更简洁、更灵活的方式来处理多路分支逻辑。下面是它的具体用法:
public class Main {
public static void main(String[] args) {
int day = 3;
String dayString = switch (day) {
case 1 -> "Monday";
case 2 -> "Tuesday";
case 3 -> "Wednesday";
case 4 -> "Thursday";
case 5 -> "Friday";
default -> throw new IllegalArgumentException("Invalid day of the week: " + day);
};
System.out.println("Today is " + dayString);
}
}
在这个例子中,switch
语句根据 day
的值执行不同的分支,并将结果赋给 dayString
变量。这里的新语法包括:
- 使用
->
替代了case
关键字后的冒号。 - 语句块中的最后一个表达式会作为整个
switch
语句的结果返回。 - 可以使用
yield
关键字来显式返回值,但这是可选的,如果没有yield
,则会隐式地返回语句块的最后一个表达式的值。
这种增强的 switch
语句简化了多路分支逻辑的编写,使代码更加清晰易读。它还允许 switch
语句作为表达式使用,从而在更多情况下简化代码。
Java 13(2019年9月)
- 文本块(预览): 多行字符串文字。
- 动态 CDS 档案: 改进的类数据共享。
- ZGC 改进: 支持解除未使用的内存。
Java 14(2020年3月)
- Switch 表达式:
switch
语句的新语法已正式发布。 - 记录类型(预览): 简化数据载体类的定义。
- 模式匹配(预览):
instanceof
操作符的模式匹配。
instanceof
操作符的模式匹配。这个功能使得对对象进行类型检查和类型转换变得更加简洁和易读。下面是一个示例:
public class Main {
public static void main(String[] args) {
Object obj = "Hello, world!";
if (obj instanceof String str) {
System.out.println("obj 是一个字符串: " + str);
// 在这里可以直接使用 str 变量,它已经被声明为 String 类型
int length = str.length();
System.out.println("字符串长度为:" + length);
} else {
System.out.println("obj 不是一个字符串");
}
}
}
在这个示例中,我们使用 instanceof
操作符进行类型检查,并且将匹配的结果赋值给了一个新的变量 str
。如果 obj
是一个 String
类型的实例,那么 str
变量就会被声明为 String
类型,并且可以直接在 if
语句的代码块中使用。这样,我们就避免了在 if
语句中进行类型转换的繁琐操作,使代码更加简洁和易读。
重点是判断类型后不用强转,之前我们使用时是需要强转的。
Java 15(2020年9月)
- 文本块: 多行字符串文字成为正式功能。
- 隐藏类: 用于框架的动态类生成。
- ZGC 改进: ZGC 的多项性能改进。
- Sealed 类(预览): 控制哪个类可以继承某个类。
Sealed 类,这是一种控制继承关系的机制,可以限制哪些类可以继承某个类。通过将一个类声明为 sealed,可以明确指定允许继承它的类,并对其他类进行限制。这个功能有助于提高代码的安全性和可维护性,减少意外的继承和扩展,从而降低代码的复杂度。下面是一个示例:
public sealed class Shape permits Circle, Rectangle, Triangle {
// Shape 类的定义
}
public final class Circle extends Shape {
// Circle 类的定义
}
public final class Rectangle extends Shape {
// Rectangle 类的定义
}
public final class Triangle extends Shape {
// Triangle 类的定义
}
在这个示例中,我们首先声明了一个 sealed 类 Shape
,并通过 permits
关键字明确指定了允许继承它的子类。在这个例子中,Shape
类允许被继承的子类有 Circle
、Rectangle
和 Triangle
。其他类不被允许继承 Shape
,如果有其他类尝试继承 Shape
,编译器会报错。
通过使用 Sealed 类,可以更加精确地控制继承关系,防止不合理的类继承和扩展,从而提高代码的安全性和可维护性。需要注意的是,Sealed 类是一个预览功能,可能会在未来的版本中发生变化,因此在实际项目中使用时需要谨慎考虑,并且可能需要等到它成为稳定特性后才能广泛应用。
Java 16(2021年3月)
- 记录类型: 记录类型成为正式功能。
- 强封闭 JDK: 更严格的封装 JDK 内部 API。
- 模式匹配(instanceof):
instanceof
模式匹配成为正式功能。
Java 17(2021年9月,长期支持版本)
- Sealed 类: 控制哪个类可以继承某个类成为正式功能。
- 增强的 switch 语句:
switch
表达式成为正式功能。 - 新 JEP 集合: 包括改进的伪随机数生成器、增强的
Foreign Function & Memory API
、上下文自适应G1
和ZGC
等。
Java 18(2022年3月)
- UTF-8 默认字符集: 默认字符集改为 UTF-8。
- 简单的 Web 服务器: 方便测试和开发的小型 Web 服务器。
- 代码段的 Javadoc: 在 Javadoc 中嵌入代码段。
Java 19(2022年9月)
- 虚拟线程(预览): 更轻量级的线程实现,改进并发性能。
- 结构化并发(预览): 简化并发任务管理。
- 外部函数和内存 API(预览): 更好的本地代码调用支持。
- 模式匹配: 增强的模式匹配功能。
Java 20(2023年3月)
- 记录模式(预览): 用于解构记录的模式匹配。
- 模式匹配 for switch(第三个预览): 继续改进和完善 switch 语句的模式匹配。
- 外部函数和内存 API(第二个预览): 继续改进和完善。
一种用于解构记录的模式匹配方式。记录模式使得在模式匹配中可以更方便地访问记录中的字段,并且可以根据记录的结构进行匹配和解构。下面是一个简单的示例,展示了如何使用记录模式:
public record Point(int x, int y) {}
public class Main {
public static void main(String[] args) {
Point point = new Point(10, 20);
if (point instanceof Point p) {
System.out.println("x = " + p.x() + ", y = " + p.y());
}
}
}
在这个示例中,我们定义了一个名为 Point
的记录(record),包含两个字段 x
和 y
。然后,我们创建了一个 Point
对象,并使用模式匹配检查它是否是一个 Point
类型的实例,并将其解构为变量 p
。如果匹配成功,则打印出 x
和 y
字段的值。
记录模式使得在 Java 中更方便地处理记录类型的数据,可以通过模式匹配的方式轻松地解构记录,并访问其中的字段。
Java 21(2023年9月,长期支持版本)
- 虚拟线程: 作为正式功能发布,更轻量级的线程实现。
- 结构化并发: 作为正式功能发布,简化并发任务管理。
- 记录模式: 作为正式功能发布,用于解构记录的模式匹配。
- 模式匹配 for switch: 作为正式功能发布,增强 switch 语句的模式匹配。
- 外部函数和内存 API: 作为正式功能发布,更好的本地代码调用支持。
- 字符串插值: 通过直接在字符串中插入变量和表达式,简化字符串操作。
让我们逐个解释并提供代码示例来说明每个功能:
虚拟线程
虚拟线程是 Java 21 中的一项正式功能,它是一种更轻量级的线程实现,旨在提高并发性能和资源利用率。虚拟线程是通过 Executors.newVirtualThreadExecutor()
方法创建的,可以与传统的线程一起使用,但是更轻量级。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
for (int i = 0; i < 10; i++) {
executor.submit(() -> {
System.out.println("Hello from virtual thread: " + Thread.currentThread());
});
}
executor.shutdown();
}
}
这是一个虚拟线程线程池的演示,虚拟线程有得搞,可以单独学习一下,然后其他的还行1.8中的lambda、Stream API、Optional也是常搞的API
结构化并发
结构化并发是 Java 21 中的一项正式功能,它旨在简化并发任务的管理。通过结构化并发,可以更轻松地管理并发任务的执行和结果处理,提高代码的可读性和可维护性。
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("Running in parallel");
});
future.thenRun(() -> {
System.out.println("After completion");
}).join();
}
}
记录模式
记录模式是 Java 21 中的一项正式功能,它用于解构记录的模式匹配。记录模式使得在模式匹配中可以更方便地访问记录中的字段,并根据记录的结构进行匹配和解构。
public record Point(int x, int y) {}
public class Main {
public static void main(String[] args) {
Point point = new Point(10, 20);
if (point instanceof Point p) {
System.out.println("x = " + p.x() + ", y = " + p.y());
}
}
}
模式匹配 for switch
模式匹配 for switch 是 Java 21 中的一项正式功能,它增强了 switch 语句的模式匹配能力,使得在 switch 语句中可以更方便地进行模式匹配操作。
public class Main {
public static void main(String[] args) {
Object obj = "Hello";
switch (obj) {
case String s -> System.out.println("String: " + s);
case Integer i -> System.out.println("Integer: " + i);
default -> System.out.println("Unknown type");
}
}
}
外部函数和内存 API
外部函数和内存 API 是 Java 21 中的一项正式功能,它提供了更好的本地代码调用支持,使得在 Java 中更容易地调用本地代码并处理本地内存。
import jdk.incubator.foreign.*;
import static jdk.incubator.foreign.CLinker.*;
public class Main {
public static void main(String[] args) throws Exception {
MemorySegment segment = CLinker.toCString("Hello, world!");
try (var scope = ResourceScope.newConfinedScope()) {
var printfFn = CLinker.getInstance().lookup("printf",
FunctionDescriptor.ofVoid(CLinker.C_POINTER),
FunctionDescriptor.ofVoid(CLinker.C_POINTER)
);
printfFn.invokeExact(scope, "%s%n", segment);
}
}
}
字符串插值
字符串插值是 Java 21 中的一项正式功能,它通过直接在字符串中插入变量和表达式,简化了字符串操作,提高了代码的可读性和可维护性。
public class Main {
public static void main(String[] args) {
String name = "Alice";
int age = 30;
String message = String.format("Hello, my name is %s and I am %d years old.", name, age);
System.out.println(message);
}
}
转载自:https://juejin.cn/post/7379431208429584393