玩转微服务-基础篇-JDK17实用特性

一. 文档及下载地址

OpenJDK Java 17 下载:https://jdk.java.net/archive/

OpenJDK Java 17 文档:https://openjdk.java.net/projects/jdk/17/

二. 特性功能

1. VAR 类型推导

JDK 10 的新特性, 这里说明下

  • var 来支持局部变量的类型推断,var 不是关键字,且只能用于局部变量;

  • var 不能用于字段、方法参数、构造方法参数、方法返回值,也不能用于 catch 参数,也不能用作类名接口名;

  • 可以用 for 循环和增强 for 循环里

代码 Demo:

public class Jdk17Demo {
    private var name;  // error

    Jdk17Demo(var name) {  // error
        this.name = name;
    }

    void text(var str) {  // error
        var st = "";
        var testList = new ArrayList<>();
        for (var st : testList) {
            System.out.println(st);
        }
    }
}

2. instanceof 模式匹配

instanceof 最常用的是用来判断 Object 类型,然后强转具体类型

private static void instanceDemo (Object obj) {
    if (obj instanceof String str) {
        // String str = (String) obj;  以前需要强制转换
        System.out.println("字符串的值:" + str);
    }
}

3. JEP 406:switch 的类型匹配 (预览)

部分还是预览功能,不建议大量使用,如:类型推导自动赋值,空值判定等

String PatternSwitch(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();
    };
}

void test(String tst) {
    var st = switch (tst) {
        case null -> "null";  // 预览版的不能使用
        case "张三" -> "张三";
        case "李四" -> "李四";
        case "王五" -> {
            //注意如果需要多条语句,需要在花括号内进行,在switch表达式中,
            // 如果多条语句,最后的值返回需要使用yield关键字进行返回
            yield "王五";	//注意这里返回的关键字不是return,而是yield 以前是break
        }
        default -> "赵六";
    };
    System.out.println(st);
}

4. 文本块 Text Blocks

字符串的拼接以前需要使用+进行连接,不方便阅读,现在:

public void now() {
    var address="shanxi";
    String jsonText = """
            {
              "name": "张三",
              "address": "${address}"
            }
            """;
    System.out.println(jsonText.formatted(address)); // 使用字符串的formatted 替换
}

5. 增强的伪随机数生成器

可以使用 Random、ThreadLocalRandom 和 SplittableRandom 来生成随机数。不过,这 3 个类都各有缺陷

伪随机数生成器(pseudorandom number generator,PRNG),又称为确定性随机位生成器(deterministic random bit generator,DRBG),是用来生成接近于绝对随机数序列的数字序列的算法。一般来说,PRNG 会依赖于一个初始值,也称为种子,来生成对应的伪随机数序列。只要种子确定了,PRNG 所生成的随机数就是完全确定的,因此其生成的随机数序列并不是真正随机的。

就目前而言,PRNG 在众多应用都发挥着重要的作用,比如模拟(蒙特卡洛方法),电子竞技,密码应用。

这次增加了 RandomGenerator 接口,为所有的 PRNG 算法提供统一的 API,并且可以获取不同类型的 PRNG 对象流。同时也提供了一个新类 RandomGeneratorFactory 用于构造各种 RandomGenerator 实例,在 RandomGeneratorFactory 中使用 ServiceLoader.provider 来加载各种 PRNG 实现。

//通过 RandomGeneratorFactory.of(“随机数生成算法”) 方法获得生成器
RandomGeneratorFactory<RandomGenerator> l128X256MixRandom = RandomGeneratorFactory.of("L128X256MixRandom");
// 使用时间戳作为随机数种子
RandomGenerator randomGenerator = l128X256MixRandom.create(System.currentTimeMillis());
// 生成0-9随机数
System.out.println(randomGenerator.nextInt(10));

获取所有的随机数算法

RandomGeneratorFactory.all().forEach(factory -> {
    System.out.println(factory.group() + ":" + factory.name());
});

6. 密封类 sealed class

限制哪些其他类或接口可以扩展或实现它们。提供一种比访问修饰符更具声明性的方式来限制超类的使用,

public abstract sealed class Furit permits Apple,Pear {

}

public non-sealed class Apple extends Furit {

}

public final class Pear extends Furit {

}
  • 在定义 Furit 时通过关键字 sealed 声明为密封类,通过 permits 可以指定 Apple,Pear 类可以进行继承扩展。

  • 指定的类 permits 必须位于超类附近:在同一个模块中(如果超类在命名模块中)或在同一个包中(如果超类在未命名模块中)。当 permits 的子类的大小和数量较小时,可以将它们声明在与密封类相同的源文件中,同时可以省略 permits 语句,Java 编译器将从源文件中的声明推断出 permits 的子类。

  • 指定的类 permits 必须具有规范名称,否则会报告编译时错误。这意味着匿名类和本地类不能成为密封类的子类型。

  • 密封类对其允许的子类施加三个约束:

      1. 密封类及其允许的子类必须属于同一个模块,并且如果在未命名的模块中声明,则必须属于同一个包。
      1. 每个允许的子类都必须直接扩展密封类。
      1. 每个允许的子类都必须使用修饰符来描述它如何传播由其超类发起的密封:
      • 可以声明允许的子类 final 以防止其在类层次结构中的一部分被进一步扩 展。(记录类是隐式声明的 final。)
      • 可以声明允许的子类 sealed 以允许其层次结构的一部分比其密封的超类所设想的扩展得更远,但以受限制的方式。
      • 可以声明一个允许的子类 non-sealed,以便它的层次结构部分恢复为对未知子类的扩展开放。密封类不能阻止其允许的子类这样做。(修饰符 non-sealed 是 为 Java 提出的第一个连字符关键字。)

