Java 17

为什么要了解Java 17?

之所以关心Java 17是因为和Java 8、Java 11一样它是下一个LTS版本。

image.png

Spring的官宣
image.png

kafka 3.0版本之后也会弃用java 8,升级已经是一个趋势,未来更多框架和中间件会弃用java 8,作为开发人员也不能停止脚步

使用前的准备

  • IDE升级

检查 IDE 是否支持 java 17,例如 IDEA 在 file -> project structure -> project -> language level,查看是否支持17,如果最高不支持则需要升级IDEA。

  • 依赖项的升级

  • java 11

java 11删除了这些原本在jdk中的包:
1.javaFX:JavaFX是取代Swing和AMT,用于开发富GUI(图形用户界面)应用的框架
2.删除java EE和CORBA模块:SE中删除java EE相关的包是因为这些包已经由java EE提供,而且由于oracle的政策,一些包的命名空间也改变了,例如JAXB包下的 javax.xml.bind.* 更改为 jakarta.xml.bind.* ,下图列举了包名的改动,如果项目使用了这些包,需要在代码和pom.xml中更改相应包名
...

  • java 14

删除了CMS GC,对于老项目或针对CMS专门调优过的项目,建议升级后使用G1 GC

  • java 16

java 16对jdk内部的很多api做了强封装,默认情况下不可访问(可以通过选项 --illegal-access 更改这个行为,但官方不建议),这个主要影响一些工具,比如lombok,而lombok在java 16发布后不久更新了版本解决这个问题。

  • java 17

1.删除applet API:Applet 是一种 Java 程序。它一般运行在支持 Java 的 Web 浏览器内。因为它有完整的 Java API支持,所以Applet 是一个全功能的 Java 应用程序
2.AOT和JIT被删除:AOT 编译是在程序运行之前,便将字节码转换为机器码。JIT是在程序的运行过程中,将字节码转换为可在硬件上直接运行的机器码。


8到17 (累积)语法新特性

  • 新增类型推断
import java.util.ArrayList;
import java.util.List;

public class Var {

    // var a = "a";

    void get() {
        var b = "b";

        // 泛型为Object
        var list = List.of("a", 1);
        var list2 = new ArrayList<>();
        list2.add("a");
        list2.add(1);

        // 无法推断匿名类
        Runnable runnable = () -> System.out.println("interface var");
        // var runnable2 = () -> System.out.println("interface var");
    }
}

  • Stream 增强
import java.util.*;
import java.util.stream.Stream;

public class StreamJava {
    // max 10
    Map<String, Integer> map = Map.of("a", 1, "b", 2, "c", 3);

    // List<String> list = List.of("a", 1);


    // toList()
    List<Integer> list = Stream.of(1, 2, 3).toList();

    // 多元素替换一个元素
    public static void main(String[] args) {
        Stream.of(1, 2, 3)
                //.flatMap(num -> Stream.of(num + num, num * num, " "))
                .mapMulti((num, downstream) -> {
                    downstream.accept(num + num);
                    downstream.accept(num * num);
                    downstream.accept(" ");
                })
                .forEach(System.out::println);
    }

    // optional
    long count = Stream.of(
            Optional.of(1),
            Optional.empty(),
            Optional.of(2)).flatMap(Optional::stream).count();

}

  • instance of 增强
public class InstanceOf {
    void get() {
        Object obj = "string value";
        //传统写法
        if (obj instanceof String) {
            String str = (String) obj;
            if (str.contains("val")) {
                
            }
        }

        //新写法
        if (obj instanceof String s) {
        }

        //还可以做其他操作
        if (obj instanceof String s && s.contains("val")) {
        }
    }
}

  • 接口支持 private 方法
public interface Interface {
    private void get() {
        System.out.println();
    }
}

  • 新增封闭类
public sealed class Sealed permits A {

}

final class A extends Sealed {} // OK
// final class B extends Sealed {} // error

  • switch 语句增强
public class Switch {

    public void switchNowTest() {
        var c = 'B';
        var res = switch (c) {
            case 'A' -> "优秀";
            case 'B' -> "良好";
            case 'C' -> "及格";
            default -> "未知等级";
        };
    }

