Java 七 中的新功能[翻译]

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
        }
}

       上面的代码,也代表此功能的另一个方面:与多个资源的工作。 FileOutputStreamDataOutputStream资源都包含在try语句中一前一后,每一个资源分别由分号分隔(;)。我们不需要去手动的废除或关闭资源,它们会在控制推出try语言块后自动的在后台关闭。需要被自动关闭的资源必须实现java.lang.AutoCloseable接口。  

       任何实现AutoCloseable接口的资源都会被作为自动资源管理的对象, AutoCloseablejava.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
    }
}

       在上述案例中,ExceptionTwoExceptionThree属于不同层次,所以你会想用一个catch的不同方法来处理它们。 

 

新的文件系统 API (NIO 2.0)

    使用过Java IO的人可能任然记得框架带来的麻烦。 跨操作系统或者多文件系统的无缝使用从来都不一件容易的事情。像删除文件、重命名一类的动作在很多时候都会引起不可意料的结果。 使用符号链接是另一个问题。 总地来说,API需要大修。

    为了解决上述JavaIO问题Java 7推出了新的APINIO 2.0带来了许多新的改进,也引入了新的类,方便开发人员处理多文件系统问题。

 

文件路径

 

      新java.nio.file的包中包含Path Paths FileSystemFileSystems和其他如类和接口。 

      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:2
File Name:temp.txt
File 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.0WatchService 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_MODIFY

Event 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机制的核心类ForkJoinPoolForkJoinTask 。 ForkJoinPool基本上是一个ExecutorService的特别实现类,执行我们上面谈到的工作窃取算法

      我们创建一个ForkJoinPool的实例,提供目标并行级别——处理器的数量,如下所示:

 

 

ForkJoinPool pool = new ForkJoinPool(numberOfProcessors)

numberOfProcessors = Runtime.getRunTime().availableProcessors(); 

 

      但是,默认的ForkJoinPool实例需要设置并行级别为上面获得相同数量。

需要解决的问题写在ForkJoinTask。 但是,有两个实现类:RecursiveActionRecursiveTask,这两个类之间的唯一区别是,前者不返回值,而后者则返回指定类型的对象。

 

以下是如何创建RecursiveTaskRecursiveAction处理前面提到的问题(我使用 RecursiveAction

 

public class MyBigProblemTask extends RecursiveAction {
    @Override
    protected void compute() {
        . . . // your problem invocation goes here
    }
}

 

  你必须覆盖需要提供计算功能的方法。 现在,Executor提供ForkJoinTask,通过调用ForkJoinPoolinvoke方法: 

 

pool.invoke(task);

 

支持动态语言

      Java是一种静态类型的语言 ——变量,方法和返回值的类型检查是在编译时进行。 JVM在运行时执行强类型的字节码而不必担心找到的类型信息

      还有另一种类型的语言品种 动态语言。 RubyPythonClojure的都属于这一类。 类型信息运行时才能确定。 这是不是可能在Java,因为它不会有任何必要的类型信息。

      Java 7中,推出的一项称为invokedynamic新功能。 这使得虚拟机的变化,纳入非Java语言的要求。已经创建了一个新的软件包, java.lang.invoke , MethodHandle , CallSite和其他人组成的,如类,扩展动态语言的支持。

 

 

原文地址

 

 http://radar.oreilly.com/2011/09/java7-features.html