第十七章:Java9&10&11新特性
2021年3月13日
21:20
JDK9开始,目录结构的改变
模块化系统: Jigsaw → Modularity
模块将由通常的类和新的模块声明文件(module-info.java)组成。该文件是位于java代码结构的顶层,该模块描述符明确地定义了我们的模块需要什么依赖关系,以及哪些模块被外部使用。在exports子句中未提及的所有包默认情况下将封装在模块中,不能在外部使用。
要想在java9demo模块中调用java9test模块下包中的结构,需要在java9test的module-info.java中声明:
module java9test { exports com.atguigu.bean; } |
exports:控制着哪些包可以被其它模块访问到。所有不被导出的包默认都被封装在模块里面。
对应在java 9demo 模块的src 下创建module-info.java文件:
module java9demo { requires java9test; requires junit;
exports com.atguigu.java; } |
requires:指明对其它模块的依赖。
Java的REPL工具: jShell命令
/help 获取帮助 /imports 查看已经导入的包 /list 列出当前 session 里所有有效的代码片段 /vars 查看当前 session 下所有创建过的变量 /methods 查看当前 session 下所有创建过的方法 Tips:我们还可以重新定义相同方法名和参数列表的方法,即为对现有方法的修改(或覆盖)。 /edit 使用外部代码编辑器来编写 Java 代码 /edit 方法名 使用外部代码编辑器来编写指定的部分 /open 文件 调用文件里的代码 Exit 退出jShell Tab键 可自动补全代码 Tips:在 JShell 环境下,语句末尾的“;” 是可选的。但推荐还是最好加上。提高代码可读性。 没有受检异常(编译时异常)。说明:本来应该强迫我们捕获一个IOException,但却没有出现。因为jShell在后台为我们隐藏了。 |
JDK9语法改进:接口的私有方法
Java 8中规定接口中的方法除了抽象方法之外,还可以定义静态方法和默认的方法。一定程度上,扩展了接口的功能,此时的接口更像是一个抽象类。
在Java 9中,接口更加的灵活和强大,连方法的访问权限修饰符都可以声明为private的了,此时方法将不会成为你对外暴露的API的一部分。
复习: 接口中方法的权限修饰符都是public 接口中的静态方法只能由接口自己调用,接口的实现类不能调用接口的静态方法,会提示: Static method may be invoked on containing interface class only 接口中的默认方法实现类的对象可以直接调用,也可以重写。
jdk 9中允许接口中定义私有的方法,接口的私有方法,不能在接口外部调用,提示:'methodPrivate()' has private access in 'com.atguigu.java.MyInterface' |
语法改进:钻石操作符使用升级
我们将能够与匿名实现类共同使用钻石操作符(diamond operator)
在Java 8中如下的操作是会报错的:
Comparator<String> com = new Comparator<>() { @Override public int compare(String o1, String o2) { return 0; } }; |
Java 9中如上操作就可以正常执行通过
语法改进:try语句
//java 8之前的资源关闭的操作 InputStreamReader isr = null; try { isr = new InputStreamReader(System.in); char[] buffer = new char[20]; int len; if ((len = isr.read(buffer)) != -1) { System.out.println(len); String str = new String(buffer,0,len); System.out.println(str); } } catch (IOException e) { e.printStackTrace(); } finally { if (isr != null) { try { isr.close(); } catch (IOException e) { e.printStackTrace(); } } } |
//java 8中资源关闭操作: Java 8 中,可以实现资源的自动关闭 //要求自动关闭的资源的实例化必须放在try的一对小括号中 //多个资源之间用;分号隔开 try(InputStreamReader isr = new InputStreamReader(System.in); OutputStreamWriter osw = new OutputStreamWriter(System.out)){ char[] buffer = new char[20]; int len = 0; if ((len = isr.read(buffer)) != -1) { osw.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } |
//java9中资源关闭操作:需要自动关闭的资源的实例化可以放在try的一对小括号外。 //此时的资源属性是常量,声明为final的,不可修改 //多个资源之间用;分号隔开 InputStreamReader isr = new InputStreamReader(System.in); OutputStreamWriter osw = new OutputStreamWriter(System.out); try(isr;osw){ char[] buffer = new char[20]; int len; if((len = isr.read(buffer)) != -1){ osw.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } |
String存储结构变更
String 再也不用 char[] 来存储啦,改成了 byte[] 加上编码标记,节约了一些空间。
同时,与String相关的,StringBuffer、StringBuilder也变了
集合工厂方法:快速创建只读集合
java8中创建只读集合:
Collections.unmodifiableList(List l) 返回一个只读集合,不可增加,不可修改(原集合不变)
List<String> list = Collections.unmodifiableList(Arrays.asList("a", "b", "c")); Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("a", "b", "c"))); Map<String,Integer> map = Collections.unmodifiableMap(new HashMap<String,Integer>(){ { put("a", 1); put("b", 2); } }); |
解读: List<String> list1 = new ArrayList<>(){ { add("a"); add("b"); } }; System.out.println(list1.getClass()); //class com.atguigu.java.Java9Test1$2 System.out.println(list1.getClass().getSuperclass()); //class java.util.ArrayList
这种写法,是创建了一个ArrayList的匿名子类的对象,然后用代码块的形式调用继承的add()方法添加了两个元素
类似于下面这种: Comparator com = new Comparator() { //代码块 { System.out.println("这是一个匿名子类的代码块"); show(); //调用匿名子类的方法 } @Override public int compare(Object o1, Object o2) { return 0; } public void show(){ System.out.println("匿名子类的方法"); } }; |
Arrays.asList(1,2,3,4,5) 此时得到的集合list也是一个只读集合。不可增加,可以修改
java9新特性:集合工厂方法:创建只读集合。不可添加,不可修改
List.of()
Set.of()
Map.of()
Map.ofEntries()
List<Integer> list1 = List.of(1, 2, 3, 4, 5); Set<Integer> set1 = Set.of(234, 12, 310, 235, 21, 39, 91, 23); //K,V,K,V…… Map<String, Integer> map1 = Map.of("Tom", 12, "Jerry", 23, "Hanmeimei", 15); //static <K, V> Entry<K, V> entry(K k, V v) Map.entry()方法生成Map.Entry实现类的对象 Map<String, Integer> map2 = Map.ofEntries(Map.entry("Tom", 15), Map.entry("Jack", 18)); |
InputStream的新方法:transferTo()
transferTo() 把输入流中的所有数据直接自动地复制到输出流中
注意:Reader也有这个方法
ClassLoader loader = this.getClass().getClassLoader(); try(InputStream is = loader.getResourceAsStream("hello.txt"); FileOutputStream fos = new FileOutputStream("src\\hello1.txt")) { is.transferTo(fos); // 把输入流中的所有数据直接自动地复制到输出流中 System.out.println("复制完成"); } catch (IOException e) { e.printStackTrace(); } |
增强的 Stream API
在 Java 9 中,Stream API 变得更好,Stream 接口中添加了 4 个新的方法:takeWhile, dropWhile, ofNullable,还有个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。
takeWhile 用于从 Stream 中获取一部分数据,接收一个 Predicate 来进行选择。返回从开头开始的按照指定规则尽量多的元素
dropWhile 与 takeWhile 相反,把符合条件的舍去,返回从第一个不符合条件开始的剩余元素。
List<Integer> list = Arrays.asList(27,11,35,47,65,38,25,9,43); list.stream().takeWhile( x -> x < 48).forEach(System.out::println); //27 11 35 47 list.stream().dropWhile( x -> x < 48).forEach(System.out::println); //65 38 25 9 43 |
of() 参数中的多个元素,可以包含null值,null也算1个,也可以被输出。of()参数不能存储单个null值。否则,报异常
ofNullable():形参变量是可以为null值的单个元素。但是null不算个数,并且不会被输出
iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) 这个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。
Stream.iterate(1,x -> x < 1000,x -> x * 2).forEach(System.out::println); |
Optional获取Stream的方法
List<String> list = new ArrayList<>(); list.add("Tom"); list.add("Jerry"); list.add("Tim");
Optional<List<String>> optional = Optional.ofNullable(list); //是把Optional里面存的 元素本身 作为Stream里的数据,而不是把里面的List拆开了。 //System.out.println(optional.stream().count()); //1 //optional.stream().forEach(x -> { // System.out.println(x); //[Tom, Jerry, Tim] // System.out.println(x.getClass()); //class java.util.ArrayList //}); //遍历List,把List再用stream()方法生成流,加入到optional.stream()生成的流中 optional.stream().flatMap(x -> x.stream()).forEach(System.out::println); //Tom Jerry Tim |
JEP(JDK Enhancement Proposal特性加强提议)
Java10新特性
Local-Variable Type Inference 局部变量类型推断
适用于以下情况:
//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); } |
//泛型的类型推断中,右边的泛型是根据左边的泛型推断,可以省略。但是如果左边写成了var,就只能看右边的泛型了。如果右边没写,就是没指定泛型类型 var list = new ArrayList<String>();
在局部变量中使用时,如下情况不适用: //1.局部变量不赋值,就不能实现类型推断。(声明和赋值分开写也不行) var num; num = 10; //2.lambda表示式中,左边的函数式接口不能声明为var Supplier<Double> sup = () -> Math.random(); //var sup1= () -> Math.random();
//3.方法引用中,左边的函数式接口不能声明为var Consumer<String> con = System.out::println; //var con1 = System.out::println;
//4.数组的静态初始化中,注意如下的情况也不可以 int[] arr = {1,2,3,4}; var arr1 = new int[]{1,2,3,4}; //可以 //var arr2 = {1,2,3,4}; |
不适用以下的结构中:
//情况1:没有初始化的局部变量声明 var s = null; var num; //情况2:方法的返回类型 public var method1(){ return 0; } //情况3:方法的参数类型 public String method2(var num){ return null; } //情况4:构造器的参数类型 public Java10Test(var num){
} //情况5:属性 var num; //情况6:catch块 try{
}catch(var e){ e.printStackTrace(); } |
工作原理:在处理 var时,编译器先是查看表达式右边部分,并根据右边变量值的类型进行推断,作为左边变量的类型,然后将该类型写入字节码当中。
编译器负责推断出类型,并把结果写入字节码文件,就好像是开发人员自己敲入类型一样。
注意:
l var不是一个关键字
集合新增创建不可变集合的方法
copyOf(Xxx coll) 方 法 会 先 判 断 来 源 集 合 是 不 是AbstractImmutableList 类型的,如果是,就直接返回,如果不是,则调用 of 创建一个新的集合。
//示例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>(); list2.add("aaa"); var copy2 = List.copyOf(list2); System.out.println(list2 == copy2); // false
//示例1和2代码基本一致,为什么一个为true,一个为false? //结论:copyOf(Xxx coll):如果参数coll本身就是一个只读集合,则copyOf()返回值即为当前的coll //如果参数coll不是一个只读集合,则copyOf()返回一个新的集合,这个集合是只读的。 |
Java 11 新特性
新增了一系列字符串处理方法
//isBlank():判断字符串是否为空白 \t\n\r也算空白 System.out.println(" \t \n \r \n \t ".isBlank()); //true //strip():去除首尾空白 比trim()适用范围更广 System.out.println("---" + " \t \n \n \t ".strip() + "---"); //------ System.out.println("---" + " \t \n \n \t ".trim() + "---"); //------ //stripTrailing():去除尾部空格 包括\t\n\r System.out.println("---" + " \t \n \nabc \r \t ".stripTrailing() + "---"); //stripLeading():去除首部空格 包括\t\n\r System.out.println("---" + " \t \n \nabc \r \t ".stripLeading() + "---"); //repeat(int count):复制字符串 String str1 = "abc"; var str2 = str1.repeat(5); System.out.println(str1); //abc System.out.println(str2); //abcabcabcabcabc //lines().count():行数统计 lines()是返回了Stream<String> String str3 = "abc\nab\ncabcabca\nbc\n"; System.out.println(str3.lines().count()); //4 |
Optional 加强,新增方法
op.isPresent() 判断内部的value是否存在
op.isEmpty() 判断内部的value是否为空 JDK11
ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) value非空,执行参数1功能;如果value为空,执行参数2功能 JDK9
Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) value非空,返回对应的Optional;value为空,返回形参封装的Optional
Stream<T> stream() value非空,返回仅包含此value的Stream;否则,返回一个空的Stream
orElseThrow() value非空,返回value;否则抛异常NoSuchElementException
Optional<Object> op = Optional.empty(); System.out.println(op.isPresent()); //判断内部的value是否存在 false System.out.println(op.isEmpty()); //判断内部的value是否为空 true
op = Optional.of("abc"); //orElseThrow():value非空,返回value;否则抛异常NoSuchElementException Object obj = op.orElseThrow(); System.out.println(obj); //abc
Optional<String> op1 = Optional.of("hello"); op = Optional.empty(); //or:value非空,返回对应的Optional;value为空,返回形参封装的Optional Optional<Object> op2 = op.or(() -> op1); System.out.println(op2); //Optional[hello] |
局部变量类型推断升级
在var上添加注解的语法格式,在jdk10中是不能实现的。在JDK11中加入了这样的语法。
//错误的形式: 用注解时,必须要有类型, 可以加上var(jdk10中不行) //Consumer<String> con1 = (@Deprecated t) -> System.out.println(t.toUpperCase()); //正确的形式: //使用var的好处是在使用lambda表达式时给参数加上注解。 Consumer<String> con2 = (@Deprecated var t) -> System.out.println(t.toUpperCase()); |
全新的HTTP 客户端API
HttpClient替换原有的HttpURLConnection。
它将 替 代 仅 适 用 于 blocking 模式的 HttpURLConnection(HttpURLConnection是在HTTP 1.0的时代创建的,并使用了协议无关的方法),并提供对WebSocket 和HTTP/2的支持。
更简化的编译运行程序
在我们的认知里面,要运行一个 Java 源代码必须先编译,再运行,两步执行动作。而在未来的 Java 11 版本中,通过一个 java 命令就直接搞定了,如以下所示:
java Javastack.java
一个命令编译运行源代码的注意点:
Ø 执行源文件中的第一个类, 第一个类必须包含主方法。
Ø 并且不可以使用其它源文件中的自定义类, 本文件中的自定义类是可以使用的。
ZGC
l ZGC, A Scalable Low-Latency Garbage Collector(Experimental)ZGC, 这应该是JDK11最为瞩目的特性, 没有之一。 但是后面带了Experimental,说明这还不建议用到生产环境。
l ZGC是一个并发, 基于region, 压缩型的垃圾收集器, 只有root扫描阶段会STW(stop the world), 因此GC停顿时间不会随着堆的增长和存活对象的增长而变长。
l 优势:
Ø GC暂停时间不会超过10ms
Ø 既能处理几百兆的小堆, 也能处理几个T的大堆(OMG)
Ø 和G1相比, 应用吞吐能力不会下降超过15%
Ø 为未来的GC功能和利用colord指针以及Load barriers优化奠定基础
Ø 初始只支持64位系统
l ZGC的设计目标是:支持TB级内存容量,暂停时间低(<10ms),对整个程序吞吐量的影响小于15%。 将来还可以扩展实现机制,以支持不少令人兴奋的功能,例如多层堆(即热对象置于DRAM和冷对象置于NVMe闪存),或压缩堆
其他新特性
l Unicode 10
l Deprecate the Pack200 Tools and API
l 新的Epsilon垃圾收集器
l 完全支持Linux容器(包括Docker)
l 支持G1上的并行完全垃圾收集
l 最新的HTTPS安全协议TLS 1.3
l Java Flight Recorder
2021年3月14日21:56:49 完结,结尾拉到上面。
结尾
使用 Microsoft OneNote 2016 创建。 版权所有:古木苏