JDK8-17新特性
JDK比较重要的时间节点和版本说明1996 JDK1.0 2004 JDK5.0 最重要的一个里程碑式的版本 2014 JDK8.0 排第二的里程碑式的版本 --- LTS 2017.9 JDK9.0 从此版本开始每半年发布一个新的版本 2018.9 JDK11 --- LTS 2021.9 JDK17 --- LTS新特性学习方向- 角度1新的语法规则多关注自动装箱、自动拆箱、注解、enum、Lambda表达式、方法引用、switch表达式、try-catch变化、record等- 角度2增加、过时、删除APIStringBuilder、ArrayList、新的日期时间的API、Optional等- 角度3底层的优化、JVM参数的调整、GC的变化、内存结构永久代----元空间lambda表达式Lambda表达式的使用举例(o1, o2) - Integer.compare(o1, o2);public void test() { ComparatorInteger com1 new ComparatorInteger() { Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1, o2); } }; int compare1 com1.compare(12, 21); System.out.println(compare1); System.out.println(***********************); //Lambda表达式的写法 ComparatorInteger com2 (o1, o2) - Integer.compare(o1, o2); int compare2 com2.compare(23, 21); System.out.println(compare2); System.out.println(***********************); //方法引用 ComparatorInteger com3 Integer::compare; int compare3 com3.compare(23, 21); System.out.println(compare3); }语法格式一无参无返回值public void test1() { Runnable r1 new Runnable() { Override public void run() { System.out.println(我爱北京天安门); } }; r1.run(); System.out.println(**********************); Runnable r2 () - { System.out.println(我爱北京天安门); }; r2.run(); }语法格式二Lambda 需要一个参数但是没有返回值。public void test2() { ConsumerString con new ConsumerString() { Override public void accept(String s) { System.out.println(s); } }; con.accept(谎言和誓言的区别是什么); System.out.println(**********************); ConsumerString con1 (String s) - { System.out.println(s); }; con1.accept(一个说的人当真了一个听的人当真了); }语法格式三数据类型可以省略因为可由编译器推断得出称为“类型推断”public void test3() { ConsumerString con1 (String s) - { System.out.println(s); }; con1.accept(如果大学可以重来你最想重来的事是啥); System.out.println(********************); ConsumerString con2 (s) - { System.out.println(s); }; con1.accept(谈一场轰轰烈烈的爱情); }语法格式四Lambda 若只需要一个参数时参数小括号可以省略public void test4() { ConsumerString con1 (s) - { System.out.println(s); }; con1.accept(世界那么大我想去看看); System.out.println(**********************); ConsumerString con2 s - { System.out.println(s); }; con2.accept(世界那么大我想去看看); }语法格式五Lambda 需要两个或以上的参数多条执行语句并且可以有返回值public void test5() { ComparatorInteger com1 new ComparatorInteger() { Override public int compare(Integer o1, Integer o2) { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); } }; System.out.println(com1.compare(12, 21)); System.out.println(*******************************); ComparatorInteger com2 (o1, o2) - { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); }; System.out.println(com2.compare(12, 21)); }语法格式六当 Lambda 体只有一条语句时return 与大括号若有都可以省略public void test6() { ComparatorInteger com1 (o1, o2) - { return o1.compareTo(o2); }; System.out.println(com1.compare(12, 6)); System.out.println(****************************); ComparatorInteger com2 (o1, o2) - o1.compareTo(o2); System.out.println(com2.compare(12, 6)); }Lambda表达式的格式举例Lambda形参列表 - Lambda体Lambda表达式的格式- : lambda操作符或箭头操作符- 的左边lambda形参列表对应着要重写的接口中的抽象方法的形参列表。- 的右边lambda体对应着接口的实现类要重写的方法的方法体。Lambda表达式的本质 一方面Lambda表达式作为接口的实现类的对象。 --- 万事万物皆对象 另一方面Lambda表达式是一个匿名函数。函数式接口函数式接口定义 如果接口中只声明一个抽象方法则此接口就称为函数式接口。 因为只有给函数式接口提供实现类的对象时我们才可以使用lambda表达式。api中函数式接口所在的包jdk8中声明的函数式接口都在java.util.function包下。4个基本的函数式接口| 接口 | 对应的抽象方法 || 消费型接口ConsumerT | void accept(T t) || 供给型接口SupplierT | T get() || 函数型接口FunctionT,R | R apply(T t) || 判断型接口PredicateT | boolean test(T t) |Lambda表达式的语法规则总结- 的左边Lambda形参列表参数的类型都可以省略。如果形参只有一个则一对()也可以省略。- 的右边Lambda体对应着重写的方法的写法。如果方法体中只有一行执行语句则一对{}可以省略。如果有return关键字则必须一并省略。方法引用举例Integer :: compare;public void test() { ComparatorInteger com1 new ComparatorInteger() { Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1, o2); } }; int compare1 com1.compare(12, 21); System.out.println(compare1); System.out.println(***********************); //Lambda表达式的写法 ComparatorInteger com2 (o1, o2) - Integer.compare(o1, o2); int compare2 com2.compare(23, 21); System.out.println(compare2); System.out.println(***********************); //方法引用 ComparatorInteger com3 Integer::compare; int compare3 com3.compare(23, 21); System.out.println(compare3); }方法引用的理解 方法引用可以看做是基于lambda表达式的进一步刻画。 当需要提供一个函数式接口的实例时我们可以使用lambda表达式提供此实例。 当满足一定的条件的情况下我们还可以使用方法引用或构造器引用替换lambda表达式。方法引用的本质方法引用作为了函数式接口的实例。 --- “万事万物皆对象”格式类(或对象) :: 方法名具体使用情况说明情况1对象 :: 实例方法要求函数式接口中的抽象方法a与其内部实现时调用的对象的某个方法b的形参列表和返回值类型都相同或一致、兼容。此时可以考虑使用方法b实现对方法a的替换、覆盖。此替换或覆盖即为方法引用。注意此方法b是非静态的方法需要对象调用。import java.io.PrintStream; import java.util.function.Consumer; import java.util.function.Supplier; public class Main { // 情况一对象 :: 实例方法 //Consumer中的void accept(T t) //PrintStream中的void println(T t) public void test1() { //1. ConsumerString con1 new ConsumerString() { Override public void accept(String s) { System.out.println(s); } }; con1.accept(hello!); //2. lambda表达式 ConsumerString con2 s - System.out.println(s); con2.accept(hello!); //3. 方法引用 PrintStream out System.out; ConsumerString con3 out::println; con3.accept(hello!); } public void test2() { Employee emp new Employee(1001, 马化腾, 34, 6000.38); //1. SupplierString sup1 new SupplierString() { Override public String get() { return emp.getName(); } }; System.out.println(sup1.get()); //2. lambda表达式 SupplierString sup2 () - emp.getName(); System.out.println(sup2.get()); //3. 方法引用 SupplierString sup3 emp::getName; } }情况2类 :: 静态方法要求函数式接口中的抽象方法a与其内部实现时调用的类的某个静态方法b的形参列表和返回值类型都相同或一致、兼容。此时可以考虑使用方法b实现对应方法的替换、覆盖。此替换或覆盖即为方法引用。注意此方法b是静态的方法需要类调用。import java.util.Comparator; import java.util.function.Function; public class Main { // 情况二类 :: 静态方法 //Comparator中的int compare(T t1, T t2) //Integer中的int compare(T t1, T t2) public void test3() { //1 ComparatorInteger com1 new ComparatorInteger() { Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1, o2); } }; System.out.println(com1.compare(12, 21)); //2. ComparatorInteger com2 (o1, o2) - Integer.compare(o1, o2); System.out.println(com2.compare(21, 34)); //3. 方法引用 ComparatorInteger com3 Integer::compare; System.out.println(com3.compare(34, 34)); } public void test4() { //1. FunctionDouble, Long fun1 new FunctionDouble, Long() { Override public Long apply(Double aDouble) { return Math.round(aDouble); } }; //2. FunctionDouble, Long fun2 aDouble - Math.round(aDouble); //3. 方法引用 FunctionDouble, Long fun3 Math::round; } }情况3类 :: 实例方法要求函数式接口中的抽象方法a与其内部实现时调用的对象的某个方法b的返回值类型相同。同时抽象方法a中有n个参数方法b中有n-1个参数且抽象方法a的第1个参数作为方法b的调用者且抽象方法a的后n-1个参数与方法b的后n-1个参数的类型相同或一致。则可以考虑使用方法b实现对方法a的替换、覆盖。此替换或覆盖即为方法引用。注意此方法b是非静态的方法需要对象调用。但是形式上写出对象a所属的类import java.util.Comparator; import java.util.function.BiPredicate; public class Main { // 情况三类 :: 实例方法难 // Comparator中的int compare(T t1, T t2) // String中的int t1.compareTo(t2) public void test5() { //1. ComparatorString com1 new ComparatorString() { Override public int compare(String o1, String o2) { return o1.compareTo(o2); } }; System.out.println(com1.compare(abc, abd)); //2. ComparatorString com2 (s1, s2) - s1.compareTo(s2); System.out.println(com2.compare(abc, abb)); //3. ComparatorString com3 String::compareTo; System.out.println(com3.compare(abc, abb)); } public void test6() { //1. BiPredicateString, String biPre1 new BiPredicateString, String() { Override public boolean test(String s1, String s2) { return s1.equals(s2); } }; //2. BiPredicateString, String biPre2 (s1, s2) - s1.equals(s2); //3. 方法引用 BiPredicateString, String biPre3 String::equals; } }构造器引用格式类名 :: newimport java.util.function.Supplier; public class Main { //构造器引用 //Supplier中的T get() public void test1() { //1. SupplierEmployee sup1 new SupplierEmployee() { Override public Employee get() { return new Employee(); } }; System.out.println(sup1.get()); //2.构造器引用 SupplierEmployee sup2 Employee::new;//调用的是Employee类中空参的构造器 System.out.println(sup2.get()); } }说明 调用了类名对应的类中的某一个确定的构造器 具体调用的是类中的哪一个构造器呢取决于函数式接口的抽象方法的形参列表数组引用格式数组名[ ] :: newimport java.util.function.Function; public class Main { // 数组引用 //Function中的R apply(T t) public void test4() { //1. FunctionInteger, Employee[] func1 new FunctionInteger, Employee[]() { Override public Employee[] apply(Integer length) { return new Employee[length]; } }; System.out.println(func1.apply(10).length); //2. FunctionInteger, Employee[] func2 Employee[]::new; System.out.println(func2.apply(20).length); } }Stream APIStream API vs 集合框架 Stream API 关注的是多个数据的计算排序、查找、过滤、映射、遍历等面向CPU的。集合关注的数据的存储向向内存的。 Stream API 之于集合类似于SQL之于数据表的查询。使用说明①Stream 自己不会存储元素。②Stream 不会改变源对象。相反他们会返回一个持有结果的新Stream。③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。即一旦执行终止操作就执行中间操作链并产生结果。④Stream一旦执行了终止操作就不能再调用其它中间操作或终止操作了。Stream 执行流程步骤1Stream的实例化import java.util.Arrays; import java.util.stream.IntStream; import java.util.stream.Stream; public class Main { // 创建 Stream 方式一通过集合 stream() public void test1() { ListEmployee list EmployeeData.getEmployees(); // default StreamE stream() : 返回一个顺序流 StreamEmployee stream list.stream(); // default StreamE parallelStream() : 返回一个并行流 StreamEmployee stream1 list.parallelStream(); System.out.println(stream); System.out.println(stream1); } // 创建 Stream 方式二通过数组 Arrays.stream public void test2() { // 调用Arrays类的static T StreamT stream(T[] array)返回一个流 Integer[] arr new Integer[]{1, 2, 3, 4, 5}; StreamInteger stream Arrays.stream(arr); int[] arr1 new int[]{1, 2, 3, 4, 5}; IntStream stream1 Arrays.stream(arr1); } // 创建 Stream 方式三通过Stream的 of() public void test3() { StreamString stream Stream.of(AA, BB, CC, SS, DD); } }步骤2一系列的中间操作import java.util.Arrays; import java.util.List; public class Main { //1-筛选与切片 public void test1() { // filter(Predicate p) — 接收 Lambda从流中排除某些元素。 // 练习查询员工表中薪资大于7000的员工信息 ListEmployee list EmployeeData.getEmployees(); StreamEmployee stream list.stream(); stream.filter(emp - emp.getSalary() 7000).forEach(System.out::println); System.out.println(); // limit(n) — 截断流使其元素不超过给定数量。 // 错误的。因为stream已经执行了终止操作就不可以再调用其它的中间操作或终止操作了。 // stream.limit(2).forEach(System.out::println); //先过滤后打印 // list.stream().filter(emp - emp.getSalary() 7000).limit(4).forEach(System.out::println); list.stream().limit(4).forEach(System.out::println); System.out.println(); // skip(n) — 跳过元素返回一个扔掉了前 n 个元素的流。 // 若流中元素不足 n 个则返回一个空流。与 limit() 的区别在于 skip() 不会影响流的长度。 list.stream().skip(5).forEach(System.out::println); System.out.println(); // distinct() — 筛选通过流所生成元素的 hashCode() 和 equals() 去除重复元素 list.stream().distinct().forEach(System.out::println); } //2-映射 public void test2() { //map(Function f)——接收一个函数作为参数将元素转换成其他形式或提取信息该函数会被应用到每个元素上。 //练习转换为大写 ListString list Arrays.asList(aa, bb, cc, dd); //方式1: list.stream().map(str - str.toUpperCase()).forEach(System.out::println); //方式2: list.stream().map(String::toUpperCase).forEach(System.out::println); //练习获取员工姓名长度大于3的员工。 ListEmployee employees EmployeeData.getEmployees(); employees.stream().filter(emp - emp.getName().length() 3).forEach(System.out::println); //练习获取员工姓名长度大于3的员工的姓名。 //方式1: employees.stream().filter(emp - emp.getName().length() 3).map(emp - emp.getName()).forEach(System.out::println); //方式2: employees.stream().map(emp - emp.getName()).filter(name - name.length() 3).forEach(System.out::println); //方式3: employees.stream().map(Employee::getName ()).filter(name - name.length() 3).forEach(System.out::println); } //3-排序 public void test3() { //sorted()——自然排序 Integer[] arr new Integer[]{345, 3, 64, 3, 46, 7, 3, 34, 65, 68}; String[] arr1 new String[]{GG, DD, MM, SS, JJ}; Arrays.stream(arr).sorted().forEach(System.out::println); System.out.println(Arrays.toString(arr)); //arr数组并没有因为升序做调整。 Arrays.stream(arr1).sorted().forEach(System.out::println); //因为Employee没有实现Comparable接口所以报错 // ListEmployee list EmployeeData.getEmployees(); // list.stream().sorted().forEach(System.out::println); //sorted(Comparator com)——定制排序 ListEmployee list EmployeeData.getEmployees(); list.stream().sorted((e1, e2) - e1.getAge() - e2.getAge()).forEach(System.out::print); //针对于字符串从大大小排列 Arrays.stream(arr1).sorted((s1, s2) - -s1.compareTo(s2)).forEach(System.out::print); // Arrays.stream(arr1).sorted(String::compareTo).forEach(System.out::println); } }步骤3执行终止操作import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class Main { //1-匹配与查找 public void test1() { // allMatch(Predicate p) —— 检查是否匹配所有元素。 // 练习是否所有的员工的年龄都大于18 ListEmployee list EmployeeData.getEmployees(); 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().get()); } public void test2() { // count——返回流中元素的总个数 ListEmployee list EmployeeData.getEmployees(); System.out.println(list.stream().filter(emp - emp.getSalary() 7000).count()); // max(Comparator c) — 返回流中最大值 //练习返回最高工资的员工 System.out.println(list.stream().max((e1, e2) - Double.compare(e1.getSalary(), e2.getSalary()))); //练习返回最高的工资 //方式1: System.out.println(list.stream().max((e1, e2) - Double.compare(e1.getSalary(), e2.getSalary())).get().getSalary()); //方式2: System.out.println(list.stream().map(emp - emp.getSalary()).max(Double::compare).get()); // min(Comparator c) —— 返回流中最小值 // 练习返回最低工资的员工 System.out.println(list.stream().min((e1, e2) - Double.compare(e1.getSalary(), e2.getSalary()))); // forEach(Consumer c) —— 内部迭代 list.stream().forEach(System.out::println); // 针对于集合jdk8中增加了一个遍历的方法 list.forEach(System.out::println); // 针对于List来说遍历的方式① 使用Iterator ② 增强for ③ 一般for ④ forEach() } //2-归约 public void test3() { // reduce(T ident, BinaryOperator) —— 可以将流中元素反复结合起来得到一个值。返回 T // 练习1计算1-10的自然数的和 ListInteger 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)); System.out.println(list.stream().reduce(10, (x1, x2) - x1 x2)); // reduce(BinaryOperator) —— 可以将流中元素反复结合起来得到一个值。返回 OptionalT // 练习2计算公司所有员工工资的总和 ListEmployee employeeList EmployeeData.getEmployees(); System.out.println(employeeList.stream().map(emp - emp.getSalary()).reduce((salary1, salary2) - Double.sum(salary1, salary2))); System.out.println(employeeList.stream().map(emp - emp.getSalary()).reduce(Double::sum)); } //3-收集 public void test4() { // collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现用于给Stream中元素做汇总的方法 // 练习1查找工资大于6000的员工结果返回为一个List或Set ListEmployee list1 list.stream().filter(emp - emp.getSalary() 60000).collect(Collectors.toList()); list1.forEach(System.out::println); System.out.println(); list.forEach(System.out::println); System.out.println(); // 练习2按照员工的年龄进行排序返回到一个新的List中 ListEmployee list2 list.stream().sorted((e1, e2) - e1.getAge() - e2.getAge()).collect(Collectors.toList()); list2.forEach(System.out::println); } }语法层面jShell工具REPO工具交互式编程环境工具利用shell在没有创建类的情况下在命令行里直接声明变量计算表达式执行语句。无需跟人解释“public static void main(String[] args)”这句“废话”。try-catch结构的变化。try(...){}在try的后面可以增加一个在括号中可以声明流对象需要直接或间接实现AutoClassable接口并初始化。try中的代码执行完毕会自动把流对象释放就不用写finally了。try(资源对象的声明和初始化) { 业务逻辑代码可能会产生异常 } catch(异常类型1 e) { 处理异常代码 } catch(异常类型2 e) { 处理异常代码 }public void test02() { try( FileWriter fw new FileWriter(test.txt); BufferedWriter bw new BufferedWriter(fw); ) { bw.write(hello,世界); } catch(IOException e) { e.printStackTrace(); } }try的前面可以定义流对象try后面的中可以直接引用流对象的名称。在try代码执行完毕后流对象也可以释放掉也不用写finally了。A a new A(); B b new B(); try(a; b){ 可能产生的异常代码 } catch(异常类名 变量名) { 异常处理的逻辑 }public void test05() { InputStreamReader reader new InputStreamReader(System.in); OutputStreamWriter writer new OutputStreamWriter(System.out); try (reader; writer) { // reader、writer是final的不可再被赋值 // reader new InputStreamReader(System.in); } catch (IOException e) { e.printStackTrace(); } }局部变量的类型推断varpublic void test1(){ //1. 局部变量的实例化 var list new ArrayListString(); var set new LinkedHashSetInteger(); //2. 增强for循环中的索引 for (var v : list) { System.out.println(v); } //3. 传统for循环中 for (var i 0; i 100; i) { System.out.println(i); } //4. 返回值类型含复杂泛型结构 var iterator set.iterator(); //IteratorMap.EntryInteger, Student iterator set.iterator(); }instanceof 的模式匹配if (obj instanceof String s) { // 如果类型匹配 直接使用 } else { // 如果类型不匹配则不能直接使用 }public boolean equals(Object o){ if(o instanceof Computer other){ return this.model.equals(other.model) this.price other.price; } return false; }switch表达式、switch的模式匹配使用-代替:代替break的使用public void test() { Week day Week.FRIDAY; int a switch (day) { case MONDAY - 1; case TUESDAY, WEDNESDAY, THURSDAY - 2; case FRIDAY - 3; case SATURDAY, SUNDAY - 4; default - throw new RuntimeException(What day is today? day); }; System.out.println(a); }使用yield返回数据同时结束运行可使用:替代-public void test() { Week day Week.FRIDAY; int a switch (day) { case MONDAY - { yield 1; } case TUESDAY, WEDNESDAY, THURSDAY - { yield 2; } case FRIDAY - { yield 3; } case SATURDAY, SUNDAY - { yield 4; } default - throw new RuntimeException(What day is today? day); }; System.out.println(a); }预览特性public void test(Object o) { String formatted switch (o) { case Integer i - int i; case Long l - long l; case Double d - double d; case String s - String s; default - o.toString(); }; System.out.println(formatted); }文本块的使用“”文本块“”\ 取消换行操作\s表示一个空格public void test(){ String newQuery1 SELECT id,name,email \ FROM customers\s WHERE id 4 \ ORDER BY email DESC ; System.out.println(newQuery1); }新的引用数据类型record记录一定程度上看作JavaBeanpublic record Order1(int orderId, String orderName) { // 编译器会自动生成构造器、equals()、hashCode()、toString() 等 //- 还可以在record声明的类中定义静态字段、静态方法、构造器或实例方法。 static String info 我是一个人; public static void show() { System.out.println(我是一个人); } public Person() { this(0, null); } public void eat() { System.out.println(人吃饭); } //- 不能在record声明的类中定义实例字段类不能声明为abstract不能声明显式的父类等。 // final int age; }public void test2(){ Order1 order1 new Order1(1001, orderAA); //测试toString() System.out.println(order1); //测试getter() System.out.println(order1.orderId()); System.out.println(order1.orderName()); Order1 order2 new Order1(1001, orderAA); //测试equals() System.out.println(order1.equals(order2)); //测试hashCode()和equals() HashSetOrder1 set new HashSet(); set.add(order1); set.add(order2); System.out.println(set); }密封类sealed class密封类可以被指定的子类继承非指定的类不能继承要求指定的子类必须是final、sealed、non-sealed三者之一//Person 是一个密封类可以被指定的子类继承非指定的类不能继承Person类 public sealed class Person permits Student, Teacher, Worker { } //要求指定的子类必须是final、sealed、non-sealed三者之一 final class Student extends Person { } //Student类不能被继承了。 sealed class Teacher extends Person permits SeniorTeacher { } //Teacher类只能被指定的子类继承 non-sealed class SeniorTeacher extends Teacher { } non-sealed class Worker extends Person { } //Worker类在继承时没有任何限制 class WatchWorker extends Worker { } //class Farmer extends Person {}其他Optional类的使用public void test(){ String star 迪丽热巴; star null; // 使用Optional避免空指针的问题 // 1. 实例化: // ofNullable(T value): 用来创建一个Optional实例, value可能是空, 也可能非空 OptionalString optional Optional.ofNullable(star); // orElse(T other): 如果Optional实例内部的value属性不为null, 则返回value。如果value为null, // 则返回other. String otherStar 杨幂; String finalStar optional.orElse(otherStar); System.out.println(finalStar.toString()); }