JAVA8之后的版本履历

目录

一、JDK介绍

1.1 Java 的发布周期

1.2 OpenJDK VS Oracle JDK

1.3 Android 和 JDK

1.4 JVM 和 TCK

1.5 Harmony 和 OpenJDK

1.6 Oracle 和 Google 关于 JDK 纠纷

二、JAVA版本发布

2.1 JAVA8

2.1.1  Lambda 和 函数式接口

2.1.2. 方法引用

2.1.3 接口默认方法和静态方法

2.1.4 重复注解

2.1.5. 类型注解

2.1.6. 更好的类型推断

2.1.7. Optional

2.1.8. Stream

2.1.9. 日期时间 API

2.1.10. Base64 支持

2.1.11. 并行数组 ParallelSort

2.1.12. 其他新特性

2.2 Java 9

2.2.1 Jigsaw 模块系统

2.2.2 JShell REPL

2.2.3. 私有接口方法,接口中使用私有方法

2.2.4. 集合不可变实例工厂方法

2.2.5. 改进 try-with-resources

2.2.6. 多版本兼容 jar 包

2.2.7. 增强了 Stream,Optional,Process API

2.2.8. 新增 HTTP2 Client

2.2.9. 增强 Javadoc,增加了 HTML 5 文档的输出,并且增加了搜索功能

2.2.10. 增强 @Deprecated

2.2.11. 增强了钻石操作符 "<>",可以在 匿名内部类中使用了。

2.2.12. 多分辨率图像 API:定义多分辨率图像API,开发者可以很容易的操作和展示不同分辨率的图像了。

2.2.13. 改进的 CompletableFuture API

2.3 Java 10

2.3.1. 局部变量类型推断

2.3.2. GC改进和内存管理

2.3.3. 线程本地握手(JEP 312)

2.3.4. 备用内存设备上的堆分配(JEP 316)

2.3.5. 其他Unicode语言 - 标记扩展(JEP 314)

2.3.6. 基于Java的实验性JIT编译器

2.3.7. 根证书(JEP 319)

2.3.8. 根证书颁发认证

2.3.9. 将JDK生态整合单个存储库(JEP 296)

2.3.10. 删除工具javah(JEP 313)

2.4 Java 11

2.4.1 本地变量类型推断

2.4.2 字符串加强

2.4.3 集合加强

2.4.4 Stream 加强

2.4.5 Optional 加强

2.4.6 InputStream 加强

2.4.7 HTTP Client API

2.4.8 化繁为简,一个命令编译运行源代码

2.5 Java 12

2.5.1 switch 表达式

2.5.2 2 默认CDS归档

2.5.3 Shenandoah GC

2.5.4 JMH 基准测试

2.5.5 JVM 常量 API 

2.5.6 G1的可中断 mixed GC

2.5.7 G1归还不使用的内存

2.5.8移除多余ARM64实现

三、其他参考资料


不知不觉,JAVA13都快要发布了,现在还在使用java8,。毕竟java也收费了。HTTP2.0未来应该也是趋势,不升级是不可能的了。未来该怎么样,还是静静的观望吧,不管怎样,新版本特性我们还是要了解的。

JDK版本总结,持续更新

一、JDK介绍

JDK 全称 Java Development Kit,是 Java 开发环境。我们通常所说的 JDK 指的是 Java SE (Standard Edition) Development Kit。除此之外还有 Java EE(Enterprise Edition)和 Java ME(Micro Edition platforms)。

1.1 Java 的发布周期

聊聊 Java8 以后各个版本的新特性

下面我们看一些 Java 发展过程中重要的节点。

1995 年 alpha 和 beta Java 公开版本发布,取名为 WebRunner。

1996.1.23 Java 第一个版本发布,取名叫 Oak。但是第一个稳定版本是 JDK 1.0.2,被称做 Java 1。

1998.12.8 发布了 J2SE 1.2。这个版本到 J2SE 5.0 更名为 Java 2。其中的 SE 指的是 Standard Edition,为了区别于 J2EE(Enterprise Edition)和 J2ME(Micro Edition)。

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。

2006.12.11,J2SE 改名为 Java SE,版本号去掉了 .0。此后对应版本就是 Java 6 = JDK 1.6,Java 7 = JDK 1.7。

2011.7.7. 发布 Java SE 7,是一个重大版本更新。更新了众多特性。

