网站首页/技术开发列表/内容

J2SE 1.5 in a Nutshell 中文版

技术开发2022-07-27阅读
Java2平台标准版(J2SE)1.5版本(代号"Tiger")是对Java平台和语言的下一个重要修改;目前1.5版本的J2SE包含了15个JSR的请求,相应的JCP导致了将近100个显著的修改.

看到这个版本带来了这么多激动人心的变动,你可能想知道你应该从哪里开始.和以往的发布一样,所有的改动都会有一个清晰的列表保存在"Release notes guide".这篇来自J2SE开发团队的文章将带领你巡视主要的变动,使得你可以在深入API文档之前快速的把握J2SE 1.5能提供什么.

J2SE 1.5版本将关注在下面几个关键的主题:

简易的开发 Ease of Development
可扩展性和性能 Scalability and Performance
监视和管理能力 Monitoring and Manageability
桌面客户端 Desktop Client

还有一些特性也很重要,但是和这些主题不相关,所以他们被列在了文末:

其他的特性 Miscellaneous Features

简易开发 Ease of Development
你可能已经看到了一些能使得开发变得更加简易的Java语言上新特性的报告.这些特性包括了泛型类别,元数据,自动装箱,一个增强的for循环,枚举类型,静态引入,C语言风格的输入输出,变量参数,并行工具和一个简单化了的RMI接口生成.

JSR 201包含了上述语言特性中的4个:增强的for循环,枚举类型,静态导入和自动装箱;JSR 175 说明了元数据机能;而JSR 14则详细说明了泛型类别.


在javac编译器中使用的默认的语言是J2SE1.4.这就意味着如果你想使用任何的这些新语言特性必须在编译的时候在javac的命令行上添加一个参数 -source 1.5.(译者按:这也是目前很多的开发人员下载了这个版本以后发现无法使用这些新特性的原因.)


元数据 Metadata
J2SE 1.5 中的元数据特性提供了一个连接附加数据到Java Class,Interface,Method和Field中的能力.这些附加的数据或者注解能被javac编译器或者其他的工具识别,并且可以根据配置保存在class文件中,并且能在运行时通过Java的reflection 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[i]+" ");
}
} catch(NoSuchMethodException e) {
System.out.println(e);
}
}
}




有了元数据处理工具,很多重复的编码步骤可以减少到一个简洁的元数据标签中.比如访问JAX-RPC的时候需要的远程接口服务实现可以按照下面这样实现:

以前 Before

public interface PingIF extends Remote {
public void ping() throws RemoteException;
}
public class Ping implements PingIF {
public void ping() {
}
}




用了元数据后 After

public class Ping {
public @remote void ping() {
}
}




泛型类型 Generic Types
泛型类别在Java社区中已经期待已久,现在泛型成了J2SE 1.5 的一部分.第一显著的能看到泛型类别的地方是集合的API中.集合的API提供了一些通用功能例如:LinkedLists,ArrayLists和HashMaps等等这些能用于多种类型的集合类型.下面的例子使用J2SE 1.4.2的类库,并且使用默认的javac编译模式.


ArrayList list = new ArrayList();
list.add(0, new Integer(42));
int total = ((Integer)list.get(0)).intValue();




上面例子中最后一行一个强制转型到Integer类型是泛型类别想解决的强制转型问题.这个问题就是J2SE 1.4.2的集合API是用Object来存储集合对象的,这就意味这集合不能在编译的时候侦测到类型不符这样的问题.一个典型的问题就是会在运行时抛出ClassCastException.

下面是运用了泛型类别后改写的上面的范例:


ArrayList<Integer> list = new ArrayList<Integer>();
list.add(0, new Integer(42));
int total = list.get(0).intValue();




这些泛型化的集合API强制在编译的时候用<>来指定集合中存储的数据类型.用了泛型以后就不再有转型的需要了,而且在这个例子中如果试图添加一个String类型的变量到这个声明为Integer的集合中去将会导致一个编译错误.


泛型类别使得API的设计者能提供通用的机能,并且能运用于多种数据类型,并且能在编译的时候就检查类型的安全性.

泛型的API要稍微复杂一些.建议可以从java.util.Collection包的源代码和API的使用指南开始看起.


基本类型的自动装箱和自动拆箱 Autoboxing and Auto-unboxing of Primitive Types
在基本类型和对应的对象副本之间的转化,例如从基本型int,boolean到他们相对应的对象副本Integer和Boolean的转化会需要一些根本不必要的代码,特别是一些只在在集合API中会出现的转化操作.


对Java基本类型的自动装箱和自动拆箱使得代码能更加简洁易懂.下面的这个例子示范了将一个int类型的数据存储到一个ArrayList中,并且再取出来.J2SE 1.5使得在int和Integer对象之间的转化能自动进行.

以前 Before

ArrayList<Integer> list = new ArrayList<Integer>();
list.add(0, new Integer(42));
int total = (list.get(0)).intValue();




