【这是 ZY 第 11 篇原创技术文章】javascript
某天在网上闲逛,忽然看到有篇介绍 Java 11 新特性的文章,顿时内心一惊,毕竟我对于 Java 的版本认识还停留在 Java 8 上,而平常使用的语法和 API 还停留在 Java 7 上。因而抽时间看了看 Java 8 之后各个版本的特性,作了一个总结。html
JDK 全称 Java Development Kit,是 Java 开发环境。咱们一般所说的 JDK 指的是 Java SE (Standard Edition) Development Kit。除此以外还有 Java EE(Enterprise Edition)和 Java ME(Micro Edition platforms)。java
版本 | 发布时间 | 名称 |
---|---|---|
JDK Beta | 1995 | WebRunner |
JDK 1.0 | 1996.1 | Oak |
JDK 1.1 | 1997.2 | |
J2SE 1.2 | 1998.12 | Playground |
J2SE 1.3 | 2000.5 | Kestrel |
J2SE 1.4 | 2002.2 | Merlin |
J2SE 5.0 | 2004.9 | Tiger |
Java SE 6 | 2006.12 | Mustang |
Java SE 7 | 2011.7 | Dolphin |
Java SE 8 (LTS) | 2014.3 | |
Java SE 9 | 2017.9 | |
Java SE 10 (18.3) | 2018.3 | |
Java SE 11 (18.9 LTS) | 2018.9 | |
Java SE 12 (19.3) | 2019.3 | |
Java SE 13 (19.9) | 2019.9 |
下面咱们看一些 Java 发展过程当中重要的节点。
1995 年 alpha 和 beta Java 公开版本发布,取名为 WebRunner。android
1996.1.23 Java 第一个版本发布,取名叫 Oak。可是第一个稳定版本是 JDK 1.0.2,被称作 Java 1。git
1998.12.8 发布了 J2SE 1.2。这个版本到 J2SE 5.0 改名为 Java 2。其中的 SE 指的是 Standard Edition,为了区别于 J2EE(Enterprise Edition)和 J2ME(Micro Edition)。api
2000.5 发布了 J2SE 1.3,其中包含了 HotSpot JVM。而 HotSpot JVM 首次发布是在 1999.4,名为 J2SE 1.2 JVM。数组
2004.9.30 发布了 J2SE 5.0。为何这个版本命名和前面几个版本不同呢?这个版本本来计划以 1.5 命名的,沿用之前的命名方式。可是为了更好的反映这个版本的成熟度,因此更名为 5.0。
这个版本之后,有了一个新的版本控制系统,5.0 用来表示产品版本,用来表示稳定的 J2SE 版本,而 1.5.0 用来表示开发者版本,也就是 Java 5.0 = JDK 1.5.0。bash
2006.12.11,J2SE 更名为 Java SE,版本号去掉了 .0。此后对应版本就是 Java 6 = JDK 1.6,Java 7 = JDK 1.7。并发
2011.7.7. 发布 Java SE 7,是一个重大版本更新。更新了众多特性。oracle
2018.3 发布 Java SE 10。在此以前,Java 基本上是两年一个版本,除了 Java SE 7 通过了五年,Java SE 8 通过了三年。在此以后,就是每六个月发布一次新版本。可是不是每一个版本都是 LTS(Long-Term-Support)。按照 Oracle 的计划,每三年会有一个 LTS 版本。最近的 LTS 版本就是 Java SE 11 了。
OpenJDK 是 在 2007 年由 Sun Corporation(如今的Oracle Corporation) 发布的。是 Oracle JDK 的开源实现版本,以 GPL 协议发布。在 JDK 7 的时候,Sub JDK 就是在 Open JDK 7 的基础上发布的,只替换了少许的源码。在 Sun 公司被 Oracle 收购之后,Sun SDK 就被称为 Oracle JDK。Oracle JDK 是基于 Oracle Binary COde License Agreement 协议。 二者的区别以下:
提及 Android 和 OpenJDK 的历史渊源,仍是略微复杂。 简单来讲,Android 最开始使用的 Java 是基于 Apache 协议发布的 Harmony,后来因为 Harmony 自己的限制和 Oracle 公司的起诉,从 Android N 之后, Google 开始使用 OpenJDK。 而后咱们再稍微展开聊聊。
Sun 公司最初开发了 Java 语言,同时也开发了 JVM,而且定义了 JVM 规范。这个咱们比较清楚,只要基于 JVM 规范开发本身的语言,就能够运行在 JVM 上。可是依照规范开发了语言以后,须要经过 Sun 的 TCK(Technology Compatibility Kit)测试,以后才能成为官方承认的 JVM 语言。
基于 JVM 规范,Apache 开发了一个开源免费的 Java 实现 Harmony,而且根据 Apache License v2 发布。可是 Sun 公司并无给 Harmony TCK 许可。
在 2009.4.15 Sun 公司发布了 OpenJDK,基于 GNU GPL 发布。同时 Sun 公司规定只有衍生自 OpenJDK 采用的 GPL 协议的开源实现才能运行 OpenJDK 的 TCK。以后 Oracle 收购 Sun 公司之后接管了 OpenJDK。 因为 Apache 的 Harmony 是 Apache 协议,与 OpenJDK 的 GPL 协议不兼容,因此 Harmony 一直没有获得 TCK 受权。
Android 最开始是采用了 Harmony 做为本身的 Java 类库,由于 Harmony 使用的 Apache 协议更自由。而因为 Harmony 没有经过 TCK 认证,也为后来 Oracle 起诉 Google 埋下伏笔。
后来 Oracle 起诉 Google 主要集中在两点,一是 Oracle 认为 Google 代码中使用了 Java 的 37 个 API,二是 Sun 公司前员工在跳槽后为 Android 项目开发时,直接复制了 OpenJDK 中的九行代码,而 Android 项目并无按照 GPL 协议受权,因此复制 OpenJDK 代码是没有经过 GPL 受权的。
因此到后来为了解决专利的问题,Android N 之后,Android 开始使用 OpenJDK 替换 Harmony。
以上 Android 和 JDK 参考资料:
juejin.im/entry/5abc5…
zh.wikipedia.org/zh/Android#…
gudong.name/2019/04/05/…
聊了一些关于 Java 的历史,下面咱们看看各个 Java 版本有那些新特性。这里只列出了对开发者影响比较大的一些特性~
Lambda 表达式相信不用再过多的介绍,终于在 Java 8 引入了,能够极大的减小代码量,代码看起来更清爽。
函数式接口就是有且仅有一个抽象方法,可是能够有多个非抽象方法的接口。能够隐式转化为 Lambda 表达式。 咱们定义一个函数式接口以下:
@FunctionalInterface
interface Operation {
int operation(int a, int b);
}
复制代码
再定义一个 Class 用来操做 Operation 接口。
class Test {
private int operate(int a, int b, Operation operation) {
return operation.operation(a, b);
}
}
Test test = new Test();
复制代码
在 Java 8 以前,咱们想要实现 Operation 接口并传给 Test.operate() 方法使用,咱们要定义一个匿名类,实现 Operation 方法。
test.operate(1, 2, new Operation() {
@Override
public int operation(int a, int b) {
return a + b;
}
});
复制代码
而使用 Lambda 表达式,咱们就能够这样写了:
test.operate(1, 2, (a, b) -> a + b);
复制代码
经过方法引用,可使用方法的名字来指向一个方法。使用一对冒号来引 "::" 用方法。 仍是以上面的例子来看,咱们再添加几个方法:
@FunctionalInterface
interface Operation {
int operation(int a, int b);
}
interface Creater<T> {
T get();
}
interface TestInt {
int cp(Test test1, Test test2);
}
class Test {
public static Test create(Creater<Test> creater) {
return creater.get();
}
private int operate(int a, int b, Operation operation) {
return operation.operation(a, b);
}
private static int add(int a, int b) {
return a + b;
}
private int sub(int a, int b) {
return a - b;
}
public int testM(Test test) {
return 0;
}
public void test(TestInt testInt) {
Test t1 = Test.create(Test::new);
Test t2 = Test.create(Test::new);
testInt.cp(t1, t2);
}
}
复制代码
那么对应的方法引用有四种:
构造方法引用
使用方式:Class::new
Test test = Test.create(Test::new);
复制代码
静态方法引用
使用方式:Class::staticMethod
test.operate(1, 2, Test::add);
复制代码
对象的实例方法引用
使用方式:instance::method
test.operate(1, 2, test::sub);
复制代码
类的实例方法引用
使用方式:Class::method
test.test(Test::testM);
复制代码
其实上面三种方法引用都好理解,最后类的实例方法引用,有两个条件:
Java 8 新增了接口的默认实现,经过 default 关键字表示。同时也能够提供静态默认方法。
public interface TestInterface {
String test();
// 接口默认方法
default String defaultTest() {
return "default";
}
static String staticTest() {
return "static";
}
}
复制代码
Java 8 支持了重复注解。在 Java 8 以前想实现重复注解,须要用一些方法来绕过限制。好比下面的代码。
@interface Author {
String name();
}
@interface Authors {
Author[] value();
}
@Authors({@Author(name="a"), @Author(name = "b")})
class Article {
}
复制代码
而在 Java 8 中,能够直接用下面的方式。
@Repeatable(Authors.class)
@interface Author {
String name();
}
@interface Authors {
Author[] value();
}
@Author(name = "a")
@Author(name = "b")
class Article {
}
复制代码
在解析注解的时候,Java 8 也提供了新的 API。
AnnotatedElement.getAnnotationsByType(Class<T>)
复制代码
Java 8 以前注解只能用在声明中,在 Java 8 中,注解可使用在 任何地方。
@Author(name="a")
private Object name = "";
private String author = (@Author(name="a")String) name;
复制代码
Java 8 对于类型推断作了改进。
好比在 Java 7 中下面的写法:
List<String> stringList = new ArrayList<>();
stringList.add("A");
stringList.addAll(Arrays.<String>asList());
复制代码
在 Java 8 中改进后的写法,能够自动作类型推断。
List<String> stringList = new ArrayList<>();
stringList.add("A");
stringList.addAll(Arrays.asList());
复制代码
Java 8 中新增了 Optional 类用来解决空指针异常。Optional 是一个能够保存 null 的容器对象。经过 isPresent() 方法检测值是否存在,经过 get() 方法返回对象。
除此以外,Optional 还提供了不少其余有用的方法,具体能够查看文档。下面是一些示例代码。
// 建立一个 String 类型的容器
Optional<String> str = Optional.of("str");
// 值是否存在
boolean pre = str.isPresent();
// 值若是存在就调用 println 方法,这里传入的是 println 的方法引用
str.ifPresent(System.out::println);
// 获取值
String res = str.get();
// 传入空值
str = Optional.ofNullable(null);
// 若是值存在,返回值,不然返回传入的参数
res = str.orElse("aa");
str = Optional.of("str");
// 若是有值,对其调用映射函数获得返回值,对返回值进行 Optional 包装并返回
res = str.map(s -> "aa" + s).get();
// 返回一个带有映射函数的 Optional 对象
res = str.flatMap(s -> Optional.of(s + "bb")).flatMap(s -> Optional.of(s + "cc")).get();
复制代码
Java 8 中新增的 Stream 类提供了一种新的数据处理方式。这种方式将元素集合看作一种流,在管道中传输,通过一系列处理节点,最终输出结果。
关于 Stream 提供的具体方法,能够参照 API。下面是一些示例代码。
List<String> list = Arrays.asList("maa", "a", "ab", "c");
list.stream()
.filter(s -> s.contains("a"))
.map(s -> s + "aa")
.sorted()
.forEach(System.out::println);
System.out.println("####");
list.parallelStream().forEach(System.out::println);
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
int res = numbers.stream().map(i -> i + 1).mapToInt(i -> i).summaryStatistics().getMax();
System.out.println(res);
复制代码
Java 8 中新增了日期时间 API 用来增强对日期时间的处理,其中包括了 LocalDate,LocalTime,LocalDateTime,ZonedDateTime 等等,关于 API 能够参照官方文档以及这篇博客,写的很详细。下面是示例代码。
LocalDate now = LocalDate.now();
System.out.println(now);
System.out.println(now.getYear());
System.out.println(now.getMonth());
System.out.println(now.getDayOfMonth());
LocalTime localTime = LocalTime.now();
System.out.println(localTime);
LocalDateTime localDateTime = now.atTime(localTime);
System.out.println(localDateTime);
复制代码
Java 8 标准库中提供了对 Base 64 编码的支持。具体 API 见可参照文档。下面是示例代码。
String base64 = Base64.getEncoder().encodeToString("aaa".getBytes());
System.out.println(base64);
byte[] bytes = Base64.getDecoder().decode(base64);
System.out.println(new String(bytes));
复制代码
Java 8 中提供了对数组的并行操做,包括 parallelSort 等等,具体可参照 API。
Arrays.parallelSort(new int[] {1, 2, 3, 4, 5});
复制代码
能够看到,Java 8 总体上的改进是很大的,最重要的是引入 Lambda 表达式,简化代码。
其余一些改进可参照 www.oracle.com/technetwork…
在 Java 9 之前,打包和依赖都是基于 JAR 包进行的。JRE 中包含了 rt.jar,将近 63M,也就是说要运行一个简单的 Hello World,也须要依赖这么大的 jar 包。在 Java 9 中提出的模块化系统,对这点进行了改善。 关于模块化系统具体能够看看这篇文章。
Java 9 提供了交互式解释器。有了 JShell 之后,Java 终于能够像 Python,Node.js 同样在 Shell 中运行一些代码并直接得出结果了。
Java 9 中能够在接口中定义私有方法。示例代码以下:
public interface TestInterface {
String test();
// 接口默认方法
default String defaultTest() {
pmethod();
return "default";
}
private String pmethod() {
System.out.println("private method in interface");
return "private";
}
}
复制代码
在之前,咱们想要建立一个不可变的集合,须要先建立一个可变集合,而后使用 unmodifiableSet 建立不可变集合。代码以下:
Set<String> set = new HashSet<>();
set.add("A");
set.add("B");
set.add("C");
set = Collections.unmodifiableSet(set);
System.out.println(set);
复制代码
Java 9 中提供了新的 API 用来建立不可变集合。
List<String> list = List.of("A", "B", "C");
Set<String> set = Set.of("A", "B", "C");
Map<String, String> map = Map.of("KA", "VA", "KB", "VB");
复制代码
Java 9 中不须要在 try 中额外定义一个变量。Java 9 以前须要这样使用 try-with-resources:
InputStream inputStream = new StringBufferInputStream("a");
try (InputStream in = inputStream) {
in.read();
} catch (IOException e) {
e.printStackTrace();
}
复制代码
在 Java 9 中能够直接使用 inputStream 变量,不须要再额外定义新的变量了。
InputStream inputStream = new StringBufferInputStream("a");
try (inputStream) {
inputStream.read();
} catch (IOException e) {
e.printStackTrace();
}
复制代码
Java 9 中支持在同一个 JAR 中维护不一样版本的 Java 类和资源。
对 Deprecated 新增了 since 和 forRemoval 属性
在 Java 9 以前,内部匿名类须要指定泛型类型,以下:
Handler<? extends Number> intHandler1 = new Handler<Number>(2) {
}
复制代码
而在 Java 9 中,能够自动作类型推导,以下:
Handler<? extends Number> intHandler1 = new Handler<>(2) {
}
复制代码
CompletableFuture 类的异步机制能够在 ProcessHandle.onExit 方法退出时执行操做。
其余一些改进可参照 docs.oracle.com/javase/9/wh…
var a = "aa";
System.out.println(a);
复制代码
var 关键字目前只能用于局部变量以及 for 循环变量声明中。
从JDK中移除了 javah 工具,使用 javac -h 代替。
ThreadLocal 握手交互
JDK 10 引入一种在线程上执行回调的新方法,很方便的中止单个线程而不是中止所有线程或者一个都不停。
基于Java的实验性JIT编译器
Java 10 开启了 Java JIT编译器 Graal,用做Linux / x64平台上的实验性JIT编译器。
提供默认的 CA 根证书
将 JDK 生态整合到单个仓库
此JEP的主要目标是执行一些内存管理,并将JDK生态的众多存储库组合到一个存储库中。
其余一些改进能够参照 www.oracle.com/technetwork…
(var x, var y) -> x.process(y)
复制代码
Java 11 新增了 一系列字符串处理方法,例如:
// 判断字符串是否为空白
" ".isBlank();
" Javastack ".stripTrailing(); // " Javastack"
" Javastack ".stripLeading(); // "Javastack "
复制代码
其余一些改进能够参照 www.oracle.com/technetwork…
Java 12 之后,switch 不只能够做为语句,也能够做为表达式。
private String switchTest(int i) {
return switch (i) {
case 1 -> "1";
default -> "0";
};
}
复制代码
其余一些改进能够参照 www.oracle.com/technetwork…
Java8
ifeve.com/java-8-feat…
juejin.im/post/5ae6bf…
wizardforcel.gitbooks.io/java8-tutor…
www.oracle.com/technetwork…
www.oracle.com/technetwork…
Java9
www.runoob.com/java/java9-…
docs.oracle.com/javase/9/wh…
www.twle.cn/c/yufei/jav…
Java10
baijiahao.baidu.com/s?id=159443…
blog.csdn.net/visant/arti…
www.oracle.com/technetwork…
Java 11
openjdk.java.net/projects/jd…
www.ctolib.com/topics-1351…
Java 12
zhuanlan.zhihu.com/p/59798800
其余
www.baeldung.com/oracle-jdk-…
zh.wikipedia.org/wiki/Java版本…
www.zhihu.com/question/19…
juejin.im/post/5ca1c7…
gudong.name/2019/04/05/…