Java8至Java18新特性汇总

Java8至Java18新特性汇总整理


目录如下:

Java 7 (LTS) 新特性:

Java 8 (LTS) 新特性:

Java 9 新特性:

  •  2017-9-22

Java 10 新特性:

  • 2018-3-21

Java 11 (LTS) 新特性:

  • 2018-09-25

Java 12 新特性:

  • 2019-3-19

Java 13 新特性:

  • 2019-9-17

Java 14 新特性:

  • 2020-3-17

Java 15 新特性:

  • 2020-9-15

Java 16 新特性:

  • 2021-3-16

Java 17(LTS) 新特性:

  • 2021-9-14

Java 18 新特性:

  • 2022-03-22

Java 19 新特性:

  • 2020-09



下面会以 (★★★☆☆)星级来描述“推荐使用”的程度

  • 五星满分代表此功能常用,且比较完美且非常实用。

  • 三星、四星代表此功能比较常用,但是不够实用或者说有瑕疵需要注意。

  • 一星、二星代表此功能 不常用 或者 因瑕疵大、副作用大而不建议使用。


Java 7 -> 2011-07-28

1. switch支持字符串变量(★★★★★)

 switch (dayOfWeekArg) {
     case "Monday":
         typeOfDay = "Start of work week";
         break;
     ...
 }

2. 泛型实例化类型自动推断(★★★★★)

    ArrayList<String> list = new ArrayList<>();

3.  新增整数类型二进制字面量(★★☆☆☆)

整数类型例如(byte,short,int,long)能够用二进制来表示了。通过在数字前面加入0b或者0B来标示一个二进制的字面量,看下面的例子:

// 十六进制字面量
public static final int[] phases = { 
    0x31, 0x62, 0xC4, 0x89, 0x13, 0x26, 0x4C, 0x98 
}
// 二进制字面量(更加直观)
public static final int[] phases = {
  0b00110001,
  0b01100010,
  0b11000100,
  0b10001001,
  0b00010011,
  0b00100110,
  0b01001100,
  0b10011000
}

4. 新增数字字面量使用下划线 "_"连接符(★★☆☆☆)

下划线字符(_)能够出现在数字字面量的数字之间的任何位置。这个功能可以用来对一个数字字面量根据位数分组,从而提高你代码的可读性。例如:

long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_99_9999L;
float pi =  3.14_15F;
long hexWords = 0xCAFE_BABE;
long maxLong = 0x7fff_ffff_ffff_ffffL;
byte nybbles = 0b0010_0101;
long bytes = 0b11010010_01101001_10010100_10010010;

注意,下面的情形不能出现下划线:

  • 数字的开头和结尾

  • 在浮点数中与小数点相邻

  • F或者L后缀之前

  • 在预期数字的位置

float pi1 = 3_.1415F;      // 无效; 不能和小数点相邻
float pi2 = 3._1415F;      // 无效; 不能和小数点相邻
long socialSecurityNumber1 = 999_99_9999_L;         // 无效; 不能放在L后缀之前

int x1 = _52;              // 无效;这是个标识符,不是数字的字面量
int x2 = 5_2;              // OK
int x3 = 52_;              // 无效; 不能放在数字的结尾
int x4 = 5_______2;        // OK

int x5 = 0_x52;            // 无效; 不能放在 0x 中间 
int x6 = 0x_52;            // 无效; 不能放在数字的开头
int x7 = 0x5_2;            // OK
int x8 = 0x52_;            // 无效; 不能放在数字的结尾

int x9 = 0_52;             // OK 
int x10 = 05_2;            // OK
int x11 = 052_;            // Invalid; 不能放在数字的结尾

5. 被抑制的异常(★★☆☆☆)

在Java7中为Throwable类增加addSuppressed方法。当一个异常被抛出的时候,可能有其他异常因为该异常而被抑制住,从而无法正常抛出(主要就是指finally{}代码段中异常会覆盖try{}代码段中的异常)。这时可以通过addSuppressed方法把这些被抑制的方法记录下来。被抑制的异常会出现在抛出的异常的堆栈信息中(如下例所示),也可以通过getSuppressed方法来获取这些异常。这样做的好处是不会丢失任何异常,方便开发人员进行调试。

java.lang.ArithmeticException: / by zero
	at io.hilo.example.StartApplication$TestC.test(StartApplication.java:50)
	at io.hilo.example.StartApplication.main(StartApplication.java:26)
	Suppressed: java.lang.ArithmeticException: / by zero
		at io.hilo.example.StartApplication$TestC.close(StartApplication.java:57)
		at io.hilo.example.StartApplication.main(StartApplication.java:27)

注意,被抑制异常和嵌套异常是有概念区别的,因为被抑制异常本身并不属于嵌套的概念。

用法示例(一般都是在finally里面使用,当然也可以在其他地方使用):

public void read(String filename) throws IOException {
    FileInputStream input = null;
    IOException readException = null;
    try {
        input = new FileInputStream(filename);
    } catch (IOException ex) {
        readException = ex;
    } finally {
        if(input != null){
            try {
                input.close();
            } catch (IOException ex2) {
                if(readException != null){
                //把finally里面的异常作为‘被抑制异常’添加进原始异常中
                    readException.addSuppressed(ex2);
                }else{
                    readException = ex2;
                }
            }
        }
        if(readException != null){
            throw readException;
        }
    }
}

这里其实有一个问题:上面代码中catch只有IOException一个类型,如果再加另一个类型比如IllegalArgumentException,那此时要保持throw类型不扩大成父类Exception,就得用两个变量在finally里面来分别throw,非常的麻烦,所以这个功能比较鸡肋。

6. 对重新抛出异常的高级类型检查(★★☆☆☆)

