jdk1.7新特性详解

来源:互联网 发布:算法导论中文版pdf 编辑:程序博客网 时间:2024/05/16 15:08

jdk1.7新特性详解

开发期间略知jdk1.7的一些特性,没有真正的一个一个得展开研究,而是需要说明再去查,导致最整个新特性不是特别的清楚,这种情况以后得需要改变了,否则就会变成代码的奴隶。现在正好有时间可以细细的研究研究了。文章主要参照oracle jdk1.7的官方地址:https://docs.oracle.com/javase/7/docs/technotes/guides/language/enhancements.html#javase7

  • jdk17新特性详解
    • 二进制字面量
    • 在数字字面量使用下划线
    • switch可以使用string了
    • 实例创建的类型推断
    • 使用Varargs方法使用不可维护的形式参数时改进了编译器警告和错误
    • try-with-resources 资源的自动管理
    • 捕捉多个异常类型和对重新抛出异常的高级类型检查

二进制字面量

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

//一个8位'byte'值:byte aByte = (byte)0b00100001;//一个16位'short'值:short aShort = (short)0b1010000101000101;//一些32位'int'值:int anInt1 = 0b10100001010001011010000101000101;int anInt2 = 0b101;int anInt3 = 0B101; // B可以是大写也可以是小写.//一个64位的'long'值. 注意"L"结尾:long aLong = 0b1010000101000101101000010100010110100001010001011010000101000101L;

二进制字面量可以使数据之间的关系比十六进制或八进制更明显。例如,下面数组的每一个连续数组都是通过一个位循环的。

public static final int[] phases = {  0b00110001,  0b01100010,  0b11000100,  0b10001001,  0b00010011,  0b00100110,  0b01001100,  0b10011000}

在16进制中,数字之间的关系就不是那么的明显了:

public static final int[] phases = {    0x31, 0x62, 0xC4, 0x89, 0x13, 0x26, 0x4C, 0x98}

在数字字面量使用下划线

在JAVA SE 7和以后的版本中,下划线字符(_)能够出现在数字字面量的数字之间的任何位置。这个功能可以用来对一个数字字面量根据位数分组,从而提高你代码的可读性。比如,如果你的代码包含一些数字有很多的位数,你能够用下划线字符把位数分为三组,类似于你用一个像逗号或者一个空格作为分隔符。

long creditCardNumber = 1234_5678_9012_3456L;long socialSecurityNumber = 999_99_9999L;float pi =  3.14_15F;long hexBytes = 0xFF_EC_DE_5E;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;              // OKint x3 = 52_;              // 无效; 不能放在数字的结尾int x4 = 5_______2;        // OKint x5 = 0_x52;            // 无效; 不能放在 0x 中间 int x6 = 0x_52;            // 无效; 不能放在数字的开头int x7 = 0x5_2;            // OKint x8 = 0x52_;            // 无效; 不能放在数字的结尾int x9 = 0_52;             // OK int x10 = 05_2;            // OKint x11 = 052_;            // Invalid; 不能放在数字的结尾

switch可以使用string了

在JDK7 的正式版本中,你可以在switch的表达式中用String类型

public String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) {     String typeOfDay;     switch (dayOfWeekArg) {         case "Monday":             typeOfDay = "Start of work week";             break;         case "Tuesday":         case "Wednesday":         case "Thursday":             typeOfDay = "Midweek";             break;         case "Friday":             typeOfDay = "End of work week";             break;         case "Saturday":         case "Sunday":             typeOfDay = "Weekend";             break;         default:             throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg);     }     return typeOfDay;}

switch语句比较与每个case标签关联就好像使用string.equals方法表达的表达式的字符串对象;因此,在switch语句的字符串对象的比较是区分大小写的。java编译器生成更有效的字节码从switch语句中使用字符串对象比链式if-then-else语句。

实例创建的类型推断

由于编译器能够从上下文推断出参数类型,所以你可以用一组空类型参数(<>)替换泛型类构造函数的调用所需的类型参数。这对角括号通俗地称为菱形。
例如,细想下面的变量申明:

Map<String, List<String>> myMap = new HashMap<String, List<String>>();

在Java SE 7中,你可以用一组空类型参数(<>)替换构造器的参数类型:

Map<String, List<String>> myMap = new HashMap<>();

