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表达式语法

// 无参数无返回值:                       
() -> System.out.println("Hello World")

// 有一个参数无返回值:                    
(x) -> System.out.println(x)

// 有一个参数无返回值,可省略参数小括号:               
x -> System.out.println(x)

// 有多个参数,有返回值,有多条Lambda语句:  
(x,y) -> {System.out.println("xxx"); return "sdf";}

// 有多个参数,有返回值,只有一条Lambda语句:
(x, y) -> xxx

1.2、Stream类

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

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

        Stream不会存储元素;

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

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

中间操作符

终止操作符

 示例

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

1.3、Optional类

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

构造方法

Optional.of(T)  

        该方式的入参不能为null,否则会有NPE,在确定入参不为空时使用该方式。

Optional.ofNullable(T)

         该方式的入参可以为null,当入参不确定为非null时使用。

Optional.empty()

        这种方式是返回一个空Optional,等效Optional.ofNullable(null)

常用方法       

ifPresent() <==> obj != null
        当Optional实例的值非空时返回true,否则返回false; 

orElseGet()
        当Optional包含非空值时返回该值,否则通过接收的function生成一个默认的;

map()
        转换当前Optional的值,并返回一个新的Optional实例;

orElse()
        与orElseGet方法相似,不同的是orElse()直接返回传入的默认值。

orElseThrow()
        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举例介绍

// 获取当前日期
LocalDate now = LocalDate.now();

// 指定日期 LocalDate.of(year,month,day)
LocalDate date = LocalDate.of(2008, 8, 8);

// 获取当前时间
LocalTime now = LocalTime.now();

// 指定日期 LocalTime.of(hour,minute,second)
LocalTime date = LocalTime.of(13, 26, 39);

// 获取当前日期时间
LocalDateTime now = LocalDateTime.now();

// 指定日期时间 LocalDateTime.of(year,month,day,hour,minute,second)
LocalDateTime date = LocalDateTime.of(2018, 7, 23, 18, 59, 31);

// 修改年[修改时间]
System.out.println("修改年后:" + now.withYear(9102));

// 增加年(减使用 minusYear()方法)
System.out.println("+2年后:" + now.plusYears(2));

1.6、Base64

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

import java.io.UnsupportedEncodingException;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;

/**
 * jdk8提供的Base64编解码效率远大于sun.misc和 Apache Commons Codec的效率
 */
public class Base64Test {

	public static void main(String[] args) {
		String str = "这是一次Base64编解码测试";

		Encoder encoder = Base64.getEncoder(); // 编码对象
		Decoder decoder = Base64.getDecoder(); // 解码对象
		try {
			// 1.编码
			String encoderStr = encoder.encodeToString(str.getBytes("UTF-8"));
			System.out.println(encoderStr);//6L+Z5piv5LiA5qyhQmFzZTY057yW6Kej56CB5rWL6K+V

			// 2.解码
			byte[] decode = decoder.decode(encoderStr);
			System.out.println(new String(decode, "UTF-8")); // 这是一次Base64编解码测试
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}

}

1.7、方法引用

常与Lambda表达式一起使用。

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

引用方式

1、instanceName::methodName 对象::方法名

2、ClassName::staticMethodName 类名::静态方法

3、ClassName::MethodName 类名::普通方法

4、ClassName::new 类名::new 调用的构造器

5、TypeName[]::new String[]::new 调用数组构造器

举例说明

List<LocalDate> dates = new ArrayList<>();
dates.add(LocalDate.now());
dates.add(LocalDate.of(2019, 11, 12));
dates.add(LocalDate.of(2020, 5, 6));

// 获取年份集合
List<Integer> years = dates.stream().map(LocalDate::getYear).collect(Collectors.toList());
System.out.println(years);

1.8、并行(parallel)数组

  • 对于Stream流,增加parallelStream并行流;
  • 对于普通数组,增加Arrays.parallelSort()并行排序。
// Stream串行
long count = IntStream.range(0, 100000).filter(this::isPrime).count();
System.out.println(count);

// Stream并行
long count1 = IntStream.range(0, 100000).parallel().filter(this::isPrime).count();
System.out.println(count1);


// 获取一个并行流
List<JSONObject> objects = Lists.newArrayList();
Stream<JSONObject> jsonObjectStream = objects.parallelStream();


// 并行数组
long[] arrayOfLong = new long [20000]; 

。。。省略数组的初始化。。。

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。

// 简化类型,变量名尽量见名知意
// DocumentationTool dtl = new DocumentationTool();
var dockmentationTool = new DocumentationTool();


// 使用数据类型标志来帮助var去推断出预期的基本数据类型(int, long, float, double)
var intNumber = 20;     // 推断为int
var longNumber = 20L;   // 推断为long
var floatNumber = 20F;  // 推断为float, 20.0
var doubleNumber = 20D; // 推断为double, 20.0

4、JDK14

4.1、switch中的箭头语法

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

// switch case旧方式
public void old(int i) {
    switch(i) {
        case 1: System.out.println("one");
                break;
        case 2: System.out.println("two");
                break;
        case 3: System.out.println("three");
                break;
        default: System.out.println("default");
    }
}


// switch case新方式
public void new(int i) {
    switch(i) {
        case 1 -> System.out.println("one");
        case 2 -> System.out.println("two");
        case 3 -> System.out.println("three");
        default -> System.out.println("default");
    }
}

4.2、将switch作为表达式

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

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

public void test1(int i) {
    String result = switch(i) {
        case 1: yield "one";
        case 2: yield "two";
        case 3: yield "three";
        default: yield "default";
    }
    System.out.println(result);
}


public void test2(int i) {
    String result = switch(i) {
        case 1 -> "one";
        case 2 -> "two";
        case 3 -> "three";
        default -> "default";
    }
    System.out.println(result);
}

5、JDK16

5.1、instanceof 智能转型

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

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

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

public void f1(Object o) {
    if (!(o instanceof String s)) {
        System.out.println("Not a String");
        throw new RuntimeException();
    }
    // 此处s仍在作用域中!
    System.out.println(s.toUpperCase()); //[1]
}

public void f2(Object o) {
    if (!(o instanceof String s)) {
        System.out.println("Not a String");
    }
    // 编译报错:无法找到s
    System.out.println(s.toUpperCase()); //[1]
}

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。

// 老版本switch case
public void old(String s) {
    if (s == null) {
        System.out.println("null");
        return;
    }
    switch(s) {
        case "XX" -> System.out.println("XX");
        default -> System.out.println("default");
    }
}


// 新版本switch case
public void new(String s) {
    switch(s) {
        case null -> System.out.println("null");
        case "XX" -> System.out.println("XX");
        default -> System.out.println("default");
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值