JDK8-17性特性

JDK8-17性特性

  • 如何学习新特性?

    角度1:新的语法规则(多关注)
      	 自动装箱、自动拆箱、注解、enum、Lambda表达式、方法引用、switch表达式、try-catch变化、record等
      	 
    角度2:增加、过时、删除API
         StringBuilder、ArrayList、新的日期时间的API、Optional等
         
    角度3:底层的优化、JVM参数的调整、GC的变化、内存结构(永久代--->元空间)|
    

1. JDK8新特性——lambda表达式

1.1 JDK8新特性特点

  • 速度更快
  • 代码更少(增加了新的语法:Lambda表达式)。
  • 强大的**Stream APl**
  • 便于并行
    • **并行流**就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。
    • Java 8中将并行进行了优化,可以很容易的对数据进行并行操作。Stream API可以声明性地通过parallel()与sequential()在并行流与顺序流之间进行切换。
  • 最大化减少空指针异常:Optional
  • Nashorn引擎,允许在JVM上运行JS应用
    • javascript可以在jvm运行
    • Nashorn项目在JDK 9中得到改进;在JDK11中 Deprecated,后续JDK15版本中remove。在JDK11中取以代之的是GraalVM。(GraalvM是一个运行时平台,它支持Java和其他基于Java字节码的语言,但也支持其他语言,如JavaScript,Ruby,Python或LLVM。性能是Nashorn的2倍以上。)

1.2 lambda表达式的使用

  • lambda表达式

    * Lambda表达式的使用举例:(o1,o2) -> Integer.compare(o1,o2);
      
    * Lambda表达式的格式举例:lambda形参列表-> lambda体
      
    * Lambda表达式的格式
      -> : lambda操作符(箭头操作符)
      ->的左边: lambda形参列表,对应着要重写的接口中的抽象方法的形参列表。
      ->的右边: lambda体,对应着接口的实现类要重写的方法的方法体。
      
    * Lambda表达式的本质:
      -> lambda表达式作为接口的实现类的对象。
      -> lambda表达式是一个匿名函数。
    
  • 函数式接口

    • 什么是函数式接口?为什么需要函数式接口?

      -> 如果接口中只声明一个抽象方法,则此接口就称为函数式接口

      -> 因为只有给函数式接口提供实现类的对象时,我们才可以使用lambda表达式。

    • api中函数式接口所在的包:jdk8中声明的函数式接口都在java.util.function包下。

    • 4个基本的函数式接口

      接口类型接口对应的抽象方法
      消费型接口Consumer< T >void accept(T t)
      供给型接口Supplier< T >T get()
      函数型接口Function<T, R>R apply(T t)
      判断型接口Predicate< T >boolean test(T t)
  • Lambda表达式的语法规则总结

    • ->的左边:lambda形参列表,参数的类型都可以省略。如果形参只有一个,则一对 () 也可以省略
    • ->的右边:lambda体,对应着重写的方法的方法体。
      如果方法体中只有一行执行语句,则一对 {} 可以省略
      如果有return关键字,则必须一并省略
  • 	@Test
        public void test2(){
            Comparator<Integer> com1 = new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return Integer.compare(o1, o2);
                }
            };
            System.out.println(com1.compare(12, 32));
            System.out.println("*******************");
    
            // lambda表达式
            Comparator<Integer> com2 = (o1, o2) ->{
                return Integer.compare(o1, o2);
            };
            System.out.println(com2.compare(23, 11));
            
        }
    

