当前位置:   article > 正文

JDK新特性

jdk新特性

目录

1、JDK8

1.1、Lambda表达式

1.2、Stream类

1.3、Optional类

​编辑1.4、接口默认方法

1.5、Date Time API

1.6、Base64

1.7、方法引用

1.8、并行(parallel)数组

1.9、对并发的新支持 

2、JDK9

2.1、接口的private方法

3、JDK11

3.1、局部变量类型推断(var)

4、JDK14

4.1、switch中的箭头语法

4.2、将switch作为表达式

5、JDK16

5.1、instanceof 智能转型

6、JDK17

6.1、密封类和密封接口

6.2、switch中的case null


1、JDK8

1.1、Lambda表达式

允许把函数作为一个方法的参数,可替换匿名内部类

 Lambda表达式语法

  1. // 无参数无返回值:
  2. () -> System.out.println("Hello World")
  3. // 有一个参数无返回值:
  4. (x) -> System.out.println(x)
  5. // 有一个参数无返回值,可省略参数小括号:
  6. x -> System.out.println(x)
  7. // 有多个参数,有返回值,有多条Lambda语句:
  8. (x,y) -> {System.out.println("xxx"); return "sdf";}
  9. // 有多个参数,有返回值,只有一条Lambda语句:
  10. (x, y) -> xxx

1.2、Stream类

        对一个集合中的一系列元素进行聚合操作。

        Stream 的一系列操作必须要使用终止操作,否者整个数据流是不会流动起来的,即处理操作不会执行。

        Stream不会存储元素;

        Stream不会改变源对象,会返回一个持有结果的Stream;

        Stream操作是延时执行的,会等到需要结果的时候才会去真正执行。

中间操作符

终止操作符

 示例

  1. List<String> list = Arrays.asList(new String[]{"15","32","25","64","32"});
  2. list.stream().distinct().forEach(item -> System.out.println(item));

1.3、Optional类

        主要是为了解决空指针异常NullPointException问题

构造方法

  1. Optional.of(T)  
  2.         该方式的入参不能为null,否则会有NPE,在确定入参不为空时使用该方式。
  3. Optional.ofNullable(T)
  4.          该方式的入参可以为null,当入参不确定为非null时使用。
  5. Optional.empty()
  6.         这种方式是返回一个空Optional,等效Optional.ofNullable(null)

常用方法       

  1. ifPresent() <==> obj != null
  2.         当Optional实例的值非空时返回true,否则返回false
  3. orElseGet()
  4.         当Optional包含非空值时返回该值,否则通过接收的function生成一个默认的;
  5. map()
  6.         转换当前Optional的值,并返回一个新的Optional实例;
  7. orElse()
  8.         与orElseGet方法相似,不同的是orElse()直接返回传入的默认值。
  9. orElseThrow()
  10.         value值为null时,直接抛出一个异常

示例

1.4、接口默认方法

        接口允许有实现方法,只需要在方法名前新增default关键字即可。

作用

        为接口添加新方法,同时保证不影响已有的实现。

解决default方法冲突的三步骤

  1. 方法签名相同时,才表示出现了冲突。
  2. 类中的方法优先级最高。类或者父类中的方法实现优先级大于任何接口的默认方法
  3. 子接口的默认方法优先级更高。
  4. 若最终还是无法判断,那么实现类必须通过显示复写的方式复写默认方法,然后再自己通过xxx.super.xxx()的方式来指定具体使用哪个接口的实现

示例(default方法冲突)

        多重继承中,如果出现了同名的默认方法

 示例1:若继承的两个接口相互独立且存在相同的默认方法,则需要重写default方法,并制定继承哪一个接口的default方法。

 

示例2:若继承的两个接口存在相同的默认方法且为继承关系,则调用的是子类接口的default方法。

 

1.5、Date Time API

旧版时间API

  • 设计差:两个Date类,java.util.Date包含日期和时间,java.sql.Date仅包含日期,设计差;
  • 非线程安全:日期可变,用于时间转换的SimpleDateFormat也不是线程安全的。

新版时间API        

主要有LocalDate、LocalTime和LocalDateTime三个日期类,实例为不可变对象,线程安全。JDK8新增的日期及时间位于java.time包下。

  • LocalDate:表示日期,包含:年月日。格式为:2020-01-13
  • LocalTime:表示时间,包含:时分秒。格式为:16:39:09.307
  • LocalDateTime:表示日期时间,包含:年月日 时分秒。格式为:2020-01-13T16:40:59.138
  • DateTimeFormatter:日期时间格式化类
  • Instant:时间戳类
  • Duration:用于计算 2 个时间(LocalTime,时分秒)之间的差距
  • Period:用于计算 2 个日期(LocalDate,年月日)之间的差距
  • ZonedDateTime:包含时区的时间