使用自动装箱和自动拆箱后 After

ArrayList<Integer> list = new ArrayList<Integer>();
list.add(0, 42);
int total = list.get(0);




增强的for循环 Enhanced for loop
在集合的API中,Iterator类别是使用率非常高的一个class.他提供了一种顺序访问集合元素的能力.新增强的for循环能在简单的顺序访问集合元素的时候代替Iterator.编译器会自动产生必要的带类型安全,并且不需要转型操作的循环代码.

以前 Before

ArrayList<Integer> list = new ArrayList<Integer>();
for (Iterator i = list.iterator(); i.hasNext();) {
Integer value=(Integer)i.next();
}




使用增强for循环以后 After

ArrayList<Integer> list = new ArrayList<Integer>();
for (Integer i : list) { ... }




枚举化的类型 Enumerated types
这种类型和使用static final的常量相比提供了枚举化的类型.如果你曾经在你的代码中用enum这样的变量名,当你用javac -source 1.5进行编译的时候将需要调整一下你的代码,因为J2SE 1.5 引入了一个新的关键字enum.


public enum StopLight { red, amber, green };




静态引入 Static Import
静态引入的特性是用"import static"这样的语句使得你能不输入类名就可以直接使用一个类中的静态常量.比如我们经常在添加一个控件的时候用的BorderLayout.CENTER,使用了静态引入后,你只需要简单的调用CENTER就可以了.


import static java.awt.BorderLayout.*;
getContentPane().add(new JPanel(), CENTER);




格式化的输出 Formatted Output
开发人员现在可以使用类似C语言中的printf这样的功能来产生格式化的输出.现在可以象C语言中一样使用printf,而且语言上基本没有变化,一般可以不变,有些格式可能需要稍微的变化一下.

大部分的通用C printf格式都可以使用,同时一些Java的类例如Date和BigInteger也拥有了格式化规则.可以在java.util.Formatter类中找到更多的信息.


System.out.printf("name count\n");
System.out.printf("%s %5d\n", user,total);




格式化的输入 Formatted Input
这个Scanner API提供了基本的读入数据的功能,例如从控制台上或者其他任何的数据流中读入数据的功能.下面的范例从标准输入中读入了一个字符串并且希望字符串随后是一个int值.


Scanner中的方法例如next和nextInt会在没有数据的时候自动失效.如果你需要处理一个非常复杂的输入,那么还可以使用java.util.Formatter类中的模式匹配算法.


Scanner s=Scanner.create(System.in);
String param= s.next();
int value=s.nextInt();
s.close();




不定参数 Varargs
不定参数(varargs)机能允许在一个方法中传入多个不定数量的参数.而这只是简单需要用...来表示某个方法接受不定参数.而这正是在printf方法中接受任意数量个参数的基础.


void argtest(Object ... args) {
for (int i=0;i <args.length; i++) {
}
}

argtest("test", "data");





并行工具 Concurrency Utilities
并行工具包是由Doug Lea在带领的JSR-166中提出要添加到J2SE 1.5中去的,这是一个非常流行的并行工具包.他提供了功能强大的,高层次的线程构造器,包含了执行器,例如一个线程任务框架,线程安全的队列,计时器,锁(包含了原子级别的锁)和其他一些同步的基本类型.


其中的锁是一个众所周知的信号.一个信号能在现在使用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




rmi编译器--rmic rmic -- the rmi Compiler
你可能再也不需要使用rmic来产生那些远程的接口桩.动态代理的方法说明了通常由桩提供的信息可以在在运行时被发现.更多的信息请参看RMI release notes.


可扩展性和性能 Scalability and Performance
J2SE 1.5版本的发布保证改进可扩展性和性能,尤其是在启动时和内存印记的时候,使得能更加简单的发布一个应用程序并且运行的非常快.


一个非常显著的更新是介绍类数据在Hotspot JVM中的分享.这个技术不仅在多个运行的JVM之间分享只读数据,并且改进了启动时间,使得看起来这些类都象JVM的核心的类一样是预先装载的.

性能的人体工程学是J2SE 1.5中的一个新特性,这意味这如果你在过去的J2SE版本中使用过特殊的JVM运行时选项来提升性能,这将值得你在J2SE的JVM中没有JVM运行时没有参数或者少量参数的情况下重新验证你的性能,因为J2SE的性能已经提升了很多了.


监视和管理 Monitoring and Manageability
监视和管理是Java平台上RAS(Reliability, Availability, Serviceability)的一个关键组件.

JVM的监视和管理API(JSR-174)详细说明了一套非常容易理解的JVM内部机制,这些内部机制在一个运行的JVM上被监视到.这个信息是通过JMX(JSR-003)消息Bean来访问的,并且能通过JMX的远程接口(JSR-160)来远程的访问或者通过业界标准的SNMP工具来访问.

