至今,毫无疑问你已经看过不止一篇网上文章列举了Java5.0的伟大的语言新特性:泛型,标注,枚举类型,自动装箱,可变参数, for/in循环,甚至静态引入。我也认为这些是伟大的特性,但是,你已经读过他们了。因此,在此我将集中于你可能没有听说过的Java5.0的新API特性。
那么,在下面,是我喜欢的Java5.0的五个新API特性。那些看完本文后的细心的读者会发现额外的奖励?第六个特性:很少有人知道的Java5.0支持的新语言语法,当然使用者就更少了。我非常喜欢它,因为它很新异。
Callable 和 Future 接口
我喜欢的第一个特性发掘自新的java.util.concurrent包。如它的名字暗示的,这是个并行编程工具包。在此有很多要探索的,而我要提的第一喜欢的特性是TimeUnit枚举类型。TimeUnit让我感兴趣的是它包含有用的时间相关工具--你通过一个枚举常量来调用它们,该常量代表度量时间的单位。例如:
TimeUnit.MILLISECONDS.sleep(200);
然而,TimeUnit并不是最值得夸奖的。java.util.concurrent最强大的特性之一是它的任务-执行/线程-池结构。ExecutorService接口提供了执行任务的能力。Executors类定义了工厂方法用于获取使用线程池的ExecutorService的实现。这是强大的要素。
我所喜欢的任务-执行框架的部分是它如何表现任务以及执行它的结果:Callable和Future接口。我们都熟悉用于定义线程的Runnable接口和它的run()方法。Callable像Runnable,但它的方法叫做call(),并且这个方法可以返回一个结果或者抛出一个异常,而这两点是Runnable.run()做不到的。
Callable是一个泛型,并且它的结果已经参数化。例如,一个计算BigInteger的任务,是Callable<BigInteger>,并且它的方法call()被声明为返回BigInteger。下面是仅有三行代码的Callable接口:
public interface Callable<V> { V call() throws Exception; }
当我想要异步执行一个Callable任务,我将它传递给ExecutorService的submit()方法。submit()的返回值?这也是我喜欢的部分?是一个Future对象:本质上是一个对将来某时刻的结果的“借条”。如果我准备使用我的任务的结果,我简单的调用Future对象的get()方法即可。如果任务的执行已完成,那么get()立刻返回结果。否则,它将阻塞直到结果可用。如果Callable抛出异常,那么get()方法将该异常包装为ExecutionException并且抛出它。Future还有方法用来对任务的执行进行取消和查询状态,但是你必须自己查找它们(这些方法)。Future也用了泛型,并且结果的类型也参数化了。因此如果我submit()一个Callable<BigInteger>来执行,我将获得一个Future< BigInteger >。
下面是一个简短的例子:
/** * 这是一个用来计算大素数的Callable。 */ public class PrimeSearch implements Callable<BigInteger> { static Random prng = new SecureRandom(); int n; public PrimeSearch(int bitsize) { n = bitsize; } public BigInteger call() { return BigInteger.probablePrime(n, prng); } } // 尝试同时计算两个素数 ExecutorService pool = Executors.newFixedThreadPool(2); Future<BigInteger> p = pool.submit(new PrimeSearch(512)); Future<BigInteger> q = pool.submit(new PrimeSearch(512)); // 将两个素数相乘来得到一个合数 BigInteger product = p.get().multiply(q.get());
可变参数和自动装箱
我说过我不想谈论Java5.0的新语言特性,我不会,但是我确实关注由于可变参数和自动装箱才变为可能的(或者被增强的旧API)新的API。
首先,当然,是Java5.0的printf风格的文本格式化能力,通过java.util.Formatter类和类似String.format()的工具方法。这类文本格式化是最常被引用来支持语言的增加的可变参数和自动装箱的那种用例。考虑这个:
String s = String.format("%s:%d: %s%n", filename,
lineNumber,
exception.getMessage());
关于这段代码没有什么特别值得注意的东西。我将它列在这是为了说明因为可变参数和自动装箱所以比下面的例子显得简单:
String s = String.format("%s:%d: %s%n", new Object[] { filename, new Integer(lineNumber), exception.getMessage()});
可变参数和自动装箱还对java.lang.reflect API有一个实质性的影响。那就是当查找和调用方法时不再需要类和对象数组:
Method m = c.getMethod("put", Object.class,Object.class); m.invoke(map, "key", "value");
如果我必须选择一个最喜欢的可变参数方法,那么,将是java.util.Arrays.asList()。这个方法真是个用于创建不变的对象列表的方便的工厂方法。它接受任何数量的类型T的参数并且将它们作为List返回:
List<Integer> smallPrimes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 1Array);
能力
我们在上面谈论了Runnable和Callable,并且你毫无疑问已经听说过重要的Comparable, Serializable,和Cloneable接口。Java5.0新增加了五个重要的能力接口。第一个,当然,是java.lang.Iterable。你或许知道Java5.0新的for/in循环可以迭代数组和集合。你可能不知道它能够对任何实现了可迭代(Iterable)接口的对象工作。因此,如果你想让一个不是集合的数据结构可以简单地迭代,只需实现Iterable接口。你要做的就是增加一个返回java.util.Iterator 的iterator()方法。当然,写这个迭代器(Iterator)可能不是那么简单的。
下面的代码是一个实现了Iterable<String>(是的,Iterable是泛型的)的文本文件类,因而允许文本文件可以用for/in循环逐行的迭代。你可以用类似下面的代码使用它:
TextFile textfile = new TextFile(new File(f), "UTF-8"); int lineNumber = 0; for(String line : textfile) System.out.printf("%6d: %s%n", ++lineNumber, line);
下面是TextFile的代码。注意,迭代器不尝试检测对底层文件的并发的修改。如果你想自己做,看一看
java.nio.channels.FileLock。 import java.io.*; import java.util.Iterator; public class TextFile implements Iterable<String> { File f; String charsetName; public TextFile(File f, String charsetName) throws IOException { this.f = f; this.charsetName = charsetName; if (!f.exists()) throw new FileNotFoundException(f.getPath()); if (!f.canRead()) throw new IOException("Can’t read: " + f.getPath()); } public Iterator<String> iterator() { try { return new TextFileIterator(f, charsetName); } catch(IOException e) { throw new IllegalArgumentException(e); } } static class TextFileIterator implements Iterator<String> { BufferedReader in; String nextline; boolean closed = false; public TextFileIterator(File f, String charsetName) throws IOException { InputStream fis = new FileInputStream(f); Reader isr = new InputStreamReader(fis, charsetName); in = new BufferedReader(isr); getNextLine(); } public boolean hasNext() { return nextline != null; } public String next() { String returnValue = nextline; getNextLine(); return returnValue; } public void remove() { throw new UnsupportedOperationException(); } void getNextLine() { if (!closed) { try { nextline = in.readLine(); } catch(IOException e) { throw new IllegalArgumentException(e); } if (nextline == null) { try { in.close(); } catch(IOException ignored) {} closed = true; } } } } }
Iterable是到目前为止最重要的新能力接口,但是其它的也是非常的漂亮。接下来,我们碰到java.lang.Appendable。一个Appendable对象可以追加字符或字符序列(或者一个字符序列的子序列)。实现者包括StringBuffer和StringBuilder(如果你还没有听说过它,一定要看一看),Writer(及其子类),PrintStream,还有java.nio.CharBuffer。将可追加性从这些类中分离出来成为Appendable接口,使得新的java.util.Formatter类更强大:它能将文本格式化为任何可追加的对象,包括你自己的实现。(练习留给读者:你能否将上面的TextFile类变得既可迭代又可追加么?)。
java.lang.Readable接口和Appendable相反:一个可读对象可以将字符传输给给定的CharBuffer。java.io.Reader和它的全部子类都是可读的(当然了),CharBuffer本身也一样。就像Appendable是为了java.util.Formatter的利益而创造,Readable是为了java.util.Scanner的利益而创造。(Java5.0增加了Scanner,连同Formatter。这是Java对C的scanf()函数的适应,但是它(Scanner)不像Formatter之对应于printf()的关系那样密切。)
我想讨论的最后两个能力接口是java.io.Closeable和java.io.Flushable。如它们的名字暗示的,它们趋向于被任何类实现,通过一个close()或者flush()方法。Closeable被所有的输入和输出流类,RandomAccessFile和Formatter实现。Flushable被输出流类和Formatter实现。这些接口也是为了Formatter类的利益而定义。注意,Appendable对象(像StringBuilder)不总是可关闭或者可冲刷(flushable)。通过将可关闭性和可冲刷性分解出来成为这些接口,Formatter的close()和flush()方法能够决定它们操作的Appendable对象是否需要被关闭或被冲刷。(Java5.0还增加了第六个能力接口,并且它也是有关Formatter类的。那些想要控制它们的实例怎样被格式化的类可以实现java.util.Formattable接口。然而这个接口的API是难用的,我不想谈论它。)
@Override
毫无疑问,你已经听说过能用元数据标注Java5.0的类型和方法。但是你可能不熟悉增加到java.lang的标准标注类型。我喜欢的第四个特性就是java.lang.Override标注。当你写一个方法准备覆盖另一个的方法时,用@Override来标注它,这样编译器会进行检查来确保你确实,实际上,覆盖了你想覆盖的方法。
如果你拼写错了方法名字或者弄错了方法参数,那么你实际上并没有覆盖那个你认为你覆盖了的方法。这样就造成了一个如果不用@Override很难捕捉的臭虫。我所以知道是因为我的关于Java1.4的新API特性的文章就讲到了这个臭虫,并且这个错误至少有一年一直没被检测到(至少没有被报告)。在那篇文章中,你可以在第一页结尾看到我犯的错误。那篇文章现在包含一个链接到我的博客入口,在那里我改正了这个臭虫并且在代码中增加了@Override声明。
MatchResult
我喜欢的Java5.0的最后一个特性是java.util.regex.MatchResult。对于用于正则表达式的模式/匹配API我从来没有真正非常满意。Java5.0增加的MatchResult在让我大大地更加满意。当使用一个不太平凡的模式(Pattern),每次调用匹配者(Matcher)的find()方法会生成许多状态:开始位置,结束位置,匹配的文本,同时还有模式的开始,结束,每个子表达式的文本。在Java5.0以前,你只能从Matcher获取它们,通过在调用find()后再调用start(),end(),还有group(),如果需要的话。
然而,到了Java5.0,你可以只调用toMatchResult()来获取MatchResult对象再获取全部的状态,MatchResult对象可以保存并且可以以后再检查。MatchResult像Matcher一样有start(),end(),以及group()方法,并且,实际上,Matcher现在实现了MatchResult。
这里是一个有用的返回MatchResult的方法:
public static List<MatchResult> findAll(Pattern pattern, CharSequence text) { List<MatchResult> results = new ArrayList<MatchResult>(); Matcher m = pattern.matcher(text); while(m.find()) results.add(m.toMatchResult()); return results; }
还有使用这个方法的代码:
List<MatchResult> results = findAll(pattern, text); for(MatchResult r : results) { System.out.printf("Found ’%s’ at (%d,%d)%n", r.group(), r.start(), r.end()); }
十六进制浮点数字面值
我承诺谈论Java5.0的最晦涩的新语言特性。这就是:十六进制格式的浮点常量!这里是奇异的详情:一个十六进制符号的浮点常量以0X或者0x开头。随后的十六进制数字形成了数的基数。关键是这些数字可以包含一个小数点(一个十六进制小数点?)。在基数后面是指数,是必需的。十六进制浮点常量使用p或者P而不是e或者E来引入指数。(想一下“幂”来帮助记忆)。P或者P后面是指数,必须是一个十进制数,而不是十六进制数。而且这是个以二为根的指数,而不是以十为根。那就是,表示基数要乘以的2的幂。最后,整个常量可以跟随一个f或者F来表示一个浮点常量,或者一个d或者D表示一个双精度常量,就像一个十进制浮点数一样。
下面是一些例子:
double x = 0XaP0; // 10 * 2^0 = 10.0 double y = 0XfP2D; // 15 * 2^2 = 60.0 float z = 0Xf.aP1F; // (15 + 10/16ths) * 2^1 = 31.25f // 用十进制来打印 System.out.printf("%f %f %f%n", x, y, z); // 用十六进制来打印 System.out.printf("%a %a %a%n", x, y, z);
为什么Sun要对语言做这些?5.0的发行说明说:
为了允许特定浮点值实现精确及可预见的规范,十六进制符号可用于Float和Double的浮点字面值和字符串到浮点数的转换方法中。
这点是合理的。十进制小数像0.1是不能精确地用浮点格式表示的,并且如果你真的需要确切知道在一个浮点或者双精度值中比特位是怎么设的,那么你真的想要一个十六进制字面值。例如,Float.MAX_VALUE的Javadoc指出最大的浮点值是0x1.fffffeP+127f。
如果你知道并且喜欢IEEE-754浮点标准,那么十六进制浮点字段值或许是你喜欢的一个特性。我只是认为他们有趣。
相关推荐
6. 另外,书中可能还涵盖了其他Java 5.0的重要特性,如注解(Annotations),它为元数据提供支持,可以用于编译时和运行时的处理;类型安全的枚举(Enums),使得枚举类型更加安全和强大;以及并发处理的改进,如...
这个"Java5.0 Tiger全书代码.rar"压缩包包含的是《Java5.0 Tiger程序员高手秘笈》这本书的配套源码,这是一本深受Java开发者喜爱的进阶指南,旨在帮助读者深入理解Java 5.0的新特性和最佳实践。 首先,Java 5.0的...
Java JDK 5.0是Java发展历程中的一个重要里程碑,它引入了一系列新特性,极大地提升了开发效率和代码质量。这里,我们将详细探讨这些关键知识点。 首先,我们来看类型安全的泛型。在Java 5.0之前,集合框架并不支持...
另外,枚举(Enums)是JDK 5.0中的另一个重要特性。枚举类型允许创建一组预定义的常量,例如颜色的红、黄、蓝。它们具有自己的类型,可以有方法和属性,还可以通过values()方法获取枚举的所有值。枚举的引入使得表示...
这份教材以JDK 5.0版本为基础,该版本是Java发展史上的一个重要里程碑,引入了许多创新特性,提升了开发效率和代码质量。下面,我们将详细探讨其中的关键知识点。 首先,JDK 5.0引入了泛型(Generics),这是一个...
另外,自动装箱/拆箱(Autoboxing and Unboxing)是Java 5.0中的另一个重要特性,它简化了基本类型与它们对应的包装器类之间的操作。这意味着程序员不再需要手动进行类型转换,比如将int与Integer之间进行转换。 ...
Spring 5.0.x版本是该框架的一个重要里程碑,带来了许多新特性和改进,旨在提升性能和开发者体验。在这个版本中,Spring团队专注于增强微服务支持、响应式编程模型以及对Java 8和其他现代技术的兼容性。 一、核心...
这款名为“自助交易系统(客户端)_v5.0.19.320”的软件,是一个专为自动交易设计的客户端应用,它基于Java编程语言实现,旨在提供高效、便捷且可定制化的交易体验。 首先,我们来深入了解自动交易系统。自动交易系统...
Spring 5.0.x是该框架的一个重要版本,引入了许多新特性和改进,旨在提升性能和开发者体验。在这个压缩包中,包含的是Spring 5.0.x的源代码,已经通过了IntelliJ IDEA(简称Idea)的编译,确保没有错误,为深入理解...
总的来说,JDK 1.5(Java 5.0)的新特性极大地提高了Java的生产力和代码质量。从泛型到增强的for循环,再到注解和枚举,这些改进都让Java开发者能够编写出更安全、更易于维护的代码。在实际开发中,理解并充分利用...
### ACCP 5.0 使用Java语言理解程序逻辑——数组篇 #### 一、数组基本概念 在Java编程中,数组是一种非常基础且重要的数据结构,它允许我们以连续的内存空间来存储相同类型的多个数据项。数组的概念相对简单:它...
首先,Understand 5.0提供了全面的代码分析能力,支持多种编程语言,如C、C++、C#、Java、Python等,能够帮助开发者快速了解项目的整体结构和关系。通过其直观的界面,用户可以查看代码依赖性、函数调用图、类继承...
另外,“绿色汉化版”表示这个软件已经进行了中文本地化处理,对于中文用户来说,使用起来更加友好,无需担心语言障碍。 EditPlus包含的注册秘钥使得用户可以完全解锁软件的所有功能,避免了试用期限制,提供了一个...
在这个部分,我们将深入探讨Java语言编程规范的重要方面,并结合JDK 5.0的新特性进行讨论。 首先,我们从命名规范开始。在Java中,变量、方法和类的命名都应遵循一定的规则。变量名和方法名通常使用小写字母,单词...
J2EE(Java 2 Platform, Enterprise Edition)5.0是Java平台的一个版本,专为构建企业级分布式应用程序而设计。这个版本引入了许多新特性和改进,以提升开发效率、可移植性和可维护性。J2EE 5.0 API中文帮助手册提供...
另外,Netty的零拷贝特性也是其性能优秀的重要原因。通过利用Java NIO的FileChannel.transferTo()和FileChannel.transferFrom()方法,以及DirectBuffer,Netty能够在不涉及用户空间和内核空间之间数据拷贝的情况下,...
Java API 2 Platform Standard Edition 5.0(通常称为Java SE 5.0)是一个重要的版本,它引入了许多新的特性和改进,对Java的性能、可维护性以及开发效率带来了显著提升。 首先,Java SE 5.0 引入了泛型(Generics...