Java 7 新特性 -- 实战篇

标签: java | 发表时间:2011-08-09 21:32 | 作者:(author unknown) wangjia
出处:http://www.iteye.com
   “举世瞩目”的java 7近日发布,oracle网站上列出了java 语言的新特性 http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html,最近出差,晚上闲来无事,将这些java语言的新特性试了下,very cool。
    下面介绍了java 7的一些新特性,翻译自oracle网站,结合自己的实战。对Java 7研究不深,以下内容有对有错,欢迎大家批评指正,共同学习!

    环境: ubuntu 11.04+eclipse 3.8
    本来是用netbeans7.0.1,可是非常难用(不习惯 netBeans),加之用了几天之后,eclipse3.8推出,也支持java7,就切换到了更熟悉的eclipse下。顺便说一句,ubuntu11.04界面做的挺不错的,非常喜欢(附图是win 7下的无缝模式)。

    java 7下载: http://www.oracle.com/technetwork/java/javase/downloads/index.html
    eclipse 3.8下载: http://download.eclipse.org/eclipse/downloads/drops/S-3.8M1-201108031800/
    Netbeans 7.0.1 下载:http://netbeans.org/downloads/start.html?platform=linux&lang=zh_CN&option=javaee


    特性1:二进制字面值(Binary Literals)

    在java7里,整形(byte,short,int,long)类型的值可以用二进制类型来表示了,在使用二进制的值时,需要在前面加上ob或oB,看代码
   
        //b 大小写都可以
	int a = 0b01111_00000_11111_00000_10101_01010_10;
	short b = (short)0b01100_00000_11111_0;
	byte c = (byte)0B0000_0001;
    


    其次,二进制同十进制和十六进制相比,可以一目了然的看出数据间的关系。例如下面这个数组中展示了每次移动一位后数字的变化。
   
  public static final int[] phases = {
     0b00110001,
     0b01100010,
     0b11000100,
     0b10001001,
     0b00010011,
     0b00100110,
     0b01001100,
     0b10011000
  }

如果用十六进制来表示的,它们之间的关系就无法一眼看出来了。
   public static final int[] phases = {
    0x31, 0x62, 0xC4, 0x89, 0x13, 0x26, 0x4C, 0x98
   }


    特性2:数字变量对下划线_的支持

    你可以在数值类型的变量里添加下滑线,除了以下的几个地方不能添加:
      数字的开头和结尾
      小数点前后
      F或者L前
      需要出现string类型值的地方(针对用0x或0b表示十六进制和二进制,参考第一点),比如0x101,不能用0_x101
   
      int num = 1234_5678_9;
      float num2 = 222_33F;
      long num3 = 123_000_111L;
      
      //下面的不行
      //数字开头和结尾
      int nu = _123;
      int nu = 123_;
      //小数点前后
      float f = 123_.12;
      float f = 123._12;
      //F或者L前
      long l = 123_L;
      float f = 123_F;
      //需要出现String的地方
      int num = 0_b123;
      float f = 0_x123F;
    


    这个,我个人觉得没什么实际作用,只是可以提升代码的可读性。

    特性3:switch 对String的支持

    这个大家期待很久了,switch终于支持String了
   
    public static void first() {
        //项目状态
        String status = "approval";
        //我们之前经常根据项目状态不同来进行不同的操作
        //目前已经换成enum类型
        
        switch (status) {
            case "shouli":
                System.out.println("状态是受理");
                break;
            case "approval":
                System.out.println("状态是审批");
                break;
            case "finish":
                System.out.println("状态是结束");
                break;
            default:
                System.out.println("状态未知");
        }
    }


    每个case是使用String的equals方法来进行比较的,对大小写敏感。

    和一连串的if-else-then想比,使用switch来计较String,Java编译器会生成更加有效的字节码,写一个例子测试一下。

  public static void second() {
        String status = "approval";
        if ("shouli".equals(status)) {
            System.out.println("状态是受理");
        } else if ("approval".equals(status)) {
            System.out.println("状态是审批");
        } else if ("finish".equals(status)) {
            System.out.println("状态是结束");
        } else {
            System.out.println("状态未知");
        }
  }