最重要的特性之一是一个底层的内存探测器 .当入口(threshold)交叉的时候,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 整形API(JSR-163) New JVM profiling API (JSR-163)
这次的发布还包含了一个非常强劲的本地整形API叫做JVMTI.这套API在JSR163中已经详细说明过,并且是针对改进整形接口的需求而来的.不管怎么样,JVMTI想关注所有本地进程内的工具访问.除了整形以外,还包括了监视,调试和一个可能将会支持多种代码分析的工具.

这些API的实现包括了一套bytecode测试设备的机制,JPLIS(Java Programming Language Instrumentation Services -Java编程语言测试设备服务).这将允许分析工具在必要的时候来添加额外的整形.这个技术的优点是他允许更多的焦点分析并且限制JVM中跑的不同的整形工具之间的冲突.这套测试设备甚至能在运行时自动的生成,就如同class的装载时候和预处理的class文件一样.

下面的范例建立了一套能从磁盘上装载一个修改过 class文件的测试设备勾子.要运行下面的测试,在启动JRE的时候,用java -javaagent:myBCI BCITest的方式.


//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");
}
}





改进的诊断能力 Improved Diagnostic Ability
在没有控制台窗口的时候生成堆栈跟踪是无用的.现在有两个新的API,getStackTrace和Thread.getAllStackTraces提供了使得这些信息可编程的能力.


StackTraceElement e[]=Thread.currentThread().getStackTrace();
for (int i=0; i <e.length; i++) {
System.out.println(e[i]);
}
System.out.println("\n"+Thread.getAllStackTraces());




Hotspot JVM包括了一个致命错误的处理机制,使得在JVM放弃的时候运行一个用户支持的脚本或者一段程序.一个调试工具也能用Hotspot JVM的可服务代理连接器连接到这个已经挂起的JVM或者那些核心文件.


-XX:OnError="command"

-XX:OnError="pmap %p"
-XX:OnError="gdb %p"
optional %p used as process id




桌面客户端 Desktop Client
Java的桌面客户端仍然是作为Java平台的一个关键组件,同时这个组件在J2SE 1.5中也有了很大的改进.


在这次的Beta发布中包含了一些早先对启动时间和内存印记的改进.不仅变的快速了,而且Swing的工具中还带了一个崭新的界面风格叫做Ocean.


在J2SE 1.4.2的基础上,对GTK可更换的Look and Feel以及Window XP的Look and Feel有了进一步的改进.



Windows XP
Click to Enlarge

Linux/Redhat
Click to Enlarge



Linux和Solaris的用户只要有最新的OpenGL驱动和显卡,就能让Java2D得到本地的硬件加速,可以非常显著的改善显示效果,只需要在启动的时候添加下面这样的参数就可以了:

java -Dsun.java2d.opengl=true -jar Java2D.

同时在Linux的发布版本中还包含了一个快速的X11工具包,叫做XAWT,默认就是使用这个工具包的.如果你需要使用过去的motif的工具包,你需要在启动的时候添加如下的系统参数:


java -Dawt.toolkit=sun.awt.motif.MToolkit -jar Notepad.jar

(X11的工具包叫做sun.awt.X11.XToolkit)

同时X11的工具包还使用了xDnD的协议,所以你可以在Java和其他应用程序之间,比如StarOffice或者Mozilla,拖拽简单的组件.


其他的一些新特性 Miscellaneous Features
核心支持XML Core XML Support
J2SE 1.5 介绍了一些对核心XML平台的修改,包括了XML 1.1,Namespace,XML Schema.还有SAX 2.0.1,XSLT和快速的XLSTC编译器,还有对DOM level 3的支持.


除了对核心XML的支持特性以外,JWSDP(Java Web Services Developer Pack)将会发布最新的Web services标准:JAX-RPC & SAAJ(WSDL/SOAP),JAXB,XML加密以及数字签名和注册用的JAXR.


补充的字符支持 Supplementary Character Support
32位的补充字符支持是被非常小心的添加到了J2SE 1.5中来的,他是作为过渡到Unicode 4.0支持的一个部分.补充的字符被编码成特殊的一对对UTF16的值来生成一个个不同的字符,或者从代码的角度来看,被代替的一对值是由一个高位的UTF16值紧随一个低位的UTF16值,而这个高位和低位的值都是从一个特殊的UTF16值范围内取出来的.


通俗点说,当使用一个String或者字符序列的时候,核心的API库会透明的为你处理这些补充的字符.但是java的char类型数据仍然是16位的,少数使用char作为参数的方法现在有了相应的能接受一个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的行集 JDBC RowSets
对JDBC 行集的支持上有两个更新.一个是CachedRowSet,包含了保存在内存中从数据库中取回来的行集合.并且他们是未连接的,这就意味着对这些行集的更新可以在过一段时间后同步到数据库中.


另外一个是WebRowSet,是一种用XML的方式来传递数据库中的行数据信息.
相关阅读