2018.3 发布 Java SE 10。在此之前,Java 基本上是两年一个版本,除了 Java SE 7 经过了五年,Java SE 8 经过了三年。在此之后,就是每六个月发布一次新版本。但是不是每个版本都是 LTS(Long-Term-Support)。按照 Oracle 的计划,每三年会有一个 LTS 版本。最近的 LTS 版本就是 Java SE 11 了。

1.2 OpenJDK VS Oracle JDK

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 协议。

两者的区别如下:

  • Oracle JDK 将三年发布一次稳定版本,OpenJDK 每三个月发布一次。

  • Oracle JDK 支持 LTS,OpenJDK 只支持当前版本至下一个版本发布。

  • Oracle JDK 采用 Oracle Binary Code License 协议,OpenJDK 采用 GPL v2 协议。

  • Oracle JDK 基于 OpenJDK 构建,技术上基本没有差异。

1.3 Android 和 JDK

说起 Android 和 OpenJDK 的历史渊源,还是略微复杂。

简单来说,Android 最开始使用的 Java 是基于 Apache 协议发布的 Harmony,后来由于 Harmony 本身的限制和 Oracle 公司的起诉,从 Android N 以后, Google 开始使用 OpenJDK。

然后我们再稍微展开聊聊。

1.4 JVM 和 TCK

Sun 公司最初开发了 Java 语言,同时也开发了 JVM,并且定义了 JVM 规范。这个我们比较清楚,只要基于 JVM 规范开发自己的语言,就可以运行在 JVM 上。但是依照规范开发了语言之后,需要通过 Sun 的 TCK(Technology Compatibility Kit)测试,之后才能成为官方认可的 JVM 语言。

1.5 Harmony 和 OpenJDK

基于 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 埋下伏笔。

1.6 Oracle 和 Google 关于 JDK 纠纷

后来 Oracle 起诉 Google 主要集中在两点,一是 Oracle 认为 Google 代码中使用了 Java 的 37 个 API,二是 Sun 公司前员工在跳槽后为 Android 项目开发时,直接复制了 OpenJDK 中的九行代码,而 Android 项目并没有按照 GPL 协议授权,所以复制 OpenJDK 代码是没有通过 GPL 授权的。

所以到后来为了解决专利的问题,Android N 以后,Android 开始使用 OpenJDK 替换 Harmony。

以上 Android 和 JDK 参考资料:

https://juejin.im/entry/5abc516b518825556f557f90
https://gudong.name/2019/04/05/android-why-java-harmony.html

聊了一些关于 Java 的历史,下面我们看看各个 Java 版本有那些新特性。这里只列出了对开发者影响比较大的一些特性~(如果需要Java面试知识点,可以在Java知音公众号回复”Java面试题聚合“)

二、JAVA版本发布

2.1 JAVA8

Java8 参考资料

http://ifeve.com/java-8-features-tutorial/
https://juejin.im/post/5ae6bfb66fb9a07a9b35bac1
https://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html
https://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html

2.1.1  Lambda 和 函数式接口

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);

2.1.2. 方法引用

通过方法引用,可以使用方法的名字来指向一个方法。使用一对冒号来引 "::" 用方法。还是以上面的例子来看,我们再添加几个方法:

@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);

其实上面三种方法引用都好理解,最后类的实例方法引用,有两个条件:

  • 首先要满足实例方法,而不是静态方法

  • Lambda 表达式的第一个参数会成为调用实例方法的对象

根据这两点我们看上面的例子,test 方法接受一个 TestInt 实例,用 Lambda 表达式表示就是 (Test t1, Test t2) -> res,而我们调用 test 方法时传入的方法引用是 Test::testM,其参数也是一个 Test 实例,最终 test.test(Test::testM) 的调用效果就是 t1.testM(t2)

2.1.3 接口默认方法和静态方法

Java 8 新增了接口的默认实现,通过 default 关键字表示。同时也可以提供静态默认方法。

public interface TestInterface {
    String test();

    // 接口默认方法
    default String defaultTest() {
        return "default";
    }

    static String staticTest() {
        return "static";
    }
}

2.1.4 重复注解

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>)

2.1.5. 类型注解

Java 8 之前注解只能用在声明中,在 Java 8 中,注解可以使用在 任何地方。

@Author(name="a")
private Object name = "";
private String author = (@Author(name="a")String) name;

2.1.6. 更好的类型推断

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());

