Java版本特性

更多Java内容查看:https://dullfan.notion.site/Java-71a7344dba09438c8851b8514a9b5df3

目录

Java8

Lambda表达式

函数式接口

Stream API

默认方法和静态方法

方法引用

Optional

新日期和时间

Base64

重复注解

Java9

改进的集合工厂方法

接口私有化

HTTP

 Java10

var

Java11

var 扩展 Lambda

String 新方法

Java12

Switch表达式

Java13

Switch表达式改进

文本块

Java14

Record Classes

instanceof 匹配

Java15

密封类

Java16

Java17

伪随机数生成器

switch类型匹配

Java18

Java19

虚拟线程

结构化并发(Structured Concurrency)

Java20

Java21

字符串模板(预览)

序列化集合

Java22


可以自行查看官方以及wiki

Java SE | Oracle Technology Network | Oracleicon-default.png?t=N7T8https://www.oracle.com/java/technologies/java-se-glance.htmlhttps://zh.wikipedia.org/wiki/Java%E7%89%88%E6%9C%AC%E6%AD%B7%E5%8F%B2#Java_SE_23icon-default.png?t=N7T8https://zh.wikipedia.org/wiki/Java%E7%89%88%E6%9C%AC%E6%AD%B7%E5%8F%B2#Java_SE_23

Java8

Lambda表达式

主要用于简化匿名类的写法

Runnable runnable = () -> System.out.println("我是一个Lambda");
runnable.run();

函数式接口

函数式接口是只有一个抽象方法的接口

@Test
void contextLoads() {
	StringSplice splice = (a,b)-> a + b;
	System.out.println(splice.splice("函数式","接口"));
}
// 表明该接口被设计用于支持 Lambda 表达式和函数式编程。这为其他开发人员阅读和理解代码提供了明确的提示
// Java 编译器会对被标记为@FunctionalInterface的接口进行检查,确保该接口只有一个抽象方法(除了从Object类继承的方法之外)。如果违反了这个规则,编译器会报错。
@FunctionalInterface
interface StringSplice{
	String splice(String a,String b);
}

Stream API

提供了高效的数据处理方式,支持对集合进行过滤、排序等操作

List<String> names = Arrays.asList("张三", "李四", "王五");
// 过滤以 '张' 开头的名字并排序
names.stream()
		.filter(name -> name.startsWith("张"))
		.sorted()
		.forEach(System.out::println);

详细:https://dullfan.notion.site/Stream-46cdab65e6cb46b0b5a21caae34d535a

默认方法和静态方法

@Test
void contextLoads() {
	Interface.staticMethod();
	new MyClass().defaultMethod();
}
interface Interface{
	// default 只能使用在接口中
	// 作用:只要在一个接口中添加了默认方法,所有的实现类就会自动继承,不需要改动任何代码,选择性覆盖
	default void defaultMethod(){
		System.out.println("默认方法执行");
	}
	
	// 不能被重写
	static void staticMethod(){
		System.out.println("静态方法执行");
	}
}
static class MyClass implements Interface{
	// 选择性覆盖默认方法,
}

方法引用

是对Lambda一种简化写法,可以直接引用现有的方法或构造函数

List<String> names = Arrays.asList("张三", "李四", "王五");
// 使用方法引用替代 Lambda 表达式
names.forEach(System.out::println);	

Optional

表示该值可能为空,为了防止空指针异常

Optional<String> optional = Optional.ofNullable("Optional");
optional.ifPresent(System.out::println);
// 如果值为空,返回默认值
String result = optional.orElse("默认值");
System.out.println(result);

新日期和时间

提供了更好的日期和时间处理功能

LocalDate date = LocalDate.now();
LocalTime time = LocalTime.now();
LocalDateTime dateTime = LocalDateTime.now();

// 格式化日期时间
DateTimeFormatter formatterDate = DateTimeFormatter.ofPattern("yyyy-MM-dd");
DateTimeFormatter formatterTime = DateTimeFormatter.ofPattern("HH:mm:ss");
DateTimeFormatter formatterDateTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

