JDK8-20特性汇总

414 阅读28分钟

前言:

你发任你发,我用java8.

segmentfault.com/a/119000004…

JDK8新特性(2014年初)(LTS版本)

1、Lambda表达式

场景语法
无参数无返回值() -> System.out.println(“Hello WOrld”)
有一个参数无返回值(x) -> System.out.println(x)
有且只有一个参数无返回值x -> System.out.println(x)
有多个参数,有返回值,有多条lambda体语句(x,y) -> {System.out.println(“xxx”);return xxxx;};
有多个参数,有返回值,只有一条lambda体语句(x,y) -> xxxx
总结:左右遇一省括号,左侧推断类型省

2、函数式编程

函数接口指的是只有一个抽象方法的接口,这样的接口可以隐式转换为Lambda表达式。 当然接口中可以包含其他的方法(默认,静态) .

java.lang.Runnable和java.util.concurrent.Callable是函数式接口的最佳例子。函数式接口非常脆弱:只要某个开发者在该接口中添加一个函数,则该接口就不再是函数式接口进而导致编译失败。为了克服这种代码层面的脆弱性,并显式说明某个接口是函数式接口,Java 8 提供了一个特殊的注解 @FunctionalInterface , 所以可以通过@FunctionalInterface注解检测接口是否为一个函数式接口 .

@FunctionalInterface
public interface FunctionalDefaultMethods {
    void method();
    // 默认方法
    default void defaultMethod() {}    
    // 静态方法
    static void staticMethod(){}
}

JDK8提供的常用函数式接口API, 主要有Consumer, Supplier , Predicate , Function.

2.1、Consumer

消费型接口,有参无返回值 , 参数类型由泛型决定

package jdk8.functional.functionalInterface;

import org.junit.Test;

import java.util.function.Consumer;

public class ConsumerTest {
    private void accept(String name, Consumer<String> consumer){
        consumer.accept(name);
    }

    @Test
    public void test(){
        accept("admin",(name)->{
            String reName= new StringBuilder(name).reverse().toString();
            System.out.println(reName);
        });
    }

    private void andThen(String s, Consumer<String> c1,
                         Consumer<String> c2) {
        //使用andThen方法,把两个Consumer接口连接到一起,先消费c1再消费c2
        c1.andThen(c2).accept(s);
    }

    @Test
    public void test2() {
        andThen("admin",
                (name) -> {
                    System.out.println(name);
                },
                (name) -> {
                    //消费方式,把字符串转换为大写输出
                    System.out.println(name.toUpperCase());
                });
    }

}

2.2、Supplier

供给型接口,无参有返回值 ,Supplier接口仅包含一个无参的方法:T get()

package jdk8.functional.functionalInterface;

import org.junit.Test;

import java.util.function.Supplier;

/**
 * Supplier<T>:供给型接口,无参有返回值
 */
public class SupplierTest {

    public String getString(Supplier<String> supplier) {
        return supplier.get();
    }

    @Test
    public void test() {
        //定义一个方法,方法的参数传递Supplier<T>接口,泛型执行String,get方法就会返回一个String
        String s = getString(() -> "人才");
        System.out.println(s);
    }

    private Integer getMax(Supplier<Integer> supplier) {
        //定义一个方法,用于获取int类型数组中元素的最大值,方法的参数传递Supplier接口,泛型使用包装类Integer
        return supplier.get();
    }

    @Test
    public void test2() {
        //定义一个int类型的数组,并赋值
        int[] arr = {100, 78, -887, 66, 90};
        //调用getMax方法,方法参数Supplier是一个函数式接口,可以传递Lambda表达式
        int maxValue = getMax(() -> {
            int max = arr[0];
            for (int a : arr) {
                if (a > max) {
                    max = a;
                }
            }
            return max;
        });
        System.out.println(maxValue);
    }
}

2.3、Predicate

言型接口,有参有返回值, 对某种类型的数据进行判断 , 返回boolean类型 .

package jdk8.functional.functionalInterface;

import org.apache.commons.lang3.StringUtils;
import org.junit.Test;

import java.util.function.Predicate;

/**
 * Predicate<T>: 断言型接口,有参有返回值,返回值是boolean类型
 */
public class PredicateTest {
    //定义一个方法
    //参数传递一个String类型的字符串
    //传递一个Predicate接口,泛型使用String
    //使用predicate中的方法test对字符串进行判断,并把判断的结果返回
    public boolean checkString(String s, Predicate<String> predicate) {
        // 对某种数据类型的数据进行判断,结果返回一个boolean值
        return predicate.test(s);
    }

    @Test
    public void test() {
        //定义一个字符串
        String s = "abcdef";
        //调用checkString方法对字符串进行校验,参数传递字符串和Lambda表达式
        //对参数传递的字符串进行判断,判断字符串的长度是否大于5,并把判断的结果返回
        boolean b = checkString(s, str -> str.length() > 5);
        System.out.println(b);
    }

