JDK8升级JDK17的新特性

jdk17继jdk8后免费商用,持续维护的版本,确实已经被一批人选择,笔者主要从jdk8直接跳jdk17的注意实现

现有项目分析依赖

解析当前项目中需要升级的依赖

jdeps --jdk-internals --multi-release 17 --class-path . 包名.jar

文本块

3个引号 “”“文本”“”-自带转义符,换行输出更方便

   @Test
    void testStr(){
        String txtOld = "{\n" +
                "\"name\":\"网站\",\n" +
                "\"num\":3,\n" +
                "\"sites\":[ \"Google\", \"FileFox\", \"Edge\" ]\n" +
                "}";
        System.out.println(txtOld);
        String txtNew = """
                {
                "name":"网站",
                "num":3,
                "sites":[ "Google", "Runoob", "Taobao" ]
                }
                """;
        System.out.println(txtNew);
    }
switch表达式

支持lamda表达式

    @Test
    void testSwitch(){
        Week day = Week.FRIDAY;
        switch (day){
            case MONDAY,TUESDAY,WEDNESDAY,THURSDAY -> System.out.println("red");
            case FRIDAY -> System.out.println("yellow");
            case SATURDAY,SUNDAY -> System.out.println("green");
            default -> {
            	throw new IllegalStateException("what day is today?"+day);
            }
        }
    }

records

简化构造函数设置值

/**
 * 类似于
 * @Data
 * public class Person{
 *     private String name;
 *     private Integer age;
 * }
 */
public record Person(String name ,Integer age) {
}
接口可以有私有方法

有个大病写来给default调用

interface Jdk9Interface{

    default String defaultTest() {
        privateTest();
        return "default";
    }

    private String privateTest() {
        System.out.println("私有方法");
        return "private";
    }
}
异常处理try升级

jdk9可以传一个变量就可以关闭流

public static void jdk9Try(){
    // jdk8:
    try(InputStreamReader reader =new InputStreamReader(System.in)){
    reader.read();
    }catch (IOException e){
    e.printStackTrace();
    }
    // jdk9以后:
    InputStreamReader reader =new InputStreamReader(System.in);
    try(reader){
    reader.read();
    }catch (IOException e){
    e.printStackTrace();
    }
}

VAR

这种写法估计会被规范不让用

public static void jdk10Var() {
    var number = 10;
    var str    = "aabbcc";
    var list   = new ArrayList<>(1);
    var map    = new HashMap<>(1);
    var set    = new HashSet<>(1);
}
copyOf

在java.util.List/ Set/Map
新增加了一个静态方法copyOf,这些方法按照其迭代顺序返回一个不可修改的列表、集合或映射包含了给定的元素的集合。但是如果将返回后的集合继续修改,那么报异常。

    LinkedList<Object> linkedList = new LinkedList<>();
        List<Object> copyLinkedList = List.copyOf(linkedList);
Stream 和 Optional

在集合框架中,Java 9 增加 了 List.of()、Set.of()、Map.of() 和 Map.ofEntries() 等工厂方法来创建不可变集合

@Test
public void unmodifiableCollectionTest() {
    List<Integer> integers = List.of(1, 2, 3);
    Set<String> strings = Set.of("a", "b", "c");
    Map<String, Integer> stringIntegerMap = Map.of("a", 1, "b", 2, "c", 3);
}

Stream 中增加了新的方法 ofNullable、dropWhile、takeWhile 和 iterate;Collectors 中增加了新的方法 filtering 和 flatMapping。

@Test
public void streamTest() {
    List<Integer> list = Arrays.asList(1, 2, 4, 5, 3, 2, 8);
    //输出1,2,4,碰到5不成立停止
    list.stream().takeWhile(x -> x < 5).forEach(System.out::println);

    //丢弃1,2,碰到3不成立停止
    List<Integer> collect = Stream.of(1, 2, 3, 4, 5).dropWhile(i -> i%3!=0).collect(Collectors.toList());
    System.out.println(collect);

    //允许值为空
    Stream<Object> stream = Stream.ofNullable(null);

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

    //空值 throw
     Optional<Integer> optionalInteger = Optional.of(11);
     optionalInteger.empty().orElseThrow();
}

Stream 还提供一个 Predicate (判断条件)来指定什么时候结束迭代。

@Test
public void iterateTest() {
    Stream.iterate(1, i -> ++i).limit(5).forEach(System.out::println);
    //可以直接在 iterate 内部判断
    Stream.iterate(1, i -> i <= 5, i -> ++i).forEach(System.out::println);
}
sealed

更加精细控制继承
一个类没有被 final 关键字修饰,那么其他类就可以继承该类
Java15(15、16预览,17正式) 开始,可以使用 sealed 修饰一个类或接口,并通过 permits 指定哪几个类可以从该类继承,继承的子类必须使用 final 或者 non-sealed 修饰。

//接口也可以 sealed interface Animal permits Cat, Dog {
abstract sealed class Animal permits Cat, Dog {
    void eat() {
        System.out.println("have a meal.");
    }
    abstract void speak();
}
//需要明确用 final 修饰
final class Cat extends Animal {
    @Override
    void speak() {
        System.out.println("miao");
    }
}
//如果想被继承的话用 non-sealed 修饰
non-sealed class Dog extends Animal {
    @Override
    void speak() {
        System.out.println("wang");
    }
}

class Husky extends Dog {
    @Override
    void speak() {
        System.out.println("Stupid humans!");
    }
}