String formattedDate= date.format(formatterDate);
String formattedTime = time.format(formatterTime);
String formattedDateTime = dateTime.format(formatterDateTime);
System.out.println(formattedDate);  // 输出当前日期
System.out.println(formattedTime);  // 输出当前时间
System.out.println(formattedDateTime);  // 输出格式化后的日期时间

详细:https://dullfan.notion.site/LocalDate-ad0c2470ff34447dac9e58d36575902d

Base64

引入了Base64类,用于处理Base64编码和解码操作

String originalInput = "Hello, Base64!";
// Base64 是一种编码方式,主要用于将二进制数据编码成 ASCII 字符集的可打印字符,以便在不同的系统和协议中进行传输和存储。
String encodedString = Base64.getEncoder().encodeToString(originalInput.getBytes());
System.out.println(encodedString);  // 输出编码后的字符串
byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
String decodedString = new String(decodedBytes);
System.out.println(decodedString);  // 输出 Hello, Base64!

重复注解

允许在同一个位置多次使用相同的注解

@Test
void contextLoads() {
	try {
		// 通过反射获取注解中的参数
		Method method = MyClass.class.getMethod("myMethod");
		MyAnnotation[] annotations = method.getAnnotationsByType(MyAnnotation.class);
		for (MyAnnotation annotation : annotations) {
			System.out.println("Annotation value: " + annotation.value());
		}
	} catch (NoSuchMethodException e) {
		e.printStackTrace();
	}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Repeatable(MyAnnotations.class)
@interface MyAnnotation {
	String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@interface MyAnnotations {
	MyAnnotation[] value();
}
public class MyClass {
	@MyAnnotation("First method annotation")
	@MyAnnotation("Second method annotation")
	public void myMethod() {
		// 方法实现
	}
}

详细:https://dullfan.notion.site/2b21f66c2a814477b156771958f79957

Java9

改进的集合工厂方法

ListSetMap 引入了方便的工厂方法,使用这些方法可以轻松地创建不可变的集合。

List<String> list = List.of("A", "B", "C");
Set<String> set = Set.of("A", "B", "C");
Map<Integer, String> map = Map.of(1, "A", 2, "B", 3, "C");

System.out.println(list);  // 输出 [A, B, C]
System.out.println(set);   // 输出 [A, B, C]
System.out.println(map);   // 输出 {1=A, 2=B, 3=C}

接口私有化

允许在接口中定义私有方法。

@Test
void contextLoads() {
	MyInterface myClass = new MyClass();
	myClass.publicMethod();
}

interface MyInterface {
	default void publicMethod() {
		System.out.println("Public Method");
		privateMethod();
	}
	private void privateMethod() {
		System.out.println("Private Method");
	}
}

class MyClass implements MyInterface {}

HTTP

引入了一个新的 HTTP/2 客户端 API(在 java.net.http 包中),它可以处理同步和异步的 HTTP 请求。这是一个实验性特性,在 Java 11 中被正式引入

@Test
void contextLoads() throws URISyntaxException, IOException, InterruptedException {
	HttpClient client = HttpClient.newHttpClient();
	HttpRequest request = HttpRequest.newBuilder()
			.uri(new URI("<https://postman-echo.com/get>"))
			.GET()
			.build();
	HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
	System.out.println(response.body());
}

 Java10

var

var关键字,用于局部变量类型推断,这意味着在声明局部变量时,编译器可以根据赋值的表达式自动推断变量的类型

var number = 10;  // 自动推断为 int 类型
var text = "Hello, Java 10";  // 自动推断为 String 类型
var list = List.of(1, 2, 3, 4);  // 自动推断为 List<Integer> 类型
System.out.println(number);  // 输出 10
System.out.println(text);    // 输出 Hello, Java 10
System.out.println(list);    // 输出 [1, 2, 3, 4]

注意:

  • var 只能用于局部变量,包括循环变量、for-each 循环中的变量、以及局部变量的初始化
  • var 不能用于类级别的字段声明,也不能用于方法参数或返回类型

Java11

var 扩展 Lambda

允许在 lambda 表达式的参数中使用 var 关键字

var list = List.of("Apple", "Banana", "Orange");
list.forEach((var item) -> System.out.println(item)); // 使用 var 关键字
// 可以为 lambda 参数添加注解
Consumer<String> consumer = (var item) -> System.out.println(item);
list.forEach(consumer);

String 新方法

String 类增加了一些非常实用的新方法,比如 isBlank()lines()strip()repeat()stripLeading()

// isBlank()
System.out.println(" ".isBlank()); // 输出 true
// lines()
String multilineStr = "Java\\nis\\nawesome";
multilineStr.lines().forEach(System.out::println); // 输出三行
// strip(), stripLeading(), stripTrailing()
String str = "  hello  ";
System.out.println(">" + str.strip() + "<");  // 输出 >hello<
System.out.println(">" + str.stripLeading() + "<");  // 输出 >hello  <
System.out.println(">" + str.stripTrailing() + "<");  // 输出 >  hello<
// repeat()
String repeatStr = "Hello ";
System.out.println(repeatStr.repeat(3));  // 输出 Hello Hello Hello 

Java12

Switch表达式

Switch 表达式作为预览功能。这使得 switch 语句可以作为一个表达式来使用,从而返回值并减少样板代码

int day = 3;
String dayName = switch (day){
    case 1 -> "星期一";
    case 2 -> "星期二";
    case 3 -> "星期三";
    case 4 -> "星期四";
    case 5 -> "星期五";
    case 6 -> "星期六";
    case 7 -> "星期七";
    default -> "未知";
};
System.out.println(dayName);

Java13

Switch表达式改进

可以使用yield返回值

int day = 3;
String dayName = switch (day){
    case 1 -> "星期一";
    case 2 -> "星期二";
    case 3 -> "星期三";
    case 4 -> "星期四";
    case 5 -> "星期五";
    case 6 -> "星期六";
    case 7 -> "星期七";
    default -> {
        String result = "数据处理: " + day;
        yield result;
    }
    // default -> throw new IllegalArgumentException("错误: " + day);
};
System.out.println(dayName);

文本块

文本块(Text Blocks)是一个用于处理多行字符串的预览特性。它允许开发者编写多行字符串而无需使用繁琐的转义字符,并保留格式。

String json = """
        {
            "name":"DullFan",
            "age":20
        }
        """;
System.out.println(json);

说明:

  • 文本块使用 """ 作为分隔符。
  • 文本块保留了字符串的格式和缩进,使得代码更具可读性。

Java14

Record Classes

用于简化不可变数据类的定义,记录类自动生成构造函数等方法,该类中字段使用的是final

Point point = new Point(1, 2);
System.out.println(point); // 输出 Point[x=1, y=2]
System.out.println(point.x); // 1
System.out.println(point.y); // 2

instanceof 匹配

减少了类型转换的样板代码

Object obj = "Hello!";
if (obj instanceof String s) {
    System.out.println(s.toUpperCase());
}

Java15

密封类

允许开发者控制哪些类可以继承或实现它。这为构建受控层次结构提供了更多的灵活性和安全性

sealed class Shape permits Circle,Square{}
final class Circle extends Shape{}
final class Square extends Shape{}

说明:

  • sealed 关键字定义了一个密封类,后面跟随 permits 关键字,指定允许扩展该类的子类。
  • 其他类如果没有被允许,将不能继承或实现密封类

Java16

JDK 16 发布于 2021 年 3 月 16 日。Java 16 移除了 AOT 编译及 Graal JIT 的选项。自 Java 16 开始,允许使用 C++14 来编写 Java 的实现(但仍不允许 C++17、C++20 等版本),且代码迁移到了 GitHub,不再使用 Mercurial 版本控制系统。

  • JEP 338:Vector API(孵化)
  • JEP 347:启用 C++14 语言特性
  • JEP 357:从 Mercurial 迁移到 Git
  • JEP 369:迁移到 GitHub
  • JEP 376:ZGC: 并发的线程栈处理
  • JEP 380:用于 Unix 域套接字的 Channel
  • JEP 386:Alpine Linux 的移植版本
  • JEP 387:可伸缩的 Metaspace
  • JEP 388:Windows/AArch64 的移植版本
  • JEP 389:外部链接器 API(孵化)
  • JEP 390:对值类型的类(Value-based Classes)发出警告
  • JEP 392:打包工具
  • JEP 393:外部内存访问 API(第三孵化版本)
  • JEP 394:使用 instanceof 的模式匹配
  • JEP 395:记录类
  • JEP 396:在缺省情况下对 JDK 内部进行强封装
  • JEP 397:密封类(第二预览版本)

Java17

伪随机数生成器

伪随机数生成器 (pseudorandom number generator,PRNG,又称为确定性随机位生成器)增加了新的接口类型和实现,使得开发者更容易在应用程序中互换使用各种 PRNG 算法。

RandomGeneratorFactory<RandomGenerator> l128X256MixRandom = RandomGeneratorFactory.of("L128X256MixRandom");
// 使用时间戳作为随机数种子
RandomGenerator randomGenerator = l128X256MixRandom.create(System.currentTimeMillis());
// 生成随机数
int i = randomGenerator.nextInt(100);
System.out.println(i);

switch类型匹配

static void testFooBar(String s) {
    switch (s) {
        case null -> System.out.println("Oops");
        case "Foo", "Bar" -> System.out.println("Great");
        default -> System.out.println("Ok");
    }
}
static String formatterPatternSwitch(Object o) {
    return switch (o) {
        case Integer i -> String.format("int %d", i);
        case Long l    -> String.format("long %d", l);
        case Double d  -> String.format("double %f", d);
        case String s  -> String.format("String %s", s);
        default        -> o.toString();
    };
}

Java18

JDK 18 发布于 2022 年 3 月 22 日。

  • JEP 400:默认使用 UTF-8
  • JEP 408:简易 Web 服务器
  • JEP 413:Java API 文档中的代码片段
  • JEP 416:使用 Method Handle 重新实现核心反射
  • JEP 417:Vector API(第三孵化版本)
  • JEP 418:因特网地址解析 SPI
  • JEP 419:外部函数与内存 API(第二预览版本)
  • JEP 420:使用 switch 的模式匹配(第二预览版本)
  • JEP 421:弃用 Finalization 以待后续移除

Java19

虚拟线程

虚拟线程(Virtual Thread)是 JDK 而不是 OS 实现的轻量级线程(Lightweight Process,LWP),由 JVM 调度。许多虚拟线程共享同一个操作系统线程,虚拟线程的数量可以远大于操作系统线程的数量。

在引入虚拟线程之前,java.lang.Thread 包已经支持所谓的平台线程,也就是没有虚拟线程之前,我们一直使用的线程。JVM 调度程序通过平台线程(载体线程)来管理虚拟线程,一个平台线程可以在不同的时间执行不同的虚拟线程(多个虚拟线程挂载在一个平台线程上),当虚拟线程被阻塞或等待时,平台线程可以切换到执行另一个虚拟线程。

虚拟线程、平台线程和系统内核线程的关系图如下所示

使并发编程更加高效和易于使用。虚拟线程是轻量级的线程,运行在平台线程之上,可以大幅提高并发任务的效率。

四种创建虚拟线程的方法:

// 1、通过 Thread.ofVirtual() 创建
Runnable fn = () -> {
};
Thread thread = Thread.ofVirtual(fn)
        .start();
// 2、通过 Thread.startVirtualThread() 、创建
Thread thread = Thread.startVirtualThread(() -> {
});
// 3、通过 Executors.newVirtualThreadPerTaskExecutor() 创建
ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
executorService.submit(() -> {
    
});
class CustomThread implements Runnable {
    @Override
    public void run() {
        System.out.println("CustomThread run");
    }
}
//4、通过 ThreadFactory 创建
CustomThread customThread = new CustomThread();
// 获取线程工厂类
ThreadFactory factory = Thread.ofVirtual().factory();
// 创建虚拟线程
Thread thread = factory.newThread(customThread);
// 启动线程
thread.start();

// 等待虚拟线程结束
thread.join()

说明:

  • 使用 Thread.startVirtualThread 方法创建虚拟线程,虚拟线程比传统线程更轻量,可以有效利用系统资源,适合大规模并发任务。
  • 在JavaSE21转正

结构化并发(Structured Concurrency)

结构化并发旨在简化多线程处理,将相关的任务管理为一个单元,减少线程之间的复杂性和错误。

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Future<String> task1 = scope.fork(() -> {
        Thread.sleep(1000);
        return "Result from task 1";
    });
    Future<String> task2 = scope.fork(() -> {
        Thread.sleep(2000);
        return "Result from task 2";
    });
    scope.join(); // 等待所有任务完成
    System.out.println(task1.resultNow());
    System.out.println(task2.resultNow());
}

说明:

  • StructuredTaskScope 允许将并发任务组织为一个结构化的任务组,确保它们的生命周期一致性,减少错误处理的复杂性。

Java20

JDK 20 发布于 2023 年 3 月 21 日。

  • JEP 429:作用域值(孵化)
  • JEP 432:记录类的模式(第二预览版本)
  • JEP 433:使用 switch 的模式匹配(第四预览版本)
  • JEP 434:外部函数与内存 API(第二预览版本)
  • JEP 436:虚拟线程(第二预览版本)
  • JEP 437:结构化并发(第二孵化版本)
  • JEP 438:Vector API(第五孵化版本)

Java21

JDK 21 在 2023 年 9 月 19 日发布[319]。与仅预览和孵化 JEP 的 Java 20 相比,Java 21 有 8 个 JEP 不是处于预览或孵化状态。Java 21 正式引入了在 Java 17 和 Java 19 中首次预览的功能(使用 switch 的模式匹配,和记录类的模式)。 x86 上的 32 位 Windows 版本已弃用并删除。

  • JEP 430:字符串范本(预览)
  • JEP 431:有序的 Collections
  • JEP 439:世代 ZGC
  • JEP 440:记录类的模式
  • JEP 441:使用 switch 的模式匹配
  • JEP 442:外部函数与存储器 API(第三预览版本)
  • JEP 443:未命名模式和变量(预览)
  • JEP 444:虚拟线程
  • JEP 445:未命名的类别和实例 main 方法(预览)
  • JEP 446:作用域值(预览)
  • JEP 448:Vector API(第六孵化版本)
  • JEP 449:弃用 Windows 32 位 x86 端口以待后续移除
  • JEP 451:准备禁止动态加载代理
  • JEP 452:密钥封装机制 API
  • JEP 453:结构化并发(预览)

JEP 445 允许main方法位于未命名的类中:

void main() {
    System.out.println("Hello, World!");
}

而不用:

public class HelloWorld { 
    public static void main(String[] args) { 
        System.out.println("Hello, World!");
    }
}

字符串模板(预览)

引入了字符串模板作为孵化器特性,这一特性允许开发者更方便地在字符串中插入和格式化变量。

int x = 5;
int y = 10;
String message = STR."The sum of \\{x} and \\{y} is \\{x + y}.";
System.out.println(message);  // 输出 "The sum of 5 and 10 is 15."

序列化集合

https://javaguide.cn/java/new-features/java21.html#jep431-序列化集合

JDK 21 引入了一种新的集合类型:Sequenced Collections(序列化集合,也叫有序集合),这是一种具有确定出现顺序(encounter order)的集合(无论我们遍历这样的集合多少次,元素的出现顺序始终是固定的)。序列化集合提供了处理集合的第一个和最后一个元素以及反向视图(与原始集合相反的顺序)的简单方法。

Sequenced Collections 包括以下三个接口:

SequencedCollection 接口继承了 Collection接口, 提供了在集合两端访问、添加或删除元素以及获取集合的反向视图的方法。

interface SequencedCollection<E> extends Collection<E> {

  // New Method

  SequencedCollection<E> reversed();

  // Promoted methods from Deque<E>

  void addFirst(E);
  void addLast(E);

  E getFirst();
  E getLast();

  E removeFirst();
  E removeLast();
}

List 和 Deque 接口实现了SequencedCollection 接口。

这里以 ArrayList 为例,演示一下实际使用效果:

ArrayList<Integer> arrayList = new ArrayList<>();

arrayList.add(1);   // List contains: [1]

arrayList.addFirst(0);  // List contains: [0, 1]
arrayList.addLast(2);   // List contains: [0, 1, 2]

Integer firstElement = arrayList.getFirst();  // 0
Integer lastElement = arrayList.getLast();  // 2

List<Integer> reversed = arrayList.reversed();
System.out.println(reversed); // Prints [2, 1, 0]

SequencedSet接口直接继承了 SequencedCollection 接口并重写了 reversed() 方法。

interface SequencedSet<E> extends SequencedCollection<E>, Set<E> {

    SequencedSet<E> reversed();
}

SortedSet 和 LinkedHashSet 实现了SequencedSet接口。

这里以 LinkedHashSet 为例,演示一下实际使用效果:

LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>(List.of(1, 2, 3));

Integer firstElement = linkedHashSet.getFirst();   // 1
Integer lastElement = linkedHashSet.getLast();    // 3

linkedHashSet.addFirst(0);  //List contains: [0, 1, 2, 3]
linkedHashSet.addLast(4);   //List contains: [0, 1, 2, 3, 4]

System.out.println(linkedHashSet.reversed());   //Prints [5, 3, 2, 1, 0]

SequencedMap 接口继承了 Map接口, 提供了在集合两端访问、添加或删除键值对、获取包含 key 的 SequencedSet、包含 value 的 SequencedCollection、包含 entry(键值对) 的 SequencedSet以及获取集合的反向视图的方法。

interface SequencedMap<K,V> extends Map<K,V> {

  // New Methods

  SequencedMap<K,V> reversed();

  SequencedSet<K> sequencedKeySet();
  SequencedCollection<V> sequencedValues();
  SequencedSet<Entry<K,V>> sequencedEntrySet();

  V putFirst(K, V);
  V putLast(K, V);

  // Promoted Methods from NavigableMap<K, V>

  Entry<K, V> firstEntry();
  Entry<K, V> lastEntry();

  Entry<K, V> pollFirstEntry();
  Entry<K, V> pollLastEntry();
}

SortedMap 和LinkedHashMap 实现了SequencedMap 接口。

这里以 LinkedHashMap 为例,演示一下实际使用效果:

LinkedHashMap<Integer, String> map = new LinkedHashMap<>();

map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");

map.firstEntry();   //1=One
map.lastEntry();    //3=Three

System.out.println(map);  //{1=One, 2=Two, 3=Three}

Map.Entry<Integer, String> first = map.pollFirstEntry();   //1=One
Map.Entry<Integer, String> last = map.pollLastEntry();    //3=Three

System.out.println(map);  //{2=Two}

map.putFirst(1, "One");     //{1=One, 2=Two}
map.putLast(3, "Three");    //{1=One, 2=Two, 3=Three}

System.out.println(map);  //{1=One, 2=Two, 3=Three}
System.out.println(map.reversed());   //{3=Three, 2=Two, 1=One}

Java22

JDK 22 在 2024 年 3 月 19 日发布[323]

  • JEP 423:Region Pinning for G1
  • JEP 447:在super()前的语句(预览)
  • JEP 454:外部函数和存储器API
  • JEP 456:未命名模式和变量
  • JEP 457:类别文件API
  • JEP 458:启动多个源代码文件程序
  • JEP 459:字符串范本(第二预览版本)
  • JEP 460:Vector API(第七孵化版本)
  • JEP 461:Stream Gatherers(预览)
  • JEP 462:结构化并发(第二预览版本)
  • JEP 463:未命名的类别和实例 main 方法(第二预览版本)
  • JEP 464:作用域值(第二预览版本)

至少有一个 API 已从 Java 中删除;即从 Java 22 中删除了一个很少使用的 API(用于线程)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值