2.1.7. Optional

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();

2.1.8. Stream

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);

2.1.9. 日期时间 API

Java 8 中新增了日期时间 API 用来加强对日期时间的处理,其中包括了 LocalDate,LocalTime,LocalDateTime,ZonedDateTime 等等,关于 API 可以参照官方文档以及这篇博客,写的很详细。

https://www.cnblogs.com/muscleape/p/9956754.html

下面是示例代码。

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);

2.1.10. Base64 支持

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));

2.1.11. 并行数组 ParallelSort

Java 8 中提供了对数组的并行操作,包括 parallelSort 等等,具体可参照 API。

Arrays.parallelSort(new int[] {1, 2, 3, 4, 5});

2.1.12. 其他新特性

  • 对并发的增强

  • 在java.util.concurrent.atomic包中还增加了下面这些类:
    DoubleAccumulator
    DoubleAdder
    LongAccumulator
    LongAdder

  • 提供了新的 Nashorn javascript 引擎

  • 提供了 jjs,是一个给予 Nashorn 的命令行工具,可以用来执行 JavaScript 源码

  • 提供了新的类依赖分析工具 jdeps

  • JVM 的新特性
    JVM内存永久区已经被metaspace替换(JEP 122)。JVM参数 -XX:PermSize 和 –XX:MaxPermSize被XX:MetaSpaceSize 和 -XX:MaxMetaspaceSize代替。

可以看到,Java 8 整体上的改进是很大的,最重要的是引入 Lambda 表达式,简化代码。

其他一些改进可参照:

https://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html

2.2 Java 9

参考资料

https://www.runoob.com/java/java9-new-features.html
https://www.twle.cn/c/yufei/java9/java9-basic-process-manage-api.html
https://docs.oracle.com/javase/9/whatsnew/toc.htm

2.2.1 Jigsaw 模块系统

在 Java 9 以前,打包和依赖都是基于 JAR 包进行的。JRE 中包含了 rt.jar,将近 63M,也就是说要运行一个简单的 Hello World,也需要依赖这么大的 jar 包。在 Java 9 中提出的模块化系统,对这点进行了改善。

关于模块化系统具体可以看看这篇文章。

https://zhuanlan.zhihu.com/p/24800180

2.2.2 JShell REPL

Java 9 提供了交互式解释器。有了 JShell 以后,Java 终于可以像 Python,Node.js 一样在 Shell 中运行一些代码并直接得出结果了。

2.2.3. 私有接口方法,接口中使用私有方法

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";
    }
}

2.2.4. 集合不可变实例工厂方法

在以前,我们想要创建一个不可变的集合,需要先创建一个可变集合,然后使用 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");

2.2.5. 改进 try-with-resources

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();
}

2.2.6. 多版本兼容 jar 包

Java 9 中支持在同一个 JAR 中维护不同版本的 Java 类和资源。

2.2.7. 增强了 Stream,Optional,Process API

2.2.8. 新增 HTTP2 Client

2.2.9. 增强 Javadoc,增加了 HTML 5 文档的输出,并且增加了搜索功能

2.2.10. 增强 @Deprecated

对 Deprecated 新增了 since 和 forRemoval 属性

2.2.11. 增强了钻石操作符 "<>",可以在 匿名内部类中使用了。

在 Java 9 之前,内部匿名类需要指定泛型类型,如下:

Handler<? extends Number> intHandler1 = new Handler<Number>(2) {
}

而在 Java 9 中,可以自动做类型推导,如下:

Handler<? extends Number> intHandler1 = new Handler<>(2) {
}

2.2.12. 多分辨率图像 API:定义多分辨率图像API,开发者可以很容易的操作和展示不同分辨率的图像了。

2.2.13. 改进的 CompletableFuture API

CompletableFuture 类的异步机制可以在 ProcessHandle.onExit 方法退出时执行操作。

其他一些改进可参照:

https://docs.oracle.com/javase/9/whatsnew/toc.htm

2.3 Java 10

参考资料

https://baijiahao.baidu.com/s?id=1594437679552808575&wfr=spider&for=pc
https://blog.csdn.net/visant/article/details/79778967
https://www.oracle.com/technetwork/java/javase/10-relnote-issues-4108729.html

2.3.1. 局部变量类型推断

局部变量类型推断可以说是Java 10中最值得注意的特性,这是Java语言开发人员为了简化Java应用程序的编写而采取的又一步,如下图所示。