    public void formatTest() {
        Object o = 100;
        String formatted = switch (o) {
            //相当于 if (o instanceof Integer && (int)o > 10)
            case Integer i && i > 10 -> String.format("a large Integer %d", i);
            case Integer i -> String.format("a small Integer %d", i);
            case Long l -> String.format("a Long %d", l);
            default -> o.toString();
        };
        System.out.println(formatted);
    }

}

  • try 语句增强
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Try {
    void get() {
        var reader = new InputStreamReader(System.in);
        var writer = new OutputStreamWriter(System.out);
        try (reader; writer) {
            //reader是final的,不可再被赋值
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

  • 新增文本块
public class Text {
//    String a = """abc
//            """;


    static String text = """
            abc
            b
            c
                def
            """;

    static String text2 = """
            abc
            b
            c
                def
ghi
            """;

    static String text3 = """
            {
                "name": "abc",
                "age": 123
                /
                //
                \n
            }
            """;

    public static void main(String[] args) {
        System.out.println(Text.text2);
    }
}

  • 新增 record 基本引用类型
public record Record(String name, Integer age) {

    // public int a;

//    Record(int a) {
//        this.a = a;
//    }

    // public static String b = "b";

    public Record {
        if (age < 0) {
            throw new RuntimeException("年龄不正确");
        }
        if (age > 100) {
            age = 100;
        }
    }

    public static void main(String[] args) {
        Record a = new Record("a", 200);
        System.out.println(a);
        System.out.println(a.age);
        // System.out.println(Record.b);
    }
}

  • 新增 http client 工具
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class Http {

    HttpClient client;


    public void init() {
        client = HttpClient.newBuilder().connectTimeout(Duration.ofMillis(20000L)).build();
    }

    public void reqTest() throws IOException, InterruptedException {
        var request = HttpRequest.newBuilder(URI.create("https://baidu.com/")).build();
        /**
         * 没有指定协议默认是 GET
         */
        String body = client.send(request, HttpResponse.BodyHandlers.ofString()).body();
        System.out.println(body);
    }

    public void getTest() {
        var request = HttpRequest.newBuilder()
                .uri(URI.create("https://baidu.com/"))
                .header("Cookie", cookie)
                .timeout(Duration.ofSeconds(10000L))
                .GET()
                .build();
        client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                .whenCompleteAsync((res, exp) -> {
                    System.out.println(res.body());
                }).join();
    }


    public void postTest() {
        var requestBody = "{'key':'val'}";
        var request = HttpRequest.newBuilder()
                .uri(URI.create("http://baidu.com/"))
                .header("Contend-Type","application/json")
                .timeout(Duration.ofSeconds(10000L))
                .POST(HttpRequest.BodyPublishers.ofString(requestBody))
                .build();
        client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                .whenCompleteAsync((res, exp) -> {
                    System.out.println(res.body());
                }).join();
    }


    public void Http2Test() throws URISyntaxException {
        HttpClient.newBuilder()
                .followRedirects(HttpClient.Redirect.NEVER)
                .version(HttpClient.Version.HTTP_2)
                .build()
                .sendAsync(HttpRequest.newBuilder()
                                .uri(new URI("https://baidu.com/"))
                                .GET()
                                .build(),
                        HttpResponse.BodyHandlers.ofString())
                .whenComplete((resp, t) -> {
                    if (t != null) {
                        t.printStackTrace();
                    } else {
                        System.out.println(resp.version());
                        System.out.println(resp.statusCode());
                    }
                }).join();
    }

}


其他重要更新

  • 恢复始终严格的浮点语义(曾需使用strictfp关键字)

过于严格的浮点计算在当初流行的 x86 架构和 x87 浮点协议处理器上运行,需要大量的额外的指令开销,所以java1.2之后去掉严格浮点,使用strictfp关键字
但是非严格浮点数计算会导致不同平台的计算结果可能不一致


image.png
  • 使用新的 macOS 渲染库,支持 macOS/AArch64 架构

    ···

  • 指定上下文的反序列化过滤器

反序列化危险的一个原因是,有时候我们不好验证将要进行反序列化的内容是否存在风险,而传入的数据流可以自由引用对象,很有可能这个数据流就是攻击者精心构造的恶意代码。

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{
}


GC优化 - G1收集器

  • 简介

G1收集器最早出现在Java 7中,但需要手动开启,从Java 9开始将G1收集器作为默认垃圾收集器,以取代CMS收集器。
G1垃圾收集器可以给你设定一个你希望Stop the world停顿时间,G1会根据这个时间尽量满足你。

  • 内存模型

在G1垃圾收集器中,堆的划分不再是物理形式,而是以逻辑的形式进行划分


image.png
  • GC过程
image.png
  • Minor GC
    扫描、处理跨Region引用,收集至CSet,复制清除、处理引用
  • Mixed GC
    当堆空间的占用率达到一定阈值后会触发Mixed GC(默认45%,由参数决定)。Mixed GC会依赖全局并发标记统计后的Region数据
    初始标记(STW)、并发标记、最终标记(STW)、清理(STW)
  • Full GC
    特殊场景会发生full GC

  • 性能
    性能测试
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351

推荐阅读更多精彩内容