2. 方法引用

  • 方法引用举例:

    Integer :: compare;
    
  • 方法引用的理解:

    • 方法引用,可以看做是基于lambda表达式的进一步刻画。(可以使用方法引用的前提是可以使用lambda表达式)

    • 当需要提供一个函数式接口的实例时,我们可以使用lambda表达式提供此实例。

    • 当满足一定的条件的情况下,我们还可以使用方法引用或构造器引用替换lambda表达式。

  • 方法引用的本质:方法引用作为了函数式接口的实例

  • 方法引用格式:

    类(或对象) :: 方法名
    
  • 具体使用情况说明:

    • 对象 :: 实例方法

      要求:函数式接口中的抽象方法a与其内部实现时调用的对象的某个方法b的形参列表和返回值类型都相同(或一致)。此时,可以

      ​ 考虑使用方法b实现对方法a的替换、覆盖。此替换或覆盖即为方法引用

      注意:此方法b是非静态的方法,需要对象调用

    • 类 :: 静态方法

      要求:函数式接口中的抽象方法a与其内部实现时调用的类的某个静态方法b的形参列表和返回值类型都相同(或一致)。此时,可 以考虑使用方法b实现对方法a的替换、覆盖。

      注意:此方法b是静态的方法,需要调用

    • 类 : 实例方法

      要求:函数式接口中的抽象方法a与其内部实现时调用的对象的某个方法b的返回值类型相同。同时,抽象方法a中有n个参数,方 法b中有n-1个参数,且抽象方法a的第1个参数作为方法b的调用者,且抽象方法a的后n-1个参数与方法b的n-1个参数的类 型相同(或一致)。则可以考虑使用方法b实现对方法a的替换、覆盖。

      注意:此方法b是非静态的方法,需要对象调用(但是形式上,写出对象a所属的类)

package methodRef;

import org.junit.Test;
import java.util.Comparator;

public class MethodRefTest {

    // 1. 对象 :: 实例方法
    @Test
    public void test1(){
        // 1.
        Consumer<String> con1 = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        con1.accept("hello!");

        // 2. lambda表达式
        Consumer<String> con2 = s -> System.out.println(s);
        con2.accept("hello!");

        // 3. 方法引用
        Consumer<String> con3 = System.out :: println;
        con3.accept("hello!");
    }

    // 2. 类 :: 静态方法
    @Test
    public void test2(){
        // 1.
        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };
        System.out.println(com1.compare(12, 23));

        // 2. lambda表达式
        Comparator<Integer> com2 = (o1, o2) -> Integer.compare(o1, o2);
        System.out.println(com2.compare(12, 23));

        // 3. 方法引用
        Comparator<Integer> com3 = Integer :: compare;
        System.out.println(com3.compare(12, 23));
    }

    // 3. 类 :: 实例方法
    @Test
    public void test3(){
        // 1.
        Comparator<String> com1 = new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return s1.compareTo(s2);
            }
        };
        System.out.println(com1.compare("abc", "abd"));

        // 2. lambda表达式
        Comparator<String> com2 = (s1, s2) -> s1.compareTo(s2);
        System.out.println(com2.compare("abc", "abd"));

        // 3. 方法引用
        Comparator<String> com3 = String :: compareTo;
        System.out.println(com3.compare("abc", "abd"));
    }

}

3. 构造器引用

  • 格式:

    类名 :: new
    
  • 说明:调用了类名对应的类中的某一个确定的构造器

    ​ 具体调用的是类中的哪一个构造器:取决于函数式接口的抽象方法的形参列表

  • 数组引用格式:

    数组名[] :: new
    
  • package methodRef;
    
    import org.junit.Test;
    
    public class ConsRefTest {
        //构造器方法引用
        @Test
        public void test1(){
    
            // 1.
            Supplier<Employee> sup1 = new Supplier<Employee>() {
                @Override
                public Employee get() {
                    return new Employee();
                }
            };
            System.out.println(sup1.get());
    
            // 2. 方法引用
            Supplier<Employee> sup2 = Employee::new;
            System.out.println(sup2.get());
    
        }
    
        //数组方法引用
        @Test
        public void test2(){
            // 1.
            Function<Integer, Employee[]> fun1 = new Function<Integer, Employee[]>() {
                @Override
                public Employee[] apply(Integer len) {
                    return new Employee[len];
                }
            };
            System.out.println(fun1.apply(2).length);
    
            // 2. 方法引用
            Function<Integer, Employee[]> fun2 = Employee[]::new;
            System.out.println(fun2.apply(2).length);
        }
    
    }
    
    