instanceof 模式匹配

Java 14(14、15预览,16正式) 中,对 instanceof 模式匹配做了改进,允许程序中的逻辑判断从对象中有条件地提取组件,以编写更简洁和安全的表达式。

public class InstanceofExample {

    @Test
    public void instanceofTest() {
        Object obj = "string value";
        //传统写法
        if (obj instanceof String) {
            String str = (String) obj;
            Assert.assertEquals(str, obj);
        }
        //新写法
        if (obj instanceof String s) {
            Assert.assertEquals(s, obj);
        }
        //还可以做其他操作
        if (obj instanceof String s && s.contains("val")) {
            Assert.assertEquals(s, obj);
        }
    }

}

飞行记录器

飞行记录器之前是商业版 JDK 的一项分析工具,低开销的事件信息收集框架,主要用于对应用程序和 JVM 进行故障检查、分析

-XX:StartFlightRecording

也可以使用 bin/jcmd 工具启动和配置飞行记录器

    $ jcmd <pid> JFR.start
    $ jcmd <pid> JFR.dump filename=recording.jfr
    $ jcmd <pid> JFR.stop

JFR 使用示例

    public class FlightRecorderTest extends Event {
        @Label("Hello World")
        @Description("Helps the programmer getting started")
        static class HelloWorld extends Event {
            @Label("Message")
            String message;
        }
 
        public static void main(String[] args) {
            HelloWorld event = new HelloWorld();
            event.message = "hello, world!";
            event.commit();
        }
    }

在运行时加上如下参数:

java -XX:StartFlightRecording=duration=1s, filename=recording.jfr

飞行记录器分析示例

    public void readRecordFile() throws IOException {
        final Path path = Paths.get("D:\\ java \\recording.jfr");
        final List<RecordedEvent> recordedEvents = RecordingFile.readAllEvents(path);
        for (RecordedEvent event : recordedEvents) {
            System.out.println(event.getStartTime() + "," + event.getValue("message"));
        }
    } 
ZGC

zgc成为了默认GC

  1. 支持TB级内存
  2. 停顿时间控制在10ms之内,垃圾回收所引起的暂停时间并不会随着内存的增大而延长
  3. 对程序吞吐量影响小于15%

G1的目标是在可控的停顿时间内完成垃圾回收,所以进行了分区设计,停顿时间主要来自垃圾回收(Youth-GC)阶段中的复制算法,在复制算法中,需要把对象转移到新的空间中,并且更新其他对象到这个对象的引用。实际中对象的转移涉及内存的分配和对象成员变量的复制,而对象成员变量的复制是非常耗时的。且都是在STW中并行执行的,而ZGC就是把对象的转移也并发执行,从而满足停顿时间在10ms以下
详细介绍

参数迁移

关于JVM的参数变更、或JDK的一些变更

Unified Logging

在 Java 领域,有广为人知的日志框架,slf4j、log4j 等,这些框架提供了统一的编程接口,让用户可以通过简单的配置实现日志输出的个性化配置,比如日志 tag、级别(info、debug 等)、上下文(线程 id、行号、时间等),在 JVM 内部之前一直缺乏这样的规范,于是出来了 Unified Logging,实现了日志格式的大一统,这就是我们接下来要介绍的重点 Unified Logging。

-Xloggc:/tmp/gc.log -升级> -Xlog:/temp/jvm.log
-XX:+PrintGCDetails -升级>-Xlog:all

请添加图片描述

我们接触最多的是 gc 的日志,在 java8 中,我们配置 gc 日志的参数是 -Xloggc:/tmp/gc.log。在 JVM 中除了 GC,还有大量的其它相关的日志,比如线程、os 等,在新的 Unified Logging 日志中,日志输出的方式变更为了 java -Xlog:xxx,GC 不再特殊只是做为日志的一种存在形式。

java -Xlog -version

可以看到日志输出里,不仅有 GC 相关的日志,还有 os 线程相关的信息。事实上 java 的日志的生产者有非常多部分,比如 thread、class load、unload、safepoint、cds 等。

推荐配置

-Xlog:
  // selections
    codecache+sweep*=trace,
    class+unload,                      // TraceClassUnloading
    class+load,                        // TraceClassLoading
    os+thread,
    safepoint,                        // TraceSafepoint
    gc*,                              // PrintGCDetails
    gc+stringdedup=debug,             // PrintStringDeduplicationStatistics
    gc+ergo*=trace,
    gc+age=trace,                     // PrintTenuringDistribution
    gc+phases=trace,
    gc+humongous=trace,
    jit+compilation=debug
// output
:file=/path_to_logs/app.log   
// decorators               
:level,tags,time,uptime,pid
// output-options                
:filesize=104857600,filecount=5

G1 参数调整

G1作为JDK17的默认垃圾收集器,也有一些不同于JDK8的垃圾收集器配置

不要配置新生代的大小

这个在《JVM G1 源码分析和调优》一书里有详细的介绍,有两个主要的原因:

G1对内存的管理是不连续的,重新分配一个分区代价很低 G1 的需要根据目标停顿时间动态调整搜集的分区的个数,如果不能调整新生代的大小,那么
G1 可能不能满足停顿时间的要求 诸如 -Xmn, -XX:NewSize, -XX:MaxNewSize,
-XX:SurvivorRatio 都不要在 G1 中出现,只需要控制最大、最小堆和目标暂停时间即可

调整 -XX:InitiatingHeapOccupancyPercent 到合适的值

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值