美文网首页
Java JDK1.7的新特性

Java JDK1.7的新特性

作者: carway | 来源:发表于2017-06-23 18:08 被阅读0次

    JDK1.7新特性:

    1.二进制面值
    2.数字变量对下滑线的支持
    3.switch对String的支持
    4.try-with-resource
    5.捕获多种异常并用改进后的类型检查来重新抛出异常
    6.创建泛型时类型推断
    7.全新的集合声明以及获取集合中的值的方式
    8.新增一些取环境信息的工具方法
    9.安全的加减乘除

    1.二进制面值

    在java7里,整形(byte,short,int,long)类型的值可以用二进制类型来表示了,在使用二进制的值时,需要在前面加上ob或oB表示二进制字面值的前缀0b。

    比如以下b1、b2、b3三个变量的值相同:

    int a = 0b01111_00000_11111_00000_10101_01010_10;     // New
    short b = (short)0b01100_00000_11111_0;               // New
    byte c = (byte)0B0000_0001;                           // New
    byte b1 = 0b00100001;     // New
    byte b2 = 0x21;          // Old
    byte b3 = 33;            // Old
    
    
    2.数字变量对下滑线的支持

    字面常量数字的下划线。用下划线连接整数提升其可读性,自身无含义,不可用在数字的起始和末尾。

    Java编码语言对给数值型的字面值加下划线有严格的规定。如上所述,你只能在数字之间用下划线。你不能用把一个数字用下划线开头,或者已下划线结尾。这里有一些其它的不能在数值型字面值上用下划线的地方:

    • 在数字的开始或结尾
    • 对浮点型数字的小数点附件
    • F或L下标的前面
    • 该数值型字面值是字符串类型的时候
    float pi1 = 3_.1415F; // 无效的; 不能在小数点之前有下划线
    float pi2 = 3._1415F; // 无效的; 不能在小数点之后有下划线
    long socialSecurityNumber1=999_99_9999_L;//无效的,不能在L下标之前加下划线
    int a1 = _52; // 这是一个下划线开头的标识符,不是个数字
    int a2 = 5_2; // 有效
    int a3 = 52_; // 无效的,不能以下划线结尾
    int a4 = 5_______2; // 有效的
    int a5 = 0_x52; // 无效,不能在0x之间有下划线
    int a6 = 0x_52; // 无效的,不能在数字开头有下划线
    int a7 = 0x5_2; // 有效的 (16进制数字)
    int a8 = 0x52_; // 无效的,不能以下划线结尾
    int a9 = 0_52; // 有效的(8进制数)
    int a10 = 05_2; // 有效的(8进制数)
    int a11 = 052_; // 无效的,不能以下划线结尾
    
    3.switch对String的支持

    之前就一直有一个打问号?为什么C#可以Java却不行呢?哈,不过还有JDK1.7以后Java也可以了
    例如:

    String status = "orderState";   
      switch (status) {  
        case "ordercancel":  
          System.out.println("订单取消");  
          break;  
        case "orderSuccess":  
          System.out.println("预订成功");  
          break;  
        default:  
          System.out.println("状态未知");  
      }
    
    

    switch 语句比较表达式中的String对象和每个case标签关联的表达式,就好像它是在使用String.equals方法一样;因此,switch语句中 String对象的比较是大小写敏感的。相比于链式的if-then-else语句,Java编译器通常会从使用String对象的switch语句中生成更高效的字节码

    4.try-with-resource

    try-with-resources语句是一个声明一个或多个资源的try语句。一个资源作为一个对象,必须在程序结束之后关闭。
    try-with-resources语句确保在语句的最后每个资源都被关闭,任何实现了Java.lang.AutoCloseable和java.io.Closeable的对象都可以使用try-with-resource来实现异常处理和关闭资源。

    下面通过对比来体会这个新特性。
    JDK1.7之前:

    /** 
     * JDK1.7之前我们必须在finally块中手动关闭资源,否则会导致资源的泄露 
     * @author Liao 
     * 
     */  
    public class PreJDK7 {  
    
        public static String readFirstLingFromFile(String path) throws IOException {  
            BufferedReader br = null;  
    
            try {  
                br = new BufferedReader(new FileReader(path));  
                return br.readLine();  
            } catch (IOException e) {  
                e.printStackTrace();  
            } finally {//必须在这里关闭资源  
                if (br != null)  
                    br.close();  
            }  
            return null;  
        }  
    }
    

    JDK1.7及以后版本

    /** 
     * JDK1.7之后就可以使用try-with-resources,不需要 
     * 我们在finally块中手动关闭资源 
     * @author Liao 
     */  
    public class AboveJDK7 {  
    
        static String readFirstLineFromFile(String path) throws IOException {  
    
            try (BufferedReader br = new BufferedReader(new FileReader(path))) {  
                return br.readLine();  
            }  
        }  
    }
    

    通过上面的对比,try-with-resources的优点

    1. ** 代码精炼** ,在JDK1.7之前都有finally块,如果使用一些扩建可能会将finally块交由框架处理,如spring。JDK及以后的版本只要资源类实现了AutoCloseable或Closeable程序在执行完try块后会自动close所使用的资源无论br.readLine()是否抛出异常,我估计针对JDK1.7像Spring这些框架也会做出一些比较大的调整。

    2. ** 代码更完全** 。在出现资源泄漏的程序中,很多情况是开发人员没有或者开发人员没有正确的关闭资源所导致的。JDK1.7之后采用try-with-resources的方式,则可以将资源关闭这种与业务实现没有很大直接关系的工作交给JVM完成,省去了部分开发中可能出现的代码风险。

    ** 异常抛出顺序**

    在JDK1.7之前如果rd.readLine()与rd.close()都抛出异常则只会抛出finally块中的异常,不会抛出rd.readLine()中的异常,这样经常会导致得到的异常信息不是调用程序想要得到的。

    在JDK1.7及以后采用了try-with-resource机制,如果在try-with-resource声明中抛出异常(如文件无法打开或无法关闭)的同时rd.readLine()也抛出异常,则只会抛出rd.readLine()的异常。

    ** try-with-resource可以声明多个资源** 。下面的例子是在一个ZIP文件中检索文件名并将检索后的文件存入一个txt文件中。

    JDK1.7及以上版本:

    public class AboveJDK7_2 {  
    
        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);  
    
            //打开zip文件,创建输出流  
            try (  
                    java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);  
    
                    java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset)  
                )   
    
                {//遍历文件写入txt  
                    for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) {  
    
                            String newLine = System.getProperty("line.separator");  
    
                            String zipEntryName = ((java.util.zip.ZipEntry) entries.nextElement()).getName() + newLine;  
                            writer.write(zipEntryName, 0, zipEntryName.length());  
                    }  
                }  
        }  
    }
    

    注:上面的例子,无论正常执行还是有异常抛出,zf和write都会被执行close()方法,不过需要注意的是在JVM里调用的顺序是与生命的顺序相反。在JVM中调用的顺讯为:
    writer.close();
    zf.close();
    所以在使用时一定要注意资源关闭的顺序。

    5.在单个catch代码块中捕获多个异常,以及用升级版的类型检查重新抛出异常

    在Java 7中,catch代码块得到了升级,用以在单个catch块中处理多个异常。如果你要捕获多个异常并且它们包含相似的代码,使用这一特性将会减少代码重复度。下面用一个例子来理解。

    Java 7之前的版本:

    catch (IOException ex) {
        logger.error(ex);
        throw new MyException(ex.getMessage());
    catch (SQLException ex) {
        logger.error(ex);
        throw new MyException(ex.getMessage());
    }catch (Exception ex) {
        logger.error(ex);
        throw new MyException(ex.getMessage());
    }
    
    

    在Java 7中,我们可以用一个catch块捕获所有这些异常:

    catch(IOException | SQLException | Exception ex){
        logger.error(ex);
        throw new MyException(ex.getMessage());
    }
    

    如果用一个catch块处理多个异常,可以用管道符(|)将它们分开,在这种情况下异常参数变量(ex)是定义为final的,所以不能被修改。这一特性将生成更少的字节码并减少代码冗余。

    另一个升级是编译器对重新抛出异常(rethrown exceptions)的处理。这一特性允许在一个方法声明的throws从句中指定更多特定的异常类型。

    与以前版本相比,Java SE 7 的编译器能够对再次抛出的异常(rethrown exception)做出更精确的分析。这使得你可以在一个方法声明的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类型的异常。设想一下,你想在rethrowException方法声明的throws从句中指定这些异常类型。在Java SE 7之前的版本,你无法做到。因为在catch子句中的异常参数e是java.lang.Exception类型的,catch子句对外抛出异常参数e,你只能在rethrowException方法声明的throws从句中指定抛出的异常类型为java.lang.Exception (或其父类java.lang.Throwable)。

    不过,在Java SE 7中,你可以在rethrowException方法声明的throws从句中指定抛出的异常类型为FirstException和SecondException。Java SE 7的编译器能够判定这个被throw语句抛出的异常参数e肯定是来自于try子句,而try子句只会抛出FirstException或SecondException类型的异常。尽管catch子句的异常参数e是java.lang.Exception类型,但是编译器可以判断出它是FirstException或SecondException类型的一个实例:

    public class Java7MultipleExceptions {
    
        public static void main(String[] args) {
            try{
                rethrow("abc");
            }catch(FirstException | SecondException | ThirdException e){
                //以下赋值将会在编译期抛出异常,因为e是final型的
                //e = new Exception();
                System.out.println(e.getMessage());
            }
        }
    
        static void rethrow(String s) throws FirstException, SecondException, ThirdException {
            try {
                if (s.equals("First"))
                    throw new FirstException("First");
                else if (s.equals("Second"))
                    throw new SecondException("Second");
                else
                    throw new ThirdException("Third");
            } catch (Exception e) {
                //下面的赋值没有启用重新抛出异常的类型检查功能,这是Java 7的新特性
                // e=new ThirdException();
                throw e;
            }
        }
    
        static class FirstException extends Exception {
    
            public FirstException(String msg) {
                super(msg);
            }
        }
    
        static class SecondException extends Exception {
    
            public SecondException(String msg) {
                super(msg);
            }
        }
    
        static class ThirdException extends Exception {
    
            public ThirdException(String msg) {
                super(msg);
            }
        }
    
    }
    
    

    不过,如果catch捕获的异常变量在catch子句中被重新赋值,那么异常类型检查的分析将不会启用,因此在这种情况下,你不得不在方法声明的throws从句中指定异常类型为java.lang.Exception。

    更具体地说,从Java SE 7开始,当你在单个catch子句中声明一种或多种类型的异常,并且重新抛出这些被捕获的异常时,需符合下列条件,编译器才会对再次抛出的异常进行类型验证:

    • try子句会抛出该异常。
    • 在此之前,没有其他的catch子句捕获该异常。
    • 该异常类型是catch子句捕获的多个异常中的一个异常类型的父类或子类。
    6.创建泛型时类型推断
        只要编译器可以从上下文中推断出类型参数,你就可以用一对空着的尖括号<>来代替泛型参数。这对括号私下被称为菱形(diamond)。 在Java SE 7之前,你声明泛型对象时要这样
        List<String> list = new ArrayList<String>(); 
        而在Java SE7以后,你可以这样 
        List<String> list = new ArrayList<>(); 
        因为编译器可以从前面(List)推断出推断出类型参数,所以后面的ArrayList之后可以不用写泛型参数了,只用一对空着的尖括号就行。当然,你必须带着”菱形”<>,否则会有警告的。 
        Java SE7 只支持有限的类型推断:只有构造器的参数化类型在上下文中被显著的声明了,你才可以使用类型推断,否则不行。 
        List<String> list = new ArrayList<>(); 
        list.add("A"); 
        //这个不行 
        list.addAll(new ArrayList<>()); 
        // 这个可以 
        List<? extends String> list2 = new ArrayList<>(); 
        list.addAll(list2);
    
    7.全新的集合声明以及获取集合中的值的方式
        JDK1.7以前声明集合的方式
        List<String> strs  = new ArrayList<String>();
        Map<String,String> map = new HashMap<String,String>();
        strs.add("abc");
        strs.get(index);
        map.put("key","全新集合");
        map.get(key);
    
        JDK1.7以后
        List<String> list = [item1,item2,item3];
        String item1 = list[0];
        String item2 = list[1];
        String item3 = list[2];
        Map<String,String> map = {key:value,key:value}
        String mapValue = map[key];
        这样的定义是不是非常方便?肯定的!(有点象JSON)呵呵
    
    8.新增一些取环境信息的工具方法
        例如:
        File System.getUserHomeDir() // 当前用户目录
        File System.getUserDir() // 启动java进程时所在的目录
        File System.getJavaIoTempDir() // IO临时文件夹
        File System.getJavaHomeDir() // JRE的安装目录
    
    9.安全的加减乘除
        例如:
        int Math.safeToInt(long value)
        int Math.safeNegate(int value)
        long Math.safeSubtract(long value1, int value2)
        long Math.safeSubtract(long value1, long value2)
        int Math.safeMultiply(int value1, int value2)
        long Math.safeMultiply(long value1, int value2)
        long Math.safeMultiply(long value1, long value2)
        long Math.safeNegate(long value)
        int Math.safeAdd(int value1, int value2)
        long Math.safeAdd(long value1, int value2)
        long Math.safeAdd(long value1, long value2)
        int Math.safeSubtract(int value1, int value2)
    
    参考文章:

    http://www.cnblogs.com/tony-yang-flutter/p/3503935.html
    http://www.jianshu.com/p/5ae0cb34622e

    相关文章

      网友评论

          本文标题:Java JDK1.7的新特性

          本文链接:https://www.haomeiwen.com/subject/qagccxtx.html