    //定义一个方法,方法的参数,传递一个字符串
    //传递俩个Predicate接口
    //1、判断字符串的长度是否大于5  2、判断字符串中是否包含a  两个条件必须同时满足,使用&&运算符连接两个条件
    private boolean checkString(String s, Predicate<String> predicate, Predicate<String> predicate2) {
        //等价于return pre1.test(s)&&pre2.test(s);
        return predicate.and(predicate2).test(s);
    }

    @Test
    public void test2() {
        //定义一个字符串
        String s = "asdfgi";
        //调用checkString方法,参数传递字符串和两个Lambda表达式
        boolean b = checkString(s,
                str -> !StringUtils.isEmpty(str) && str.length() > 5,
                str -> !StringUtils.isEmpty(str) && str.contains("a"));
        System.out.println(b);
    }

    //定义一个方法,方法的参数,传递一个字符串
    //传递俩个Predicate接口
    //1、判断字符串的长度是否大于5  2、判断字符串中是否包含a  满足一个条件即可,使用||运算符连接两个条件
    private boolean checkStringOr(String s, Predicate<String> predicate, Predicate<String> predicate2) {
        return predicate.or(predicate2).test(s);
    }

    @Test
    public void test3() {
        //定义一个字符串
        String s = "asdfgi";
        //调用checkStringOr方法,参数传递字符串和两个Lambda表达式
        boolean b = checkStringOr(s,
                str -> !StringUtils.isEmpty(str) && str.length() > 5,
                str -> !StringUtils.isEmpty(str) && str.contains("a")
        );
        System.out.println(b);
    }

    //定义一个方法,方法的参数,传递一个字符串
    public boolean checkStringNegate(String s, Predicate<String> predicate) {
        //使用Predicate接口判断字符串的长度是否大于5
        //return !pre.test(s);
        return predicate.negate().test(s);
    }

    @Test
    public void test4() {
        //定义一个字符串
        String s = "asdfg";
        //调用checkString方法,参数传递字符串和Lambda表达式
        boolean b = checkStringNegate(s, str -> str.length() > 4);
        System.out.println(b);
    }

}

2.4、Function

函数式接口,有参有返回值 . java.util.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据.

package jdk8.functional.functionalInterface;

import org.junit.Test;

import java.math.BigDecimal;
import java.util.function.Function;

/**
 * Function<T, R> : 函数式接口,有参有返回值
 * java.util.function<T,R>接口用来根据一个类型的数据得到另一个类型的数据
 */
public class FunctionTest {
    //定义一个方法
    //方法的参数传递一个字符串类型的整数
    //方法的参数传递一个Function接口,泛型使用<String,Integer>
    //使用Function接口中的方法apply,把字符串类型的整数,转换为Integer类型的整数
    private Integer change(String s, Function<String, Integer> function) {
        Integer apply = function.apply(s);
        return apply;
    }

    @Test
    public void test() {
        //定义一个字符串类型的整数
        String s = "1234";
        //调用change方法传递字符串类型的整数,和Lambda表达式
        Integer change = change(s, str -> Integer.parseInt(str));
        System.out.println(change instanceof Integer);
    }


    //定义一个方法
    //参数传一个字符串类型的整数
    //参数在传两个Function接口
    //一个泛型使用Function<String,Integer>
    //一个泛型使用Function<Integer,String>
    private String andThen(String s, Function<String, Integer> fun, Function<Integer, String> fun2) {
        return fun.andThen(fun2).apply(s);
    }

    @Test
    public void test2() {
        //定义一个字符串类型的整数
        String s = "123";
        //调用change方法,传递字符串和两个Lambda表达式
        String change = andThen(s,
                str -> Integer.parseInt(s) + 10,
                integer -> String.valueOf(integer)
        );
        System.out.println(change + ", " + change instanceof String);
        System.out.println(change);
    }

    /**
     * 传入number 先执行fun2 Integer转String 再执行fun1 String 转泛型R
     */
    public static <R> R typeConvert2(Integer number, Function<String, R> fun1, Function<Integer, String> fun2) {
        return fun1.compose(fun2).apply(number);
    }

    @Test
    public void test3() {
        BigDecimal amount = typeConvert2(100, BigDecimal::new, s2 -> String.valueOf(s2 * s2));
        System.out.printf("function --> {%s}%n", amount);
    }

}

2.5、实现延时加载

/**
 * 根据不同日志级别输出日志信息
 */
public class LogPrintTest {
    @Test
    public void test() {
        //定义三个日志信息
        String msg1 = "Hello ";
        String msg2 = "World ";
        String msg3 = "Java";
        //调用showLog方法,传递日志级别和日志信息
        printLog(2, msg1 + msg2 + msg3);
    }

    //定义一个根据日志的级别,显示日志信息的方法
    public void printLog(int level, String message) {
        //对日志的等级进行判断,如果式1级别,那么输出日志信息
        if (level == 1) {
            System.out.println(message);
        }
    }
}



/**
 * 根据不同日志级别输出日志信息
 */