throw一个更大范围内的异常类型,但throws声明的异常类型不需要跟随throw扩大。

例如,下面代码是Java7以前唯一正确的最小化throws声明:

public void rethrowException(String en) throws Exception {
    try {
      if (en.equals("First")) {
        throw new FirstException();
      } else {
        throw new SecondException();
      }
    } catch (Exception e) {
      throw e;
    }
}

但是在Java7及以后可以做到更精细的throws声明:

public void rethrowException(String en) throws FirstException,SecondException {
    try {
      if (en.equals("First")) {
        throw new FirstException();
      } else {
        throw new SecondException();
      }
    } catch (Exception e) {
      throw e;
    }
}

7. 自动资源管理(try-with-resources 语句)(★★★☆☆)

任何实现了java.lang.AutoCloseable接口(Java7新增的)或者实现了java.io.Closeable,可以作为一个资源。

try-with-resources可声明多个资源,保证每一个资源都会被关闭在声明结束的时候。例如:

public static void write(String zipFileName, String outputFileName)
    throws java.io.IOException {

    Charset charset = Charset.forName("US-ASCII");
    Path outputFilePath = Paths.get(outputFileName);

    try (
      ZipFile zf = new ZipFile(zipFileName);
      BufferedWriter writer = Files.newBufferedWriter(outputFilePath, charset)
    ) {

      for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) {
        String newLine = System.getProperty("line.separator");
        String zipEntryName = ((ZipEntry)entries.nextElement()).getName() + newLine;
        writer.write(zipEntryName, 0, zipEntryName.length());
      }
    }
}

public static void viewTable(Connection con) throws SQLException {

    String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES";

    try (Statement stmt = con.createStatement()) {

      ResultSet rs = stmt.executeQuery(query);

      while (rs.next()) {
        String coffeeName = rs.getString("COF_NAME");
        System.out.println(coffeeName);
      }

    }
}

其实就是类似于 在 finally {} 中JVM帮我们自动调用了close方法。

只是我有点好奇,既然是自动调用close关闭,那么如果close报错,会抛异常吗,还是说异常会被忽略?于是我试验了一下,结论是:

  • 1)如果try-catch内部运行无异常,则close方法报错等同于在try {}内部运行时报错,因此可以在catch的时候也catch住close方法的报错(如果close方法有声明的异常,则会强制提示编程者catch该异常)。

  • 2)如果try-catch内部运行出现异常,则close方法报错等同于在finally {}内部运行时报错,此时如果close出现异常,则会被塞进 try-catch异常的addSuppressed()方法中,当做被抑制的异常。

注意,‘1)的逻辑可能和一般处理逻辑不同,如果你想忽略close异常(try-catch的时候不往外抛),则建议还是手动调用close方法来处理,try-with-resources只是一个偷懒的写法,但并不一定是良药。


8. Catch多个Exception(捕捉多个异常类型)(★★★★★)

如下所示:

} catch (IOException | SQLException ex) {
    logger.log(ex); // 一次性处理两种异常
}


Java 8 -> 2014-03-18

1. 接口默认方法和静态方法(★★★★☆)

public interface Defaulable {

    default String notRequired() { 
        return "Default implementation"; 
    }

    // static methods
    static Defaulable create(Supplier<Defaulable> supplier) {
        return supplier.get();
    }

}

    默认方法允许在不打破现有继承体系的基础上改进接口。该特性在官方库中的应用是:给java.util.Collection接口添加新方法,如stream()、parallelStream()、**forEach()和removeIf()**等等。接口默认方法的主要用途就在于此,可以在不影响接口的其他实现类的情况下,给接口增加新方法,然后只由其中某个类来重载实现该方法。

    当然,接口默认方法也可以代替原来的抽象父类(Abstract)的部分功能,并且比抽象类的单继承更灵活,实际性通过接口默认方法,可以变相实现多继承。Java 8中的默认方法可以看作是一种多重继承的形式(除了属性不能被继承)。但是这种用法不是它的设计初衷,不应该被滥用——普遍情况下,还是应该保持接口和实现分离,不要用默认方法来代替实现类或者抽象类。

    静态方法可以定义在接口上,算是一种小的改进吧,如果不用它,把静态方法单独定义到一个工具类中也是可以的。

1. Lambda表达式(★★★☆☆)

        俗称箭头函数,但Java里面不叫函数,Java里面其实它属于一种特殊的“匿名内部类”,是个残废品。匿名内部类可以完全替代Lambda表达式,但是反正则不行。

// 例1:标准语法
list.sort(new Comparator<Integer>() {
    public int compare(Integer o1, Integer o2) {
      if(o1>o2)
        return -1;
      else if(o1<o2)
        return 1;
      else
        return 0;
    }
} );
// 例1:lambda语法
list.sort( ( o1, o2 ) -> {
    if(o1>o2)
        return -1;
      else if(o1<o2)
        return 1;
      else
        return 0;
} );

    在《浅论各种编程语言》一文中,我对Java的Lambda表达式的缺点有过深刻描述。

    总之,建议使用匿名内部类的写法,而不是刻意使用Lambda表达式。使用传统的匿名内部类写法代码更加统一、更直观,只在某些常见、容易理解的方法中,可以使用习惯性的lambda写法(比如集合的forEach方法配合lambda使用)。

2. 方法引用 

        方法引用提供了非常有用的语法,可以直接引用已有的Java类或对象(实例)的方法或构造器.

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


4. Stream API

        新添加的Stream API(java.utils.stream)把真正的函数式编程风格引入到Java中

5. Data Time API

        加强对日期与时间的处理

6. Optional类

        Optional类已经成为Java8类库的一部分,可用来解决空指针异常。


© 2009-2020 Zollty.com 版权所有。渝ICP备20008982号