JDK1.5到1.7的进化
1. JDK1.5
1.1 枚举
增加了一个关键字enum。
enum非常像public static final int声明,后者作为枚举值已经使用了很多年。对int所做的最大也是最明显的改进是类型安全――您不能错误地用枚举的一种类型代替另一种类型,这一点和 int不同,所有的int对编译器来说都是一样的。除去极少数例外的情况,通常都应该用enum实例替换全部的枚举风格的int结构。
枚举提供了一些附加的特性。EnumMap和EnumSet这两个实用类是专门为枚举优化的标准集合实现。如果知道集合只包含枚举类型,那么应该使用这些专门的集合来代替HashMap或HashSet。
大部分情况下,可以使用enum对代码中的所有public static final int做插入替换。它们是可比的,并且可以静态导入,所以对它们的引用看起来是等同的,即使是对于内部类(或内部枚举类型)。注意,比较枚举类型的时候,声明它们的指令表明了它们的顺序值。
1.2 可变参数
方法参数数量可以不固定多少,正确地使用可变参数确实可以清理一些垃圾代码。
1.3 泛型
在Collection API中加入的泛型API,用于编译时验证集合中元素类型。
泛型不仅可以使用,还可以构造对应的泛型类型或泛型方法,并支持通配符。
1.4 自动拆箱和自动装箱
基本类型与包装类型可混用
1.5 Foreach循环
用于遍历数组和集合而避免了下标的使用
1.6 静态导入
可使用通配符进行导入其中的静态方法和静态变量,不需要设置类名称
1.7 Annotation
采用@注释,为简化JavaEE准备的,在JavaEE中的EJB3, JPA等随处可见
Suppress Warnings
该注释关闭了类或方法级别的编译器警告。有时候你比编译器更清楚地知道,代码必须使用一个被否决的方法或执行一些无法静态确定是否类型安全的动作,而使用:
2 JDK 1.6
2.1 Desktop类和SystemTray类
在JDK6中 ,AWT新增加了两个类:Desktop和SystemTray。
前者可以用来打开系统默认浏览器浏览指定的URL,打开系统默认邮件客户端给指定的邮箱发邮件,用默认应用程序打开或编辑文件(比如,用记事本打开以txt为后缀名的文件),用系统默认的打印机打印文档;后者可以用来在系统托盘区创建一个托盘程序.
2.2 使用JAXB2来实现对象与XML之间的映射
JAXB是Java Architecture for XML Binding的缩写,可以将一个Java对象转变成为XML格式,反之亦然。
我们把对象与关系数据库之间的映射称为ORM, 其实也可以把对象与XML之间的映射称为OXM(Object XML Mapping). 原来JAXB是Java EE的一部分,在JDK6中,SUN将其放到了Java SE中,这也是SUN的一贯做法。JDK6中自带的这个JAXB版本是2.0, 比起1.0(JSR 31)来,JAXB2(JSR 222)用JDK5的新特性Annotation来标识要作绑定的类和属性等,这就极大简化了开发的工作量。
实际上,在Java EE 5.0中,EJB和Web Services也通过Annotation来简化开发工作。另外,JAXB2在底层是用StAX(JSR 173)来处理XML文档。除了JAXB之外,我们还可以通过XMLBeans和Castor等来实现同样的功能。
2.3 理解StAX
StAX是The Streaming API for XML的缩写,一种利用拉模式解析(pull-parsing)XML文档的API。StAX通过提供一种基于事件迭代器(Iterator)的API让程序员去控制xml文档解析过程,程序遍历这个事件迭代器去处理每一个解析事件,解析事件可以看做是程序拉出来的,也就是程序促使解析器产生一个解析事件然后处理该事件,之后又促使解析器产生下一个解析事件,如此循环直到碰到文档结束符;
SAX也是基于事件处理xml文档,但却是用推模式解析,解析器解析完整个xml文档后,才产生解析事件,然后推给程序去处理这些事件;DOM采用的方式是将整个xml文档映射到一颗内存树,这样就可以很容易地得到父节点和子结点以及兄弟节点的数据,但如果文档很大,将会严重影响性能。
2.4 使用Compiler API
现在我们可以用JDK6的Compiler API(JSR 199)去动态编译Java源文件,Compiler API结合反射功能就可以实现动态的产生Java代码并编译执行这些代码,有点动态语言的特征。
这个特性对于某些需要用到动态编译的应用程序相当有用,比如JSP Web Server,当我们手动修改JSP后,是不希望需要重启Web Server才可以看到效果的,这时候我们就可以用Compiler API来实现动态编译JSP文件,当然,现在的JSP Web Server也是支持JSP热部署的,现在的JSP Web Server通过在运行期间通过Runtime.exec或ProcessBuilder来调用javac来编译代码,这种方式需要我们产生另一个进程去做编译工作,不够优雅而且容易使代码依赖与特定的操作系统;Compiler API通过一套易用的标准的API提供了更加丰富的方式去做动态编译,而且是跨平台的。
2.5 轻量级Http Server API
JDK6 提供了一个简单的Http Server API,据此我们可以构建自己的嵌入式Http Server,它支持Http和Https协议,提供了HTTP1.1的部分实现,没有被实现的那部分可以通过扩展已有的Http Server API来实现,程序员必须自己实现HttpHandler接口,HttpServer会调用HttpHandler实现类的回调方法来处理客户端请求,在这里,我们把一个Http请求和它的响应称为一个交换,包装成HttpExchange类,HttpServer负责将HttpExchange传给 HttpHandler实现类的回调方法。
2.6 插入式注解处理API(Pluggable Annotation Processing API)
插入式注解处理API(JSR 269)提供一套标准API来处理Annotations(JSR 175)
实际上JSR 269不仅仅用来处理Annotation,我觉得更强大的功能是它建立了Java 语言本身的一个模型,它把method, package, constructor, type, variable, enum, annotation等Java语言元素映射为Types和Elements(两者有什么区别?), 从而将Java语言的语义映射成为对象, 我们可以在javax.lang.model包下面可以看到这些类. 所以我们可以利用JSR 269提供的API来构建一个功能丰富的元编程(metaprogramming)环境.
JSR 269用Annotation Processor在编译期间而不是运行期间处理Annotation, Annotation Processor相当于编译器的一个插件,所以称为插入式注解处理.如果Annotation Processor处理Annotation时(执行process方法)产生了新的Java代码,编译器会再调用一次Annotation Processor,如果第二次处理还有新代码产生,就会接着调用Annotation Processor,直到没有新代码产生为止.每执行一次process()方法被称为一个"round",这样整个Annotation processing过程可以看作是一个round的序列.
JSR 269主要被设计成为针对Tools或者容器的API. 举个例子,我们想建立一套基于Annotation的单元测试框架(如TestNG),在测试类里面用Annotation来标识测试期间需要执行的测试方法。
2.7 用Console开发控制台程序
JDK6中提供了java.io.Console 类专用来访问基于字符的控制台设备. 你的程序如果要与Windows下的cmd或者Linux下的Terminal交互,就可以用Console类代劳. 但我们不总是能得到可用的Console, 一个JVM是否有可用的Console依赖于底层平台和JVM如何被调用. 如果JVM是在交互式命令行(比如Windows的cmd)中启动的,并且输入输出没有重定向到另外的地方,那么就可以得到一个可用的Console实例.
2.8 对脚本语言的支持
ruby, groovy, javascript
2.9 Common Annotations
Common annotations原本是Java EE 5.0(JSR 244)规范的一部分,现在SUN把它的一部分放到了Java SE 6.0中.
随着Annotation元数据功能(JSR 175)加入到Java SE 5.0里面,很多Java 技术(比如EJB,Web Services)都会用Annotation部分代替XML文件来配置运行参数(或者说是支持声明式编程,如EJB的声明式事务), 如果这些技术为通用目的都单独定义了自己的Annotations,显然有点重复建设, 所以,为其他相关的Java技术定义一套公共的Annotation是有价值的,可以避免重复建设的同时,也保证Java SE和Java EE 各种技术的一致性.
下面列举出Common Annotations 1.0里面的10个Annotations Common Annotations :
l Annotation Retention Target Description;
l Generated Source ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE 用于标注生成的源代码;
l Resource Runtime TYPE, METHOD, FIELD 用于标注所依赖的资源,容器据此注入外部资源依赖,有基于字段的注入和基于setter方法的注入两种方式;
l Resources Runtime TYPE 同时标注多个外部依赖,容器会把所有这些外部依赖注入;
l PostConstruct Runtime METHOD 标注当容器注入所有依赖之后运行的方法,用来进行依赖注入后的初始化工作,只有一个方法可以标注为PostConstruct;
l PreDestroy Runtime METHOD 当对象实例将要被从容器当中删掉之前,要执行的回调方法要标注为PreDestroy RunAs Runtime TYPE 用于标注用什么安全角色来执行被标注类的方法,这个安全角色必须和Container 的Security角色一致的。RolesAllowed Runtime TYPE, METHOD 用于标注允许执行被标注类或方法的安全角色,这个安全角色必须和Container 的Security角色一致的;
l PermitAll Runtime TYPE, METHOD 允许所有角色执行被标注的类或方法;
l DenyAll Runtime TYPE, METHOD 不允许任何角色执行被标注的类或方法,表明该类或方法不能在Java EE容器里面运行;
l DeclareRoles Runtime TYPE 用来定义可以被应用程序检验的安全角色,通常用isUserInRole来检验安全角色。
3 JDK 1.7
3.1 二进制字面值(Binary Literals)
在java7里,整形(byte,short,int,long)类型的值可以用二进制类型来表示了,在使用二进制的值时,需要在前面加上ob或Ob,
int a = 0b01111_00000_11111_00000_10101_01010_10; short b = (short)0b01100_00000_11111_0; byte c = (byte)0B0000_0001;
其次,二进制同十进制和十六进制相比,可以一目了然的看出数据间的关系。例如下面这个数组中展示了每次移动一位后数字的变化。
public static final int[] phases = { 0b00110001, 0b01100010, 0b11000100, 0b10001001, 0b00010011, 0b00100110, 0b01001100, 0b10011000 }
如果用十六进制来表示的,它们之间的关系就无法一眼看出来了。
public static final int[] phases = { 0x31, 0x62, 0xC4, 0x89, 0x13, 0x26, 0x4C, 0x98 }
3.2 数字变量对下划线_的支持
可以在数值类型的变量里添加下滑线,除了以下的几个地方不能添加:
l 数字的开头和结尾
l 小数点前后
l F或者L前
需要出现string类型值的地方(针对用0x或0b表示十六进制和二进制,参考第一点),比如0x101,不能用0_x101
num = 1234_5678_9; float num2 = 222_33F; long num3 = 123_000_111L; //下面的不行 //数字开头和结尾 int nu = _123; int nu = 123_; //小数点前后 float f = 123_.12; float f = 123._12; //F或者L前 long l = 123_L; float f = 123_F; //需要出现String的地方 int num = 0_b123; float f = 0_x123F;
这个除了可以提升代码的可读性没什么作用。
3.3 switch 对String的支持
这个大家期待已久,switch终于支持String了
public static void first() { //项目状态 String status = "approval"; //我们之前经常根据项目状态不同来进行不同的操作 //目前已经换成enum类型 switch (status) { case "shouli": System.out.println("状态是受理"); break; case "approval": System.out.println("状态是审批"); break; case "finish": System.out.println("状态是结束"); break; default: System.out.println("状态未知"); } }
每个case是使用String的equals方法来进行比较的,对大小写敏感。
3.4 try-with-resources 声明
try-with-resources 是一个定义了一个或多个资源的try 声明,这个资源是指程序处理完它之后需要关闭它的对象。try-with-resources 确保每一个资源在处理完成后都会被关闭。
可以使用try-with-resources的资源有:
任何实现了java.lang.AutoCloseable 接口和java.io.Closeable 接口的对象。
public static String readFirstLineFromFile(String path) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); } }
在java 7 以及以后的版本里,BufferedReader实现了java.lang.AutoCloseable接口。
由于BufferedReader定义在try-with-resources 声明里,无论try语句正常还是异常的结束, 它都会自动的关掉。而在java7以前,你需要使用finally块来关掉这个对象。
public static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException { BufferedReader br = new BufferedReader(new FileReader(path)); try { return br.readLine(); } finally { if (br != null) br.close(); } }
然而,如果 readLine() 和 close() 这两个方法都抛出异常,那么readFirstLineFromFileWithFinallyBlock 方法只会抛出后面部分也就是finally块中的内容,try块中的异常就被抑制了,对于我们的程序来说,这显然不是一种好的方式。
而在java 7中,无论是try块还是try-with-resource中抛出异常,都能捕捉到。
另外,一个try-with-resourcse声明了可以包含多个对象的声明,用分号隔开,和声明一个对象相同,会在结束后自动调用close方法,调用顺序和生命顺序相反。
try ( java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName); java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset) ) { // do something }
此外,try-with-resources 可以跟catch和finally,catch和finally的是在try-with-resources里声明的对象关闭之后才执行的。
3.5 捕获多种异常并用改进后的类型检查来重新抛出异常
在Java SE7里,一个catch可以捕获多个异常,这样可以减少重复代码。每个异常之间用 | 隔开。
public static void first(){ try { BufferedReader reader = new BufferedReader(new FileReader("")); Connection con = null; Statement stmt = con.createStatement(); } catch (IOException | SQLException e) { //捕获多个异常,e就是final类型的 e.printStackTrace(); } }
注意,如果一个
catch处理了多个异常,那么这个catch的参数默认就是final的,你不能在catch块里修改它的值。
另外,用一个catch处理多个异常,比用多个catch每个处理一个异常生成的字节码要更小更高效。
3.6 用更包容性的类型检查来重新抛出异常
在方法的声明上,使用throws语句时,你可以指定更加详细的异常类型。
static class FirstException extends Exception { } static class SecondException extends Exception { } public void rethrowException(String exceptionName) throws Exception { try { if (exceptionName.equals("First")) { throw new FirstException(); } else { throw new SecondException(); } } catch (Exception e) { throw e; } }
这个例子,
try块中只能抛出两种异常,但是因为catch里的类型是 Exception,在java SE7以前的版本中,在方法声明中throws 只能写Exception,但是在java SE7及以后的版本中,可以在throws后面写 FirstException和SecondException——编译器能判断出throw e语句抛出的异常一定来自try块,并且try块只能抛出FirstException和SecondException。
public static void reThrowException(String exceptionName) throws FirstException, SecondException{ try { if ("first".equals(exceptionName)) throw new FirstException(); else throw new SecondException(); } catch (Exception e) { throw e; } }
所以尽管
catch里的异常类型是Exception,编译器仍然能够知道它是FirstException和 SecondException的实例。怎么样,编译器变得更智能了吧。
但是,如果在catch里对异常重新赋值了,在方法的throws后无法再象上面那样写成FirstException和SecondException了,而需要写成 Exception。
具体来说,在Java SE 7及以后版本中,当你在catch语句里声明了一个或多个异常类型,并且在catch块里重新抛出了这些异常,编译器根据下面几个条件来去核实异常的类型:
l Try块里抛出它
l 前面没有catch块处理它
l 它是catch里一个异常类型的父类或子类。
3.7 创建泛型对象时类型推断
只要编译器可以从上下文中推断出类型参数,你就可以用一对空着的尖括号<>来代替泛型参数。这对括号私下被称为菱形(diamond)。在Java SE 7之前,你声明泛型对象时要这样
List<String> list = new ArrayList<String>();
而在Java SE7以后,你可以这样:
List<String> list = new ArrayList<>();
因为编译器可以从前面(List)推断出推断出类型参数,所以后面的ArrayList之后可以不用写泛型参数了,只用一对空着的尖括号就行。当然,你必须带着”菱形”<>,否则会有警告的。
Java SE7 只支持有限的类型推断:只有构造器的参数化类型在上下文中被显著的声明了,你才可以使用类型推断,否则不行。
已有 0 人发表留言,猛击->> 这里<<-参与讨论
ITeye推荐