使用javap之后,生成字节码如下:
switch
public static void first();
    Code:
       0: ldc           #2                  // String approval
       2: astore_0      
       3: aload_0       
       4: astore_1      
       5: iconst_m1     
       6: istore_2      
       7: aload_1       
       8: invokevirtual #3                  // Method java/lang/String.hashCode:()I
      11: lookupswitch  { // 3
           -1274442605: 72
            -903146056: 44
            1185244739: 58
               default: 83
          }
      44: aload_1       
      45: ldc           #4                  // String shouli
      47: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      50: ifeq          83
      53: iconst_0      
      54: istore_2      
      55: goto          83
      58: aload_1       
      59: ldc           #2                  // String approval
      61: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      64: ifeq          83
      67: iconst_1      
      68: istore_2      
      69: goto          83
      72: aload_1       
      73: ldc           #6                  // String finish
      75: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      78: ifeq          83
      81: iconst_2      
      82: istore_2      
      83: iload_2       
      84: tableswitch   { // 0 to 2
                     0: 112
                     1: 123
                     2: 134
               default: 145
          }
     112: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
     115: ldc           #8                  
     117: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     120: goto          153
     123: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
     126: ldc           #10                 
     128: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     131: goto          153
     134: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
     137: ldc           #11                 
     139: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     142: goto          153
     145: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
     148: ldc           #12                
     150: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
     153: return  


   用if-else串生成的字节码
  
       public static void second();
    Code:
       0: ldc           #2                  // String approval
       2: astore_0      
       3: ldc           #4                  // String shouli
       5: aload_0       
       6: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
       9: ifeq          23
      12: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      15: ldc           #8                  
      17: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      20: goto          71
      23: ldc           #2                  // String approval
      25: aload_0       
      26: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      29: ifeq          43
      32: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      35: ldc           #10                 
      37: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      40: goto          71
      43: ldc           #6                  // String finish
      45: aload_0       
      46: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
      49: ifeq          63
      52: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      55: ldc   
      57: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      60: goto          71
      63: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      66: ldc           #12                 
      68: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      71: return 
   

   网上说,用switch之后比用if-else生成的字节码更有效一些,不过目前至少从长度上来说,switch还是长一些
  

   特性4:try-with-resources 声明

   try-with-resources 是一个定义了一个或多个资源的try 声明,这个资源是指程序处理完它之后需要关闭它的对象。try-with-resources 确保每一个资源在处理完成后都会被关闭。
可以使用try-with-resources的资源有:
任何实现了java.lang.AutoCloseable 接口和java.io.Closeable 接口的对象。