public class LogPrintTest {
    @Test
    public void test2() {
        //定义三个日志信息
        String msg1 = "Hello ";
        String msg2 = "World ";
        String msg3 = "Java";
        //调用showLog方法,参数MessageBulider是一个函数式接口,所以可以传递Lambda表达式
        showLog(1, () -> msg1 + msg2 + msg3);
    }

    //定义一个显示日志的方法,方法的参数传递日志的等级和Message接口
    public void showLog(int level, MessageBulider mb) {
        //对日志的等级进行判断,如果是1级,则调用MessageBulider接口中的BuilderMessage方法
        if (level == 1) {
            System.out.println(mb.builderMessage());
        }
    }

    // 定义一个函数式接口
    @FunctionalInterface
    public interface MessageBulider {
        //定义一个拼接消息的抽象方法,返回拼接的消息
        String builderMessage();
    }
}

3、接口可以添加默认方法和静态方法,也就是定义不需要实现类实现的方法

4、方法引用

若lambda体中的内容有方法已经实现了,那么可以使用方法引用 , 也可以理解为方法引用是lambda表达式的另外一种表现形式, 并且其语法比lambda表达式更加简单 . 下面先通过一个简单的案例来说明.

双冒号:: 为引用运算符 , 它所在的表达式被称为方法引用 . 如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号::来引用该方法作为Lambda表达式的替代者。

/**
 * 方法的基本引用
 */
public class PrintFunctionalTest {
    @Test
    public void test() {
        // 方式1: lambda表达式实现了函数式接口, 进行字符串的输出打印
        this.printstring("test print", str -> System.out.println(str));

        // 方式2: 方法引用方式实现字符串的输出打印
        // 引用方法的参数就是lambda表达式的参数
        this.printstring("test print by qout", System.out::println);
    }
    
        //定义一个方法,方法的参数传递PrintFunctionInterface接口
    private void printstring(String str, PrintFunctionInterface printFunctionInterface) {
        printFunctionInterface.printStr(str);
    }

    // 定义一个函数式接口
    @FunctionalInterface
    public interface PrintFunctionInterface {
        //定义打印字符串的抽象方法
        void printStr(String str);
    }
}

4.1、对象名引用成员方法

对象名已经存在,成员方法也已经存在的, 就可以使用对象名来引用成员方法 .

public class PrintFunctionalTest1 {
    @Test
    public void test2() {
        // 方式1: 通过对象直接调用类的成员方法
        this.printString2("test print by qout", str -> {
            MethodRerObject methodRerObject = new MethodRerObject();
            methodRerObject.printUpperCaseString(str);
        });

        // 方式2: 使用方法引用来优化lambda, 使用对象名引用成员方法
        MethodRerObject methodRerObject = new MethodRerObject();
        this.printString2("test print by qout", methodRerObject::printUpperCaseString);
    }

    //定义一个方法,方法的参数传递PrintFunctionInterface接口
    private void printString2(String str, Consumer<String> consumer) {
        consumer.accept(str);
    }

    // 定义一个函数式接口
    @FunctionalInterface
    public interface PrintFunctionInterface {
        //定义打印字符串的抽象方法
        void printStr(String str);
    }

    // 定义一个类,提供一个字符串输出方法
    class MethodRerObject {
        //定义一个成员方法,传递字符串,把字符串按照大写输出
        public void printUpperCaseString(String str) {
            System.out.println(str.toUpperCase());
        }
    }
}

4.2、类名引用成员方法

当lambda表达式的实现内容有两个相同类型的参数 , 且一个参数对象调用成员方法的参数列表是另外一个参数, 那么可以使用类名引用成员方法来简化代码.

@Test
public void test4() {
        // 方式1: 参数对象进行调用
        boolean b = this.equalsStr("hello", "world",
                        (str1, str2) -> !StringUtils.isEmpty(str1) && str1.equals(str2));
        // 方式2: 方法引用-类名::实例方法名
        b = this.equalsStr("hello", "world", String::equals);

        System.out.println(b);
}

// 定义一个成员方法, 比较两个字符串是否相等, 第三个参数传递一个BiFunction函数
private boolean equalsStr(String str1, String str2, BiFunction<String, String, Boolean> fun) {
        return fun.apply(str1, str2);
}

4.3、类名引用静态方法

类已经存在,静态成员方法也已经存在,就可以通过类名直接引用静态成员方法

public class PrintFunctionalTest3 {
    @Test
    public void test() {
        //方式1: 调用method方法,传递计算绝对值的整数和Lambda表达式
        int number = method(-10, num -> Math.abs(num));
        System.out.println(number);
        //方式2: 使用方法引用优化Lambda表达式, 通过类名引用静态方法
        number = method(-10, Math::abs);
        System.out.println(number);
    }

//    //定义一个方法,方法的参数传递要计算绝对值的整数和函数式接口Calcable
//    public int method(int num, Calcable c) {
//        return c.calsAbs(num);
//    }

//     @FunctionalInterface
//        public interface Calcable {
//        //定义一个抽象方法,传递一个整数,对整数进行绝对值计算并返回
//        int calsAbs(int num);
//    }


