jdk8已经出来好长时间了,这里自己学习时简单总结的jdk5,jdk6和jdk7的新特性:
本文提纲:
一.jdk5的新特性
二.jdk6的新特性
三.jdk7的新特性
一.jdk5的新特性
首先简单介绍一下各种特性及其使用
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)
A、类型安全
抛弃List、Map,使用List、Map给它们添加元素或者使用Iterator遍历时,编译期就可以给你检查出类型错误
B、方法参数和返回值加上了Type
抛弃List、Map,使用List、Map
C、不需要类型转换
List list=new ArrayList();
String str=list.get(i);
D、类型通配符“?”
假设一个打印List中元素的方法printList,我们希望任何类型T的List都可以被打印:
代码:
public void printList(List> list,PrintStream out)throws IOException{
for(Iterator> i=list.iterator();i.hasNext();){
System.out.println(i.next.toString());
}
}
如果通配符?让我们的参数类型过于广泛,我们可以把List>、Iterator> 修改为
List Extends Number>、Iterator Extends Number>限制一下它。
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之前,我们总是对集合不能存放基本类型而耿耿于怀,现在自动转换机制解决了我们的问题。
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)可变参数使程序员可以声明一个接受可变数目参数的方法。注意,可变参数必须是函数声明中的最后一个参数。
假设我们要写一个简单的方法打印一些对象,
在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);
但是,过度使用这个特性也会一定程度上降低代码地可读性。
6.Annotations 它是java中的metadata
A.Tiger中预定义的三种标准annotation
(1).Override 方法重载
指出某个method覆盖了superclass 的method当你要覆盖的方法名拼写错时编译不通过
(2).Deprecated 方法过时
指出某个method或element类型的使用是被阻止的,子类将不能覆盖该方法
(3).SupressWarnings 编译器警告
关闭class、method、field、variable 初始化的编译期警告,比如:List没有使用 Generic,则@SuppressWarnings("unchecked")去掉编译期警告。
B.自定义annotation
public @interface Marked{}
C.meta-annotation
或者说annotation的annotation
四种标准的meta-annotation全部定义在java.lang.annotaion包中:
Target
指定所定义的annotation可以用在哪些程序单元上
如果Target没有指定,则表示该annotation可以使用在任意程序单元上
代码
@Target({ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR,
ElementType.FIELD,
ElementType.LOCAL_VARIABLE,
ElementType.METHOD,
ElementType.PACKAGE,
ElementType.PARAMETER,
ElementType.TYPE})
public @interface TODO {}
Annotation的反射
我们发现java.lang.Class有许多与Annotation的反射相关的方法,如getAnnotations、isAnnotationpresent
我们可以利用Annotation反射来做许多事情,比如自定义Annotation来做Model对象验证
代码
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface RejectEmpty {
/** hint title used in error message */
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.FIELD, ElementType.METHOD })
public @interface AcceptInt {
int min() default Integer.MIN_VALUE;
int max() default Integer.MAX_VALUE;
String hint() default "";
}
使用@RejectEmpty和@AcceptInt标注我们的Model的field,然后利用反射来做Model验证
7.新的格式化方法(java.util.Formatter)
formatter.format("Remaining account balance: $%.2f", balance);
8.新的线程模型和并发库Thread Framework
HashMap的替代者ConcurrentHashMap和ArrayList的替代者CopyOnWriteArrayList
在大并发量读取时采用java.util.concurrent包里的一些类会让大家满意BlockingQueue、Callable、Executor、Semaphore...
二.jdk6的新特性
1.引入了一个支持脚本引擎的新框架
2.UI的增强
3.对WebService支持的增强(JAX-WS2.0和JAXB2.0)
4.一系列新的安全相关的增强
5.JDBC4.0
6.Compiler API
7.通用的Annotations支持
三.jdk7的新特性
1.二进制字面量
JDK7开始,终于可以用二进制来表示整数(byte,short,int和long)。使用二进制字面量的好处是,可以是
代码更容易被理解。语法非常简单,只要在二进制数值前面加0b或者0B
byte nByte = (byte)0b0001;
short nShort = (short)0B0010;
int nInt = 0b0011;
long nLong = 0b0100L;
2.数字字面量可以出现下划线
对于一些比较大的数字,我们定义起来总是不方面,经常缺少或者增加位数。JDK7为我们提供了一种解决
方案,下划线可以出现在数字字面量。
int a = 10_0000_0000;
long b = 0xffff_ffff_ffff_ffffl;
byte c = 0b0001_1000;
注意:你只能将下划线置于数字之间,以下使用方法是错误的,
(1).数字的开头或者结尾
(2).小数点的前后
(3).‘F’或者‘f’的后缀
(4).只能用数字的位置
int err1 = _11,err2=11_;
float err3=3._4,err4=3_.4;
long err5=0x888_f;
3.switch 语句可以用字符串了
private static void switchString(String str){
switch(str){
case "one":
System.err.println("1");
break;
case "two":
System.out.println("2");
break;
default :
System.out.println("err");
}
}
4.泛型实例的创建可以通过类型推断来简化
以后你创建一个泛型实例,不需要再详细说明类型,只需用<>,编译器会自动帮你匹配
#例如
Map> myMap = new HashMap>();
#可以简化为
Map> myMap = new HashMap<>();
5.在可变参数方法中传递非具体化参数(Non-Reifiable Formal Parameters),改进编译警告和错误
有些参数类型,例如ArrayList 和List,是非具体化的(non-reifiable).在编译阶段,
编译器会擦除该类型信息。
Heap pollution 指一个变量被指向另外一个不是相同类型的变量。例如
List l = new ArrayList();
List ls = l; // unchecked warning
l.add(0, new Integer(42)); // another unchecked warning
String s = ls.get(0); // ClassCastException is thrown
回到我们的主题,在jdk7中,当你定义下面的函数时
public static void addToList (List listArg, T... elements) {
for (T x : elements) {
listArg.add(x);
}
}
你会得到一个warning
warning: [varargs] Possible heap pollution from parameterized vararg type
在jdk7之前,当你调用一个含有非具体化参数的可变参数方法,你必须自行保证不会发生“heap
pollution”。这有一个问题,如果调用者对方法不熟悉,他根本无法判断。JDK7对此做了改进,在该方法被定
义时久发出警告
1.加 annotation @SafeVarargs
2.加 annotation @SuppressWarnings({"unchecked", "varargs"})
3.使用编译器参数 –Xlint:varargs;
6.try-with-resources 语句
jdk7提供了try-with-resources,可以自动关闭相关的资源(只要该资源实现了AutoCloseable接口,jdk7为绝
大部分资源对象都实现了这个接口)
static String readFirstLineFromFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
try 语句块中还可以同时处理多个资源,可以跟普通的try语句一样catch异常,有finally语句块
try (
java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);
java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
) {
}catch(…){
}finally{
}
7.Catch多个Exception,rethrow exception 改进了类型检测
很多时候,我们捕获了多个异常,却做了相同的事情,比如记日志,包装成新的异常,然后rethrow。这
时,代码就不那么优雅了,例如
catch (IOException ex) {
logger.log(ex);
throw ex;
catch (SQLException ex) {
logger.log(ex);
throw ex;
}
Jdk7允许捕获多个异常
catch (IOException|SQLException ex) {
logger.log(ex);
throw ex;
}
注意,catch后面的异常参数是final的,不能重新再复制
当你重新抛出多个异常时,不再需要详细定义异常类型了,编译器已经知道你具体抛出的是哪个异常了。你
只需在方法定义的时候声明需要抛出的异常即可
public void call() throws ReflectiveOperationException, IOException {
try {
callWithReflection(arg);
} catch (final Exception e) {
logger.trace("Exception in reflection", e);
throw e;
}
}
8.JDBC4.1更新了两个新特性
1. Connection,ResultSet 和Statement 都实现了Closeable 接口,所有在try-with-resources 语句中调
用,就可以自动关闭相关资源了
try (Statement stmt = con.createStatement()){
…
}
2. RowSet 1.1:引入RowSetFactory接口和RowSetProvider类,可以创建JDBC driver支持的各种row sets
RowSetFactory myRowSetFactory = null;
JdbcRowSet jdbcRs = null;
ResultSet rs = null;
Statement stmt = null;
try {
myRowSetFactory = RowSetProvider.newFactory();//用缺省的RowSetFactory 实现
jdbcRs = myRowSetFactory.createJdbcRowSet();
//创建一个 JdbcRowSet 对象,配置数据库连接属性
jdbcRs.setUrl("jdbc:myDriver:myAttribute");
jdbcRs.setUsername(username);
jdbcRs.setPassword(password);
jdbcRs.setCommand("select ID from TEST");
jdbcRs.execute();
}
RowSetFactory 接口包括了创建不同类型的RowSet的方法
createCachedRowSet
createFilteredRowSet
createJdbcRowSet
createJoinRowSet
createWebRowSet
9.更新了NIO2.0文件系统
java.io.File 不够完美吧。Jdk7提供了一套新的文件系统,会让你满意的。
先来聊聊java.io.File的七宗罪吧:)
1.很多方法失败时候都没有抛出异常,很难查找原因
2.方法 rename 在不同平台中运行有问题
3.不能真正支持 symbolic links
4.不能读取文件的更详细属性,比如权限,所有者……
5.访问 文件的 metadata 效率低下
6.很多方法性能不行。例如处理比较大的目录
7.无法递归查找文件树,以及存在循环的symbolic links可能造成问题
主要包括:
FileSystem 提供了许多方法来获得当前文件系统的相关信息。
Path 处理路径(文件和目录),包括
创建path,Paths.get(String s)
获得path的详细信息 getName(),getXX()…
删除path的冗余信息 toRealPath
转换path toAbsolutePath()
合并两个path resolve()
在两个path之间创建相对路径 relativeze()
比较路径 equal() startsWith(),endWith()
Files 支持各种文件操作,包括
移动文件,
复制文件,
删除文件,
更详细的文件属性,包括文件权限,创建者,修改时间……
Walking the File Tree(递归遍历文件树)
Watch a Directory for Change (监听文件更改)
9.异步IO AIO
概述
JDK7引入了Asynchronous I/O。I/O编程中,常用到两种模式:Reactor 和Proactor。Reactor就
是Java的NIO。当有事件触发时,我们得到通知,进行相应的处理。Proactor就是我们今天要讲的AIO了。
AIO进行I/O操作,都是异步处理,当事件完成时,我们会得到通知。
JDK7的AIO包括网络和文件操作。两者大同小异,本文通过一个完整的客户端/服务器Sample来详细说
明aio的网络操作。
AIO提供了两种异步操作的监听机制。第一种通过返回一个Future对象来事件,调用其get()会等到操作
完成。第二种类似于回调函数。在进行异步操作时,传递一个CompletionHandler,当异步操作结束时,会调
用CompletionHandler.complete 接口