4. StreamAPI

4.1 说明

  • Stream APl ( java.util.stream)把真正的函数式编程风格引入到Java中。可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码
  • Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用stream API集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用stream API来并行执行操作。

4.2 为什么要使用Stream API

  • 实际开发中,项目中多数数据源都来自于MySQL、Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理。

4.3 StreamAPI的使用说明

  • Stream自己不会存储元素。
  • stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。
  • Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。即一旦执行终止操作,就执行中间操作链,并产生结果。
  • Stream一旦执行了终止操作,就不能再调用其它中间操作或终止操作了。

4.4 Stream执行流程步骤

  • Stream的实例化步骤

    package streamAPI;
    
    import org.junit.Test;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Stream;
    
    /*  Stream实例化
    * */
    public class StreamAPITest {
    
        //创建Stream方式一:通过集合
        @Test
        public void test1(){
            List<Employee> list = EmployeeData.getEmployee();
    
            // default Stream<E> stream():返回一个顺序流
            Stream<Employee> stream = list.stream();
    
            // default Stream<E> parallelStream():返回一个并行流
            Stream<Employee> stream1 = list.parallelStream();
        }
    
        //创建Stream方式二:通过数组
        @Test
        public void test2(){
            Integer[] arr = new Integer[]{1,2,3,4,5};
    
            //调用Arrays类的static <T> Stream<T> stream(T[] array):返回一个流
            Stream<Integer> stream = Arrays.stream(arr);
        }
    
        //创建Stream方式三:通过Stream的of()
        @Test
        public void test3(){
            Stream<String> stream = Stream.of("AA", "BB", "CC", "DD", "EE");
    
        }
    
    }
    
    
  • 一系列的中间操作步骤

    package streamAPI;
    
    import org.junit.Test;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Locale;
    import java.util.stream.Stream;
    
    /* Stream的中间操作
    * */
    public class StreamAPITest1 {
    
        // 1-筛选与切片
        @Test
        public void test1(){
            // filter(Predicate p)——接受Lambda,从流中排除某些元素
            // 查询员工表中薪资大于5000的员工信息
            List<Employee> list = EmployeeData.getEmployee();
            Stream<Employee> stream = list.stream();
            stream.filter(emp -> emp.getSalary() > 5000).forEach(System.out::println);
    
            System.out.println();
            // limit(n)——截断流,使其元素不超过给定数量
            list.stream().limit(3).forEach(System.out::println);
    
            System.out.println();
            // skip()n——跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流
            list.stream().skip(2).forEach(System.out::println);
    
            System.out.println();
            // distinct()——筛选,通过流所生成元素的hashcode()和equals()去除重复元素
            list.add(new Employee(1007,"小顾", 40,12200.5));
            list.add(new Employee(1007,"小顾", 40,12200.5));
            list.add(new Employee(1007,"小顾", 40,12200.5));
            list.add(new Employee(1007,"小顾", 40,12200.5));
            list.stream().distinct().forEach(System.out::println);
        }
    
        // 2-映射
        @Test
        public void test2(){
            // map(Function f)——接受一个函数作为参数,将元素转换为其他形式或提取信息
    
            // 转换为大写
            List<String> list = Arrays.asList("aa","bb","cc","dd");
            list.stream().map(str->str.toUpperCase()).forEach(System.out::println);
    
            // 获取员工姓名长度大于3的员工
            List<Employee> list1 = EmployeeData.getEmployee();
            list1.stream().filter(emp->emp.getName().length()>3).forEach(System.out::println);
    
            // 获取员工姓名长度大于3的员工姓名
            // 方式一:
            list1.stream().filter(emp->emp.getName().length()>3).map(emp->emp.getName()).forEach(System.out::println);
            // 方式二:
            list1.stream().filter(emp->emp.getName().length()>3).map(Employee::getName).forEach(System.out::println);
        }
    
        // 3-排序
        @Test
        public void test3(){
            // sorted()——自然排序
            Integer[] arr = new Integer[]{325,0,2,58,79,658,258,10};
            String[] arr1 = new String[]{"RR","GG","WW","JJ","BB"};
    
            Arrays.stream(arr).sorted().forEach(System.out::println);
            System.out.println(Arrays.toString(arr));//Stream不会改变原对象,还是原来数组的顺序
    
            // sorted(Comparator com)——定制排序
            List<Employee> list = EmployeeData.getEmployee();
            list.stream().sorted((e1, e2)->e1.age- e2.age).forEach(System.out::println);
    
            // 对字符串进行降序排列
            Arrays.stream(arr1).sorted((s1, s2)->-s1.compareTo(s2)).forEach(System.out::println);
        }
    
    }
    
    
  • 执行终止操作

    package streamAPI;
    
    import org.junit.Test;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
    
    /* Stream的终止操作
     * */
    public class StreamAPITest2 {
    
        // 1-匹配与查找
        @Test
        public void test1(){
            // allMatch(Predicate p)——检查是否匹配所有元素
            // 练习:是否所有员工的年龄都大于18
            List<Employee> list = EmployeeData.getEmployee();
            System.out.println(list.stream().allMatch(emp -> emp.age > 18));
    
            // anyMatch(Predicate p)——检查是否至少匹配一个元素
            // 练习:是否存在员工的年龄都大于18
            System.out.println(list.stream().anyMatch(emp -> emp.age > 18));
            // 练习:是否存在员工工资大于10000
            System.out.println(list.stream().anyMatch(emp -> emp.salary >10000));
    
            // findFirst()——返回第一个元素
            System.out.println(list.stream().findFirst());
        }
    
        @Test
        public void test2(){
            // count——返回流中元素的总个数
            List<Employee> list = EmployeeData.getEmployee();
            System.out.println(list.stream().count());
    
            // max(Comparator c)——返回流中最大值
            // 练习:返回最高工资的员工
            System.out.println(list.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
            // 练习:返回最高工资
            System.out.println(list.stream().map(emp -> emp.salary).max((salary1, salary2) -> Double.compare(salary1, salary2)).get());
            System.out.println(list.stream().map(emp -> emp.salary).max(Double::compare).get());
    
            // min(Comparator c)——返回流中最小值
            // 练习:返回最低工资
            System.out.println(list.stream().map(emp -> emp.salary).min((salary1, salary2) -> Double.compare(salary1, salary2)).get());
    
            // forEach(Consumner c)——内部迭代
            list.stream().forEach(System.out::println);
    
            // 对于集合,jdk8新增遍历方法
            list.forEach(System.out::println);
        }
    
        // 2-归约
        @Test
        public void test3(){
            // reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值,返回T
            // 练习:计算1-10的和
            List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
            System.out.println(list.stream().reduce(0, (x1, x2) -> x1 + x2));
            System.out.println(list.stream().reduce(0, (x1, x2) -> Integer.sum(x1,x2)));
            System.out.println(list.stream().reduce(0, Integer::sum));
    
            // reduce(BinaryOperator)——可以将流中元素反复结合起来,得到一个值,返回Optional<T>
            // 计算公司中所有员工工资总和
            List<Employee> list1 = EmployeeData.getEmployee();
            System.out.println(list1.stream().map(emp -> emp.getSalary()).reduce(Double::sum));
    
    
        }
    
        // 3-收集
        @Test
        public void test4(){
            // collect(Collector c)——将流转换为其他形式,接受一个Collector接口的实现,用于给Stream中元素做汇总的方法
            // 练习:查找工资大于6000的员工,结果返回一个List或Set
            List<Employee> list = EmployeeData.getEmployee();
            List<Employee> list1 = list.stream().filter(emp -> emp.getSalary() > 6000).collect(Collectors.toList());
            list1.forEach(System.out::println);
    
            System.out.println();
            // 练习:按照员工年龄进行排序,返回一个新的List中
            List<Employee> list2 = list.stream().sorted((emp1,emp2)->emp1.getAge()- emp2.getAge()).collect(Collectors.toList());
            list2.forEach(System.out::println);
        }
    
    }
    
    

5. 新语法结构

5.1 Java的REPL工具: jShell命令

  • 说明:

    • JDK9的新特性:Java终于拥有了像Python和scala之类语言的REPL工具(交互式编程环境,read - evaluate - print - loop) :jShell。
    • 以交互式的方式对语句和表达式进行求值。即写即得、快速运行。
    • 利用 jshell 在没有创建类的情况下,在命令行里直接声明变量,计算表达式,执行语句
  • 常用指令:

    /help:获取有关使用jshell工具的信息
    /help intro : jshell工具的简介
    /list :列出当前session里所有有效的代码片段
    /vars :查看当前session下所有创建过的变量
    /methods :查看当前session下所有创建过的方法
    /imports :列出导入的包
    /history :键入的内容的历史记录
    /edit :使用外部代码编辑器来编写Java代码
    /exit :退出jshell工具
    

5.2 异常处理之try-catch资源关闭

  • 说明

    • 在try()中声明的资源,无论是否发生异常,无论是否处理异常,都会自动关闭资源对象,不用手动关闭了。
    • 这些资源实现类必须实现AutoCloseable或Closeable接口,实现其中的close()方法。Closeable是AutoCloseable的子接口。Java7几乎把所有的“资源类”(包括文件IO的各种类、JDBC编程的Connection、Statement等接口)都进行了改写,改写后资源类都实现了AutoCloseable或Closeable接口,并实现了close()方法。
    • 写到try()中的资源类的变量默认是final声明的,不能修改。
  • JDK9新特性

    • try的前面可以定义流对象,try后面的(中可以直接引用流对象的名称。在try代码执行完毕后,流对象也可以释放掉,也不用写finally了。

      格式:

      A a = new A();
      B b = new B();
      try(a;b){
          可能产生的异常代码
      }catch(异常类名 变量名){
          异常处理逻辑
      }
      

5.3 switch表达式

  • JDK12中预览特性:
    • Java 12将会对switch声明语句进行扩展,使用case L ->来替代以前的break;,省去了break 语句,避免了因少写break 而出错。
    • 同时将多个case合并到一行,显得简洁、清晰,也更加优雅的表达逻辑分支。
    • 为了保持兼容性,case条件语句中依然可以使用字符 : ,但是**同一个switch结构里不能混用 -> 和 : **,否则编译错误。

5.4 新的引用数据类型:record(记录)

  • JDK15中第二次预览特性

    JDK16中转正特性

  • 记录不适合的场景:

    • 不能在record声明的类中定义实例字段
    • 不能声明为abstract
    • 不能声明显式的父类
  • record的设计目标是提供一种将数据建模为数据的好方法。

    record不是JavaBeans的直接替代品,因为record的方法不符合JavaBeans的get标准。

    JavaBeans通常是可变的,而记录是不可变的。尽管它们的用途有点像,但记录并不会以某种方式取代JavaBean。

6. API变化

6.1 Optional类

  • JDK8的新特性

  • 空指针异常是导致Java应用程序失败的最常见原因。

  • optional< T >类(java.util.Optional)是一个容器类,它可以保存类型T的值
    代表这个值存在。或者仅仅保存null,表示这个值不存在。

    如果值存在,则isPresent()方法会返回true,调用get()方法会返回该对象。

  • 使用Optional避免空指针的问题:

    // 1.实例化:
    // ofNullable(T value):用来创建一个Optional实例,value可能是空,也可能非空
    Optional<String> optional = 0ptional.ofNullable(star);
    
    // orElse(T other):如果Optional实例内部的value属性不为null,则返回value。如果value为null,则返回other.
    
  • 22
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值