一直对JDK每个版本特性不甚了解,当项目开发时进行JDK选择时也比较盲目。一味的追求最新的版本不可取,但沉迷于老版本而不愿意去尝试新版本带来的便利性也同样不可取,所以这次整理下各版本的特性,一是让自己能够清楚认知,另一方面在将来选择版本时游刃有余。
JDK1.5(代号Tiger)特性:
1. 泛型(Generic)
在JDK1.5之前一个集合可以放任何类型的对象,相应地从集合里面拿对象的时候我们也不得不对他们进行强制的类型转换。引入了泛型之后,它允许指定集合里元素的类型,这样在编译阶段就会对对象进行类型检查。
2. for-each循环(Enhanced for loop)
3. 自动拆装箱(Autoboxing/unboxing)
- 自动拆装箱大大方便了基本类型数据和它们包装类的使用
- 自动装箱:基本类型自动转为包装类(int >> Integer)
- 自动拆箱:包装类自动转为基本类型(Integer >> int)
- 在JDK1.5之前,我们总是对集合不能存放基本类型而耿耿于怀,现在自动转换机制能解决我们的问题
4. 类型安全的枚举(Type safe enums)
JDK1.5加入了一个全新的“类”--枚举类型。为此JDK1.5引入了一个新关键字enum
5. 可变参数(Var args)
可变参数使程序员可以声明一个接受可变数目参数的方法。注意,可变参数必须是函数生命中的最后一个参数。
6. 静态导入(static import)
要使用静态成员(方法和变量)我们给出提供这个方法的类。使用静态导入可以使被导入类的所有静态变量和静态方法在当前类直接可见,使用这些静态成员无需再给出他们的类名
import static java.lang.Math.*;
...
r = sin(PI * 2);//无需再写 r = Math.sin(Math.PI * 2);
7. 元数据(Metadata)
JDK1.6 (代号Mustang)特性:
1. Desktop类和SystemTray
2. 使用JAXB2来实现对象与XML之间的映射
3. 理解StAX
4. 使用Compiler API
5. 轻量级Http Server API
6. 插入式注解处理API(Pluggable Annotation Processing API)
7. 用Console开发控制台程序
8. 对脚本语言的支持
9. Common Annotations
JDK1.7 特性:
1. switch语句支持字符串变量
switch 语句比较表达式中的String对象和每个case标签关联的表达式,就好像它是在使用String.equals方法一样;因此,switch语句中 String对象的比较是大小写敏感的。相比于链式的if-then-else语句,Java编译器通常会从使用String对象的switch语句中生成更高效的字节码。
2. 泛型实例化类型自动推断
ArrayList<String> al1 = new ArrayList<String>(); // Old
ArrayList<String> al2 = new ArrayList<>(); // New
3. 支持二进制数字字面量
从JDK1.7开始,可以用二进制来表示整数(byte,short,int和long)。使用二进制字面量的好处是,可以是代码更容易被理解。语法非常简单,只要在二进制数值前面加 0b或者0B。
byte nByte = (byte)0b0001;
short nShort = (short)0B0010;
int nInt = 0b0011;
long nLong = 0b0100L;
4. 数字字面量可以出现下划线
用下划线连接整数提升其可读性,自身无含义,不可用在数字的起始和末尾。
Java编码语言对给数值型的字面值加下划线有严格的规定。如上所述,你只能在数字之间用下划线。你不能用把一个数字用下划线开头,或者已下划线结尾。这里有一些其它的不能在数值型字面值上用下划线的地方:
- 在数字的开始或结尾
- 对浮点型数字的小数点附件
- F或L下标的前面
- 该数值型字面值是字符串类型的时候
float pi1 = 3_.1415F; // 无效的; 不能在小数点之前有下划线
float pi2 = 3._1415F; // 无效的; 不能在小数点之后有下划线
long socialSecurityNumber1=999_99_9999_L;//无效的,不能在L下标之前加下划线
int a1 = _52; // 这是一个下划线开头的标识符,不是个数字
int a2 = 5_2; // 有效
int a3 = 52_; // 无效的,不能以下划线结尾
int a4 = 5_______2; // 有效的
int a5 = 0_x52; // 无效,不能在0x之间有下划线
int a6 = 0x_52; // 无效的,不能在数字开头有下划线
int a7 = 0x5_2; // 有效的 (16进制数字)
int a8 = 0x52_; // 无效的,不能以下划线结尾
int a9 = 0_52; // 有效的(8进制数)
int a10 = 05_2; // 有效的(8进制数)
int a11 = 052_; // 无效的,不能以下划线结尾
5. 单个catch中捕获多个异常
在Java 7中,catch代码块得到了升级,用以在单个catch块中处理多个异常。如果你要捕获多个异常并且它们包含相似的代码,使用这一特性将会减少代码重复度。下面用一个例子来理解。
catch(IOException | SQLException | Exception ex){
logger.error(ex);
throw new MyException(ex.getMessage());
}
6. try-with-resources语句
try-with-resources语句是一个声明一个或多个资源的try语句。一个资源作为一个对象,必须在程序结束之后关闭。try-with-resources语句确保在语句的最后每个资源都被关闭,任何实现了Java.lang.AutoCloseable和java.io.Closeable的对象都可以使用try-with-resource来实现异常处理和关闭资源。
下面通过对比来体会这个新特性。
JDK1.7之前:
/**
* JDK1.7之前我们必须在finally块中手动关闭资源,否则会导致资源的泄露
* @author Liao
*
*/
public class PreJDK7 {
public static String readFirstLingFromFile(String path) throws IOException {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(path));
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
} finally {//必须在这里关闭资源
if (br != null)
br.close();
}
return null;
}
}
JDK1.7及以后版本:
/**
* JDK1.7之后就可以使用try-with-resources,不需要
* 我们在finally块中手动关闭资源
* @author Liao
*/
public class AboveJDK7 {
static String readFirstLineFromFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
}
JDK1.8 特性:
1. 接口的默认方法
Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法,示例如下:
public interface Formula {
double calculate(int a);
// jdk8能给接口添加一个非抽象的方法实现
default double sqrt(int a){
return Math.sqrt(a);
}
}
2. Lambda表达式
@Test
public void test2() {
// 如果用Lambda表达式,一定要写明泛型
List<String> list = Arrays.asList("peter","anna","make");
// ①.老版本的Java中是这样排列字符串的
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
// ②.Java 8提供了更简洁的语法,lambda表达式:
/*
Collections.sort(list ,(String a,String b) -> {
return a.compareTo(b);
});
// ③.还可以写得更短
Collections.sort(list, (String a, String b) -> a.compareTo(b));
// ④.还可以这么写
Collections.sort(list, String::compareTo);
System.out.println(Collections.singletonList(list)); //[[anna, make, peter]]
}
什么是λ表达式
λ表达式本质上是一个匿名方法。让我们来看下面这个例子:
public int add(int x, int y) {
return x + y;
}
转成λ表达式后是这个样子:
(int x, int y) -> x + y;
参数类型也可以省略,Java编译器会根据上下文推断出来:
(x, y) -> x + y; //返回两数之和
或者
(x, y) -> { return x + y; } //显式指明返回值
可见λ表达式有三部分组成:参数列表,箭头(->),以及一个表达式或语句块。
下面这个例子里的λ表达式没有参数,也没有返回值(相当于一个方法接受0个参数,返回void,其实就是Runnable里run方法的一个实现):
() -> { System.out.println("Hello Lambda!"); }
如果只有一个参数且可以被Java推断出类型,那么参数列表的括号也可以省略:
c -> { return c.size(); }
lambda包含3个部分:
(1)括弧包起来的参数
(2)一个箭头
(3)方法体,可以是单个语句,也可以是语句块
参数可以写类型,也可以不写,jvm很智能的,它能自己推算出来
方法可以有返回,也可以无返回,如果有多个语句,还要返回值,需要加上return 。
3. 函数式接口
(1) 是一个接口
(2) 只有一个待实现的方法 !!!!
因JDK1.8开始,接口可以有default方法,所以,函数式接口也是可以有default方法的,但是,只能有一个未实现的方法。
与此对应,新引入了一个注解: @FunctionalInterface。这个注解只是起文档的作用,说明这个接口是函数式接口,编译器并不会使用这个注解来决定一个接口是不是函数式接口。不管加不加@FunctionalInterface这个注解,下面的接口都是函数式接口:
interface Something {
public String doit(Integer i);
}
Lambda表达式是如何在java的类型系统中表示的呢?每一个lambda表达式都对应一个类型,通常是接口类型。而“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为默认方法 不算抽象方法,所以你也可以给你的函数式接口添加默认方法。
我们可以将lambda表达式当作任意只包含一个抽象方法的接口类型,确保你的接口一定达到这个要求,你只需要给你的接口添加 @FunctionalInterface 注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的。
4. 方法引用
通常与Lambda表达式联合使用,可以直接引用已有Java类或对象的方法。一般有四种不同的方法引用:构造器引用。语法是Class::new,或者更一般的Class< T >::new,要求构造器方法是没有参数;静态方法引用。语法是Class::static_method,要求接受一个Class类型的参数;特定类的任意对象方法引用。它的语法是Class::method。要求方法是没有参数的;特定对象的方法引用,它的语法是instance::method。要求方法接受一个参数,与3不同的地方在于,3是在列表元素上分别调用方法,而4是在某个对象上调用方法,将列表元素作为参数传入;5. 重复注解
在Java 5中使用注解有一个限制,即相同的注解在同一位置只能声明一次。Java 8引入重复注解,这样相同的注解在同一地方也可以声明多次。重复注解机制本身需要用@Repeatable注解。Java 8在编译器层做了优化,相同注解会以集合的方式保存,因此底层的原理并没有变化。
6. 扩展注解的支持
Java 8扩展了注解的上下文,几乎可以为任何东西添加注解,包括局部变量、泛型类、父类与接口的实现,连方法的异常也能添加注解。
7. Optional
Java 8引入Optional类来防止空指针异常,Optional类最先是由Google的Guava项目引入的。Optional类实际上是个容器:它可以保存类型T的值,或者保存null。使用Optional类我们就不用显式进行空指针检查了。
8. Stream
Stream API是把真正的函数式编程风格引入到Java中。其实简单来说可以把Stream理解为MapReduce,当然Google的MapReduce的灵感也是来自函数式编程。她其实是一连串支持连续、并行聚集操作的元素。从语法上看,也很像linux的管道、或者链式编程,代码写起来简洁明了,非常酷帅!
9. Date/Time API (JSR 310)
Java 8新的Date-Time API (JSR 310)受Joda-Time的影响,提供了新的java.time包,可以用来替代 java.util.Date和java.util.Calendar。一般会用到Clock、LocaleDate、LocalTime、LocaleDateTime、ZonedDateTime、Duration这些类,对于时间日期的改进还是非常不错的。
10. JavaScript引擎Nashorn
Nashorn允许在JVM上开发运行JavaScript应用,允许Java与JavaScript相互调用。
11. Base64
在Java 8中,Base64编码成为了Java类库的标准。Base64类同时还提供了对URL、MIME友好的编码器与解码器。