687.复习:动态代理
688.复习:Lambda表达式
689.复习:函数式接口
690.复习:方法引用与构造器引用
691.复习:Stream API
692.复习:Optional类的使用
693.jdk版本更新说明
JDK 9 的发布
- 经过4次跳票,历经曲折的Java 9 终于终于在2017年9月21日发布。
- 从Java 9 这个版本开始,Java 的计划发布周期是
6 个月
,下一个 Java 的主版本将于 2018 年 3 月发布,命名为Java 18.3
,紧接着再过六个月将发布 Java18.9
。 - 这意味着Java的更新从传统的以
特性驱动
的发布周期,转变为以时间驱动
的(6 个月为周期)发布模式,并逐步的将 Oracle JDK 原商业特性进行开源。 - 针对企业客户的需求,Oracle 将以
三年为周期
发布长期支持版本(long termsupport)。 - Java 9 提供了
超过150项新功能
特性,包括备受期待的模块化系统、可交互 的 REPL 工具:jshell,JDK 编译工具,Java 公共 API 和私有代码,以及安全增强、扩展提升、性能管理改善等。可以说Java 9是一个庞大的系统工程, 完全做了一个整体改变
。
官 方 提 供 的 新 特 性 列 表 : https://docs.oracle.com/javase/9/whatsnew/toc.htm#JSNEW-GUID- C23AFD78-C777-460B-8ACE-58BE5EA681F6
或参考 Open JDK
http://openjdk.java.net/projects/jdk9/
在线Oracle JDK 9 Documentation
https://docs.oracle.com/javase/9/
694.新版本jdk下载与IDEA开发环境的设置
新安装IDEA
第一个选项,是否要删除旧版本IDEA
第二个选项,原有的设置和配置文件是否要删除
第一个选项,使用旧有的配置
第三个选项,导入配置文件
695.java9新特性:jdk目录结构的改变
696.java9新特性:模块化系统
谈到 Java 9 大家往往第一个想到的就是 Jigsaw 项目。众所周知,Java 已经 发展超过 20 年(95 年最初发布),Java 和相关生态在不断丰富的同时也越 来越暴露出一些问题:
Java 运行环境的膨胀和臃肿
。每次JVM启动的时候,至少会有30~60MB的内存加载,主要原因是JVM需要加载rt.jar,不管其中的类是否被classloader加载,第一步整个jar都会被JVM加载到内存当中去(而模块化可以根据模块的需要加载程序运行需要的class)当代码库越来越大,创建复杂,盘根错节的“意大利面条式代码”的几率呈指数级的增长
。不同版本的类库交叉依赖导致让人头疼的问题,这些都阻碍了 Java 开发和运行效率的提升。- 很难真正地对代码进行封装, 而系统并没有对不同部分(也就是 JAR 文件)之间 的依赖关系有个明确的概念。
每一个公共类都可以被类路径之下任何其它的公共类所访问到,这样就会导致无意中使用了并不想被公开访问的 API。
本质上讲
也就是说,用模块来管理各个package,通过声明某个package 暴露,模块(module)的概念,其实就是package外再裹一层
,不声明默认就是隐藏。因此,模块化使得代码组织上更安全
,因为它可以指定哪 些部分可以暴露,哪些部分隐藏
。
实现目标
1.模块化的主要目的在于减少内存的开销
2.只须必要模块,而非全部jdk模块,可简化各种类库和大型应用的开 发和维护
3.改进 Java SE 平台,使其可以适应不同大小的计算设备
4.改进其安全性,可维护性,提高性能
模块将由通常的类和新的模块声明文件(module-info.java)组成。该文件是位于java代码结构的顶层,该模块描述符明确地定义了我们的模块需要什么依赖关系,以及哪些模块被外部使用
。在exports子句中未提及的所有包默认情况下将封装在模块中,不能在外部使用。
要想在java9demo模块中调用java9test模块下包中的结构,需要在java9test 的module-info.java中声明:
module java9test {
//package we export
exports com.atguigui.bean;
}
exports:控制着哪些包可以被其它模块访问到。所有不被导出的包默认 都被封装在模块里面。
对应在java 9demo 模块的src 下创建module-info.java文件:
module java9demo {
requires java9test;
}
requires:指明对其它模块的依赖。
697.java9新特性:Java的REPL工具:jshell
产生背景
像Python 和 Scala 之类的语言早就有交互式编程环境 REPL (read - evaluate - print -loop)了,以交互式的方式对语句和表达式进行求值。开发者只需要输入一些代码, 就可以在编译前获得对程序的反馈。而之前的Java版本要想执行代码,必须创建文 件、声明类、提供测试方法方可实现。
设计理念:即写即得、快速运行
实现目标
- Java 9 中终于拥有了 REPL工具:jShell。让Java可以像脚本语言一样运行,从 控制台启动jShell,利用jShell在没有创建类的情况下直接声明变量,计算表达式, 执行语句。即开发时可以在命令行里直接运行Java的代码,而无需创建Java文件,无需跟人解释”public static void main(String[] args)”这句废话。
- jShell也可以从文件中加载语句或者将语句保存到文件中。
- jShell也可以是tab键进行自动补全和自动添加分号。
调出jShell
获取帮助
基本使用
导入指定的包
默认已经导入如下的所有包:(包含java.lang包)
Tips:在 JShell 环境下,语句末尾的“;” 是可 选的。但推荐还是最好加上。提高代码可读 性。
只需按下 Tab 键,就能自动补全代码
列出当前 session 里所有有效的代码片段
查看当前 session 下所有创建过的变量
查看当前 session 下所有创建过的方法
Tips:我们还可以重新定义相同方法名和参 数列表的方法,即为对现有方法的修改(或 覆盖)。
使用外部代码编辑器来编写 Java 代码
使用/open命令调用:
没有受检异常(编译时异常)
说明:本来应该强迫我们捕获一个IOException,但却没有出现。因为jShell在 后台为我们隐藏了。
退出jShell
698.java9新特性:接口中声明私有方法
Java 8中规定接口中的方法除了抽象方法之外,还可以定义静态方法 和默认的方法。一定程度上,扩展了接口的功能,此时的接口更像是 一个抽象类。
在Java 9中,接口更加的灵活和强大,连方法的访问权限修饰符都可 以声明为private的
了,此时方法将不会成为你对外暴露的API的一部分。
package com.atguigu.java;
public interface MyInterface {
//如下的三个方法的权限修饰符都是public
void methodAbstract();
static void methodStatic(){
System.out.println("我是接口中的静态方法");
}
default void methodDefault(){
System.out.println("我是接口中的默认方法");
methodPrivate();
}
//jdk 9中允许接口中定义私有的方法
private void methodPrivate(){
System.out.println("我是接口中的私有方法");
}
}
package com.atguigu.java;
public class MyInterfaceImpl implements MyInterface {
@Override
public void methodAbstract() {
}
// @Override
public void methodDefault() {
System.out.println("实现类重写了接口中的默认方法");
}
public static void main(String[] args) {
//接口中的静态方法只能由接口自己调用
MyInterface.methodStatic();
//接口的实现类不能调用接口的静态方法
// MyInterfaceImpl.methodStatic();
MyInterfaceImpl impl = new MyInterfaceImpl();
impl.methodDefault();
//接口的私有方法,不能在接口外部调用
// impl.methodPrivate();
}
}
699.java9新特性:钻石操作符的语法升级
我们将能够与匿名实现类共同使用钻石操作符
(diamond operator)在Java 8中如下的操作是会报错的:
Comparator<Object> com = new Comparator<>(){
@Override
public int compare(Object o1, Object o2) {
return 0;
}
};
编译报错信息:Cannot use “<>” with anonymous inner classes.
package com.atguigu.java;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Comparator;
public class Java9Test {
@Test
public void test1() {
try {
URL url = new URL("http://www.atguigu.com");
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
//java9特性五:钻石操作符的升级
@Test
public void test2() {
//钻石操作符与匿名内部类在java 8中不能共存。在java9可以。
Comparator<Object> com = new Comparator<>() {
@Override
public int compare(Object o1, Object o2) {
return 0;
}
};
//jdk7中的新特性:类型推断
ArrayList<String> list = new ArrayList<>();
}
}
700.java9新特性:try结构的语法升级
Java 8 中,可以实现资源的自动关闭
,但是要求执行后必须关闭的所有资源必须在try子句中初始化
,否则编译不通过。如下例所示:
//java9 特性六:try操作的升级
public static void main(String[] args) {
//java 8之前的资源关闭的操作
// InputStreamReader reader = null;
// try {
// reader = new InputStreamReader(System.in);
// char[] cbuf = new char[20];
// int len;
// if((len = reader.read(cbuf) )!= -1){
// String str = new String(cbuf,0,len);
// System.out.println(str);
// }
// } catch (IOException e) {
// e.printStackTrace();
// } finally {
// if(reader != null){
// try {
// reader.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
//
// }
// }
//java 8中资源关闭操作: Java 8 中,可以实现资源的自动关闭
//要求自动关闭的资源的实例化必须放在try的一对小括号中
// try(InputStreamReader reader = new InputStreamReader(System.in)){
// char[] cbuf = new char[20];
// int len;
// if((len = reader.read(cbuf) )!= -1){
// String str = new String(cbuf,0,len);
// System.out.println(str);
// }
// } catch (IOException e) {
// e.printStackTrace();
// }
//java9中资源关闭操作:需要自动关闭的资源的实例化可以放在try的一对小括号外。
//此时的资源属性是常量,声明为final的,不可修改
InputStreamReader reader = new InputStreamReader(System.in);
try (reader) {
char[] cbuf = new char[20];
int len;
if((len = reader.read(cbuf) )!= -1){
String str = new String(cbuf,0,len);
System.out.println(str);
}
// reader = null;
} catch (IOException e) {
e.printStackTrace();
}
}
701.java9新特性:String底层存储结构的变更
结论:String 再也不用 char[] 来存储啦,改成了 byte[] 加上编码标记,节约
了一些空间。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
@Stable
private final
}
那StringBuffer 和 StringBuilder 是否仍无动于衷呢?
String-related classes such as AbstractStringBuilder, StringBuilder,
and StringBuffer will be updated touse the same representation
, as will the HotSpot VM‘s intrinsic(固有的、内置的) string operations.
702.java9新特性:Java9&10&11新特性工程方法创建只读集合
要创建一个只读、不可改变
的集合,必须构造和分配它,然后添加元素,最后 包装成一个不可修改的集合。
List<String> namesList = new ArrayList <>();
namesList.add("Joe");
namesList.add("Bob");
namesList.add("Bill");
namesList = Collections.unmodifiableList(namesList);
System.out.println(namesList);
缺点:我们一下写了五行。即:它不能表达为单个表达式
List<String> list = Collections.unmodifiableList(Arrays.asList("a", "b", "c")); Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("a", "b", "c")));
// 如下操作不适用于jdk 8 及之前版本,适用于jdk 9
Map<String, Integer> map = Collections.unmodifiableMap(new HashMap<>() {
{
put("a", 1);
put("b", 2);
put("c", 3);
}
});
map.forEach((k, v) -> System.out.println(k + ":" + v));
List firsnamesList = List.of(“Joe”,”Bob”,”Bill”);
调用集合中静态方法of(),可以将不同数量的参数传输到此工厂方法中。此功能 可用于Set和List,也可用于Map的类似形式。此时得到的集合,是不可变的
:在 创建后,继续添加元素到这些集合会导致“UnsupportedOperationException”
。
由于Java 8中接口方法的实现,可以直接在List,Set和Map的接口内定义这些方法,便于调用。
List<String> list = List.of("a", "b", "c");
Set<String> set = Set.of("a", "b", "c");
Map<String, Integer> map1 = Map.of("Tom", 12, "Jerry", 21, "Lilei", 33, "HanMeimei", 18);
Map<String, Integer> map2 = Map.ofEntries(Map.entry("Tom", 89),
Map.entry("Jim", 78), Map.entry("Tim", 98));
703.java9新特性:InputStream中的transferTo()方法
InputStream 终于有了一个非常有用的方法:transferTo
,可以用来将数据直接 传输到 OutputStream,这是在处理原始数据流时非常常见的一种用法,如下示例。
ClassLoader cl = this.getClass().getClassLoader();
try (InputStream is = cl.getResourceAsStream("hello.txt");
OutputStream os = new FileOutputStream("src\\hello1.txt")) {
is.transferTo(os); // 把输入流中的所有数据直接自动地复制到输出流中
} catch (IOException e) {
e.printStackTrace();
}
704.java9新特性:StringAPI新增的四个方法
Java 的 Steam API 是java标准库最好的改进之一,让开发者能够快速运算,从而能够有效的利用数据并行计算。Java 8 提供的 Steam 能够利用多核架构 实现声明式的数据处理。
在 Java 9 中,Stream API 变得更好,Stream 接口中添加了 4 个新的方法: takeWhile, dropWhile, ofNullable,还有个 iterate 方法的新重载方法,可以 让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。
除了对 Stream 本身的扩展,Optional 和 Stream 之间的结合也得到了改进。
现在可以通过 Optional 的新方法 stream() 将一个 Optional 对象转换为一个(可能是空的) Stream 对象。
takeWhile()
:用于从 Stream 中获取一部分数据,接收一个 Predicate 来进行选择。在有序的Stream 中,takeWhile 返回从开头开始的尽量多的元素。
dropWhile
:takeWhile 相反,返回剩余的元素。
ofNullable()
:Java 8 中 Stream 不能完全为null,否则会报空指针异常。而 Java 9 中的 ofNullable 方 法允许我们创建一个单元素 Stream,可以包含一个非空元素,也可以创建一个空Stream。
iterate()重载的使用
:这个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什 么时候结束迭代。
705.java9新特性:Optional的新方法Stream()
List<String> list = new ArrayList<>();
list.add("Tom");
list.add("Jerry");
list.add("Tim");
Optional<List<String>> optional = Optional.ofNullable(list); Stream<List<String>> stream = optional.stream(); stream.flatMap(x -> x.stream()).forEach(System.out::println);
706.java9新特性:升级的Nashorn引擎
Nashorn 项目在 JDK 9 中得到改进,它为 Java 提供轻量级的 Javascript 运行时。Nashorn 项目跟随 Netscape 的 Rhino 项目,目的是为了在 Java 中实现一个高 性能但轻量级的 Javascript 运行时。Nashorn 项目使得 Java 应用能够嵌入Javascript。它在 JDK 8 中为 Java 提供一个 Javascript 引擎。
JDK 9 包含一个用来解析 Nashorn 的 ECMAScript 语法树的 API。这个 API 使得IDE 和服务端框架不需要依赖 Nashorn 项目的内部实现类, 就能够分析ECMAScript 代码。
707.java10新特性的概述
2018年3月21日,Oracle官方宣布Java10正式发布。
需要注意的是 Java 9 和 Java 10 都不是 LTS (Long-Term-Support) 版本。和 过去的 Java 大版本升级不同,这两个只有半年左右的开发和维护期。而未 来的 Java 11,也就是 18.9 LTS,才是 Java 8 之后第一个 LTS 版本。
JDK10一共定义了109个新特性,其中包含12个JEP(对于程序员来讲,真 正的新特性其实就一个
),还有一些新API和JVM规范以及JAVA语言规范上 的改动。
JDK10的12个JEP(JDK Enhancement Proposal特性加强提议)参阅官方
文档:http://openjdk.java.net/projects/jdk/10/
708.java10新特性局部变量类型推断
产生背景
开发者经常抱怨Java中引用代码的程度。局部变量的显示类型声明,常常被认为 是不必须的
,给一个好听的名字经常可以很清楚的表达出下面应该怎样继续。
好处:减少了啰嗦和形式的代码,避免了信息冗余,而且对齐了变量名,更容易阅读!
举例如下:
场景一:类实例化时
作为 Java开发者,在声明一个变量时,我们总是习惯了敲打两次变量类型,第 一次用于声明变量类型,第二次用于构造器。
LinkedHashSet<Integer> set = new LinkedHashSet<>();
场景二:返回值类型含复杂泛型结构
变量的声明类型书写复杂且较长,尤其是加上泛型的使用
Iterator<Map.Entry<Integer, Student>> iterator = set.iterator();
场景三:
我们也经常声明一种变量,它只会被使用一次,而且是用在下一行代码中, 比如:
URL url = new URL("http://www.atguigu.com");
URLConnection connection = url.openConnection();
Reader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
尽管 IDE可以帮我们自动完成这些代码,但当变量总是跳来跳去的时候,可读 性还是会受到影响,因为变量类型的名称由各种不同长度的字符组成。而且, 有时候开发人员会尽力避免声明中间变量,因为太多的类型声明只会分散注意力,不会带来额外的好处。
适用于以下情况:
//1.局部变量的初始化
var list = new ArrayList<>();
//2.增强for循环中的索引
for(var v : list) {
System.out.println(v);
}
//3.传统for循环中
for(var i = 0;i < 100;i++) {
System.out.println(i);
}
在局部变量中使用时,如下情况不适用:
1.初始值为null
2.Lambda表达式
3.方法引用
4.为数组静态初始化
不适用以下的结构中:
情况1:没有初始化的局部变量声明
情况2:方法的返回类型
情况3:方法的参数类型
情况4:构造器的参数类型
情况5:属性
情况6:catch块
工作原理:在处理 var时,编译器先是查看表达式右边部分
,并根据右边变量值的类型进行 推断,作为左边变量的类型,然后将该类型写入字节码当中
。
注意
var不是一个关键字
你不需要担心变量名或方法名会与 var发生冲突,因为 var实际上并不是一个关键字,而是一个类型名,只有在编译器需要知道类型的地方才需要用到它。除此之外,它 就是一个普通合法的标识符。也就是说,除了不能用它作为类名,其他的都可以, 但极少人会用它作为类名。
这不是JavaScript
首先我要说明的是,var并不会改变Java是一门静态类型语言的事实
。编译器负责推 断出类型,并把结果写入字节码文件,就好像是开发人员自己敲入类型一样。
下面是使用 IntelliJ(实际上是 Fernflower的反编译器)反编译器反编译出的代码:
从代码来看,就好像之前已经声明了这些类型一样。事实上,这一特性只发 生在编译阶段,与运行时无关,所以对运行时的性能不会产生任何影响。所 以请放心,这不是 JavaScript。
709.java10新特性:Java9&10&11新特性新增创建不可变集合的方法
自 Java 9 开始,Jdk 里面为集合(List / Set / Map)都添加了 of (jdk9新增)
和 copyOf (jdk10新增)
方法,它们两个都用来创建不可变的集合,来看下它们的 使用和区别。
//示例1:
var list1 = List.of("Java", "Python", "C");
var copy1 = List.copyOf(list1);
System.out.println(list1 == copy1); // true
//示例2:
var list2 = new ArrayList<String>();
var copy2 = List.copyOf(list2);
System.out.println(list2 == copy2); // false
//示例1和2代码基本一致,为什么一个为true,一个为false?
从 源 码 分 析 , 可 以 看 出 copyOf 方 法 会 先 判 断 来 源 集 合 是 不 是
AbstractImmutableList 类型的,如果是,就直接返回,如果不是,则调用 of 创 建一个新的集合。
示例2因为用的 new 创建的集合,不属于不可变 AbstractImmutableList 类的子类,所以 copyOf 方法又创建了一个新的实例,所以为false。
注意:使用of和copyOf创建的集合为不可变集合,不能进行添加、删除、替换、 排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。
上面演示了 List 的 of 和 copyOf 方法,Set 和 Map 接口都有
710.java11新特性的概述
北京时间 2018年9 月 26 日,Oracle 官方宣布 Java 11 正式发 布。这是 Java 大版本周期变化 后的第一个长期支持版本,非 常值得关注。从官网即可下载, 最新发布的 Java11 将带来 ZGC、 Http Client 等重要特性,一共包 含 17 个 JEP(JDK Enhancement Proposals,JDK 增强提案)。其实,总共更新不止17个,只是我们更关注如下的17个JEP更新
JDK 11 将是一个 企业不可忽视的版本。从时间节点来看,JDK 11 的发布正 好处在 JDK 8 免费更新到期的前夕,同时 JDK 9、10 也陆续成为“历史版 本”
JDK 11 是一个长期支持版本(LTS, Long-Term-Support)
- 对于企业来说,
选择 11 将意味着长期的、可靠的、可预测的技术路线图。
其中免费的OpenJDK11 确定将得到 OpenJDK 社区的长期支持, LTS 版本将 是可以放心选择的版本。 - 从 JVM GC 的角度,
JDK11 引入了两种新的 GC,其中包括也许是划时代意义 的 ZGC
,虽然其目前还是实验特性,但是从能力上来看,这是 JDK 的一个巨 大突破,为特定生产环境的苛刻需求提供了一个可能的选择。例如,对部 分企业核心存储等产品,如果能够保证不超过 10ms 的 GC 暂停,可靠性会 上一个大的台阶,这是过去我们进行 GC 调优几乎做不到的,是能与不能的 问题。
711.java11新特性:String新增的方法
712.java11新特性:Optional新增的方法
713.java11新特性:局部变量类型推断的升级
714.java11新特性:HttpClient
715.java11新特性:更简化的编译运行程序
716.java11新特性:其它新特性
废除Nashorn javascript引擎,在后续版本准备移除掉,有需要的可以考虑使用GraalVM。
其它新特性
Unicode 10
Deprecate the Pack200 Tools and API
新的Epsilon垃圾收集器
完全支持Linux容器(包括Docker)
支持G1上的并行完全垃圾收集
最新的HTTPS安全协议TLS 1.3
Java Flight Recorder
717.jdk后续更新的展望
随着云计算和 AI 等技术浪潮,当前的计算模式和场景正在发生翻天覆地的变 化,不仅对 Java 的发展速度提出了更高要求,也深刻影响着 Java 技术的发展 方向。传统的大型企业或互联网应用,正在被云端、容器化应用、模块化的微 服务甚至是函数(FaaS, Function-as-a-Service)所替代。
Java虽然标榜面向对象编程,却毫不顾忌的加入面向接口编程思想,
又扯出匿 名对象
之概念,每增加一个新的东西,对Java的根本所在的面向对象思想的一 次冲击。反观Python,抓住面向对象的本质,又能在函数编程思想方面游刃有 余。Java对标C/C++,以抛掉内存管理为卖点,却又陷入了JVM优化的噩梦。
选 择比努力更重要,选择Java的人更需要对它有更清晰的认识。
Java 需要在新的计算场景下,改进开发效率。
这话说的有点笼统,我谈一些自 己的体会,Java 代码虽然进行了一些类型推断等改进,更易用的集合 API 等, 但仍然给开发者留下了过于刻板、形式主义的印象,这是一个长期的改进方向。