这个新功能将为Java增加一些语法糖 - 简化它并改善开发者体验。新的语法将减少与编写Java相关的冗长度,同时保持对静态类型安全性的承诺。

局部变量类型推断将引入"var"关键字,也就是你可以随意定义变量而不必指定变量的类型,如:

将被下面这个新语法所取代:

看完是不是有点JS的即视感???越来越像JS了吗?!虽然类型推断在Java中不是一个新概念,但在局部变量中确是很大的一个改进。

说到类型推断,从JDK 5引进泛型,到JDK 7的"<>"操作符允许不绑定类型而初始化List,再到JDK 8的Lambda表达式,再到现在JDK 10的局部变量类型推断,Java类型推断正大刀阔斧的向前发展。

局部变量类型推荐仅限于如下使用场景:

  • 局部变量初始化
  • for循环内部索引变量
  • 传统的for循环声明变量

Java官方表示,它不能用于以下几个地方:

  • 方法参数
  • 构造函数参数
  • 方法返回类型
  • 字段捕获表达式(或任何其他类型的变量声明)

2.3.2. GC改进和内存管理

JDK 10中有2个JEP专门用于改进当前的垃圾收集元素。

第一个垃圾收集器接口是(JEP 304),它将引入一个纯净的垃圾收集器接口,以帮助改进不同垃圾收集器的源代码隔离。

预定用于Java 10的第二个JEP是针对G1的并行完全GC(JEP 307),其重点在于通过完全GC并行来改善G1最坏情况的等待时间。G1是Java 9中的默认GC,并且此JEP的目标是使G1平行。

2.3.3. 线程本地握手(JEP 312)

JDK 10将引入一种在线程上执行回调的新方法,因此这将会很方便能停止单个线程而不是停止全部线程或者一个都不停。

2.3.4. 备用内存设备上的堆分配(JEP 316)

允许HotSpot VM在备用内存设备上分配Java对象堆内存,该内存设备将由用户指定。

2.3.5. 其他Unicode语言 - 标记扩展(JEP 314)

目标是增强java.util.Locale及其相关的API,以便实现语言标记语法的其他Unicode扩展(BCP 47)。

2.3.6. 基于Java的实验性JIT编译器

Oracle希望将其Java JIT编译器Graal用作Linux / x64平台上的实验性JIT编译器。

2.3.7. 根证书(JEP 319)

这个的目标是在Oracle的Java SE中开源根证书。

2.3.8. 根证书颁发认证

这将使OpenJDK对开发人员更具吸引力,它还旨在减少OpenJDK和Oracle JDK构建之间的差异。

2.3.9. 将JDK生态整合单个存储库(JEP 296)

此JEP的主要目标是执行一些内存管理,并将JDK生态的众多存储库组合到一个存储库中。

2.3.10. 删除工具javah(JEP 313)

从JDK中移除了javah工具,这个很简单并且很重要。

其他可参考:

https://www.oracle.com/technetwork/java/javase/10-relnote-issues-4108729.html#NewFeature

2.4 Java 11

参考资料

https://blog.csdn.net/weixin_38055381/article/details/82865385
https://openjdk.java.net/projects/jdk/11/

2.4.1 本地变量类型推断

什么是局部变量类型推断?

var javastack = "javastack";
System.out.println(javastack);
大家看出来了,局部变量类型推断就是左边的类型直接使用 var 定义,而不用写具体的类型,编译器能根据右边的表达式自动推断类型,如上面的 String 。

var javastack = "javastack";
就等于:
String javastack = "javastack";
 

2.4.2 字符串加强

Java 11 增加了一系列的字符串处理方法,如以下所示。

// 判断字符串是否为空白
" ".isBlank(); // true
// 去除首尾空格
" Javastack ".strip(); // "Javastack"
// 去除尾部空格
" Javastack ".stripTrailing(); // " Javastack"
// 去除首部空格
" Javastack ".stripLeading(); // "Javastack "
// 复制字符串
"Java".repeat(3);// "JavaJavaJava"
// 行数统计
"A\nB\nC".lines().count(); // 3


2.4.3 集合加强

自 Java 9 开始,Jdk 里面为集合(List/ Set/ Map)都添加了 of 和 copyOf 方法,它们两个都用来创建不可变的集合,来看下它们的使用和区别。

示例1:

var list = List.of("Java", "Python", "C");
var copy = List.copyOf(list);
System.out.println(list == copy); // true

示例2:

var list = new ArrayList<String>();
var copy = List.copyOf(list);
System.out.println(list == copy); // false

示例1和2代码差不多,为什么一个为true,一个为false?

来看下它们的源码:

static <E> List<E> of(E... elements) {
  switch (elements.length) { // implicit null check of elements
    case 0:
        return ImmutableCollections.emptyList();
    case 1:
        return new ImmutableCollections.List12<>(elements[0]);
    case 2:
        return new ImmutableCollections.List12<>(elements[0], elements[1]);
    default:
        return new ImmutableCollections.ListN<>(elements);
  }
}
static <E> List<E> copyOf(Collection<? extends E> coll) {
    return ImmutableCollections.listCopy(coll);
}
static <E> List<E> listCopy(Collection<? extends E> coll) {
    if (coll instanceof AbstractImmutableList && coll.getClass() != SubList.class) {
        return (List<E>)coll;
    } else {
        return (List<E>)List.of(coll.toArray());
    }
}


可以看出 copyOf 方法会先判断来源集合是不是 AbstractImmutableList 类型的,如果是,就直接返回,如果不是,则调用 of 创建一个新的集合。

示例2 因为用的 new 创建的集合,不属于不可变 AbstractImmutableList 类的子类,所以 copyOf 方法又创建了一个新的实例,所以为false.

注意:使用of和copyOf创建的集合为不可变集合,不能进行添加、删除、替换、排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。

上面演示了 List 的 of 和 copyOf 方法,Set 和 Map 接口都有。

2.4.4 Stream 加强

Stream 是 Java 8 中的新特性,Java 9 开始对 Stream 增加了以下 4 个新方法。

1) 增加单个参数构造方法,可为null

Stream.ofNullable(null).count(); // 0


2) 增加 takeWhile 和 dropWhile 方法

Stream.of(1, 2, 3, 2, 1)
.takeWhile(n -> n < 3)
.collect(Collectors.toList()); // [1, 2]


从开始计算,当 n < 3 时就截止。

Stream.of(1, 2, 3, 2, 1)
.dropWhile(n -> n < 3)
.collect(Collectors.toList()); // [3, 2, 1]


这个和上面的相反,一旦 n < 3 不成立就开始计算。

3)iterate重载

这个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。

如果你对 JDK 8 中的 Stream 还不熟悉,可以看之前分享的这一系列教程。

2.4.5 Optional 加强

Opthonal 也增加了几个非常酷的方法,现在可以很方便的将一个 Optional 转换成一个 Stream, 或者当一个空 Optional 时给它一个替代的。

Optional.of("javastack").orElseThrow(); // javastack
Optional.of("javastack").stream().count(); // 1
Optional.ofNullable(null)
.or(() -> Optional.of("javastack"))
.get(); // javastack


2.4.6 InputStream 加强

InputStream 终于有了一个非常有用的方法:transferTo,可以用来将数据直接传输到 OutputStream,这是在处理原始数据流时非常常见的一种用法,如下示例。

var classLoader = ClassLoader.getSystemClassLoader();
var inputStream = classLoader.getResourceAsStream("javastack.txt");
var javastack = File.createTempFile("javastack2", "txt");
try (var outputStream = new FileOutputStream(javastack)) {
    inputStream.transferTo(outputStream);
}

2.4.7 HTTP Client API

这是 Java 9 开始引入的一个处理 HTTP 请求的的孵化 HTTP Client API,该 API 支持同步和异步,而在 Java 11 中已经为正式可用状态,你可以在 java.net 包中找到这个 API。

来看一下 HTTP Client 的用法:

var request = HttpRequest.newBuilder()
.uri(URI.create("https://javastack.cn"))
.GET()
.build();
var client = HttpClient.newHttpClient();
// 同步
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
// 异步
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);


上面的 .GET() 可以省略,默认请求方式为 Get!

更多使用示例可以看这个 API,后续有机会再做演示。

现在 Java 自带了这个 HTTP Client API,我们以后还有必要用 Apache 的 HttpClient 工具包吗?

2.4.8 化繁为简,一个命令编译运行源代码

看下面的代码。

// 编译

javac Javastack.java

// 运行

java Javastack

在我们的认知里面,要运行一个 Java 源代码必须先编译,再运行,两步执行动作。而在未来的 Java 11 版本中,通过一个 java 命令就直接搞定了,如以下所示。