    public int method(int num, Function<Integer,Integer> fun) {
        return fun.apply(num);
    }

   
}

4.4、super引用父类成员方法

因为有子父类关系,所以存在一个关键字super,代表父类,所以直接使用Super调用父类的成员方法.

public class Parent {
    void sayHello() {
        System.out.println("hello, i'm parent");
    }
}

/**
 * super引用父类成员方法
 */
public class SuperReferenceTest extends Parent {

    @Test
    public void test() {
        //调用method方法,方法的参数Greetable是一个函数式接口,所以可传递Lambda
        greet(() -> {
            Parent parent = new Parent();
            parent.sayHello();
        });

        // 直接使用super调用
        greet(() -> {
            super.sayHello();
        });

        // supper优化lambda表达式, 方法引用
        greet(super::sayHello);
    }

    //定义一个方法参数传递Greetable接口
    public void greet(Greetable greetable) {
        greetable.greet();
    }

    @Override
    void sayHello() {
        System.out.println("hello, i'm children");
    }

    // 定义打招呼的函数式接口
    @FunctionalInterface
    interface Greetable {
        void greet();
    }
}

4.5、 this引用本类成员方法

使用this引用本类的成员方法

/**
 * super引用父类成员方法
 */
public class SuperReferenceTest extends Parent {

    @Test
    public void test2() {
        // 方式1
        greet(() -> {
            SuperReferenceTest children = new SuperReferenceTest();
            children.sayHello();
        });
        // 方式2
        greet(() -> this.sayHello());
        // 方式三  this引用
        greet(this::sayHello);
    }

    //定义一个方法参数传递Greetable接口
    public void greet(Greetable greetable) {
        greetable.greet();
    }

    @Override
    void sayHello() {
        System.out.println("hello, i'm children");
    }

    // 定义打招呼的函数式接口
    @FunctionalInterface
    interface Greetable {
        void greet();
    }
}

4.6、类的构造器引用

/**
 * 构造器的方法引用
 */
public class ConstructRefrenceTest {
        /**
     * 有参构造方法的引用
     */
    @Test
    public void test() {
        // 方式1: lambda表达式, 自己new对象实例化, 传入name参数
        this.printName("crysw", name -> new Person(name));
        // 方式2: 传入的name参数就是构造方法的参数, 可以使用类的构造器引用来替换lambda表达式实现
        this.printName("crysw agin", Person::new);
    }

    private void printName(String name, PersonBuilder personBuilder) {
        personBuilder.builderPerson(name);
        System.out.println(name);
    }

    //定义一个创建Person对象的函数式接口
    @FunctionalInterface
    interface PersonBuilder {
        //定义一个方法,根据传递的姓名,创建Person对象返回
        Person builderPerson(String name);
    }

    // 定义一个Person类
    public class Person {
        private String name;

        public Person() {
        }

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

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

4.7、数组的构造器引用

格式:Type[]::new

@Test
public void test2() {
    // 方式1: lambda表达式, 通过传递的参数构建String数组
        String[] strArr = this.getString(10, integer -> new String[integer]);
        System.out.println(">>" + strArr.length);
    // 方式2: String数组构造方法的引用实现
        strArr = this.getString(20, String[]::new);
        System.out.println(">>>" + strArr.length);
}

// 定义一个方式, 参数传入函数接口, 将Integer类型参数转换为一个String[interger]类型返回
private String[] getString(Integer integer, Function<Integer, String[]> function) {
        return function.apply(integer);
}

5、重复注解,同一个注解可以使用多次

package com.javacodegeeks.java8.repeatable.annotations;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
public class RepeatingAnnotations {
    @Target( ElementType.TYPE )
    @Retention( RetentionPolicy.RUNTIME )
    public @interface Filters {
        Filter[] value();
    }
 
    @Target( ElementType.TYPE )
    @Retention( RetentionPolicy.RUNTIME )
    @Repeatable( Filters.class )
    public @interface Filter {
        String value();
    };
 
    @Filter( "filter1" )
    @Filter( "filter2" )
    public interface Filterable {        
    }
 
