以下是网上摘录的: JDK5.0的新特性(2005-2006)
“JDK1.5”(开发代号猛虎)的一个重要主题就是通过新增一些特性来简化开发,
这些特性包括泛型,for-each 循环,自动装包/拆包,枚举,可变参数, 静态导入
C 风格的格式化 I/O、、并发实用程序以及更简单的 RMI 接口生成。
JSR 201 包括如下四个语言变化:增强的 for 循环、枚举类型、静态导入和 autoboxing;JSR 175 指定了新的元数据功能,而 JSR 14 则详细说明了泛型。
javac 编译器执行的默认语言规范是版本 1.4。这意味着要利用以下语言变化的任何好处,需要向 javac 命令传递参数 -source 1.5。
使用这些特性有助于我们编写更加清晰,精悍,安全的代码。
下面我们简单介绍一下这些新特性。
1.泛型(Generic)
C++通过模板技术可以指定集合的元素类型,而Java在1.5之前一直没有相对应的功
能。一个集合可以放任何类型的对象,相应地从集合里面拿对象的时候我们也不得不对
他们进行强制得类型转换。猛虎引入了泛型,它允许指定集合里元素的类型,这样你可
以得到强类型在编译时刻进行类型检查的好处。
Collection c = new ArrayList();
c.add(new Date());
编译器会给出一个错误:
add(java.lang.String) in java.util.Collection cannot be applied to
(java.util.Date)
2.For-Each循环
For-Each循环得加入简化了集合的遍历。假设我们要遍历一个集合对其中的元素进
行一些处理。典型的代码为:
void processAll(Collection c){
for(Iterator i=c.iterator(); i.hasNext();){
MyClass myObject = (MyClass)i.next();
myObject.process();
}
}
使用For-Each循环,我们可以把代码改写成:
void processAll(Collection c){
for (MyClass myObject :c)
myObject.process();
}
这段代码要比上面清晰许多,并且避免了强制类型转换。
3.自动装包/拆包(Autoboxing/unboxing)
自动装包/拆包大大方便了基本类型数据和它们包装类地使用。
自动装包:基本类型自动转为包装类.(int >> Integer)
自动拆包:包装类自动转为基本类型.(Integer >> int)
在JDK1.5之前,我们总是对集合不能存放基本类型而耿耿于怀,现在自动转换机制
解决了我们的问题。
int a = 3;
Collection c = new ArrayList();
c.add(a);//自动转换成Integer.
Integer b = new Integer(2);
c.add(b + 2);
这里Integer先自动转换为int进行加法运算,然后int再次转换为Integer.
4.枚举(Enums)
JDK1.5加入了一个全新类型的“类”-枚举类型。为此JDK1.5引入了一个新关键字
enmu. 我们可以这样来定义一个枚举类型。
public enum Color
{
Red,
White,
Blue
}
然后可以这样来使用Color myColor = Color.Red.
枚举类型还提供了两个有用的静态方法values()和valueOf(). 我们可以很方便地
使用它们,例如
for (Color c : Color.values())
System.out.println(c);
5.可变参数(Varargs)
可变参数使程序员可以声明一个接受可变数目参数的方法。注意,可变参数必须是
函数声明中的最后一个参数。假设我们要写一个简单的方法打印一些对象,
util.write(obj1);
util.write(obj1,obj2);
util.write(obj1,obj2,obj3);
…
在JDK1.5之前,我们可以用重载来实现,但是这样就需要写很多的重载函数,显得
不是很有效。如果使用可变参数的话我们只需要一个函数就行了
public void write(Object... objs) {
for (Object obj: objs)
System.out.println(obj);
}
在引入可变参数以后,Java的反射包也更加方便使用了。对于
c.getMethod("test", new Object[0]).invoke(c.newInstance(), new
Object[0])),现在我们可以这样写了
c.getMethod("test").invoke(c.newInstance()),这样的代码比原来清楚了很多。
6.静态导入(Static Imports)
要使用用静态成员(方法和变量)我们必须给出提供这个方法的类。使用静态导入
可以使被导入类的所有静态变量和静态方法在当前类直接可见,使用这些静态成员无需
再给出他们的类名。
import static java.lang.Math.*;
…….
r = sin(PI * 2); //无需再写r = Math.sin(Math.PI);
不过,过度使用这个特性也会一定程度上降低代码地可读性。
元数据
J2SE 1.5 中的元数据特性提供这样的能力,即向 Java 类、接口、方法和字段关联附加的数据。这些附加的数据或者注释,可以被 javac 编译器或其他工具读取,并且根据配置不同,可以被保存在类文件中,也可以在运行时使用 Java 反射 API 被发现。
向 Java 平台增加元数据的一个主要原因是,使得开发工具和运行工具有一个通用的基础结构,以减少开发和部署所需的成本。工具可以使用元数据信息生成附加的源代码,或者在调试时提供附加信息。
下面的例子用元数据工具创建了一个调试元数据注释,这些元数据注释然后又简单地在运行时显示出来。可以想像,大部分的元数据标签形成一个标准,即一个良好规范的集合。
import java.lang.annotation.*;
import java.lang.reflect.*;
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @interface debug {
boolean devbuild() default false;
int counter();
}
public class MetaTest {
final boolean production=true;
@debug(devbuild=production,counter=1) public void testMethod() {
}
public static void main(String[] args) {
MetaTest mt = new MetaTest();
try {
Annotation[] a = mt.getClass().getMethod("testMethod").getAnnotations();
for (int i=0; i<a.length ; i++) {
System.out.println("a["+i+"]="+a+" ");
}
} catch(NoSuchMethodException e) {
System.out.println(e);
}
}
}
利用一个元数据处理工具,许多重复的代码编写步骤可以减少成一个简练的元数据标签。例如,访问某个 JAX-RPC 服务实现时所需的远程接口可以实现为:
原来(J2SE 1.5 以前版本):
public interface PingIF extends Remote {
public void ping() throws RemoteException;
}
public class Ping implements PingIF {
public void ping() {
}
}
现在:
public class Ping {
public @remote void ping() {
}
}
二、元数据(注解)
这是JDK5.0学XDoclt的,有了注解,以后我们可以不必写接口,EJB写起来会方便很多。EJB3.0要精简写法,注解要占一些功劳。
介绍一个常用的注解:@Override,示例如下:
public class Test extends ATest{
@Override
public void test() {
}
}
在方法前加了这个注解,就限定死了Test的这个方法一定要是覆盖自ATest的方法test,否则就会报错。比如你不心把方法test()写成了tesd(),编译时就会报错。另一个要注意的是@Override用于定义覆盖接口的方法,也就是说ATest必须是一个抽象类、普通类,但不能是接口。
另一个常见到的注解是@Deprecated,表明该项(类、字段、方法)不再被推荐使用。不过我们自己一般很少用到这个注解。
好了,注解只讲这两个吧,也不必了解太多,知道个概念,以后用到的时候再说吧。关于注解,建议看看XDoclt,这是一个开源小工具,在项目开发中非常好用。
我喜爱的Java 5.0的五个特性
作者:David Flanagan, Java in a Nutshell, 第5版的作者
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
作者:
David Flanagan;xml1123
原文地址:
http://www.onjava.com/pub/a/onjava/2005/04/20/javaIAN5.html
中文地址:
http://www.matrix.org.cn/resource/article/43/43830_Java5.html
关键词: Java5
至今,毫无疑问你已经看过不止一篇网上文章列举了Java5.0的伟大的语言新特性:泛型,标注,枚举类型,自动装箱,可变参数, for/in循环,甚至静态引入。我也认为这些是伟大的特性,但是,你已经读过他们了。因此,在此我将集中于你可能没有听说过的Java5.0的新API特性。
那么,在下面,是我喜欢的Java5.0的五个新API特性。我已经在《果壳中的Java》(《Java in a nut shell》)第五版的第五章中介绍过他们。并且我也在我的网站中记录了其中的一些。那些看完本文后的细心的读者会发现额外的奖励—第六个特性:很少有人知道的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()});
一、C风格格式化输出
在JDK5.0后,我们这样写代码:
public static void main(String[] args) {
int x = 10;
int y = 20;
int sum = x + y;
System.out.printf("%d + %d = %d", x, y, sum);
}
可变参数和自动装箱还对java.lang.reflect API有一个实质性的影响。那就是当查找和调用方法时不再需要类和对象数组:
Method m = c.getMethod("put", Object.class,Object.class);
m.invoke(map, "key", "value");
如果我必须选择一个最喜欢的可变参数方法,那么,将是java.util.Arrays.asList()。这个方法真是个用于创建不变的对象列表的方便的工厂方法。它接受任何数量的类型T的参数并且将它们作为List<T>返回:
List<Integer> smallPrimes =
Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19);
能力
我们在上面谈论了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浮点标准,那么十六进制浮点字段值或许是你喜欢的一个特性。我只是认为他们有趣。
一 从xml中装载属性
格式:
<properties>
<comment>Hi</comment>
<entry key="id">1</entry>
<entry key="name">rocie</entry>
<entry key="email">rocie@sina.com</entry>
</properties>
|
通过以下方法加载:
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("d:/jdk5/properties/test.xml");
prop.loadFromXML(fis);
prop.list(System.out);
System.out.println("Name: " + prop.getProperty("name"));
|
将xml文件放在WEB-INF/classes/目录下,可以防止对路径的硬编码
InputStream is = PropertiesTest.class.getClassLoader().getResourceAsStream("rocie.xml");
Properties ps = new Properties();
ps.loadFromXML(is);
System.out.println("name:"+ps.getProperty("name"));
|
通过以下方法保存:
Properties prop = new Properties();
prop.setProperty("id", "2");
prop.setProperty("name", "彭大双");
prop.setProperty("email", "pds@dotraining.com");
FileOutputStream fos = new FileOutputStream("d:/jdk5/properties/pds.xml");
prop.storeToXML(fos, "彭大双","gbk");
fos.close();
|
xml文件细节:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Hi</comment>
<entry key="id">1</entry>
<entry key="name">rocie</entry>
<entry key="email">rocie@sina.com</entry>
</properties>
|
DTD文件:
<?xml version="1.0" encoding="UTF-8"?>
<!-- DTD for properties -->
<!ELEMENT properties ( comment?, entry* ) >
<!ATTLIST properties version CDATA #FIXED "1.0">
<!ELEMENT comment (#PCDATA) >
<!ELEMENT entry (#PCDATA) >
<!ATTLIST entry key CDATA #REQUIRED>
|
注:?表示0或1;*表示0至无限;+表示至少一个
二 并发集合
问题:ConcurrentModificationException、fail-fast
当遍历一个Iterator时,如果该Iterator对应的List里的元素发生改变,则抛出这个异常。
解决方法:java.util.concurrent
用CopyOnWriteArrayList代替原来的ArrayList
copy-on-write:这意味着如果有大量的读(即 get() ) 和迭代,不必同步操作以照顾偶尔的写(即 add() )调用。对于新的 CopyOnWriteArrayList 和 CopyOnWriteArraySet 类,所有可变的(mutable)操作都首先取得后台数组的副本,对副本进行更改,然后替换副本。这种做法保证了在遍历自身更改的集合时,永远不会抛出 ConcurrentModificationException 。遍历集合会用原来的集合完成,而在以后的操作中使用更新后的集合。
三用 for/in 在 Java 5.0 中增强循环
public static List<Integer> list = new ArrayList<Integer>();
public static int[] is = new int[]{1,2,3,4,65,456};
static
{
for(int i=0;i<100;i++)
list.add(i);
}
public static void main(String args[])
{
for(Integer i:list)
System.out.println(i);
System.out.println("-------other for each--------");
for(int i:is)
System.out.println(i);
}
|
四枚举类型 enum
public class EnumTest {
private Color color;
public static enum Color {red,yellow,blue,green};
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public static void main(String args[])
{
for(Color c:Color.values())
{
System.out.println("name:"+c.name()+"ordinal:"+c.ordinal());
}
EnumTest et = new EnumTest();
et.setColor(Color.red);
Color c = et.getColor();
switch(c)
{
case red: System.out.println("red:"+c.name()+c.ordinal());
break;
case yellow: System.out.println("yellow"+c.name()+c.ordinal());
break;
default : System.out.println("over");
}
}
}
|
并发实用程序
并发实用程序库由 Doug Lea 定义在 JSR-166 中,是 J2SE 1.5 平台中流行的并发软件包的一个特殊版本。它提供强大的、高级别的线程构造,包括 executors(这是一个线程任务框架)、线程安全队列、Timers、锁(包括原子锁)和其他同步原语。
著名的旗语(semaphore)是这样一个锁。旗语与现在使用的 wait 的使用方式相同,用于限制对一块代码的访问。旗语更加灵活,并且也允许许多并发的线程访问,同时允许您在获得一个锁之前对它进行测试。下面的例子使用刚好一个旗语,也叫做二进制旗语。更多信息请参见 java.util.concurrent 软件包。
final private Semaphore s= new Semaphore(1, true);
s.acquireUninterruptibly(); //for non-blocking version use s.acquire()
balance=balance+10; //protected value
s.release(); //return semaphore token
rmic —— rmi 编译器
您不再需要使用 rmic —— rmi 编译器工具——来生成最远程的接口存根。动态代理的引入意味着通常由存根提供的信息可以在运行时被发现。更多信息请参见 RMI 版本说明。
可扩展性和性能
1.5 版本承诺在可扩展性和性能方面的改进,新的重点在于启动时间和内存占用,使它更加易于以最大的速度部署应用程序。
最重大的一个更新是引入了 Hotspot JVM 中的类数据共享。该技术不仅在多个正在运行的 JVM 之间共享只读数据,而且改进了启动时间,因为核心的 JVM 类都是预先打包的。
性能工效是 J2SE 1.5 中的一个新特性,这意味着如果您一直使用的是以前版本中专门的 JVM 运行时选项, 那么可能值得不用选项或者用很少的选项重新验证您的性能。
监控和可管理性
监控和可管理性是 Java 平台中的 RAS (Reliability, Availability, Serviceability,即可*性、可用性、可服务性) 的一个关键组件。
JVM Monitoring & Management API (JSR-174) 指定一组全面的可以从正在运行的 JVM 进行监控的 JVM internals。 该信息可通过 JMX (JSR-003) MBeans 访问到,也可以使用 JMX 远程接口 (JSR-160) 和行业标准 SNMP 工具而远程访问得到。
最有用的一个特性是一个低内存检测程序。当超过阀值时,JMX MBeans 可以通知已注册的侦听程序。更多信息请参见 javax.management 和 java.lang.management。
为了了解新的 API 是多么容易使用,下面报告了 Hotspot JVM 中内存堆的详细使用情况。
import java.lang.management.*;
import java.util.*;
import javax.management.*;
public class MemTest {
public static void main(String args[]) {
List pools =ManagementFactory.getMemoryPoolMBeans();
for(ListIterator i = pools.listIterator(); i.hasNext();) {
MemoryPoolMBean p = (MemoryPoolMBean) i.next();
System.out.println("Memory type="+p.getType()+" Memory usage="+p.getUsage());
}
}
}
新的 JVM profiling API (JSR-163)
该版本还包含一个更强大的本机 profiling API,叫做 JVMTI。该 API 已经在 JSR 163 中指定了,并由对改善的 profiling 接口的需求所推动。但是,JVMTI 除了具有 profiling 功能之外,还想要涵盖全范围的本机内部过程工具访问,包括监控工具、调试工具以及潜在的各种各样的其他代码分析工具。
该实现包含一个用于字节码装置(instrumentation)——Java 编程语言装置服务(Java Programming Language Instrumentation Services,JPLIS)的机制。这使得分析工具只在需要的地方添加额外的配置信息(profiling)。该技术的优点是,它允许更加集中的分析,并且限制了正在运行的 JVM 上的 profiling 工具的引用。该装置甚至可以在运行时和类加载时动态地生成,并且可以作为类文件预先处理。
下面这个例子创建了一个装置钩(instrumentation hook),它可以从磁盘加载类文件的一个已修改的版本。要运行该测试,可利用 java -javaagent:myBCI BCITest 启动 JRE。
//File myBCI.java
import java.lang.instrument.Instrumentation;
public class myBCI {
private static Instrumentation instCopy;
public static void premain(String options, Instrumentation inst) {
instCopy = inst;
}
public static Instrumentation getInstrumentation() {
return instCopy;
}
}
//File BCITest.java
import java.nio.*;
import java.io.*;
import java.nio.channels.*;
import java.lang.instrument.*;
public class BCITest {
public static void main (String[] args) {
try {
OriginalClass mc = new OriginalClass();
mc.message();
FileChannel fc=new FileInputStream(new File("modified"+File.separator+"OriginalClass.class")).getChannel();
ByteBuffer buf = fc.map(FileChannel.MapMode.READ_ONLY, 0, (int)fc.size());
byte[] classBuffer = new byte[buf.capacity()];
buf.get(classBuffer, 0, classBuffer.length);
myBCI.getInstrumentation().redefineClasses(new ClassDefinition[] {new ClassDefinition(mc.getClass(), classBuffer)});
mc.message();
}catch (Exception e){}
}
}
//OriginalClass.java
//Compile in current directory
//Copy source to modified directory,change message and recompile
public class OriginalClass {
public void message() {
System.out.println("OriginalClass");
}
}
改进的诊断能力
如果没有控制台窗口可用,生成的 Stack 跟踪就很笨拙。两个新的 API —— getStackTrace 和 Thread.getAllStackTraces —— 以程序的方式提供该信息。
StackTraceElement e[]=Thread.currentThread().getStackTrace();
for (int i=0; i <e.length; i++) {
System.out.println(e);
}
System.out.println("\n"+Thread.getAllStackTraces());
Hotspot JVM 包含一个致命的错误处理程序(error hander),如果 JVM 异常中断,它可以运行用户提供的脚本。使用 Hotspot JVM 可服务性代理连接器,调试工具也可以连接到一个挂起的 JVM 或者核心文件。
-XX:OnError="command"
-XX:OnError="pmap %p"
-XX:OnError="gdb %p"
optional %p used as process id
桌面客户端
Java 桌面客户端保留有 Java 平台的一个关键组件,并且这一点成了 J2SE 1.5 中许多改进的焦点。
这个 Beta 版本包含启动时间和内存占用方面的一些早期改进。该版本不仅更快,并且 Swing 工具集采用了一个暂新的叫做 Ocean 的主题。
通过建立 J2SE 1.4.2 中的更新,GTK 和 Windows XP 外观方面有了更进一步的改进。
Windows XP
Click to Enlarge
Linux/Redhat
Click to Enlarge
具有最新 OpenGL 驱动程序并且选择了图形卡的 Linux 和 Solaris 用户,可以使用下面的运行时属性从 Java2D 获得本机硬件加速:
java -Dsun.java2d.opengl=true -jar Java2D.
Linux 版本也具有快速的 X11 Toolkit,叫做 XAWT,默认情况下是启用的。如果您需要与 motif 版本进行比较,可以使用下面的系统属性:
java -Dawt.toolkit=sun.awt.motif.MToolkit -jar Notepad.jar
(X11 Toolkit 叫做 sun.awt.X11.XToolkit)
X11 Toolkit 也使用 XDnD 协议,所以您可以在 Java 和其他应用(比如 StarOffice 或 Mozilla)之间拖放简单的组件。
其他特性
核心 XML 支持
J2SE 1.5 引入了核心 XML 平台的几个修订,包括 XML 1.1 和 Namespace、XML Schema、SAX 2.0.1、XSLT 和快速 XLSTC 编译器,以及最后的 DOM 第 3 层支持。
除了支持核心 XML 之外,未来版本的 Java Web Services Developer Pack 将交付最新的 Web 服务标准:JAX-RPC & SAAJ (WSDL/SOAP)、JAXB、XML Encryption and Digital Signature,以及用于注册的 JAXR。
辅助字符支持
32 位的辅助字符支持作为传输到 Unicode 4.0 支持的一部分,已经慎重地添加到该平台。辅助字符被编码为一对特殊的 UTF16 值,以生成一个不同的字符或者码点(codepoint)。一个代理对(surrogate pair)是一个高 UTF16 值和后面的一个低 UTF16 值的组合。这些高值和低值来自一个特殊范围的 UTF16 值。
一般来说,当使用 String 或者字符序列时,核心 API 库将透明地为您处理新的辅助字符。但是因为 Java "char" 仍然保留为 16 位,所以非常少的一些使用 char 作为参数的方法,现在有了足够的可以接受 int 值的方法,其中 int 值可以代表新的更大的值。特别是 Character 类,具有附加的方法来检索当前的字符和接下来的字符,以便检索辅助的码点值,如下所示:
String u="\uD840\uDC08";
System.out.println(u+"+ "+u.length());
System.out.println(Character.isHighSurrogate(u.charAt(0)));
System.out.println((int)u.charAt(1));
System.out.println((int)u.codePointAt(0));
更多信息请参见 Character 中的 Unicode 部分。
JDBC RowSets
JDBC 行集支持有两个主要的更新。CachedRowSet 包含从数据库检索的行的内存中的集合。但是它们也是不连接的,这意味着以后更新可以与数据库重新同步。
另一个组件是 WebRowSet,它使用数据库行通过 XML 来传输数据。