java Javastack.java

其他一些改进可以参照:

https://www.oracle.com/technetwork/java/javase/11-relnote-issues-5012449.html#NewFeature

2.5 Java 12

参考资料

https://zhuanlan.zhihu.com/p/59798800

2.5.1 switch 表达式

Java 12 以后,switch 不仅可以作为语句,也可以作为表达式。

private String switchTest(int i) {
    return switch (i) {
        case 1 -> "1";
        default -> "0";
    };
}

2.5.2 2 默认CDS归档

通过在64位平台上的默认类列表的帮助下生成CDS归档来改进JDK构建过程,从而有效地消除了运行java -Xshare:dump。 此功能的目标包括:

  1. 改进开箱即用的启动时间
  2. 摆脱使用-Xshare:dump。


2.5.3 Shenandoah GC

Shenandoah是一种垃圾收集(GC)算法,旨在保证低延迟(10 - 500 ms的下限)。 它通过在运行Java工作线程的同时执行GC操作减少GC暂停时间。 使用Shenandoah,暂停时间不依赖于堆的大小。 这意味着无论堆的大小如何,暂停时间都是差不多的。

这是一个实验性功能,不包含在默认(Oracle)的OpenJDK版本中。

2.5.4 JMH 基准测试

此功能为JDK源代码添加了一套微基准测试(大约100个),简化了现有微基准测试的运行和新基准测试的创建过程。 它基于Java Microbenchmark Harness(JMH)并支持JMH更新。

此功能使开发人员可以轻松运行当前的微基准测试并为JDK源代码添加新的微基准测试。 可以基于Java Microbenchmark Harness(JMH)轻松测试JDK性能。 它将支持JMH更新,并在套件中包含一组(约100个)基准测试。

2.5.5 JVM 常量 API 

JEP 334引入了一个API,用于建模关键类文件和运行时artifacts,例如常量池。 此API将包括ClassDesc,MethodTypeDesc,MethodHandleDesc和DynamicConstantDesc等类。此 API 对于操作类和方法的工具很有帮助。

2.5.6 G1的可中断 mixed GC

此功能通过将Mixed GC集拆分为强制部分和可选部分,使G1垃圾收集器更有效地中止垃圾收集过程。通过允许垃圾收集过程优先处理强制集,g1可以更多满足满足暂停时间目标。

G1是一个垃圾收集器,设计用于具有大量内存的多处理器机器。由于它提高了性能效率,g1垃圾收集器最终将取代cms垃圾收集器。

G1垃圾收集器的主要目标之一是满足用户设置的暂停时间。G1采用一个分析引擎来选择在收集期间要处理的工作量。此选择过程的结果是一组称为GC集的区域。一旦GC集建立并且GC已经开始,那么G1就无法停止。

如果G1发现GC集选择选择了错误的区域,它会将GC区域的拆分为两部分(强制部分和可选部分)来切换到处理Mix GC的增量模式。如果未达到暂停时间目标,则停止对可选部分的垃圾收集。


2.5.7 G1归还不使用的内存

此功能的主要目标是改进G1垃圾收集器,以便在不活动时将Java堆内存归还给操作系统。 为实现此目标,G1将在低应用程序活动期间定期生成或持续循环检查完整的Java堆使用情况。

这将立即归还未使用的部分Java堆内存给操作系统。 用户可以选择执行FULL GC以最大化返回的内存量。

2.5.8移除多余ARM64实现

Java 12将只有一个ARM 64位实现(aarch64)。 目标是删除所有与arm64实现相关的代码,同时保留32位ARM端口和64位aarch64实现。

这将把重点转移到单个64位ARM实现,并消除维护两个实现所需的重复工作。 当前的JDK 11实现中有两个64位ARM实现。

其他一些改进可以参照:

https://www.oracle.com/technetwork/java/javase/12-relnote-issues-5211422.html#NewFeature

三、其他参考资料

https://gudong.name/2019/04/05/android-why-java-harmony.html
https://juejin.im/post/5ca1c747e51d45761c7441fa
https://www.zhihu.com/question/19646618
www.zh.wikipedia.org/wiki/Java版本歷史
https://www.baeldung.com/oracle-jdk-vs-openjdk

发布了38 篇原创文章 · 获赞 80 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/b379685397/article/details/100109980
今日推荐