注意,在泛型类实例化中利用自动类型推断时,必须指定菱形。在下面的例子中,编译器因为HashMap()构造函数是指原始类型生成一个未转换的HashMap的警告,而不是Map

Map<String, List<String>> myMap = new HashMap(); // 未经检查的转换警告

JAVA SE 7 支持泛型实例创建的类型推断,你只有在构造器的参数类型在上下文中明确指定的情况下才能使用类型推断。下面的例子将会编译不通过:

List<String> list = new ArrayList<>();list.add("A");//下面的申明将会失败,因为 `addAll` 方法要求 Collection<? extends String>list.addAll(new ArrayList<>());

注意,菱形通常在方法调用中也作用,但是,建议您将菱形主要用于变量申明中。
相比较,下面的例子可以编译通过:

List<? extends String> list2 = new ArrayList<>();list.addAll(list2);

泛型和非泛型类的类型推断和泛型构造函数
注意,构造函数在泛型和非泛型类中都是通用的(换句话说,声明它们自己的正式参数类型)

class MyClass<X> {  <T> MyClass(T t) {    // ...  }}

细想下面的MyClass的实例,在java SE 7和之前的版本是有效的:

new MyClass<Integer>("")

此语句创建参数化类型的MyCLass实例;显式地申明了正式类型参数X的类型为Integer,注意,这个泛型类的构造器包含一个正式的类型参数T。编译器推断这个正式类型的参数T的类型为String(因为这个构造函数的实际参数是String对象)。

使用Varargs方法使用不可维护的形式参数时,改进了编译器警告和错误

略。。。

try-with-resources 资源的自动管理

try-with-resources 声明是try 一个或多个资源的声明。一个资源作为一个对象在程序结束之后必须关闭它。try-with-resources声明保证每一个资源都会被关闭在声明结束的时候。任何实现了java.lang.AutoCloseable接口或者实现了java.io.Closeable,可以作为一个资源。
下面的例子从文件中读取第一行。用到了BufferedReader得实例去从文件中读取数据。BufferedReader是一个资源,在程序完成之后必须关闭。

static String readFirstLineFromFile(String path) throws IOException {  try (BufferedReader br = new BufferedReader(new FileReader(path))) {    return br.readLine();  }}

在这个例子中,在try-with-resources语句中声明的资源是BufferedReader。声明语句出现在try关键字后面的括号内。BufferedReaderJava SE 7及更高版本中的类实现了接口java.lang.AutoCloseable。由于BufferedReader实例是在try-with-resource语句中声明的,因此无论try语句是正常还是意外完成(由于方法BufferedReader.readLine抛出IOException),它都将被关闭。

在Java SE 7之前,无论try语句是正常还是意外完成,都可以使用finally块来确保资源已关闭。以下示例使用finally代替try-with-resources语句的块:

static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {  BufferedReader br = new BufferedReader(new FileReader(path));  try {    return br.readLine();  } finally {    if (br != null) br.close();  }}

然而,在这个例子中,如果方法readLineclose都抛出异常,方法readFirstLineFromFileWithFinallyBlock则抛出由finally块抛出的异常,由try块排除的异常将会被抑制。相反,在例子readFirstLineFromFile中,如果try块和try-with-resources声明都抛出异常,方法readFirstLineFromFile则抛出由try块抛出的异常,由try-with-resources抛出的异常将会被抑制。

您可以在try-with-resources语句中声明一个或多个资源。以下示例将检索打包在zip文件zipFileName中的文件的名称,并创建一个包含这些文件名称的文本文件:

public static void writeToFileZipFileContents(String zipFileName, String outputFileName)    throws java.io.IOException {    java.nio.charset.Charset charset = java.nio.charset.Charset.forName("US-ASCII");    java.nio.file.Path outputFilePath = java.nio.file.Paths.get(outputFileName);    // Open zip file and create output file with try-with-resources statement    try (      java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);      java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset)    ) {      // Enumerate each entry      for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) {        // Get the entry name and write it to the output file        String newLine = System.getProperty("line.separator");        String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine;        writer.write(zipEntryName, 0, zipEntryName.length());      }    }  }

在此示例中,try-with-resources语句包含两个用分号分隔的声明:ZipFile和BufferedWrite。当直接跟随它的代码块正常结束或由于异常而终止时,close这些BufferedWriter和ZipFile对象的方法将按此顺序自动调用。请注意,close资源的方法是按照与创建相反的顺序来调用的。

以下示例使用try-with-resources语句自动关闭java.sql.Statement对象:

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");        int supplierID = rs.getInt("SUP_ID");        float price = rs.getFloat("PRICE");        int sales = rs.getInt("SALES");        int total = rs.getInt("TOTAL");        System.out.println(coffeeName + ", " + supplierID + ", " + price +                           ", " + sales + ", " + total);      }    } catch (SQLException e) {      JDBCTutorialUtilities.printSQLException(e);    }  }

java.sql.Statement本示例中使用的资源是JDBC 4.1及更高版本API的一部分。
注意:try-with-resources的语句可以像普通的try语句一样拥有catch和finally。在try-with-resources语句中,在声明的资源已关闭后,将运行任何catch或finally块。

被抑制的异常
可以从与try-with-resources语句关联的代码块中抛出异常。在该示例中writeToFileZipFileContents,可以从该try块中引发异常,并且try在尝试关闭ZipFile和BufferedWriter对象时最多可以从-with-resources语句中引发两个异常。如果从try块中抛出一个异常,并且从try-with-resources语句抛出了一个或多个异常,那么从try-with-resources语句抛出的这些异常将被抑制,并且该块抛出的异常将被抛出通过该writeToFileZipFileContents方法。您可以通过Throwable.getSuppressed从该try块抛出的异常中调用方法来检索这些抑制的异常。

实现了AutoCloseable或者Closeable接口的类
Closeable接口继承了AutoCloseable接口,当AutoCloseable接口的close方法抛出异常类型为Exception的异常时,Closeableclose方法抛出异常类型为IOException的异常。因此,接口AutoCloseable接口的实现类可以重写由close方法来抛出指定的异常,比如IOException或者完全不抛出异常。

捕捉多个异常类型和对重新抛出异常的高级类型检查

处理大于一种类型的异常
在JAVA SE 7 以及以后的版本中,一个简单的catch块可以处理大于一种类型的异常。这个功能可以减少代码重复并且减少了对捕获广泛异常的诱惑。
注意下面的例子,每一个catch块都包含重复代码

catch (IOException ex) {     logger.log(ex);     throw ex;catch (SQLException ex) {     logger.log(ex);     throw ex;}

在Java SE 7以前的版本中,创建一个通用的方法来消除重复的代码是很困难的,因为变量ex有不同的类型。
以下示例在Java SE 7及更高版本中有效,可消除重复的代码:

catch (IOException|SQLException ex) {    logger.log(ex);    throw ex;}

该catch子句指定类型的块处理异常的,以及每个异常类型与竖线分割(|)。
注意:如果一个catch块处理多个异常类型,则该catch参数是隐式的final。在这个例子中,这个catch参数ex是final,所以你不能在这个catch块中赋值。
一个catch块处理多个异常编译生成的字节码要比多个catch块且每个只处理一个异常生成的字节码要小的多,并且优化。一个catch块处理多个异常的代码块通过编译器生成的字节码代码不重复,字节码没有对异常处理程序的复制。

用更包容的类型检查重新抛出的异常
Java SE 7编译器比早期版本的Java SE执行更精确的重新分配的异常分析。这使您可以在throws方法声明的子句中指定更具体的异常类型
考虑下面的例子:

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

这个例子的try块可能会抛出FirstException或者SecondException。假设你想throws在rethrowException方法声明的子句中指定这些异常类型。在Java SE 7之前的版本中,您不能这样做。因为catch子句的异常参数e是类型Exception,并且catch块重新抛出异常参数e,所以只能在方法rethrowException声明throws的类型为Exception。
但是,在Java SE 7中,你可以在方法rethrowException的throws子句中指定FirstException和SecondException的异常类型。在Java SE 7中的编译器可以判断来自try块并且由throw e申明抛出的异常,并通过抛出的异常只能是FirstException和SecondException。即使该catch子句的异常参数e是类型是 Exception,编译器也可以确定它是FirstException或者SecondException的一个实例:

public void rethrowException(String exceptionName)  throws FirstException, SecondException {    try {      // ...    }    catch (Exception e) {      throw e;    }  }