当前位置 博文首页 > 良月柒:【原创】万字长文带你了解 JDK8 - JDK13 的新特性

    良月柒:【原创】万字长文带你了解 JDK8 - JDK13 的新特性

    作者:[db:作者] 时间:2021-09-12 12:09

    程序员的成长之路

    互联网/程序员/技术/资料共享?

    关注

    阅读本文大概需要 22.8 分钟。

    某次面试中,面试官问到我这个问题,只勉强说出了 JDK 8 的部分特性,今天就来盘一盘!

    画外音:JDK 8 就是 JDK 1.8 版本的意思,后续同理。

    JDK 8 新特性

    Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。

    1. 接口增强,default,static 关键字

    在 JDK 8 之前接口中只能使用抽象方法,而不能有任何方法的实现。

    JDK 8 里面则可以声明 default 和 static 修饰的方法。

    /** * jdk8中接口可以使用声明default和static修饰的方法 * static修饰的方法和普通的方法一样,可以被直接调用 * default修饰的方法有方法体,就和普通的方法一样,可以被重写,有点像抽象类的方法一样,但是java是单继承多实现的 */public interface Today {
        void dream();
        void striver();
        default void victory(){??????? System.out.println("公众号:程序员的成长之路");    }
        static void test(){        System.out.println("接口里的静态方法");    }
        // jdk9 中还新增了private方法    private void test3() {        System.out.println("私有方法");    };}
    

    2.Base64 API

    Base64 是网络上最常见的用于传输 8Bit 字节码的编码方式之一,Base64 就是一种基于 64 个可打印字符来表示二进制数据的方法,基于 64 个字符 A-Z,a-z,0-9,+,/ 的编码方式,是一种能将任意二进制数据用 64 种字元组合成字符串的方法。

    JDK?8?以前的写法

    编码和解码的效率比较差,公开信息说以后的版本会取消这个方法。

    BASE64Encoder?encoder?=?new?BASE64Encoder();BASE64Decoder decoder = new BASE64Decoder();byte[] textByte = "公众号:程序员的成长之路".getBytes("UTF-8");//编码String encodedText = encoder.encode(textByte);//5Zyj6a2U5a+85biI(类似这样的)System.out.println(encodedText);//解码?//公众号:程序员的成长之路System.out.println(new String(decoder.decodeBuffer(encodedText),"UTF-8"));
    

    JDK 8 的写法

    编解码效率远大于 sun.misc 和 Apache commons Codec,可以自己动收压测一下速度。

    Base64.Decoder?decoder2?=?Base64.getDecoder();Base64.Encoder encoder2 = Base64.getEncoder();byte[] textByte2 = "公众号:程序员的成长之路".getBytes("UTF-8");//编码String encodedText2 = encoder2.encodeToString(textByte2);//5Zyj6a2U5a+85biI(类似这样的)System.out.println(encodedText);//解码 //公众号:程序员的成长之路System.out.println(new String(decoder2.decode(encodedText2), "UTF-8"));
    

    3.日期处理类

    JDK 8 之前,SimpleDateFormate,Calendar 等类 API涉及比较差,涉及的比较差,日期时间的计算,比较都相对的麻烦,java.util.Date 还是非线程安全的 API。

    JDK 8 新增日期处理类:LocalDate、LocalTime、Instant、Duration 以及 Period,这些类都包含在 java.time 包中。

    LocalTime localTime = LocalTime.now();// 现在时间:18:00:49.476System.out.println("现在时间:"+localTime);
    

    4.Optionnal类

    Optional类的作用:
    主要解决的问题是:空指针异常 NullPointerException

    本质上是一个包含有可选值的包装类,Opertional类既可以为含有对象也可以为空。

    import java.util.Optional;
    public class Java8Tester {   public static void main(String args[]){
          Java8Tester java8Tester = new Java8Tester();      Integer value1 = null;      Integer value2 = new Integer(10);??????      // Optional.ofNullable - 允许传递为 null 参数      Optional<Integer> a = Optional.ofNullable(value1);
          // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException      Optional<Integer> b = Optional.of(value2);      System.out.println(java8Tester.sum(a,b));   }
       public Integer sum(Optional<Integer> a, Optional<Integer> b){
    ??????//?Optional.isPresent?-?判断值是否存在      System.out.println("第一个参数值存在: " + a.isPresent());      System.out.println("第二个参数值存在: " + b.isPresent());
          // Optional.orElse - 如果值存在,返回它,否则返回默认值      Integer value1 = a.orElse(new Integer(0));
          //Optional.get - 获取值,值需要存在      Integer value2 = b.get();      return value1 + value2;   }}
    

    输出结果为:

    第一个参数值存在: false第二个参数值存在: true10
    

    5.方法引用

    方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

    方法引用使用一对冒号?::?。

    构造器引用,它的语法是Class::new,或者更一般的Class< T >::new实例如下:

    Demo demo = Demo::new;
    

    静态方法引用,它的语法是Class::static_method,实例如下:

    Demo::静态方法();
    

    特定类的任意对象的方法引用:它的语法是Class::method实例如下:

    Demo::方法名();
    

    特定对象的方法引用:它的语法是instance::method实例如下:

    demo::方法名();
    

    6.Lambda 与 函数式编程

    函数式编程

    • 面向对象编程是对数据的抽象(各种各样的POJO类);

    • 函数式编程则是对行为的抽象(将行为作为一个参数进行传递);

    • 所谓的函数编程,即可理解是将一个函数(也称为“行为”)作为?个参数进行传递。

    ==================     自定义函数是编程(1)    ====================
    /** * 像Runnable接口一样用FunctionalInterface注解修饰 * 加了这个注解后,接口里面必须有且只能有一个方法 */@FunctionalInterfacepublic interface Test1<R,T>{
        // R表示返回值,T表示参数类型,t1 t2是具体参数    R operator(T t1, T t2);}
    public class Compute {    /**     * 定义一个函数方法     * 需要传入a和b两个参数,     *  后面的Test1<Integer,Integer> of就是传入的一个函数(行为),of是随便起的一个别名     */    public static Integer operator(Integer a,Integer b,Test1<Integer,Integer> of){        return of.operator(a,b);    }}
    public class Main {    public static void main(String[] args) throws Exception {        System.out.println(Compute.operator(2,3,(a,b)-> a+b));        System.out.println(Compute.operator(2,3,(a,b)-> a-b));        System.out.println(Compute.operator(2,3,(a,b)-> a*b));        System.out.println(Compute.operator(2,3,(a,b)-> a/b));    }}
    
    ==================     自定义函数是编程(2)    ====================
    @FunctionalInterfacepublic interface Test2{
        void test();}
    public class Main {    public static void main(String[] args) throws Exception {        Main.casesc(()-> System.out.println("函数式编程案例二"));    }
        public static void casesc(Test2 t){        t.test();    }}
    

    Lambda

    • Lambda表达式,使?场景:接口中只能有一个方法;

    • 比如Runnable接口里的run方法;Comparator接口里的compareTo方法;

    • Lambda 表达式的实现方式在本质是以匿名内部类的方式进行实现的。

    public?static?void?main(String[]?args)?throws?Exception?{    new Thread(new Runnable() {        @Override        public void run() {            System.out.println("jdk8以前创建线程");        }    });    //()对应run()没有一个参数,->后面是方法体内容    //如果{}中的代码只有?行,?论有返回值,可以省略{}、return、分号,其他则需要加上    new Thread(()-> System.out.println("lambda表达式创建线程"));
    
        List<String> list = Arrays.asList("a","c","d","b","e");    // jdk8以前排序    Collections.sort(list, new Comparator<String>() {        @Override        public int compare(String o1, String o2) {            return o2.compareTo(o1);        }    });    // lambda表达式排序    //,前面的对应接口前面的参数,a b 对应compare里面的参数    Collections.sort(list,(a,b)->b.compareTo(a));}
    

    7.?四大核心函数式接口

    Lambda表达式必须先定义接口,创建相关方法之后才能调用,这样做十分不便,其实java8已经内置了许多接口, 例如下面四个功能型接口.所有标注@FunctionalInterface注解的接口都是函数式接口。

    Consumer 消费型接口:有入参,无返回值。适用场景:因为没有出参,常?用于打印、发送短信等消费。

    public class Main {    public static void main(String[] args) throws Exception {        Consumer<String> c1 = obj->System.out.println(obj+": 调?用短信接?口发送短信,或者打印?日志");        c1.accept("订单id—001");
            Consumer<List> c2 = obj->{            if(obj==null || obj.size()<1)return;            obj.forEach(o-> System.out.println(o));        };        List<Integer> list = Arrays.asList(2,4,0,8,9,7);        c2.accept(list);    }}
    

    Supplier 供给型接口:无入参,有返回值。适用场景:泛型一定和方法的返回值类型是同一种类型,并且不需要传入参数,例如:无参的工厂方法。

    public class Main {    public static void main(String[] args) throws Exception {        Student student = newStudent();        System.out.println(student);    }
        public static Student newStudent(){        Supplier<Student> supplier = ()-> {            Student student = new Student();            student.setName("默认名称");            return student;        };        return supplier.get();    }}
    class Student{    private String name;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public String toString() {        return "Student{" +                "name='" + name + '\'' +                '}';    }}
    

    Function与BiFunctionObj 函数型接口:有入参,有返回值。适用场景:传入参数经过函数的计算返回另一个值。

    Function:只能接受一个参数
    用法一(用一个类实现接口里面的逻辑,然后直接调用)
    public class FunctionObj implements Function {
        @Override    public Object apply(Object o) {        return "对参数:"+o+"经过处理后返回结果";    }}
    public class Main {    public static void main(String[] args) throws Exception {        System.out.println(test("hello",new FunctionObj()));    }
        public static String test(String a ,FunctionObj functionObj){        return functionObj.apply(a).toString();    }}
    用法二(左边规定好参数和返回值类型,右边写方法体具体逻辑)
    public class Main {    public static void main(String[] args) throws Exception {        Function<Integer,Integer> func = p->{            return p*100;        };        System.out.println(func.apply(12));
            Function<Integer,Boolean> function = a->a<100;        System.out.println(function.apply(97));    }}
    =================================================================
    BiFunctionObj:能接受两个参数
    // 用法一
    public class BiFunctionObj implements BiFunction {    @Override    public Object apply(Object o, Object o2) {        return (Integer.valueOf(o.toString())+Integer.valueOf(o2.toString()));    }}
    public class Main {    public static void main(String[] args) throws Exception {        System.out.println(test(2,5,new BiFunctionObj()));    }
        public static Integer test(Integer a,Integer b, BiFunctionObj func){        return Integer.valueOf(func.apply(a,b).toString());    }}// 用法二
    public class Main {    public static void main(String[] args) throws Exception {        BiFunction<Integer, Integer,Boolean> func1 = (a,b)->{            return a>b;        };        System.out.println(func1.apply(1,5));
            BiFunction<String, String,String> func2 = (a,b)->a+b;        System.out.println(func2.apply("hellow","world"));    }}
    

    Predicate 断言型接口:有入参,有返回值,返回值类型确定是boolean。适用场景:接收一个参数,用于判断是否满一定的条件,过滤数据。

    public class Main {    public static void main(String[] args) throws Exception {        Predicate<Integer> predicate = a->a>10;
            List<Integer> list = Arrays.asList(1,54,9,34,3);        for(Integer l : list){            if(predicate.test(l)) System.out.println(l);        }    }}
    

    8.流操作

    Stream:通过将集合转换为这么?种叫做 “流”的元素队列,能够对集合中的每个元素进行任意操作。

    总共分为4个步骤:

    • 数据元素便是原始集合:如List、Set、Map等

    • 生成流:可以是串行流stream() 或者并行流 parallelStream()

    • 中间操作:可以是 排序,聚合,过滤,转换等

    • 终端操作:统一收集返回一个流

    一般都采用stream,因为集合操作一般里面就几百条数据,多线程的并行流效率不一定就高,还会出现线程安全问题。

    public class Main {    public static void main(String[] args) {        List<String> list = Arrays.asList("张麻子","李蛋","王二狗","Angell");        List<Student> users = Arrays.asList(new Student("张三", 23),                                            new Student("赵四", 24),                                            new Student("二狗", 23),                                            new Student("田七", 22),                                            new Student("皮特", 20),                                            new Student("Tony", 20),                                            new Student("二柱子", 25));        /**         * map:对集合的每个对象做处理         */                List<String> collect = list.stream().map(obj->"哈哈"+obj).collect(Collectors.toList());        list.forEach(obj->System.out.println(obj));        System.out.println("----------------");        collect.forEach(obj->System.out.println(obj));        /**         * filter:boolean判断,用于条件过滤         */        System.out.println("----------------");        Set<String> set = list.stream().filter(obj->obj.length()>2).collect(Collectors.toSet());        set.forEach(obj->System.out.println(obj));        /**         * sorted:对流进行自然排序         */        System.out.println("----------------");        Set<String> sorteds = list.stream().sorted().collect(Collectors.toSet());        sorteds.forEach(obj->System.out.println(obj));        // 自定义排序规则        // 根据长度排序(正序)        System.out.println("----------------");        List<String> resultList = list.stream().sorted(Comparator.comparing(obj -> obj.length())).collect(Collectors.toList());        resultList.forEach(obj->System.out.println(obj));        System.out.println("----------------");        // 根据长度排序(倒序)        List<String> resultList2 = list.stream().sorted(Comparator.comparing(obj -> obj.length(),Comparator.reverseOrder())).collect(Collectors.toList());        resultList2.forEach(obj->System.out.println(obj));        System.out.println("----------------");        // 手动指定排序规则(根据年龄大小排序)        List<Student> collect2 = users.stream().sorted(                Comparator.comparing(Student::getAge,(x,y)->{                    if(x>y) {                        return 1;                    }else {                        return -1;                    }                })                ).collect(Collectors.toList());        collect2.forEach(obj->System.out.println(obj.getAge()+" : "+obj.getProvince()));        /**         * limit:截取包含指定数量的元素         */        System.out.println("----------------");        List<String> collect3 = list.stream().limit(2).collect(Collectors.toList());        collect3.forEach(obj->System.out.println(obj));        /**         * allMatch:匹配所有元素,只有全部符合才返回true         */        System.out.println("----------------");        boolean flag = list.stream().allMatch(obj->obj.length()>2);        System.out.println(flag);        System.out.println("----------------");        /**         * anyMatch:匹配所有元素,至少一个元素满足就为true         */        boolean flag2 = list.stream().anyMatch(obj->obj.length()>2);        System.out.println(flag2);        System.out.println("----------------");        /**         * max和min:最大值和最小值                 */        Optional<Student> max = users.stream().max(Comparator.comparingInt(Student::getAge));        System.out.println(max.get().getAge()+" : "+max.get().getProvince());        System.out.println("----------------");        Optional<Student> min = users.stream().min((s1, s2)->Integer.compare(s1.getAge(),s2.getAge()));        System.out.println(min.get().getAge()+" : "+min.get().getProvince());
            /**         * reduce:对Stream中的元素进行计算后返回一个唯一的值               */            // 计算所有值的累加        int value = Stream.of(1, 2, 3, 4, 5).reduce((item1, item2) -> item1 + item2).get();        // 100作为初始值,然后累加所有值        int value2 =Stream.of(1, 2, 3, 4, 5).reduce(100, (sum, item) -> sum + item);        // 找出最大值        int value3 =Stream.of(1, 4, 5, 2, 3).reduce((x,y)->x>y?x:y).get();                System.out.println(value);        System.out.println(value2);        System.out.println(value3);    }}
    class Student {    private String province;    private int age;    public String getProvince() {        return province;    }    public void setProvince(String province) {        this.province = province;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public Student(String province, int age) {        this.age = age;        this.province = province;    }}
    

    9.终端操作收集器:Collector

    /** * 数据结构收集:Collectors */public class Main {    public static void main(String[] args) throws Exception {        List<String> data = Arrays.asList("张三","王五","李四");        List<String> list = data.stream().collect(Collectors.toList());        Set<String> set = data.stream().collect(Collectors.toSet());        LinkedList<String> linkedList = data.stream().collect(Collectors.toCollection(LinkedList::new));        System.out.println(list);        System.out.println(set);        System.out.println(linkedList);        /*         Collectors.toMap()         Collectors.toSet()         Collectors.toCollection() :?用?自定义的实现Collection的数据结构收集             Collectors.toCollection(LinkedList::new)             Collectors.toCollection(CopyOnWriteArrayList::new)             Collectors.toCollection(TreeSet::new)         */    }}
    //============================================================
    /** * 拼接函数:joining */public class Main {    public static void main(String[] args) throws Exception {        List<String> list = Arrays.asList("springBoot","springCloud","netty");        String result1 = list.stream().collect(Collectors.joining());        String result2 = list.stream().collect(Collectors.joining("——"));        String result3 = list.stream().collect(Collectors.joining("—", "【",""));
            String result4 = Stream.of("hello", "world").collect(Collectors.joining("—", "【", "】"));
            System.out.println(result1);        System.out.println(result2);        System.out.println(result3);        System.out.println(result4);    }}
    //============================================================
    /** * 分组:partitioningBy */public class Main {    public static void main(String[] args) throws Exception {        List<String> list = Arrays.asList("sdfsdf","xxxx","bbb","bbb");        Map<Boolean, List<String>> collect = list.stream().collect(Collectors.partitioningBy(obj -> obj.length() > 3));        System.out.println(collect);    }}
    //============================================================
    /** * 分组:group by * 统计:counting */public class Main {    public static void main(String[] args) throws Exception {        List<Student> students = Arrays.asList( new Student("?东", 23),                                                new Student("?东", 24),                                                new Student("?东", 23),                                                new Student("北京", 22),                                                new Student("北京", 20),                                                new Student("北京", 20),                                                new Student("海南", 25));        // 通过名称分组        Map<String, List<Student>> listMap = students.stream().collect(Collectors.groupingBy(obj -> obj.getProvince()));        listMap.forEach((key, value) -> {            System.out.println("========");            System.out.println(key);            value.forEach(obj -> {                System.out.println(obj.getAge());            });        });        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");        // 根据名称分组,并统计每个分组的个数        Map<String, Long> map = students.stream().collect(Collectors.groupingBy(Student::getProvince, Collectors.counting()));        map.forEach((key,value)->{            System.out.println(key+"省人数有"+value);        });    }}
    class Student {    private String province;    private int age;    public String getProvince() {        return province;    }    public void setProvince(String province) {        this.province = province;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public Student(String province, int age) {        this.age = age;        this.province = province;    }}
    //============================================================
    /** * 统计函数:summarizing */public class Main {    public static void main(String[] args) throws Exception {        List<Student> students = Arrays.asList( new Student("?东", 23),                                                new Student("?东", 24),                                                new Student("?东", 23),                                                new Student("北京", 22),                                                new Student("北京", 20),                                                new Student("北京", 20),                                                new Student("海南", 25));        // summarizingInt;summarizingLong;summarizingDouble        IntSummaryStatistics summaryStatistics = students.stream().collect(Collectors.summarizingInt(Student::getAge));        System.out.println("平均值:" + summaryStatistics.getAverage());        System.out.println("人数:" + summaryStatistics.getCount());        System.out.println("最大值:" + summaryStatistics.getMax());        System.out.println("最小值:" + summaryStatistics.getMin());        System.out.println("总和:" + summaryStatistics.getSum());    }}
            /**         * reduce:对Stream中的元素进行计算后返回一个唯一的值         */        // 计算所有值的累加        int value = Stream.of(1, 2, 3, 4, 5).reduce((item1, item2) -> item1 + item2).get();        // 100作为初始值,然后累加所有值        int value2 =Stream.of(1, 2, 3, 4, 5).reduce(100, (sum, item) -> sum + item);        // 找出最大值        int value3 =Stream.of(1, 4, 5, 2, 3).reduce((x,y)->x>y?x:y).get();                System.out.println(value);        System.out.println(value2);        System.out.println(value3);
    

    JDK 9 新特性

    JDK 9 发布于 2017 年 9 月 22 日,带来了很多新特性,其中最主要的变化是已经实现的模块化系统。

    1. try-with-resource

    import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;
    public class Main {        public static void main(String[] args) throws Exception {        String path = "/Users/jack/Desktop/t.txt";                /**         * jdk 1.7以前关闭资源一般是在finally里面操作的         */        OutputStream out = new FileOutputStream(path);        try {????????????out.write(("公众号:程序员的成长之路").getBytes());        } catch (Exception e) {            e.printStackTrace();        }finally {            try {                out.close();            } catch (IOException e) {                e.printStackTrace();            }        }                /**         * jdk1.7的时候,可以在try()里声明资源,会在try-catch代码块结束后自动关闭掉。         * try结束后自动调用的close方法,这个动作会早于finally里调用的方法         * bu管是否出现异常,try()里的实例都会被调用close方法         * try里面可以声明多个自动关闭的对象,越早声明的对象,会越晚被close掉         */        try (OutputStream out2 = new FileOutputStream(path);){            out2.write(("公众号:程序员的成长之路").getBytes());        } catch (Exception e) {            e.printStackTrace();        }                /**         * jdk1.9之后,对try()做了改进,在try外进行初始化,在括号内引用         */        OutputStream out3 = new FileOutputStream(path);        try (out3) {????????????out3.write(("公众号:程序员的成长之路").getBytes());        } catch (Exception e) {            e.printStackTrace();        }    }}
    

    2. stream

    public class Main {    /**     * jdk9中的Stream流新增了两个api     */    public static void main(String[] args) throws Exception {        /**         * takeWhile:遍历每个对象,直到遇到第?个false时,返回前面所有元素,如果没有false,将返回?一个空的 Stream         */        List<String> list1 = List.of("springboot","java","html","","git").stream()                            .takeWhile(obj->!obj.isEmpty()).collect(Collectors.toList());        System.out.println(list1);                /**         * dropWhile:与takeWhile相反         */        List<String> list2 = List.of("springboot","java","html","","git").stream()                            .dropWhile(obj->!obj.isEmpty()).collect(Collectors.toList());        System.out.println(list2);    }}
    

    3.?of 创建只读集合

    public class Main {        public static void main(String[] args) throws Exception {        /**         * JDK9之前创建只读集合         */        List<String> list = new ArrayList<String>();        list.add("张三");        list.add("李四");        list.add("王五");        list.remove(0);        System.out.println(list);        //设置为只读List集合        // unmodifiableMap(map); unmodifiableMap(set);        list = Collections.unmodifiableList(list);        // 报错:java.lang.UnsupportedOperationException        //list.remove(0);        System.out.println(list);        /**         * jdk9创建只读集合         */        List<String> list2 = List.of("mysql", "linux", "redis");        list2.remove(0);        System.out.println(list2);    }}