jdk6和jdk5相比的新特性有:
1、instrumentation
在 Java SE 6 里面,instrumentation 包被赋予了更强大的功能:启动后的 instrument、本地代码 instrument,以及动态改变 classpath 等等。
2、Http有所增强
3、 Java 管理扩展(JMX) 架构及其框架,以及在 Java SE 5 中新引入的 JMX API -- java.lang.management 包
4、JDK 6 中新增加编译器 API(JSR 199)。利用此 API,您可以在运行时调用 Java 编译器,可以编译不同形式的源代码文件,还可以采集编译器的诊断信息。
5、Java DB 和 JDBC 4.0
6、对脚本语言的支持
Java SE 6 新引入的对 JSR 223 的支持,它旨在定义一个统一的规范,使得 Java 应用程序可以通过一套固定的接口与各种脚本引擎交互,从而达到在 Java 平台上调用各种脚本语言的目的。
7、XML API 与 Web 服务
Java SE 6 中提供的 XML 处理框架,以及在此框架之上结合注释(Annotation) 技术,所提供的强大的针对 Web 服务的支持
要详细的可以参看:
http://www.ibm.com/developerworks/cn/java/j-lo-jse6/
多了不少新特性
一.Instrumentation
利用 Java 代码,即 java.lang.instrument 做动态 Instrumentation 是 Java SE 5 的新特性,它把 Java 的 instrument 功能从本地代码中解放出来,使之可以用 Java 代码的方式解决问题。在 Java SE 6 里面,instrumentation 包被赋予了更强大的功能:启动后的 instrument、本地代码(native code)instrument,以及动态改变 classpath 等等。在 Java SE 5 当中,开发者只能在 premain 当中施展想象力,所作的 Instrumentation 也仅限与 main 函数执行前,这样的方式存在一定的局限性。在 Java SE 6 的 Instrumentation 当中,有一个跟 premain“并驾齐驱”的“agentmain”方法,可以在 main 函数开始运行之后再运行。
二、Http
在 Java SE 6 当中,围绕着 HTTP 协议出现了很多实用的新特性:NTLM 认证提供了一种 Window 平台下较为安全的认证机制;JDK 当中提供了一个轻量级的 HTTP 服务器;提供了较为完善的 HTTP Cookie 管理功能;更为实用的 NetworkInterface;DNS 域名的国际化支持等等。
HTTP Cookie管理可以应用客户操作临时变量的保存,如查询条件,当前状态等
三、JMX与系统管理
管理系统的构架
图 1. 管理系统构架
上图分析了管理系统的基本构架模式。其中 Agent / SubAgent 起到的就是翻译的作用:把 IT 资源报告的消息以管理系统能理解的方式传送出去。
也许读者有会问,为什么需要 Agent 和 SubAgent 两层体系呢?这里有两个现实的原因:
管理系统一般是一个中央控制的控制软件,而 SubAgent 直接监控一些资源,往往和这些资源分布在同一物理位置。当这些 SubAgent 把状态信息传输到管理系统或者传达管理系统的控制指令的时候,需要提供一些网络传输的功能。
管理系统的消息是有一定规范的,消息的翻译本身是件复杂而枯燥的事情。
一般来说,管理系统会将同一物理分布或者功能类似的 SubAgent 分组成一组,由一个共用的 Agent 加以管理。在这个 Agent 里封装了 1 和 2 的功能。
JMX 和管理系统
JMX 既是 Java 管理系统的一个标准,一个规范,也是一个接口,一个框架。图 2 展示了 JMX 的基本架构。
图 2. JMX 构架
和其它的资源系统一样,JMX 是管理系统和资源之间的一个接口,它定义了管理系统和资源之间交互的标准。javax.management.MBeanServer 实现了 Agent 的功能,以标准的方式给出了管理系统访问 JMX 框架的接口。而 javax.management.MBeans 实现了 SubAgent 的功能,以标准的方式给出了 JMX 框架访问资源的接口。而从类库的层次上看,JMX 包括了核心类库 java.lang.management 和 javax.management 包。java.lang.management 包提供了基本的 VM 监控功能,而 javax.management 包则向用户提供了扩展功能。
JMX帮助开发者监控JVM的信息
四、编辑器API
JDK 6 提供了在运行时调用编译器的 API。在传统的 JSP 技术中,服务器处理 JSP 通常需要进行下面 6 个步骤:
分析 JSP 代码;
生成 Java 代码;
将 Java 代码写入存储器;
启动另外一个进程并运行编译器编译 Java 代码;
将类文件写入存储器;
服务器读入类文件并运行;
但如果采用运行时编译,可以同时简化步骤 4 和 5,节约新进程的开销和写入存储器的输出开销,提高系统效率。实际上,在 JDK 5 中,Sun 也提供了调用编译器的编程接口。然而不同的是,老版本的编程接口并不是标准 API 的一部分,而是作为 Sun 的专有实现提供的,而新版则带来了标准化的优点。
新 API 的第二个新特性是可以编译抽象文件,理论上是任何形式的对象 —— 只要该对象实现了特定的接口。有了这个特性,上述例子中的步骤 3 也可以省略。整个 JSP 的编译运行在一个进程中完成,同时消除额外的输入输出操作。
第三个新特性是可以收集编译时的诊断信息。作为对前两个新特性的补充,它可以使开发人员轻松的输出必要的编译错误或者是警告信息,从而省去了很多重定向的麻烦
五、Java DB 与jdbc4.0
新安装了 JDK 6 的程序员们也许会发现,除了传统的 bin、jre 等目录,JDK 6 新增了一个名为 db 的目录。这便是 Java 6 的新成员:Java DB。这是一个纯 Java 实现、开源的数据库管理系统(DBMS),源于 Apache 软件基金会(ASF)名下的项目 Derby。
从 Java 6 开始,应用程序不再需要显式地加载驱动程序了,DriverManager 开始能够自动地承担这项任务。
六、对脚本语言的支持
Java SE 6 引入了对 Java Specification Request(JSR)223 的支持,JSR 223 旨在定义一个统一的规范,使得 Java 应用程序可以通过一套固定的接口与各种脚本引擎交互,从而达到在 Java 平台上调用各种脚本语言的目的。
Java 脚本 API 为开发者提供了如下功能:
获取脚本程序输入,通过脚本引擎运行脚本并返回运行结果,这是最核心的接口。
发现脚本引擎,查询脚本引擎信息。
通过脚本引擎的运行上下文在脚本和 Java 平台间交换数据。
通过 Java 应用程序调用脚本函数。
七、XML API 与 Web 服务
1.4与1.5区别
1 泛型
2 自动装箱/拆箱
3 for-each
4 static import
5 变长参数
1. 泛型 1.4之前 java util包中容器类,装的是Object对象,你要装特定的类型可以,但要强制转换,这可能导致运行时错误.
例:原来ArrayList list=new ArrayList();
list.add(new Integer(3));
list.add(new Integer(4));
int i=((Integer)(list.get(0))).parseInt();
很麻烦
现在ArrayList
list=new ArrayList
();
list.add(new Integer(3));
list.add(new Integer(4));
int i=list.get(0).parseInt();
不用Cast,运行时错误变为编译时错误,这是进步.
类似与C++中的摸板templete.但机理不同.
2 自动装箱/拆箱
还是刚才例子
最后一句可改为
int i=list.get(0);
原始类型与对应的包装类不用显式转换,方便
3 for-each
循环的增强
int a[]={........};//初始化,略
for(int i:a)
{
......
}
不用以前的i=0;i
4 static import
以前调Java.math
Math.sqrt();
现在 static import java.lang.Math.sqrt;
再 sqrt();
相当于你自己类里有这个方法
5 变长参数
int sum(int ...intlist)
{
int i,sum;
sum=0;
for(int i=0;i
{
sum+=list[i];
}
return sum; }
有任意个参数,把他看作数组没多大区别,只要你注意别用那些过时的方法就行,若是新版本加的或有改变的方法,docs里会说的,在方法解释下面
jdk6.0将会有很大变化
增强的for循环
为了迭代集合和数组,增强的for循环提供了一个简单、兼容的语法.有两点值得一提:
Init表达式
在循环中,初始化表达式只计算一次.这意味着您通常可以移除一个变量声明.在这个例子中,我们必须创建一个整型数组来保存computeNumbers()的结果,以防止每一次循环都重新计算该方法.您可以看到,下面的代码要比上面的代码整洁一些,并且没有泄露变量numbers:
未增强的For:
int sum = 0;
Integer[] numbers = computeNumbers();
for (int i=0; i < numbers.length ; i++)
sum += numbers[i];
增强后的For:
int sum = 0;
for ( int number: computeNumbers() )
sum += number;
局限性
有时需要在迭代期间访问迭代器或下标,看起来增强的for循环应该允许该操作,但事实上不是这样,请看下面的例子:
for (int i=0; i < numbers.length ; i++) {
if (i != 0) System.out.print(",");
System.out.print(numbers[i]);
}
我们希望将数组中的值打印为一个用逗号分隔的清单.我们需要知道目前是否是第一项,以便确定是否应该打印逗号.使用增强的for循环是无法获知这种信息的.我们需要自己保留一个下标或一个布尔值来指示是否经过了第一项. 这是另一个例子:
for (Iterator
it = n.iterator() ; it.hasNext() ; )
if (it.next() < 0)
it.remove();
在此例中,我们想从整数集合中删除负数项.为此,需要对迭代器调用一个方法,但是当使用增强的for 循环时,迭代器对我们来说是看不到的.因此,我们只能使用Java 5之前版本的迭代方法. 顺便说一下,这里需要注意的是,由于Iterator是泛型,所以其声明是Iterator .许多人都忘记了这一点而使用了Iterator的原始格式.
注释
注释处理是一个很大的话题.因为本文只关注核心的语言特性,所以我们不打算涵盖它所有的可能形式和陷阱. 我们将讨论内置的注释(SuppressWarnings,Deprecated和Override)以及一般注释处理的局限性.
Suppress Warnings
该注释关闭了类或方法级别的编译器警告.有时候您比编译器更清楚地知道,代码必须使用一个被否决的方法或执行一些无法静态确定是否类型安全的动作,而使用:
@SuppressWarnings("deprecation")
public static void selfDestruct() {
Thread.currentThread().stop();
}
这可能是内置注释最有用的地方.遗憾的是,1.5.0_04的javac不支持它.但是1.6支持它,并且Sun正在努力将其向后移植到1.5中.
Eclipse 3.1中支持该注释,其他IDE也可能支持它.这允许您把代码彻底地从警告中解脱出来.如果在编译时出现警告,可以确定是您刚刚把它添加进来——以帮助查看那些可能不安全的代码.随着泛型的添加,它使用起来将更趁手.
Deprecated
遗憾的是,Deprecated没那么有用.它本来旨在替换@deprecated javadoc标签,但是由于它不包含任何字段,所以也就没有方法来建议deprecated类或方法的用户应该使用什么做为替代品.大多数用法都同时需要javadoc标签和这个注释.
Override
Override表示,它所注释的方法应该重写超类中具有相同签名的方法:
@Override
public int hashCode() {
...
}
看上面的例子,如果没有在hashCode中将"C"大写,在编译时不会出现错误,但是在运行时将无法像期望的那样调用该方法.通过添加Override标签,编译器会提示它是否真正地执行了重写.
在超类发生改变的情况中,这也很有帮助.如果向该方法中添加一个新参数,而且方法本身也被重命名了,那么子类将突然不能编译,因为它不再重写超类的任何东西.
其它注释
注释在其他场景中非常有用.当不是直接修改行为而是增强行为时,特别是在添加样板代码的情况下,注释在诸如EJB和Web services这样的框架中运行得非常好.
注释不能用做预处理器.Sun的设计特别预防了完全因为注释而修改类的字节码.这样可以正确地理解该语言的成果,而且IDE之类的工具也可以执行深入的代码分析和重构之类的功能.
注释不是银弹.第一次遇到的时候,人们试图尝试各种技巧.请看下面这个从别人那里获得的建议:
public class Foo {
@Property
private int bar;
}
其思想是为私有字段bar自动创建getter和setter方法.遗憾的是,这个想法有两个失败之处:1)它不能运行,2)它使代码难以阅读和处理. 它是无法实现的,因为前面已经提到了,Sun特别阻止了对出现注释的类进行修改.
即使是可能的,它也不是一个好主意,因为它使代码可读性差.第一次看到这段代码的人会不知道该注释创建了方法.此外,如果将来您需要在这些方法内部执行一些操作,注释也是没用的. 总之,不要试图用注释去做那些常规代码可以完成的事情.
枚举
enum非常像public static final int声明,后者作为枚举值已经使用了很多年.对int所做的最大也是最明显的改进是类型安全——您不能错误地用枚举的一种类型代替另一种类型,这一点和int不同,所有的int对编译器来说都是一样的.除去极少数例外的情况,通常都应该用enum实例替换全部的枚举风格的int结构.
枚举提供了一些附加的特性.EnumMap和EnumSet这两个实用类是专门为枚举优化的标准集合实现.如果知道集合只包含枚举类型,那么应该使用这些专门的集合来代替HashMap或HashSet.
大部分情况下,可以使用enum对代码中的所有public static final int做插入替换.它们是可比的,并且可以静态导入,所以对它们的引用看起来是等同的,即使是对于内部类(或内部枚举类型).注意,比较枚举类型的时候,声明它们的指令表明了它们的顺序值.
"隐藏的"静态方法
两个静态方法出现在所有枚举类型声明中.因为它们是枚举子类上的静态方法,而不是Enum本身的方法,所以它们在java.lang.Enum的javadoc中没有出现.
第一个是values(),返回一个枚举类型所有可能值的数组.
第二个是valueOf(),为提供的字符串返回一个枚举类型,该枚举类型必须精确地匹配源代码声明.
方法
关于枚举类型,我们最喜欢的一个方面是它可以有方法.过去您可能需要编写一些代码,对public static final int进行转换,把它从数据库类型转换为JDBC URL.而现在则可以让枚举类型本身带一个整理代码的方法.下面就是一个例子,包括DatabaseType枚举类型的抽象方法以及每个枚举实例中提供的实现:
public enum DatabaseType {
ORACLE {
public String getJdbcUrl() {...}
},
MYSQL {
public String getJdbcUrl() {...}
};
public abstract String getJdbcUrl();
}
现在枚举类型可以直接提供它的实用方法.例如:
DatabaseType dbType = ...;
String jdbcURL = dbType.getJdbcUrl();
要获取URL,必须预先知道该实用方法在哪里.
可变参数(Vararg)
正确地使用可变参数确实可以清理一些垃圾代码.典型的例子是一个带有可变的String参数个数的log方法:
Log.log(String code)
Log.log(String code, String arg)
Log.log(String code, String arg1, String arg2)
Log.log(String code, String[] args)
当讨论可变参数时,比较有趣的是,如果用新的可变参数替换前四个例子,将是兼容的:
Log.log(String code, String... args)
所有的可变参数都是源兼容的——那就是说,如果重新编译log()方法的所有调用程序,可以直接替换全部的四个方法.然而,如果需要向后的二进制兼容性,那么就需要舍去前三个方法.只有最后那个带一个字符串数组参数的方法等效于可变参数版本,因此可以被可变参数版本替换.
类型强制转换
如果希望调用程序了解应该使用哪种类型的参数,那么应该避免用可变参数进行类型强制转换.看下面这个例子,第一项希望是String,第二项希望是Exception:
Log.log(Object... objects) {
String message = (String)objects[0];
if (objects.length > 1) {
Exception e = (Exception)objects[1];
// Do something with the exception
}
}
方法签名应该如下所示,相应的可变参数分别使用String和Exception声明:
Log.log(String message, Exception e, Object... objects) {...}
不要使用可变参数破坏类型系统.需要强类型化时才可以使用它.对于这个规则,PrintStream.printf()是一个有趣的例外:它提供类型信息作为自己的第一个参数,以便稍后可以接受那些类型.
协变返回
协变返回的基本用法是用于在已知一个实现的返回类型比API更具体的时候避免进行类型强制转换.在下面这个例子中,有一个返回Animal对象的Zoo接口.我们的实现返回一个AnimalImpl对象,但是在JDK 1.5之前,要返回一个Animal对象就必须声明.:
public interface Zoo {
public Animal getAnimal();
}
public class ZooImpl implements Zoo {
public Animal getAnimal(){
return new AnimalImpl();
}
}
协变返回的使用替换了三个反模式:
直接字段访问.为了规避API限制,一些实现把子类直接暴露为字段:
ZooImpl._animal
另一种形式是,在知道实现的实际上是特定的子类的情况下,在调用程序中执行向下转换:
((AnimalImpl)ZooImpl.getAnimal()).implMethod();
我看到的最后一种形式是一个具体的方法,该方法用来避免由一个完全不同的签名所引发的问题:
ZooImpl._getAnimal();
这三种模式都有它们的问题和局限性.要么是不够整洁,要么就是暴露了不必要的实现细节.
协变
协变返回模式就比较整洁、安全并且易于维护,它也不需要类型强制转换或特定的方法或字段:
public AnimalImpl getAnimal(){
return new AnimalImpl();
}
使用结果:
ZooImpl.getAnimal().implMethod();
使用泛型
我们将从两个角度来了解泛型:使用泛型和构造泛型.我们不讨论List、Set和Map的显而易见的用法.知道泛型集合是强大的并且应该经常使用就足够了.
我们将讨论泛型方法的使用以及编译器推断类型的方法.通常这些都不会出问题,但是当出问题时,错误信息会非常令人费解,所以需要了解如何修复这些问题.
泛型方法
除了泛型类型,Java 5还引入了泛型方法.在这个来自java.util.Collections的例子中,构造了一个单元素列表.新的List的元素类型是根据传入方法的对象的类型来推断的:
static
List
Collections.singletonList(T o)
示例用法:
public List getListOfOne() {
return Collections.singletonList(1);
}
在示例用法中,我们传入了一个int.所以方法的返回类型就是List
.编译器把T推断为Integer.这和泛型类型是不同的,因为您通常不需要显式地指定类型参数.
这也显示了自动装箱和泛型的相互作用.类型参数必须是引用类型:这就是为什么我们得到的是List
而不是List
.
不带参数的泛型方法
emptyList()方法与泛型一起引入,作为java.util.Collections中EMPTY_LIST字段的类型安全置换:
static
List
Collections.emptyList()
示例用法:
public List getNoIntegers() {
return Collections.emptyList();
}
与先前的例子不同,这个方法没有参数,那么编译器如何推断T的类型呢?基本上,它将尝试使用一次参数.如果没有起作用,它再次尝试使用返回或赋值类型.在本例中,返回的是List
,所以T被推断为Integer.
如果在返回语句或赋值语句之外的位置调用泛型方法会怎么样呢?那么编译器将无法执行类型推断的第二次传送.在下面这个例子中,emptyList()是从条件运算符内部调用的:
public List
getNoIntegers() {
return x ? Collections.emptyList() : null;
}
因为编译器看不到返回上下文,也不能推断T,所以它放弃并采用Object.您将看到一个错误消息,比如:"无法将List