    public static void main(String[] args) {
        for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
            System.out.println( filter.value() );
        }
    }
}


-----------------------------------
filter1
filter2

6、引入Optional来避免空指针

Optional仅仅是一个容器:存放T类型的值或者null。它提供了方法来避免显式的null检查.

避免源码被各种null检查污染,以便开发者写出更加整洁的代码

Optional.of(T t) 创建一个非空Optional实例, 如果传递的参数是null,抛出NullPointerException.

Optional.empty() 创建一个空的Optional实例

Optional.ofNullable(T t) 若参数T t不为null,创建一个非空Optional实例,否则创建一个空实例.

isPresent() 如果Optional实例持有一个非空值 , 返回true ; 否则 返回false.

ifPresent(Consumer<? super T> consumer) 如果存在值,则使用该值调用指定的消费者,否则不执行任何操作。

orElse(T t) 如果Optional实例包含值,返回该值,否则返回默认值T t.

orElseGet(Supplier s) 如果Optional实例包含值,返回该值,否则返回函数接口的lambda表达式实现生成的默认值;

map(Function f) 如果Optional实例有值, 将现有值转换成新的值,否则返回Optional.empty();

flatMap(Function f, , Optional> mapper) 与 map(Function f)类似, 返回值是Optional.

提示:Optional.of(null) 会直接报NullPointerException.

7、引入Streams相关的API

Java8 中添加了一个新的流接口操作的概念, 在 java.util.stream包下面,相当于高级版的 Iterator,它可以通过 Lambda 表达式对集合进行大批量数据操作,或者各种便利、高效的聚合数据操作, 用于解决集合类库既有的弊端。

常用的:
map():将流中的元素进行再次加工形成一个新流,流中的每一个元素映射为另外的元素。
filter(): 返回结果生成新的流中只包含满足筛选条件的数据
limit():返回指定数量的元素的流。返回的是 Stream 里前面的 n 个元素。
skip():和 limit()相反,将前几个元素跳过(取出)再返回一个流,如果流中的元素小于或者等于 n,就会返回一个空的流。
sorted():将流中的元素按照自然排序方式进行排序。
distinct():将流中的元素去重之后输出。
peek():对流中每个元素执行操作,并返回一个新的流,返回的流还是包含原来流中的元素。

new-developer.aliyun.com/article/112…

8、引入新的Date/Time相关的API

8.1、LocalDateTime

LocalDateTime是一个不可变的日期时间对象,代表日期时间,通常被视为年 - 月 - 日 - 时 - 分 - 秒。 也可以访问其他日期和时间字段,例如日期,星期几和星期。 时间表示为纳秒精度。 例如,值“2007年10月2日在13:45.30.123456789”可以存储在LocalDateTime 。

8.2、LocalDate

LocalDate是一个不可变的日期对象,表示日期,通常被视为年月日。 也可以访问其他日期字段,例如日期,星期几和星期。 例如,值“2007年10月2日”可存储在LocalDate 。

8.3、LocalTime

LocalTime是一个不可变的时间对象,代表一个时间,通常被看作是小时 - 秒。 时间表示为纳秒精度。

8.4、ZonedDateTime

ZonedDateTime是具有时区的日期时间的不可变表示。 此类存储所有日期和时间字段,精度为纳秒,时区为区域偏移量,用于处理模糊的本地日期时间。

// 获取当前时间日期
ZonedDateTime date1 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]");
System.out.println("date1: " + date1); // date1: 2015-12-03T10:15:30+08:00[Asia/Shanghai]
// 获取时区
System.out.println(date1.getZone().getId()); // Asia/Shanghai
ZoneId id = ZoneId.of("Europe/Paris"); 
System.out.println("ZoneId: " + id); // Europe/Paris

8.5、Instant

Instant表示在时间线上建立单个瞬时点。 这可能用于在应用程序中记录事件时间戳。

8.6、Duration

Duration用于计算两个时间之间的间隔

public class DurationTest {


    @Test
    public void test() throws InterruptedException {
        // Duration:计算两个时间之间的间隔
        Instant ins1 = Instant.now();
        TimeUnit.MILLISECONDS.sleep(1000);
        Instant ins2 = Instant.now();
        Duration dura = Duration.between(ins1, ins2);
        System.out.println(dura);
        System.out.println(dura.toMillis());
    }

}

8.7、Period

计算两个日期之间的间隔.

public class PeriodTest {

    @Test
    public void test() throws InterruptedException {
        LocalDate oldDate = LocalDate.of(2018, 5, 1);
        LocalDate today = LocalDate.now();

        Period period = Period.between(oldDate, today);
        System.out.println(period.getYears()+"-"+period.getMonths()+"-"+period.getDays());
    }
}

9、JVM使用MetaSpace代替了永久代(PermGen Space)

重要特性:Lambda表达式、函数式接口、方法引用、Stream流式 API 、采用MetaSpace代替了永久代(PermGen Space)

JDK9新特性(2017年9月)

1、接口方法可以使用private来修饰


public interface DemoInterface {
    void method1();

    default void method2(){};

    static void method3(){};

    // private 方法
    private void method4(){};
}

2、设置G1为JVM默认垃圾收集器

3、支持http2.0和websocket的API

二进制格式、多路复用、头部数据压缩

blog.csdn.net/Hero_era/ar…

4.模块化

zhuanlan.zhihu.com/p/40623222?…

重要特性:主要是 API 的优化,如支持HTTP2的Client API、 JVM 采用G1为默认垃圾收集器

JDK10新特性(2018年3月)

1、局部变量类型推断,类似JS可以通过var来修饰局部变量,编译之后会推断出值的真实类型

2、并行Full GC,来优化G1的延迟

3、允许在不执行全局VM安全点的情况下执行线程回调,可以停止单个线程,而不需要停止所有线程或不停止线程

