Java 7 中的新功能[翻译]
??? ?Java7?中有一些令开发者很高兴的新特性,如switch语句中的使用字符串作为表达式,多catch异常处理,try-with-resources?(自动资源管理),新的文件系统API,?jvm扩展,支持动态类型语言,支持并行任务的fork/join框架,以及其他一些肯定会被社区支持的特性。
?
?? ? ??下面就概述以上功能,并提供适当的例子。?这里可以下载一个zip文件,其中包含在这篇文章中的代码片段。
?
?
语言增强
?? ? ??Java7?通过Project?Coin?提供了一些新的语言特性,这些功能对开发者来说非常方便。
?
Diamond?Operator
?? ?你可能已经注意到在使用泛型的时候,IDE经常会出现警告提示。例如,如果我们要声明一个使用trades类作为泛型的Map类时,我们会写出像下面的代码:
Map<String, List<Trade>> trades = new TreeMap<String, List<Trade>> ();
?? ? ??我们必须在等号两边都声明同样的类型,这并不是一件很方便的事情,显然右边的声明看上去有些多余。编译器能根据左边的声明推断右边的类型吗?之前是不行的,在Java7中的代码可以写成下面的样子:
Map<String, List<Trade>> trades = new TreeMap <> ();
?? ? ??是不是很酷?你不需要输入实例类型的完整列表。相反,你使用<>符号,这就是所谓的钻石操作符,请注意,虽然没有声明的钻石操作符是合法的,?trades?=?new?TreeMap?()?),但它会使编译器生成几个类型安全警告。?
?
在switch语句中使用的字符串
?? ?之前的switch语句只能使用原始数据类型或枚举类型。Java?7中引入另一种类型,我们可以在switch语句中使用字符串String类型。?
假设我们需要根据状态处理一个交易,?到现在为止,我们使用if?-?else语句。?
private void processTrade(Trade t) {String status = t.getStatus();if (status.equalsIgnoreCase(NEW)) {newTrade(t);} else if (status.equalsIgnoreCase(EXECUTE)) {executeTrade(t);} else if (status.equalsIgnoreCase(PENDING)) {pendingTrade(t);}}?? ??这是一种很粗糙的处理方法,在Java?7中,我们可以利用增强的switch语句,使用String作为参数
public void processTrade(Trade t) {String status = t.getStatus();switch (status) {case NEW:newTrade(t);break;case EXECUTE:executeTrade(t);break;case PENDING:pendingTrade(t);break;default:break;}}?
?? ? ??在上面的程序中,状态字段总是使用String.equals()?方法和case标签进行比较。
?
?
Automatic?resource?management
??? ? ?像连接、文件、I/O流一些的资源都需要开发者编写代码手动的关闭,通常我们使用try-finally块负责关闭各自的资源。目前常用的实现如下:
public void oldTry() {try {fos = new FileOutputStream("movies.txt");dos = new DataOutputStream(fos);dos.writeUTF("Java 7 Block Buster");} catch (IOException e) {e.printStackTrace();} finally {try {fos.close();dos.close();} catch (IOException e) {// log the exception}}}?? ? ??然而,Java?7中引入了另一个很酷的功能,自动管理资源。?它操作非常简单。?我们要做的是在try中声明资源,如下:?
try(resources_to_be_cleant){// your code}?? ? ??前面的方法oldtry可以使用新功能改写成如下:
public void newTry() {try (FileOutputStream fos = new FileOutputStream("movies.txt");DataOutputStream dos = new DataOutputStream(fos)){ dos.writeUTF("Java 7 Block Buster");}catch (IOException e) { // log the exception }}?? ? ??上面的代码,也代表此功能的另一个方面:与多个资源的工作。?FileOutputStream和DataOutputStream资源都包含在try语句中一前一后,每一个资源分别由分号分隔(;)。我们不需要去手动的废除或关闭资源,它们会在控制推出try语言块后自动的在后台关闭。需要被自动关闭的资源必须实现java.lang.AutoCloseable接口。 ?
?? ? ??任何实现AutoCloseable接口的资源都会被作为自动资源管理的对象,?AutoCloseable是java.io.Closeable?接口的父类,只有close()一个方法,当程序控制推出try语言块时,该方法会被jvm调用。?
数字文字用下划线
?? ? ? ?数值文字是眼睛过滤器。如果给你一个带有十个零的数,我相信你一定会像我一样的开始计算零的个数。识别一个百万甚至千万的数是很容易出错,而且繁琐的。在Java7中可以再确定的地方引入强调符号,例如你可以像下面的方式声明一千:
int thousand = 1_000;
?或者一百万
int million = 1_000_000
?注意:此版本也引入了binary??二进制文字,开发人员再也不必将它们转换为十六进制了,例如“0B1”。
?
改进的异常处理
?? ? ?在异常处理方面也有一些改进。Java7引入了多重捕获功能(multi-catch),可以再一个catch块中捕获多种不同类型的异常。
例如,你有一个方法会抛出三个异常,按之前的处理,你需要像下面这样分别处理各个异常:
public void oldMultiCatch() {try {methodThatThrowsThreeExceptions();} catch (ExceptionOne e) {// log and deal with ExceptionOne} catch (ExceptionTwo e) {// log and deal with ExceptionTwo} catch (ExceptionThree e) {// log and deal with ExceptionThree}}?? ? ?在catch块中捕获一个又一个的异常,看起来非常杂乱。如代码所示捕获一堆异常,效率非常低下,而且容易出错。?Java?7中带来了一种新的语言变化,来解决这个丑小鸭。?来看看方法的改进版本oldMultiCatch方法如下:
public void newMultiCatch() { try { methodThatThrowsThreeExceptions(); } catch (ExceptionOne | ExceptionTwo | ExceptionThree e) { / log and deal with all Exceptions }}??? ?多个异常在一个catch块中捕获,不同的异常之间用(|)操作符分隔开。使用这种方法你就不需要写许多的捕获异常块。而且,如果你有许多属于不同类型的异常,你可以使用多个multi-catch块,?下面的代码片段说明了这一点:?
public void newMultiMultiCatch() { try { methodThatThrowsThreeExceptions(); } catch (ExceptionOne e) { // log and deal with ExceptionOne } catch (ExceptionTwo | ExceptionThree e) { // log and deal with ExceptionTwo and ExceptionThree }}?? ? ??在上述案例中,ExceptionTwo和ExceptionThree属于不同层次,所以你会想用一个catch块的不同方法来处理它们。?
?
新的文件系统?API?(NIO?2.0)
?? ?使用过Java?IO的人可能任然记得框架带来的麻烦。?跨操作系统或者多文件系统的无缝使用从来都不一件容易的事情。像删除文件、重命名一类的动作在很多时候都会引起不可意料的结果。?使用符号链接是另一个问题。?总地来说,API需要大修。
?? ?为了解决上述Java的IO问题,Java?7的推出了新的API。NIO?2.0带来了许多新的改进,也引入了新的类,方便开发人员处理多文件系统问题。
?
文件路径
?
?? ? ?新java.nio.file的包中包含Path,?Paths?,FileSystem,FileSystems和其他如类和接口。?
?? ? ?Path是一个简单的引用文件的路径。?它是等价(更多的功能)?java.io.File?。?下面的代码片断演示了如何获得“Temp”文件夹的路径引用:
public void pathInfo() {Path path = Paths.get("c:\\Temp\\temp");System.out.println("Number of Nodes:" + path.getNameCount());System.out.println("File Name:" + path.getFileName());System.out.println("File Root:" + path.getRoot());System.out.println("File Parent:" + path.getParent());}?控制台会输出如下内容:
Number of Nodes:2File Name:temp.txtFile Root:c:\File Parent:c:\Temp
?? ? ??删除一个文件或目录只需要简单的调Files类的delete方法(注意复数)。?Files类公开了两个delete方法,其中一个会抛出NoSuchFileException?另一个则不会。
?
?? ? 下面的删除方法调用抛出NoSuchFileException的,所以你必须处理它:
Files.delete(path);
?? ? ??如果文件或目录不存在,Files.deleteIfExists(path)?不会抛出异常。
?
?? ? ?您可以使用Files.copy(..)和Files.move(..)等通用方法对文件系统进行操作。?同样,使用createSymbolicLink(..)方法创建符号链接。 ?
?
文件更改通知
?? ? ?在JDK?7,我最喜欢的改善之一,是增加了文件更改通知。?这一直是期待已久的的功能,终于雕刻成NIO?2.0。WatchService?API使你会在对象(目录或文件)变化后收到事件通知。?
实现API所涉及的步骤是:
?
·?创建一个WatchService?。?这项服务包括一个持有WatchKeys的队列
·?用这个WatchService注册要监视的目录/文件
·?注册时,指定您希望收到的事件的类型(创建,修改或删除事件)
·?开始一个无限循环监听事件
·?当事件发生时,一个WatchKey被放入队列
·?消耗WatchKey?,并调用它的查询
通过一个例子。?我们创建一个DirPolice?Java程序,其责任是检测一个特定的目录。?步骤如下:?
1、创建WatchService对象:
?
WatchService watchService = FileSystems.getDefault().newWatchService();
?
?2、获取到你监视的目录的路径引用。?我建议你把这个目录的参数化,所以你不要硬编码文件名;
?
path = Paths.get("C:\\Temp\\temp\");?
?3、下一步是用WatchService为该目录注册所有类型的事件:?
?
dirToWatch.register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
?
?这是java.nio.file.StandardWatchEventKind?事件类型
4、启动无限循环,并开始接受的事件:?
?
while(true){ WatchKey key = watchService.take(); // this would return you keys …}5、通过对关键事件的运行:?
?
?
for(WatchEvent<?> event : key.pollEvents()) { Kind<?> kind = event.kind(); System.out.println("Event on " + event.context().toString() + " is " + kind);}?
?例如,如果你修改或删除这个临时文件夹,你会在控制台看到入下输出:
?
Event on temp is ENTRY_MODIFYEvent on temp is ENTRY_DELETE
?
?DirPolice相关方法源代码如下(?下载完整的源代码?):?
?
try { watchService = FileSystems.getDefault().newWatchService(); path.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);} catch (IOException e) { System.out.println("IOException"+ e.getMessage());}?
?
/*** The police will start making rounds*/private void doRounds() {WatchKey key = null;while(true) {try { key = watchService.take(); for(WatchEvent<?> event : key.pollEvents()) { Kind<?> kind = event.kind(); System.out.println("Event on " + event.context().toString() + " is " + kind); }} catch (InterruptedException e) {System.out.println("InterruptedException: "+e.getMessage());} boolean reset = key.reset(); if(!reset) break;}}?
Fork?and?Join
?? ?在java程序中有效的使用并行内核一直是一个挑战,有一些框架将任务分派到多核上执行,然后合并返回的结果集。Java7纳入这个功能,提供一个fork/join框架。
?? ?总地来说,Fork-Join?把待处理的任务逐级分解成小任务,直到所有任务都已经足够简单不需要再分解。它就像一个分治算法。这个框架中的一个重要的概念是最好没有工作线程处于空闲状态。?它实现了工作-窃取算法,空闲线程会从繁忙的工作线程处“窃取”任务。?
?
?? ? ?支持fork-join机制的核心类是ForkJoinPool和ForkJoinTask?。?ForkJoinPool基本上是一个ExecutorService的特别实现类,执行我们上面谈到的工作窃取算法。
?? ? ?我们创建一个ForkJoinPool的实例,提供目标并行级别——处理器的数量,如下所示:
?
?
ForkJoinPool pool = new ForkJoinPool(numberOfProcessors)numberOfProcessors = Runtime.getRunTime().availableProcessors();
?
?? ? ?但是,默认的ForkJoinPool实例需要设置并行级别为上面获得相同数量。
需要解决的问题写在ForkJoinTask里。?但是,有两个实现类:RecursiveAction和RecursiveTask,这两个类之间的唯一区别是,前者不返回值,而后者则返回指定类型的对象。
?
以下是如何创建RecursiveTask或RecursiveAction处理前面提到的问题(我使用?RecursiveAction类)
?
public class MyBigProblemTask extends RecursiveAction { @Override protected void compute() { . . . // your problem invocation goes here }}?
?你必须覆盖需要提供计算功能的方法。?现在,向Executor提供ForkJoinTask,通过调用ForkJoinPool的invoke方法:?
?
pool.invoke(task);
?
支持动态语言
?? ? ?Java是一种静态类型的语言?——变量,方法和返回值的类型检查是在编译时进行。?JVM在运行时执行强类型的字节码,而不必担心找到的类型信息。
?? ? ?还有另一种类型的语言品种?-?动态语言。?Ruby,Python和Clojure的都属于这一类。?类型信息在运行时才能确定。?这是不是可能在Java,因为它不会有任何必要的类型信息。
?? ? ?Java?7中,推出的一项称为invokedynamic的新功能。?这使得虚拟机的变化,纳入非Java语言的要求。已经创建了一个新的软件包,?java.lang.invoke?,?MethodHandle?,?CallSite和其他人组成的,如类,扩展动态语言的支持。
?
?
原文地址
?
?http://radar.oreilly.com/2011/09/java7-features.html?
?
?