部分API举例介绍

  1. // 获取当前日期
  2. LocalDate now = LocalDate.now();
  3. // 指定日期 LocalDate.of(year,month,day)
  4. LocalDate date = LocalDate.of(2008, 8, 8);
  5. // 获取当前时间
  6. LocalTime now = LocalTime.now();
  7. // 指定日期 LocalTime.of(hour,minute,second)
  8. LocalTime date = LocalTime.of(13, 26, 39);
  9. // 获取当前日期时间
  10. LocalDateTime now = LocalDateTime.now();
  11. // 指定日期时间 LocalDateTime.of(year,month,day,hour,minute,second)
  12. LocalDateTime date = LocalDateTime.of(2018, 7, 23, 18, 59, 31);
  13. // 修改年[修改时间]
  14. System.out.println("修改年后:" + now.withYear(9102));
  15. // 增加年(减使用 minusYear()方法)
  16. System.out.println("+2年后:" + now.plusYears(2));

1.6、Base64

Base64只是一种编解码算法,而非加密算法,一般对重要信息做加密不使用Base64。

  1. import java.io.UnsupportedEncodingException;
  2. import java.util.Base64;
  3. import java.util.Base64.Decoder;
  4. import java.util.Base64.Encoder;
  5. /**
  6. * jdk8提供的Base64编解码效率远大于sun.misc和 Apache Commons Codec的效率
  7. */
  8. public class Base64Test {
  9. public static void main(String[] args) {
  10. String str = "这是一次Base64编解码测试";
  11. Encoder encoder = Base64.getEncoder(); // 编码对象
  12. Decoder decoder = Base64.getDecoder(); // 解码对象
  13. try {
  14. // 1.编码
  15. String encoderStr = encoder.encodeToString(str.getBytes("UTF-8"));
  16. System.out.println(encoderStr);//6L+Z5piv5LiA5qyhQmFzZTY057yW6Kej56CB5rWL6K+V
  17. // 2.解码
  18. byte[] decode = decoder.decode(encoderStr);
  19. System.out.println(new String(decode, "UTF-8")); // 这是一次Base64编解码测试
  20. } catch (UnsupportedEncodingException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }

1.7、方法引用

常与Lambda表达式一起使用。

如果Lambda表达式所要实现的功能,可以用其他方法替代,那么则可以使用方法引用。

引用方式

  1. 1、instanceName::methodName 对象::方法名
  2. 2、ClassName::staticMethodName 类名::静态方法
  3. 3、ClassName::MethodName 类名::普通方法
  4. 4、ClassName::new 类名::new 调用的构造器
  5. 5、TypeName[]::new String[]::new 调用数组构造器

举例说明

  1. List<LocalDate> dates = new ArrayList<>();
  2. dates.add(LocalDate.now());
  3. dates.add(LocalDate.of(2019, 11, 12));
  4. dates.add(LocalDate.of(2020, 5, 6));
  5. // 获取年份集合
  6. List<Integer> years = dates.stream().map(LocalDate::getYear).collect(Collectors.toList());
  7. System.out.println(years);

1.8、并行(parallel)数组

  • 对于Stream流,增加parallelStream并行流;
  • 对于普通数组,增加Arrays.parallelSort()并行排序。
  1. // Stream串行
  2. long count = IntStream.range(0, 100000).filter(this::isPrime).count();
  3. System.out.println(count);
  4. // Stream并行
  5. long count1 = IntStream.range(0, 100000).parallel().filter(this::isPrime).count();
  6. System.out.println(count1);
  7. // 获取一个并行流
  8. List<JSONObject> objects = Lists.newArrayList();
  9. Stream<JSONObject> jsonObjectStream = objects.parallelStream();
  10. // 并行数组
  11. long[] arrayOfLong = new long [20000];
  12. 。。。省略数组的初始化。。。
  13. Arrays.parallelSort( arrayOfLong );

1.9、对并发的新支持 

LongAdder、CompletableFuture、StampedLock

2、JDK9

2.1、接口的private方法

        JDK8中的接口新增了默认方法,允许在接口中定义方法体。从JDK9开始,可以在接口中添加private的私有方法和私有静态方法,不将私有方法暴露给接口的实现类调用。

  • 接口中private方法不能是abstract抽象方法。因为abstract抽象方法是公开的用于给接口实现类实现的方法,所以不能是private。
  • 接口中私有方法只能在接口内部的方法里面被调用。
  • 接口中私有静态方法可以在其他静态和非静态接口方法中使用。
  • 接口中私有非静态方法不能在私有静态方法内部使用。

3、JDK11

3.1、局部变量类型推断(var)

        在一个局部定义(方法内部)中,编译器可以自动发现类型。在编译时,编译器会检查赋值语句右侧代码,从而推断出具体类型。它查看声明的右侧,如果这是一个初始化语句,它会用那个类型取代var。

  1. // 简化类型,变量名尽量见名知意
  2. // DocumentationTool dtl = new DocumentationTool();
  3. var dockmentationTool = new DocumentationTool();
  4. // 使用数据类型标志来帮助var去推断出预期的基本数据类型(int, long, float, double)
  5. var intNumber = 20; // 推断为int
  6. var longNumber = 20L; // 推断为long
  7. var floatNumber = 20F; // 推断为float, 20.0
  8. var doubleNumber = 20D; // 推断为double, 20.0

4、JDK14

4.1、switch中的箭头语法

在case语句中,使用箭头替换老版的冒号,可以省略break语句。

  1. // switch case旧方式
  2. public void old(int i) {
  3. switch(i) {
  4. case 1: System.out.println("one");
  5. break;
  6. case 2: System.out.println("two");
  7. break;
  8. case 3: System.out.println("three");
  9. break;
  10. default: System.out.println("default");
  11. }
  12. }
  13. // switch case新方式
  14. public void new(int i) {
  15. switch(i) {
  16. case 1 -> System.out.println("one");
  17. case 2 -> System.out.println("two");
  18. case 3 -> System.out.println("three");
  19. default -> System.out.println("default");
  20. }
  21. }

4.2、将switch作为表达式

将switch作为一个表达式,可以通过switch获取一个值。

在使用冒号语法时,可以使用yield关键字从switch中返回结果,yield和break不能同时使用。

  1. public void test1(int i) {
  2. String result = switch(i) {
  3. case 1: yield "one";
  4. case 2: yield "two";
  5. case 3: yield "three";
  6. default: yield "default";
  7. }
  8. System.out.println(result);
  9. }
  10. public void test2(int i) {
  11. String result = switch(i) {
  12. case 1 -> "one";
  13. case 2 -> "two";
  14. case 3 -> "three";
  15. default -> "default";
  16. }
  17. System.out.println(result);
  18. }

5、JDK16

5.1、instanceof 智能转型

  1. public void dumb(Object x) {
  2. if (x instanceof String s && s.length() > 0) {
  3. System.out.format("%d %s%n", s.length(), s.toUpperCase())
  4. }
  5. }

        如上示例所示,如果x是String类型,就会自动用String类型创建一个新的变量s(s被称为模式变量),s在整个作用域中都可用。

但是,在某些极端情况,智能转型会产生一些奇怪的作用域行为:

  1. public void f1(Object o) {
  2. if (!(o instanceof String s)) {
  3. System.out.println("Not a String");
  4. throw new RuntimeException();
  5. }
  6. // 此处s仍在作用域中!
  7. System.out.println(s.toUpperCase()); //[1]
  8. }
  9. public void f2(Object o) {
  10. if (!(o instanceof String s)) {
  11. System.out.println("Not a String");
  12. }
  13. // 编译报错:无法找到s
  14. System.out.println(s.toUpperCase()); //[1]
  15. }

6、JDK17

6.1、密封类和密封接口

        密封类/接口规定了只有指定的类/接口可以扩展和实现他们。换句话说,密封类/接口可以限制自己能派生出哪些类。

举例说明:

定义密封类(sealed ... permits ...)

sealed class Shape permits Circle, Hexagon, Rectangle {...}
  • 通过使用sealed关键字,定义一个密封类Shape;
  • permits用于限制只有指定的子类能继承密封类Shape。
  • 子类继承

sealed类的子类只能通过下面的某个修饰符来定义:

  • final:不允许有进一步的子类;
  • sealed:允许有一组密封子类;
  • non-sealed:允许有未知子类。

6.2、switch中的case null

在switch中引入原本非法的case null。

  1. // 老版本switch case
  2. public void old(String s) {
  3. if (s == null) {
  4. System.out.println("null");
  5. return;
  6. }
  7. switch(s) {
  8. case "XX" -> System.out.println("XX");
  9. default -> System.out.println("default");
  10. }
  11. }
  12. // 新版本switch case
  13. public void new(String s) {
  14. switch(s) {
  15. case null -> System.out.println("null");
  16. case "XX" -> System.out.println("XX");
  17. default -> System.out.println("default");
  18. }
  19. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/53831?site
推荐阅读
相关标签
  

闽ICP备14008679号