重要特性:通过var关键字实现局部变量类型推断,使Java语言变成弱类型语言、 JVM 的G1垃圾回收由单线程改成多线程并行处理,降低G1的停顿时间

JDK11新特性(2018年9月)(LTS版本)

1、ZGC,ZGC可以看做是G1之上更细粒度的内存管理策略。由于内存的不断分配回收会产生大量的内存碎片空间,因此需要整理策略防止内存空间碎片化,在整理期间需要将对于内存引用的线程逻辑暂停,这个过程被称为"Stop the world"。只有当整理完成后,线程逻辑才可以继续运行。(并行回收)

G1:在内存较小的堆空间情况下,CMS的表现会优于G1收集器,平衡点在***6~8GB***左右。

ZGC:ZGC是能自动感知NUMA架构并可以充分利用NUMA架构特性的一款垃圾收集器。TB级内存

小型区/页(Small):固定大小为2MB,用于分配小于256KB的对象。

中型区/页(Medium):固定大小为32MB,用于分配>=256KB ~ <=4MB的对象。

大型区/页(Large):没有固定大小,容量可以动态变化,但是大小必须为2MB的整数倍,专门用于存放>4MB的巨型对象。但值得一提的是:每个Large区只能存放一个大对象,也就代表着你的这个大对象多大,那么这个Large区就为多大,所以一般情况下,Large区的容量要小于Medium区,并且需要注意:Large区的空间是不会被重新分配的(稍后分析)。

juejin.cn/post/708003…

2、Flight Recorder(飞行记录器),基于OS、JVM和JDK的事件产生的数据收集框架

blog.csdn.net/songjunyan/…

3、对Stream、Optional、集合API进行增强

集合提供了快速复制的方法copy()等。

public class CollectionDemo {

    @Test
    public void test(){
        var list1 = List.of("张三","李四","王五");
        var newList= List.copyOf(list1);
        println(newList,System.out::println);
        var set = Set.of("张三","李四");
        var newSet = Set.copyOf(set);
        println(newSet,System.out::println);
    }

    private void println(Collection c,Consumer<Collection> consumer){
        consumer.accept(c);
    }

}

Stream中新增了takeWhile()和dropWhile()截止结算的方法。

public class StreamDemo {

    @Test
    public void test(){
        var list1 = List.of("张三","李四","王五");
        println(list1,System.out::println);
        // 丢弃第一个不满足条件的元素之前的所有元素
        var newList1 = list1.stream().dropWhile(name->!name.equals("李四")).collect(Collectors.toList());
        println(newList1,System.out::println);
        // 保留第一个不满足条件之前所有的元素
        var newList2 = list1.stream().takeWhile(name->!name.equals("李四")).collect(Collectors.toList());
        println(newList2,System.out::println);
    }

    private void println(Collection c, Consumer<Collection> consumer){
        consumer.accept(c);
    }
}
JDK11在Optional类中增加了一些新的方法,使其更加方便和灵活:
1. or()和or(Supplier<? extends Optional<? extends T>> supplier)方法:如果Optional对象为空,则返回另一个Optional对象或调用Supplier提供的Optional对象。

2. stream()方法:将Optional对象转换为Stream对象。

3. ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)方法:如果Optional对象非空,则执行一个操作,否则执行另一个操作。

4. isEmpty()方法:检查Optional对象是否为空。

5. orElseThrow()方法:如果Optional对象为空,则抛出异常。

重要特性:对于JDK9和JDK10的完善,主要是对于Stream、集合等 API 的增强、新增ZGC垃圾收集器

JDK12新特性(2019年3月)

1、Shenandoah GC,新增的GC算法

2、switch 表达式语法扩展,可以有返回值

jdk12可以使用->和返回值,可以免去break

        @Test
        void test() {
                String s = "b";
                int x = switch (s) {
                case "a" -> 1;
                case "b", "c" -> 2;
                default -> 3;
                };
                System.out.println(x);
        }

3、G1收集器的优化,将GC的垃圾分为强制部分和可选部分,强制部分会被回收,可选部分可能不会被回收,提高GC的效率

重要特性:switch表达式语法扩展、G1收集器优化、新增Shenandoah GC 垃圾回收算法

JDK13新特性(2019年9月)

1、Socket的底层实现优化,引入了NIO;

Java网络编程-Netty(基础)

2、switch表达式增加yield关键字用于返回结果,作用类似于return,如果没有返回结果则使用break;

        @Test
        void test() {
                String s = "b";
                int x = switch (s) {
                case "a" -> {
                        System.out.println(111);
                        yield 1;
                }
                case "b", "c" -> {
                        System.out.println(222);
                        yield 2;
                }
                default -> {
                        System.out.println(333);
                        yield 3;
                }
                };
                System.out.println(x);
        }

3、ZGC优化,将标记长时间空闲的堆内存空间返还给操作系统,保证堆大小不会小于配置的最小堆内存大小,如果堆最大和最小内存大小设置一样,则不会释放内存还给操作系统;