类模型

public abstract sealed class Shape
    permits Circle, Rectangle, Square, WeirdShape { ... }

public final class Circle extends Shape { ... }

public sealed class Rectangle extends Shape
    permits TransparentRectangle, FilledRectangle { ... }
public final class TransparentRectangle extends Rectangle { ... }
public final class FilledRectangle extends Rectangle { ... }

public non-sealed class Square extends Shape { ... }

public class WeirdShape extends Square { ... }

密封和记录类 的配合使用


public sealed interface Expr
    permits ConstantExpr, PlusExpr, TimesExpr, NegExpr { ... }

public record ConstantExpr(int i)       implements Expr { ... }
public record PlusExpr(Expr a, Expr b)  implements Expr { ... }
public record TimesExpr(Expr a, Expr b) implements Expr { ... }
public record NegExpr(Expr e)           implements Expr { ... }

7. NullPointerExceptions 优化

会精确显示空指针报错的位置

8. Stream.toList()

Stream 转换成 List,需要使用 Collectors.toList(),代码非常冗长,

private static void before17() {
    Stream<String> stream = Stream.of("a", "b", "c");
    List<String> stringList =  stream.collect(Collectors.toList());
    for(String s : stringList) {
        System.out.println(s);
    }
}
private static void now17() {
    Stream<String> stream = Stream.of("a", "b", "c");
    List<String> stringList =  stream.toList();
    for(String s : stringList) {
        System.out.println(s);
    }
}

9. Records

Records 的目标是扩展 Java 语言语法,Records 为声明类提供了一种紧凑的语法,用于创建一种类中是“字段,只是字段,除了字段什么都没有”的类。 只读类

record 不会放堆里 所以不用垃圾回收 , 解决的是堆内存中放太多无用的类。?

record Person (String firstName, String lastName) {}

Demo

public record TestRecord(String name, String password) {
    //再定义一个构造方法
    public TestRecord(String name) {
        this(name, null);
    }

    //额外定义的方法
    public String nameToUppercase() {
        return this.name.toUpperCase();
    }

    //静态方法
    public static String nameAddPassword(TestRecord user1) {
        return user1.name + user1.password;
    }

    public static void main(String[] args) {
        TestRecord user = new TestRecord("XiaoMing", "123456");

        System.out.println(user.nameToUppercase());
        System.out.println(TestRecord.nameAddPassword(user));

        TestRecord userAnotherConstructor = new TestRecord("hello");
        System.out.println(userAnotherConstructor);
    }
}

10. JEP 415:指定上下文的反序列化过滤器

允许在反序列化时,通过一个过滤配置,来告知本次反序列化允许或者禁止操作的类,反序列化时碰到被禁止的类,则会反序列化失败。

1. 反序列化示例

假设 Dog 类中的 Poc 是恶意构造的类,但是正常反序列化是可以成功的。

package com.wdbyte.java17;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author niulang
 */
public class JEP415 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Dog dog = new Dog("哈士奇");
        dog.setPoc(new Poc());
        // 序列化 - 对象转字节数组
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);) {
            objectOutputStream.writeObject(dog);
        }
        byte[] bytes = byteArrayOutputStream.toByteArray();
        // 反序列化 - 字节数组转对象
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        Object object = objectInputStream.readObject();
        System.out.println(object.toString());
        // Dog{name='哈士奇'}
    }
}

class Dog implements Serializable {
    private String name;
    private Poc poc;

    public Dog(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Dog{" + "name='" + name + '\'' + '}';
    }
		// get...set...
}

class Poc implements Serializable{

}

2. 反序列化过滤器

在 Java 17 中可以自定义反序列化过滤器,拦截不允许的类。

package com.wdbyte.java17;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputFilter;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author niulang
 */
public class JEP415 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Dog dog = new Dog("哈士奇");
        dog.setPoc(new Poc());
        // 序列化 - 对象转字节数组
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);) {
            objectOutputStream.writeObject(dog);
        }
        byte[] bytes = byteArrayOutputStream.toByteArray();
        // 反序列化 - 字节数组转对象
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        // 允许 com.wdbyte.java17.Dog 类,允许 java.base 中的所有类,拒绝其他任何类
        ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
                        "com.wdbyte.java17.Dog;java.base/*;!*");
        objectInputStream.setObjectInputFilter(filter);
        Object object = objectInputStream.readObject();
        System.out.println(object.toString());
    }
}

class Dog implements Serializable {
    private String name;
    private Poc poc;

    public Dog(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Dog{" + "name='" + name + '\'' + '}';
    }
		// get...set...
}

class Poc implements Serializable{
}

出现异常

Exception in thread "main" java.io.InvalidClassException: filter status: REJECTED
	at java.base/java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:1412)
	at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2053)
	at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1907)
	....

11. 恢复始终严格的浮点语义

strictfp 是 Java 中的一个关键字,大多数人可能没有注意过它,它可以用在类、接口或者方法上,被 strictfp 修饰的部分中的 float 和 double 表达式会进行严格浮点计算。


public class test {
    public static void main(String[] args) {
        testStrictfp();
    }

    public strictfp static void testStrictfp() {
        float aFloat = 0.6666666666666666666f;
        double aDouble = 0.88888888888888888d;
        double sum = aFloat + aDouble;
        System.out.println("sum: " + sum);
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Elcker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值