来看例子:
  public static String readFirstLineFromFile(String path) throws IOException {
     try (BufferedReader br = new BufferedReader(new FileReader(path))) {
       return br.readLine();
     }
}

   在java 7 以及以后的版本里,BufferedReader实现了java.lang.AutoCloseable接口。
  
        try (Closeable obj = new Closeable() {
	       @Override
	       public void close() throws IOException {
		  // do something
	        }
	}) {
	// do something
	}

	try (AutoCloseable obj = new AutoCloseable() {
	        @Override
		public void close() throws IOException {
		   // do something
		}
	}) {
   	   // do something
	} 
   

  
   由于BufferedReader定义在try-with-resources 声明里,无论try语句正常还是异常的结束,它都会自动的关掉。而在java7以前,你需要使用finally块来关掉这个对象。
 
  public static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {
     BufferedReader br = new BufferedReader(new FileReader(path));
     try {
        return br.readLine();
     } finally {
        if (br != null) br.close();
     }
   }
 


   然而,如果 readLine() 和 close() 这两个方法都抛出异常,那么readFirstLineFromFileWithFinallyBlock 方法只会抛出后面部分也就是finally块中的内容,try块中的异常就被抑制了,对于我们的程序来说,这显然不是一种好的方式。
   而在java 7中,无论是try块还是try-with-resource中抛出异常,readFirstLineFromFile会捕捉到try块的异常,try-with-resources中中异常被禁止了。在java 7 中,你能捕捉到被禁止的异常。后面会介绍。
 
   另外,一个try-with-resourcse声明了可以包含多个对象的声明,用分号隔开,和声明一个对象相同,会在结束后自动调用close方法,调用顺序和生命顺序相反。
  
    try (
      java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);
      java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
    ) {
      // do something
    }
   

 
  此外,try-with-resources 可以跟catch和finally,catch和finally的是在try-with-resources里声明的对象关闭之后才执行的。


   特性5:捕获多种异常并用改进后的类型检查来重新抛出异常
  
   1、捕获多种异常

   在Java SE7里,一个catch可以捕获多个异常,这样可以减少重复代码。每个异常之间用 | 隔开。
  
     public static void first(){
         try {
	    BufferedReader reader = new BufferedReader(new FileReader(""));
	    Connection con = null;
	    Statement stmt = con.createStatement();
         } catch (IOException | SQLException e) {
	    //捕获多个异常,e就是final类型的
	    e.printStackTrace();
         }
     }
   

   而在Java SE6以前,需要这样写
  
       public static void second() {
		try {
			BufferedReader reader = new BufferedReader(new FileReader(""));
			Connection con = null;
			Statement stmt = con.createStatement();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
   

   注意,如果一个catch处理了多个异常,那么这个catch的参数默认就是final的,你不能在catch块里修改它的值。
   另外,用一个catch处理多个异常,比用多个catch每个处理一个异常生成的字节码要更小更高效。

   使用一个catch生成的字节码
  
     public static void first();
    Code:
       0: new           #2                  // class java/io/BufferedReader
       3: dup           
       4: new           #3                  // class java/io/FileReader
       7: dup           
       8: ldc           #4                  // String 
      10: invokespecial #5                  // Method java/io/FileReader."<init>":(Ljava/lang/String;)V
      13: invokespecial #6                  // Method java/io/BufferedReader."<init>":(Ljava/io/Reader;)V
      16: astore_0      
      17: aconst_null   
      18: astore_1      
      19: aload_1       
      20: invokeinterface #7,  1            // InterfaceMethod java/sql/Connection.createStatement:()Ljava/sql/Statement;
      25: astore_2      
      26: goto          34
      29: astore_0      
      30: aload_0       
      31: invokevirtual #10                 // Method java/lang/Exception.printStackTrace:()V
      34: return        
    Exception table:
       from    to  target type
           0    26    29   Class java/io/IOException
           0    26    29   Class java/sql/SQLException
   


   使用两个catch生成的字节码
  
     public static void second();
    Code:
       0: new           #2                  // class java/io/BufferedReader
       3: dup           
       4: new           #3                  // class java/io/FileReader
       7: dup           
       8: ldc           #4                  // String 
      10: invokespecial #5                  // Method java/io/FileReader."<init>":(Ljava/lang/String;)V
      13: invokespecial #6                  // Method java/io/BufferedReader."<init>":(Ljava/io/Reader;)V
      16: astore_0      
      17: aconst_null   
      18: astore_1      
      19: aload_1       
      20: invokeinterface #7,  1            // InterfaceMethod java/sql/Connection.createStatement:()Ljava/sql/Statement;
      25: astore_2      
      26: goto          42
      29: astore_0      
      30: aload_0       
      31: invokevirtual #11                 // Method java/io/IOException.printStackTrace:()V
      34: goto          42
      37: astore_0      
      38: aload_0       
      39: invokevirtual #12                 // Method java/sql/SQLException.printStackTrace:()V
      42: return        
    Exception table:
       from    to  target type
           0    26    29   Class java/io/IOException
           0    26    37   Class java/sql/SQLException
   

  
   switch那里生成的字节码不太明显看出来优化在哪里,这个很明显。首先,字节码长度变少 其次,从最后可以看出,target type都指向29行,两个catch会指向不同的行~

   2、用更包容性的类型检查来重新抛出异常

   在方法的声明上,使用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块中只能抛出两种异常,但是因为catch里的类型是 Exception,在java SE7以前的版本中,在方法声明中throws 只能写Exception,但是在java SE7及以后的版本中,可以在throws后面写 FirstException和SecondException——编译器能判断出throw e语句抛出的异常一定来自try块,并且try块只能抛出FirstException和SecondException。
  
     public static void reThrowException(String exceptionName)
			throws FirstException, SecondException{
	try {
	    if ("first".equals(exceptionName))
		throw new FirstException();
	    else
		throw new SecondException();
	    } catch (Exception e) {
		throw e;
	    }
        }
   

  
    所以尽管catch里的异常类型是Exception,编译器仍然能够知道它是FirstException和 SecondException的实例。怎么样,编译器变得更智能了吧。

    但是,如果在catch里对异常重新赋值了,在方法的throws后无法再向上面那样写成FirstException和SecondException了,而需要写成 Exception。

    具体来说,在Java SE 7及以后版本中,当你在catch语句里声明了一个或多个异常类型,并且在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);
     


     注意:菱形<>主要用在变量声明里。
 
     类里也可以使用类型推断
    


    准备返京,未完待续~


    Java 7 从呼之欲出到千呼万唤使出来,经历了好几年,期间sun也易主,真是不容易。
   
    不过想要马上大规模的使用还不现实,Java 6从推出到大规模的使用花了两三年的时间吧(这里我不太清楚)甚至更久,到现在还有好多大公司使用 java5或4呢,所以这也是我为什么把java 7装到虚拟机上的缘故,毕竟商业的东西,还是以稳定为主。
    以上介绍了些皮毛,未来还要靠大家自己多多努力,争取两年后,java 7正式商用了,大家也能对java 7掌握的更熟练写,更好的应用,提高自己的工作效率。
   
        
    
   



作者: kaka2008 
声明: 本文系ITeye网站发布的原创文章,未经作者书面许可,严禁任何网站转载本文,否则必将追究法律责任!

已有 48 人发表回复,猛击->>这里<<-参与讨论


ITeye推荐



相关 [java] 推荐:

Java中的锁(Locks in Java)

- - 并发编程网 - ifeve.com
原文链接 作者:Jakob Jenkov 译者:申章 校对:丁一. 锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂. 因为锁(以及其它更高级的线程同步机制)是由synchronized同步块的方式实现的,所以我们还不能完全摆脱synchronized关键字( 译者注:这说的是Java 5之前的情况).

Java PaaS 对决

- 呆瓜 - IBM developerWorks 中国 : 文档库
本文为 Java 开发人员比较了三种主要的 Platform as a Service (PaaS) 产品:Google App Engine for Java、Amazon Elastic Beanstalk 和 CloudBees RUN@Cloud. 它分析了每种服务独特的技术方法、优点以及缺点,而且还讨论了常见的解决方法.

Java浮点数

- d0ngd0ng - 译言-电脑/网络/数码科技
Thomas Wang, 2000年3月. Java浮点数的定义大体上遵守了二进制浮点运算标准(即IEEE 754标准). IEEE 754标准提供了浮点数无穷,负无穷,负零和非数字(Not a number,简称NaN)的定义. 在Java开发方面,这些东西经常被多数程序员混淆. 在本文中,我们将讨论计算这些特殊的浮点数相关的结果.

Qt——转战Java?

- - 博客 - 伯乐在线
编者按:事实上,在跨平台开发方面,Qt仍是最好的工具之一,无可厚非,但Qt目前没有得到任何主流移动操作系统的正式支持. 诺基亚的未来计划,定位非常模糊,这也是令很多第三方开发者感到失望,因此将导致诺基亚屡遭失败的原因. Qt的主要开发者之一Mirko Boehm在博客上强烈讽刺Nokia裁了Qt部门的决定,称其为“绝望之举”,而非“策略变更”.

java 验证码

- - ITeye博客
// 创建字体,字体的大小应该根据图片的高度来定. // 随机产生160条干扰线,使图象中的认证码不易被其它程序探测到. // randomCode用于保存随机产生的验证码,以便用户登录后进行验证. // 随机产生codeCount数字的验证码. // 得到随机产生的验证码数字. // 产生随机的颜色分量来构造颜色值,这样输出的每位数字的颜色值都将不同.

Java异常

- - CSDN博客推荐文章
“好的程序设计语言能够帮助程序员写出好程序,但是无论哪种语言都避免不了程序员写出坏的程序.                                                                                                                          ----《Java编程思想》.

java面试题

- - Java - 编程语言 - ITeye博客
 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面. 抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节. 抽象包括两个方面,一是过程抽象,二是数据抽象.  继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法. 对象的一个新类可以从现有的类中派生,这个过程称为类继承.

Java使用memcached

- - 互联网 - ITeye博客
首先到 http://danga.com/memcached下载memcached的windows版本和java客户端jar包,目前最新版本是memcached-1.2.1-win32.zip和java_memcached-release_1.6.zip,分别解压后即可. 然后是安装运行memcached服务器,我们将memcached-1.2.1-win32.zip解压后,进入其目录,然后运行如下命令:c:>;memcached.exe -d install
c:>memcached.exe -l 127.0.0.1 -m 32 -d start.

Java线程池

- - 企业架构 - ITeye博客
线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的. 在jdk1.5之后这一情况有了很大的改观. Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用. 为我们在开发中处理线程的问题提供了非常大的帮助.

java 缩略图

- - 开源软件 - ITeye博客
文章说明:根据用户上传的图片按等比例生成相应的的缩略图,两小例笔记. 第一、java-image-scaling 开源小工具生成图片缩略图. Maven地址: http://mvnrepository.com/artifact/com.mortennobel/java-image-scaling/.