4、引入了文本块,可以使用"""三个双引号表示文本块,文本块内部就不需要使用换行的转义字符;

// old
String myJson = "{\n" +
                "    "name":"Song Hongkang",\n" +
                "     "address":"www.atguigu.com",\n" +
                "    "email":"shkstart@126.com"\n" +
                "}";
System.out.println(myJson);

// new
String myJson1 = """
                {
                    "name":"Song Hongkang",
                     "address":"www.atguigu.com",
                    "email":"shkstart@126.com"
                }""";
System.out.println(myJson1);

重要特性:ZGC优化,释放内存还给操作系统、socket底层实现引入NIO

JDK14新特性(2020年3月)

1、instanceof 类型匹配语法简化,可以直接给对象赋值,如if(obj instanceof String str),如果obj是字符串类型则直接赋值给了str变量;

// 执行提示JDK2O才开始支持---记录模式
 public class InstanceDemo {

    @Test
    public void Test() {
        Object a = "123123";
        if (a instanceof String str) {
            //String str = String.valueof(a);
            System.out.println(str);
        }
    }
}

2、引入record类,类似于枚举类型,可以向Lombok一样自动生成构造器、equals、getter等方法;该类在JDK16 正式启用(jdk 16 生效)

JDK 14中引入了一个新的特性record类,它是Java中的一种新类型,其主要目的是作为对象数据传输的简单轻量级替代方案。Record类旨在提供一个简单的、不可变的、易于编写的数据聚合,它支持基于字段的构造函数和一个标准的等同性比较方法。

Record类的定义非常简单:

```java
public record Person(String name, int age) {}
```

这个定义会生成一个简单的、不可变的Person类,包含两个属性name和age,并自动为属性生成一个构造函数、getter方法和equals()和hashCode()方法。

Record类的特点如下:

1. 自动为每个属性创建一个final字段,并自动生成构造函数;
2. 自动在类中创建一个equals()方法和hashCode()方法来支持对象比较;
3. toString()方法的默认行为是打印所有属性的名称和值。

Record类的优势在于它可以减少重复的代码,提供一种快速轻便的记录数据的方法。例如,您可以很容易地使用Record类来表示一个订单、一份报告或一条推文等简单数据对象。

public class RecordClassDemo {

    public record Person(String name, int age) {};

    @Test
    public void test(){
        Person p = new Person("张三",30);
        System.out.println(p.name+"-"+p.age);
    }

}

3、NullPointerException打印优化,打印具体哪个方法抛的空指针异常,避免同一行代码多个函数调用时无法判断具体是哪个函数抛异常的困扰,方便异常排查;

JDK15新特性(2020年9月)

1、隐藏类 hidden class

它允许Java程序在运行时动态地创建一个类,并且这个类不会像普通类那样被保存到文件系统中。

Hidden class的主要用途是实现一些高度动态、随机化或者混淆的逻辑,以保护Java程序免受攻击。在实现一些高级解释器和JIT编译器时,也可以使用Hidden class来实现一些高级优化策略。

使用Hidden class非常简单,只需要调用ClassLoader.defineHiddenClass()方法即可创建一个隐藏类。以下是一个简单的示例:

```java
public class HiddenClassDemo {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        byte[] classBytes = Files.readAllBytes(Paths.get("MyHiddenClass.class"));
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        Class<?> cls = lookup.defineHiddenClass(classBytes, true, null);
        Object obj = cls.newInstance();
        Method method = cls.getDeclaredMethod("sayHello");
        method.invoke(obj);
    }
}
```

在这个例子中,我们从文件系统中读取一个class文件,然后使用MethodHandles.Lookup类的defineHiddenClass()方法创建一个隐藏类,并使用反射机制创建对象、调用方法。

需要注意的是,Hidden class需要在JVM的安全管理器中开启特定的权限,以允许Java程序在运行时动态创建类。因此,使用Hidden class需要谨慎处理,以保证程序的安全性。

blog.51cto.com/teayear/504…

2、密封类 sealed class,通过sealed关键字修饰抽象类限定只允许指定的子类才可以实现或继承抽象类,避免抽象类被滥用; Jdk 17 正式启用

具体使用看演示

blog.csdn.net/qq_45872039…

JDK16新特性(2021年3月)

1、ZGC性能优化

2、instanceof模式匹配

3、record的引入

JDK16相当于是将JDK14、JDK15的一些特性进行了正式引入

JDK17新特性(2021年9月)(LTS版本)

1、正式引入密封类sealed class,限制抽象类的实现;

虽然JDK17也是一个LTS版本,但是并没有像JDK8和JDK11一样引入比较突出的特性,主要是对前几个版本的整合和完善。

JVM性能提升,spring boot3.x.x 强依赖

developer.aliyun.com/article/983…

JDK18新特性(2022年3月22)

1、 默认UTF-8字符编码

2、简单的Web服务器

在JDK18中提供一个简单的Web服务。也就是在bin目录下提供了一个 jwebserver的命令。运行这个命令可以启动一个 简单的、最小的静态web服务器。它不支持 CGI 和 Servlet,所以最好的使用场景是用来测试、教育、演示等需求。

3、反射功能的新特性

Java 18 改进了 java.lang.reflect.Method、Constructor 的实现逻辑,使之性能更好,速度更快。这项改动不会改动相关 API ,这意味着开发中不需要改动反射相关代码,就可以体验到性能更好反射。

4、互联网地址解析SPI

为主机名和地址解析定义服务提供者接口 (SPI),以便 java.net``.InetAddress可以使用平台内置解析器以外的解析器。

    public static void main(String[] args) throws Exception {
        InetAddress inetAddress = InetAddress.getByName("cart.msb.com");
        System.out.println(inetAddress.getHostAddress());
    }

5、switch 表达式增强

// JDK17开始支持支持类型判断
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();
    };
}

sealed interface S permits A, B, C {}
final class A implements S {}
final class B implements S {}
record C(int i) implements S {}  // Implicitly final

static int testSealedExhaustive(S s) {
    return switch (s) {
        case A a -> 1;
        case B b -> 2;
        case C c -> 3;
    };
}

// 支持null
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 void testTriangle(Shape s) {
    switch (s) {
        case Triangle t && (t.calculateArea() > 100) ->
            System.out.println("Large triangle");
        default ->
            System.out.println("A shape, possibly a small triangle");
    }
}

JDK19新特性(2022年9月)

1、Record Patterns (Preview) 记录模式

使用 记录模式 增强 Java 编程语言以解构记录值,可以嵌套记录模式和类型模式,实现强大的、声明性的和可组合的数据导航和处理形式。

这是一个预览语言功能。

blog.csdn.net/m0_74931226…

2、虚拟线程

将虚拟线程引入 Java 平台。虚拟线程是轻量级线程,可显著地减少编写、维护和观察高吞吐量并发应用程序的工作量。

blog.csdn.net/java_lujj/a…

3、Switch 模式匹配

switch 表达式和语句的模式匹配,以及对模式语言的扩展来增强 Java 编程语言。将模式匹配扩展到 switch 中,允许针对一些模式测试表达式,这样就可以简明而安全地表达复杂的面向数据的查询。

4、结构化 并发

引入用于结构化并发的 API 来简化多线程编程,结构化并发将不同线程中运行的多个任务视为单个工作单元,从而简化错误处理、提高可靠性并增强可观察性。

blog.csdn.net/Huangjiazhe…

JDK20新特性(2023年3月)

1、矢量API

矢量API是引入一种API来表达矢量计算,这些计算在运行时可靠地编译为支持的CPU架构上的最佳矢量指令。这将实现优于等效标量计算的性能。

2、虚拟线程(第二次优化)

虚拟线程是轻量级线程,可以减少编写、维护和观察高吞吐量并发应用程序的工作量。在JDK19中已经少量API更改和对ThreadGroup的降级。在JDK20中将从根本上改变Java应用程序的扩展方式。

3、结构化并发

结构化并发由一个API来简化多线程编程,并将在不同线程中运行的多个任务视为单个工作单元。简化了错误处理和消除,提高了可靠性和可观察性。自从在JDK 19中孵化以来,唯一的变化是StructuredTaskScope已经更新,以支持在任务范围中创建的线程对范围值的继承。在JDK20中正在重构此功能。

4、Scoped values 作用域值

Scoped values是一种处于开发阶段的API,可以在线程内和线程间共享不可变的数据。与线程局部变量相比,这些变量更可取,尤其是在使用大量虚拟线程时。作用域值允许在大型程序中的组件之间安全有效地共享数据,而无需使用方法参数。目标包括易用性、可理解性、健壮性和性能。

5、记录模式(第二次优化)

在JDK20中记录模式通过解构记录值的模式增强了Java编程语言。可以嵌套记录模式和类型模式,以实现声明性、强大且可组合的数据导航和处理形式。目标包括扩展模式匹配以表达更复杂、可组合的数据查询,并且不更改类型模式的语法或语义。

外部函数和内存API引入了一种API,Java程序可以通过该API在Java运行时之外与代码和数据进行互操作。API使Java程序能够调用本机库并处理本机数据,而没有JNI(Java native Interface)的脆弱性和危险性。JDK 19之后添加的改进包括MemorySegment和MemoryAddress抽象的统一,增强的MemoryLayout层次结构,以及将MemorySession拆分为Arena和SegmentScope,以促进跨维护边界的段共享。

6、switch语句和表达式的模式匹配

switch语句和表达式的模式匹配可以实现复杂的面向数据的查询的简洁和安全的表达式。在JDK20中将实现与记录模式的持续共同发展,允许根据经验和反馈进行持续改进。本次更新switch模式匹配的主要变化包括switch标签的简化语法,以及对switch语句和表达式中泛型模式和记录模式的类型参数推断的支持。此外,如果在运行时没有应用开关标签,则枚举类上的穷举切换现在会抛出MatchException,而不是不兼容ClassChangeError。

blog.csdn.net/u013735734/…