JDK11新特性
OpenJDK的各种实现
这段时间比对了不少 OpenJDK 的发行版,最后找到Liberica JDK。
https://bell-sw.com/pages/java-11.0.6/
对一般用户来说,Liberica JDK 应该是最友好的 OpenJDK 发行版。
Liberica JDK 默认捆绑了 JavaFX (AdoptOpenjdk 和 OracleJDK 都没有);
提供 Java 13 的构建(Zulu 13 没有捆绑 OpenJFX 的版本,只有 Zulu 11 有);
为 Linux 与 Windows 提供 32 位构建(OracleJDK 不提供 32 位版本,Zulu 只为 Windows 提供,而且只有 Zulu 11 是提供捆绑 OpenJFX 版本的);
Windows 安装包自动配置环境变量,并且自动关联 jar 打开方式(Zulu 和 OracleJDK 好像不会配置环境变量);
国内直连下载速度很快(AdoptOpenJDK 出来挨打)。
JavaFX是可以当依赖库用,但是一来是不方便,二来你会发现…… 实际上托管的仓库是只有x86_64版的,ARM和X86都不支持, 想在这些地方跑还是得自己找其他构建。
Liberica JDK带各种指令集的JavaFX,是一个不错的OpenJDK实现。
wget -q -O - https://download.bell-sw.com/pki/GPG-KEY-bellsoft | sudo apt-key add - echo "deb [arch=amd64] https://apt.bell-sw.com/ stable main" | sudo tee /etc/apt/sources.list.d/bellsoft.list sudo apt-get update sudo apt-get install bellsoft-java8 sudo apt-get install bellsoft-java11
Java 8
HTTP2.0
需要加载alpn包:https://www.eclipse.org/jetty/documentation/current/alpn-chapter.html#alpn
日期时间 API
java.util.Date
为可变类型,以及SimpleDateFormat
非线程安全的缺点。
Java 8 推出了全新的日期时间API,
java.time
包下的所有类都是不可变类型而且线程安全。
与旧API的对应关系
Java.time ISO Calendar | Java.util Calendar |
---|---|
Instant | Date |
LocalDate | Calendar |
LocalTime | |
LocalDateTime | |
ZonedDateTime | |
OffsetDateTime, OffsetTime | Calendar |
Zoneld, ZoneOffset, ZooneRules | TimeZone |
Week Starts on Monday(1 ... 7) | Week Starts on Monday(1 ... 7) |
enum MONDAY, TUESDAY, ... SUNDAY | int values MONDAY, TUESDAY, ... SUNDAY |
12 Month (1 ... 12) | 12 Month (1 ... 12) |
enum JANUARY, FEBRUARY, ... DECEMBER | int values JANUARY, FEBRUARY, ... DECEMBER |
Java 9
集合增强
从Java 9 开始,jdk里面就为集合(List、Set、Map)增加了of和copyOf方法。 它们用来创建不可变集合。
- of() @since 9
- copyOf() @since 10
示例一:
var list = List.of("Java", "Python", "C"); //不可变集合 var copy = List.copyOf(list); //copyOf判断是否是不可变集合类型,如果是直接返回 System.out.println(list == copy); // true var list = new ArrayList<String>(); // 这里返回正常的集合 var copy = List.copyOf(list); // 这里返回一个不可变集合 System.out.println(list == copy); // false
示例二:
var set = Set.of("Java", "Python", "C"); var copy = Set.copyOf(set); System.out.println(set == copy); // true var set1 = new HashSet<String>(); var copy1 = List.copyOf(set1); System.out.println(set1 == copy1); // false
示例三:
var map = Map.of("Java", 1, "Python", 2, "C", 3); var copy = Map.copyOf(map); System.out.println(map == copy); // true var map1 = new HashMap<String, Integer>(); var copy1 = Map.copyOf(map1); System.out.println(map1 == copy1); // false
注意:
-
使用
of
和copyOf
创建的集合为不可变集合,不能进行添加、删除、替换、排序等操作, 不然会报java.lang.UnsupportedOperationException
异常, -
使用
Set.of()
不能出现重复元素、Map.of()
不能出现重复key, 否则回报java.lang.IllegalArgumentException
。 -
of(...)
方法重载了 0 ~ 10 个参数的不同方法 。Map 接口如果超过 10 个参数, 可以使用ofEntries(...)
方法。
接口支持私有方法和私有静态方法
接口中允许 | Java 8 | Java 9 |
---|---|---|
常量 | ✅ | ✅ |
抽象方法 | ✅ | ✅ |
默认方法 | ✅ | ✅ |
静态方法 | ✅ | ✅ |
私有方法 | ❌ | ✅ |
私有静态方法 | ❌ | ✅ |
例如:
interface Test{ String fields = "interface field"; public abstract void abstractMethods(); default void defaultMethods() { System.out.println("default Method"); staticMethods(); privateMethods(); privateStaticMethods(); } static void staticMethods() { System.out.println("static Method"); } private void privateMethods() { System.out.println("private Method"); } private static void privateStaticMethods() { System.out.println("private Static Method"); } } /* 接口实现类 */ public class TestImpl implements Test { @Override public void abstractMethods() { System.out.println("abstract Method"); } } /* 测试类 */ public class Demo { public static void main(String[] args) { TestImpl testImpl = new TestImpl(); System.out.println(testImpl.fields); testImpl.abstractMethods(); testImpl.defaultMethods(); } } // 输出: // interface field // abstract Method // default Method // static Method // private Method // private Static Method
Stream增强
Stream是Java 8 中的特性,在Java 9 中为其新增了4个方法:
-
ofNullable(T t)
此方法可以接收null来创建一个空流
Stream.of(null); //以前 报错 Stream.ofNullable(null); // 现在
-
takeWhile(Predicate<? super T> predicate)
此方法根据Predicate接口来判断如果为true就 取出 来生成一个新的流,只要碰到false就终止,不管后边的元素是否符合条件。
Stream<Integer> integerStream = Stream.of(6, 10, 11, 15, 20); Stream<Integer> takeWhile = integerStream.takeWhile(t -> t % 2 == 0); takeWhile.forEach(System.out::println); // 6,10
-
dropWhile(Predicate<? super T> predicate)
此方法根据Predicate接口来判断如果为true就 丢弃 来生成一个新的流,只要碰到false就终止,不管后边的元素是否符合条件。
Stream<Integer> integerStream = Stream.of(6, 10, 11, 15, 20); Stream<Integer> takeWhile = integerStream.dropWhile(t -> t % 2 == 0); takeWhile.forEach(System.out::println); //11,15,20
-
iterate重载
以前使用iterate方法生成无限流需要配合limit进行截断
Stream<Integer> limit = Stream.iterate(1, i -> i + 1).limit(5); limit.forEach(System.out::println); //1,2,3,4,5
现在重载后这个方法增加了个判断参数
Stream<Integer> iterate = Stream.iterate(1, i -> i <= 5, i -> i + 1); iterate.forEach(System.out::println); //1,2,3,4,5
Optional增强
-
stream()
如果为空返回一个空流,如果不为空将Optional的值转成一个流。
//返回Optional值的流 Stream<String> stream = Optional.of("Java 11").stream(); stream.forEach(System.out::println); // Java 11 //返回空流 Stream<Object> stream = Optional.ofNullable(null).stream(); stream.forEach(System.out::println); //
-
ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)
个人感觉这个方法就是结合isPresent()
对Else的增强,ifPresentOrElse
方法的用途是
,如果一个 Optional 包含值,则对其包含的值调用函数 action,
即action.accept(value)
,这与 ifPresent 一致;
与 ifPresent 方法的区别在于,ifPresentOrElse
还有第二个参数emptyAction
——
如果 Optional 不包含值,那么 ifPresentOrElse 便会调用 emptyAction,
即emptyAction.run()
。
Optional<Integer> optional = Optional.of(1); optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() -> System.out.println("Not Present.")); //Value: 1 optional = Optional.empty(); optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() -> System.out.println("Not Present.")); //Not Present.
-
or(Supplier<? extends Optional<? extends T>> supplier)
Optional<String> optional1 = Optional.of("Java"); Supplier<Optional<String>> supplierString = () -> Optional.of("Not Present"); optional1 = optional1.or( supplierString); optional1.ifPresent( x -> System.out.println("Value: " + x)); //Value: Java optional1 = Optional.empty(); optional1 = optional1.or( supplierString); optional1.ifPresent( x -> System.out.println("Value: " + x)); //Value: Not Present
InputStream增强
String lxs = "java"; try ( var inputStream = new ByteArrayInputStream(lxs.getBytes()); var outputStream = new ByteArrayOutputStream()) // { inputStream.transferTo(outputStream); System.out.println(outputStream); //java }
改进的 CompletableFuture API
单位在Timeout类型定义在:java.util.concurrent.Timeunits
中,比如MILLISECONDS
支持 delays 和 timeouts,提升了对子类化的支持。新的工厂方法:
在timeout前以给定的 value 完成这个 CompletableFutrue。返回这个 CompletableFutrue:
public CompletableFuture<T> completeOnTimeout( T value, long timeout, TimeUnit unit)
如果没有在给定的 timeout 内完成,就以java.util.concurrent.TimeoutException
完成这个 CompletableFutrue,并返回这个 CompletableFutrue:
public CompletableFuture<T> orTimeout(long timeout, TimeUnit unit)
使得CompletableFuture可以被更简单的继承。
public <U> CompletableFuture<U> newIncompleteFuture()
返回一个新的以指定 value 完成的CompletionStage, 并且只支持 CompletionStage 里的接口:
<U> CompletionStage<U> completedStage(U value)
返回一个新的以指定异常完成的CompletionStage, 并且只支持 CompletionStage 里的接口:
<U> CompletionStage<U> failedStage(Throwable ex)
改进try-with-resources的异常处理
try-with-resources声明在 JDK 9 已得到改进。如果你已经有一个资源是 final 或等效于 final 变量,可以在 try-with-resources 语句中使用该变量, 而无需在 try-with-resources 语句中声明一个新变量。
示例如下:
public static void main(String[] args) throws IOException { System.out.println(readData("test"));// 结果:test } static String readData(String message) throws IOException { Reader inputString = new StringReader(message); BufferedReader br = new BufferedReader(inputString); // Java8处理方式: // try (BufferedReader br1 = br) { // return br1.readLine(); // } // Java9处理方式: try (br) { return br.readLine(); } }
改进 @Deprecated 注解
Java 9 中注解增加了两个新元素:since 和 forRemoval。
- since: 元素指定已注解的API元素已被弃用的版本。
- forRemoval: 元素表示注解的 API 元素在将来的版本中被删除,应该迁移 API。
示例如下:
@Deprecated(since = "1.9", forRemoval = true) class Test{ }
内部类泛型
在java8中,匿名内部类后面的<>
里面必须带有泛型类型。Java9就不需要了:
abstract class Handler<T> { public T content; public Handler(T content) { this.content = content; } abstract void handle(); } public class Test { public static void main(String[] args) { Handler<Integer> intHandler = new Handler<>(1) { @Override public void handle() { System.out.println(content); } }; intHandler.handle(); Handler<? extends Number> intHandler1 = new Handler<>(2) { @Override public void handle() { System.out.println(content); } }; intHandler1.handle(); Handler<?> handler = new Handler<>("test") { @Override public void handle() { System.out.println(content); } }; handler.handle(); } }
Unicode 7.0
从Java SE 9,升级现有平台的API,支持7.0版本的Unicode标准,主要在以下类中:
-
java.lang.Character
和java.lang.String` -
java.text
包中的Bidi
,BreakIterator
和Normalizer
模块化(Module)
模块化就是增加了更高级别的聚合,是Package的封装体。Package是一些类路径名字的约定 ,而模块是一个或多个Package组成的封装体。
- java9以前 :package => class/interface。
- java9以后 :module => package => class/interface。
类比的话相当是Maven中项目可以有子项目,子项目之间还有依赖关系。
但是Maven中子项目的依赖是由pom.xml
来声明的,独立于jvm体系之外。
所以Java要在模块下加上一个module-info.java
文件来声明各个模块中的依赖关系
与访问隔离,编译打包后,就成为一个模块的实体。起到类似于pom.xml的作用。
JDK中自己的模块
那么JDK被拆为了哪些模块呢?打开终端执行java --list-modules
查看。
$ java --list-modules java.base@11.0.2 java.compiler@11.0.2 java.datatransfer@11.0.2 java.desktop@11.0.2 java.instrument@11.0.2 java.logging@11.0.2 java.management@11.0.2 java.management.rmi@11.0.2 java.naming@11.0.2 java.net.http@11.0.2 java.prefs@11.0.2 java.rmi@11.0.2 java.scripting@11.0.2 java.se@11.0.2 java.security.jgss@11.0.2 java.security.sasl@11.0.2 java.smartcardio@11.0.2 java.sql@11.0.2 java.sql.rowset@11.0.2 java.transaction.xa@11.0.2 java.xml@11.0.2 java.xml.crypto@11.0.2 jdk.accessibility@11.0.2 jdk.aot@11.0.2 jdk.attach@11.0.2 jdk.charsets@11.0.2 jdk.compiler@11.0.2 jdk.crypto.cryptoki@11.0.2 jdk.crypto.ec@11.0.2 jdk.dynalink@11.0.2 jdk.editpad@11.0.2 jdk.hotspot.agent@11.0.2 jdk.httpserver@11.0.2 jdk.internal.ed@11.0.2 jdk.internal.jvmstat@11.0.2 jdk.internal.le@11.0.2 jdk.internal.opt@11.0.2 jdk.internal.vm.ci@11.0.2 jdk.internal.vm.compiler@11.0.2 jdk.internal.vm.compiler.management@11.0.2 jdk.jartool@11.0.2 jdk.javadoc@11.0.2 jdk.jcmd@11.0.2 jdk.jconsole@11.0.2 jdk.jdeps@11.0.2 jdk.jdi@11.0.2 jdk.jdwp.agent@11.0.2 jdk.jfr@11.0.2 jdk.jlink@11.0.2 jdk.jshell@11.0.2 jdk.jsobject@11.0.2 jdk.jstatd@11.0.2 jdk.localedata@11.0.2 jdk.management@11.0.2 jdk.management.agent@11.0.2 jdk.management.jfr@11.0.2 jdk.naming.dns@11.0.2 jdk.naming.rmi@11.0.2 jdk.net@11.0.2 jdk.pack@11.0.2 jdk.rmic@11.0.2 jdk.scripting.nashorn@11.0.2 jdk.scripting.nashorn.shell@11.0.2 jdk.sctp@11.0.2 jdk.security.auth@11.0.2 jdk.security.jgss@11.0.2 jdk.unsupported@11.0.2 jdk.unsupported.desktop@11.0.2 jdk.xml.dom@11.0.2 jdk.zipfs@11.0.2
大家都知道JRE中有一个超级大的rt.jar
(60多M),tools.jar
也有几十兆,
以前运行一个hello world也需要上百兆的环境。
让Java SE程序更加容易轻量级部署。强大的封装能力。改进组件间的依赖管理, 引入比jar粒度更大的Module。改进性能和安全性。
例:分模块
其实只要在原来Maven的子项目的源代码根目录加上一个module-info.java
,
指定哪些类是对外的,要依赖哪些类就可以:
example-module/ |-> pom.xml | |-> example-common/ | |-> src/main/java/ | | |-> study/module/common/base/ | | | `-> SimpleRenderer.java | | |-> study/module/common/helper/ | | | `-> RendererSupport.java | | `-> module-info.java | `-> pom.xml | `-> example-ui/ |-> src/main/java/ | |-> study/module/ui/item/ | | `-> Component.java | `-> module-info.java `-> pom.xml
总项目pom.xml
:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>study.example</groupId> <artifactId>study-module</artifactId> <version>1.0.0</version> <packaging>pom</packaging> <modules> <module>example-common</module> <module>example-ui</module> </modules> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.version>1.0.0</project.version> </properties> <dependencyManagement> <dependencies> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>11</source> <target>11</target> <encoding>${project.build.sourceEncoding}</encoding> </configuration> <version>3.8.1</version> </plugin> </plugins> </build> </project>
子项目两个包:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>study.example</groupId> <artifactId>study-module</artifactId> <version>1.0.0</version> </parent> <artifactId>example-common</artifactId> <packaging>jar</packaging> </project>
工具类模块两个包,一个对外一个对内:
对内:
package study.module.common.base; public class SimpleRenderer { public void renderAsString(Object object) { System.out.println(object); } }
对外:
package study.module.common.helper; import study.module.common.base.SimpleRenderer; public class RendererSupport { public void render(Object object) { new SimpleRenderer().renderAsString(object); } }
指定这个模块对外的包:
module example.common { requires java.base; exports study.module.common.helper; }
界面模块会引用工具类的模块:
POM文件指定MAVEN项目的引用:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>study.example</groupId> <artifactId>study-module</artifactId> <version>1.0.0</version> </parent> <artifactId>example-ui</artifactId> <packaging>jar</packaging> <dependencies> <dependency> <groupId>study.example</groupId> <artifactId>example-common</artifactId> <version>${project.version}</version> </dependency> </dependencies> </project>
模块的声明文件中指定对工具模块的引用:
module example.ui { requires java.base; requires example.common; }
具体的类中使用工具模块中的类:
package study.module.ui.item; import study.module.common.helper.RendererSupport; public class Component { public static void main(String[] args) { RendererSupport support = new RendererSupport(); support.render("test object"); } }
查看jar包依赖哪些模块:
$ jdeps --list-deps example-common/target/example-common-1.0.0.jar java.base
模块的关键字
open
用来指定开放模块,开放模块的所有包都是公开的,public
的可以直接引用使用,
其他类型可以通过反射得到。
open module module.one { //导入日志包 requires java.logging; }
opens
opens 用来指定开放的包,其中public类型是可以直接访问的,其他类型可以通过反射得到。
module module.one { opens <package>; }
exports
exports用于指定模块下的哪些包可以被其他模块访问。
module module.one { exports <package>; exports <package> to <module1>, <module2>...; }
requires
该关键字声明当前模块与另一个模块的依赖关系。
module module.one { requires <package>; }
uses、provides … with …
uses语句使用服务接口的名字,当前模块就会发现它,使用java.util.ServiceLoader
类进行加载,必须是本模块中的,不能是其他模块中的.其实现类可以由其他模块提供。
module module.one { //对外提供的接口服务 ,下面指定的接口以及提供服务的impl,如果有多个实现类,用用逗号隔开 uses <接口名>; provides <接口名> with <接口实现类>,<接口实现类>; }
Jshell
交互模式
-
打开终端,键入
jshell
进入jshell环境; -
输入
/help intro
可以查看Jshell的介绍。 -
键入
/exit
就可以退出。
Jshell默认会导入下面的一些包,所以在Jshell环境中这些包的内容都是可以使用的。
import java.lang.*; import java.io.*; import java.math.*; import java.net.*; import java.nio.file.*; import java.util.*; import java.util.concurrent.*; import java.util.function.*; import java.util.prefs.*; import java.util.regex.*; import java.util.stream.*;
在程序中调用
在JDK9中提供了一个新的类JShell.java
,它属于jdk.jshell
模块。
我们可以使用它执行Java代码片段,或创建一个Java方法,而不用创建一个类。
如果这个Java代码片段中有错误,可以通过Snippet.status
状态
(只有两种状态:REJECTED
、VALID
)来检查。
SourceCodeAnalysis
是用来解析代码的,它使用分号、解析方法、或类的声明等。
例子:用来执行代码的工具类:
package ex.jshell.extension; import jdk.jshell.JShell; import jdk.jshell.Snippet; import jdk.jshell.SnippetEvent; import jdk.jshell.SourceCodeAnalysis; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Objects; /** * This class can execute jshell expressions in sequence * We can write java commands like shell script and execute it. * Just write commands in a way that we give in jshell and save it in a file and execute it. * * @author Hemamabara Vamsi, Kotari * @since 5/27/2017. */ public class JShellScriptExecutor { public static void main(String[] args){ new JShellScriptExecutor().evaluate(args[0]); } public void evaluate(String scriptFileName){ try(JShell jshell = JShell.create()){ // Handle snippet events. We can print value or take action if evaluation failed. jshell.onSnippetEvent(snippetEvent -> snippetEventHandler(snippetEvent)); String scriptContent = new String(Files.readAllBytes(Paths.get(scriptFileName))); String s = scriptContent; while (true) { // Read source line by line till semicolon (;) SourceCodeAnalysis.CompletionInfo an = jshell.sourceCodeAnalysis().analyzeCompletion(s); if (!an.completeness().isComplete()) { break; } // If there are any method declaration or class declaration // in new lines, resolve it. jshell.eval(trimNewlines(an.source())); // EOF if (an.remaining().isEmpty()) { break; } // If there is semicolon, execute next seq s = an.remaining(); } } catch (IOException e) { e.printStackTrace(); } } public void snippetEventHandler(SnippetEvent snippetEvent){ String value = snippetEvent.value(); if(!Objects.isNull(value) && value.trim().length() > 0) { // Prints output of code evaluation System.out.println(value); } // If there are any erros print and exit if(Snippet.Status.REJECTED.equals(snippetEvent.status())){ System.out.println("Evaluation failed : "+snippetEvent.snippet().toString() +"\nIgnoring execution of above script"); } } private String trimNewlines(String s) { int b = 0; while (b < s.length() && s.charAt(b) == '\n') { ++b; } int e = s.length() -1; while (e >= 0 && s.charAt(e) == '\n') { --e; } return s.substring(b, e + 1); } }
关联模块:
module ex.jshell.extension { requires jdk.jshell; }
测试脚本:
String var1 = "Hello"; System.out.println(var1); public int getInt1 () { return 2; } public int getInt2 () { return 4; } getInt1() + getInt2(); public class MyClass{ public void sayHelloWorld() { System.out.println("HelloWorld"); } } new MyClass().sayHelloWorld()
调用:
java ex.jshell.extension.JShellScriptExecutor ./java_shell_code.txt
Java 10
var关键字
var是Java10中新增的局部类型变量推断。它会根据后面的值来推断变量的类型, 所以var必须要初始化。
例:
var a; //❌ var a = 1; //✅
var定义局部变量
var a = 1; // 等于 int a = 1;
var接收方法返回时
var result = this.getResult(); // 等于 String result = this.getResult();
var循环中定义局部变量
for (var i = 0; i < 5; i++) { System.out.println(i); } 等于 for (int i = 0; i < 5; i++) { System.out.println(i); }
var结合泛型
var list1 = new ArrayList<String>(); //在<>中指定了list类型为String // 等于 List<String> list1 = new ArrayList<>(); var list2 = new ArrayList<>(); //<>里默认会是Object
var在Lambda中使用(java11才可以使用)
Consumer<String> Consumer = (var i) -> System.out.println(i); 等于 Consumer<String> Consumer = (String i) -> System.out.println(i);
var不能再哪里使用?
- 类成员变量类型。
- 方法返回值类型。
- Java10中Lambda不能使用var,Java11中可以使用。
完全支持Linux容器(包括docker)
许多运行在Java虚拟机中的应用程序(包括Apache Spark和Kafka等数据服务以及传统的 企业应用程序)都可以在Docker容器中运行。 但是在Docker容器中运行Java应用程序一直存在一个问题, 那就是在容器中运行JVM程序在设置内存大小和CPU使用率后,会导致应用程序的性能下降。 这是因为Java应用程序没有意识到它正在容器中运行。随着Java 10的发布, 这个问题总算得以解决,JVM现在可以识别由容器控制组(cgroups)设置的约束。 可以在容器中使用内存和CPU约束来直接管理Java应用程序,其中包括:
- 遵守容器中设置的内存限制
- 在容器中设置可用的CPU
- 在容器中设置CPU约束
Java 10的这个改进在Docker for Mac、Docker for Windows以及 Docker Enterprise Edition等环境均有效。
Unicode 8.0
增强了java.util.Locale
和相关的API,以实现BCP 47语言标签的其他Unicode扩展。
此次针对BCP 47语言标签扩展包括:
-
cu
(货币类型) -
fw
(一周的第一天) -
rg
(区域覆盖) -
tz
(时区)
具体API变更有:
-
java.text.DateFormat::get*Instance
将根据扩展名返回实例ca
,rg
和/或tz
-
java.text.DateFormatSymbols::getInstance
将根据扩展名返回实例rg
-
java.text.DecimalFormatSymbols::getInstance
将根据扩展名返回实例rg
-
java.text.NumberFormat::get*Instance
将根据扩展名nu
和/或返回实例rg
-
java.time.format.DateTimeFormatter::localizedBy
将返回DateTimeFormatter
基于扩展情况下ca
,rg
和/或tz
-
java.time.format.DateTimeFormatterBuilder::getLocalizedDateTimePattern
将根据rg
扩展名返回模式字符串。 -
java.time.format.DecimalStyle::of
将DecimalStyle根据扩展名返回实例nu
, 和/
或rg
-
java.time.temporal.WeekFields::of
将WeekFields根据扩展名fw和/或返回实例rg
-
java.util.Calendar::{getFirstDayOfWeek,getMinimalDaysInWeek}
将根据扩展名fw
和/
或返回值rg
-
java.util.Currency::getInstance
将Currency根据扩展名cu和/或返回实例rg
-
java.util.Locale::getDisplayName
将返回一个字符串,其中包括这些U扩展名的显示名称 -
java.util.spi.LocaleNameProvider
这些U扩展的键和类型将具有新的SPI
Java 11
移除的包
- com.sun.awt.AWTUtilities。
- sun.misc.Unsafe.defineClass 使用java.lang.invoke.MethodHandles.Lookup.defineClass来替代。
- Thread.destroy() 以及 Thread.stop(Throwable) 方法。
- sun.nio.ch.disableSystemWideOverlappingFileLockCheck 属性。
- sun.locale.formatasdefault 属性。
- jdk snmp 模块。
- javafx,openjdk 是从java10版本就移除了,oracle java10还尚未移除javafx ,而java11版本将javafx也移除了。
- Java Mission Control,从JDK中移除之后,需要自己单独下载。
- Root Certificates :Baltimore Cybertrust Code Signing CA,SECOM ,AOL and Swisscom。
- 在java11中将java9标记废弃的Java EE及CORBA模块移除掉。
Maven插件升级
构建插件的升级主要是maven compile插件的升级,需要升级到3.8.0版本, pandora-boot的maven插件升级到2.1.11.9,依赖如下:
<dependency> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> </dependency>
同时将编译的目标文件和源文件的编译版本指定下:
<maven.compiler.target>11</maven.compiler.target> <maven.compiler.source>11</maven.compiler.source>
移除的模块
<dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-core</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency>
lambda参数局部变量
Java11中的lambda表达式可以为隐式类型,其中类型的形式参数都可以被推断出。
对于隐式类型的lambda表达式的形式参数,允许使用保留的类型名称var
,以便:
(var x, var y) -> x.process(y)
等效于:
(x, y) -> x.process(y) // 这样的对的 (var x, int y) -> x.process(y) // 这样就会报错
字符串增强
// 判断字符串是否为空白 " ".isBlank(); // true // 去除首尾空格 " Hello Java11 ".strip(); // "Hello Java11" // 去除尾部空格 " Hello Java11 ".stripTrailing(); // " Hello Java11" // 去除首部空格 " Hello Java11 ".stripLeading(); // "Hello Java11 " // 复制字符串 "Java11".repeat(3); // "Java11Java11Java11" // 行数统计 "A\nB\nC".lines().count(); // 3
HTTP Client API
改api支持同步和异步两种方式,下面是两种方式的示例:
var request = HttpRequest.newBuilder() .uri(URI.create("https://www.baidu.com/")) .build(); var client = HttpClient.newHttpClient(); // 同步 HttpResponse<String> response = client.send( request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); // 异步 CompletableFuture<HttpResponse<String>> sendAsync = client.sendAsync( request, HttpResponse.BodyHandlers.ofString()); //这里会阻塞 HttpResponse<String> response1 = sendAsync.get(); System.out.println(response1.body());
当然你也可以自定义请求头,比如携带JWT Token权限信息去请求等:
var requestWithAuth = HttpRequest.newBuilder() .uri( URI.create("http://www.xxxxxx.com/sth") ) .header("Authorization", "Bearer eyJhbGciOiJIUzUxMiJ9." + "eyJzdWIiOiIxNTIwNTE2MTE5NiIsImNyZWF0ZWQiOjE1ODMzM" + "TA2ODk0MzYsImV4cCI6MTU4MzM5NzA4OSwidXNlcmlkIjoxMD" + "AwNH0.OE9R5PxxsvtVJZn8ne-ksTb2aXXi7ipzuW9kbCiQ0uN" + "oW0fJJr_wckLFmgDzxmBs3IdzIhWDAtaSIvmTshK_RQ").GET().build(); var response = HttpClient.newHttpClient() .send( requestWithAuth, HttpResponse.BodyHandlers.ofString() ); System.out.println( response.body() ); // 打印获取到的接口返回内容
文件读写增强
1、Files类增强
我们以前心心念的直接能把文件内容读取到String以及String回写到文件的功能终于支持了, 可以通过Files类的静态方法writeString()和readString()完成:
Path path = Paths.get("/Users/CodeSheep/test.txt"); String content = Files.readString(path, StandardCharsets.UTF_8); System.out.println(content); Files.writeString( path, "王老七", StandardCharsets.UTF_8 );
2、InputStream增强
InputStream则增加了一个transferTo()方法,直接将数据丢到OutputStream去:
InputStream inputStream = new FileInputStream( "/Users/CodeSheep/test.txt" ); OutputStream outputStream = new FileOutputStream( "/Users/CodeSheep/test2.txt" ); inputStream.transferTo( outputStream );
直接运行java文件
增强java启动器以运行作为Java源代码的单个文件提供的程序, 包括通过shebang文件和相关技术从脚本内部使用该程序。
从JDK 10开始,java启动器以三种模式运行:
- 启动类文件,
- 启动JAR文件的main类
- 启动模块的main类
Java 11中增加了新的第四种模式:启动在源文件中声明的类。
java Java11.java
如果文件没有.java
扩展名,则必须使用选项--source
来强制源文件模式。
例如当源文件是要执行的“脚本”并且源文件的名称不遵循Java源文件的常规命名约定时。
Java 12
switch语句扩展
扩展switch语句,以便可以将其用作语句或表达式, 并且两种形式都可以使用“传统”或“简化”作用域并控制流的行为。 这些变化将简化日常编码在switch中。这是JDK 12中的预览功能。
请注意:此JEP已被JDK 13的JEP 354取代。
普通写法:
switch (day) { case MONDAY: case FRIDAY: case SUNDAY: System.out.println(6); break; case TUESDAY: System.out.println(7); break; case THURSDAY: case SATURDAY: System.out.println(8); break; case WEDNESDAY: System.out.println(9); break; }
现在引入一种新的switch标签形式,写为case L ->
,表示如果匹配标签,
则只执行标签右边的代码。例如,现在可以编写以前的代码:
switch (day) { case MONDAY, FRIDAY, SUNDAY -> System.out.println(6); case TUESDAY -> System.out.println(7); case THURSDAY, SATURDAY -> System.out.println(8); case WEDNESDAY -> System.out.println(9); }
再比如局部变量,普通写法是这样的:
int numLetters; switch (day) { case MONDAY: case FRIDAY: case SUNDAY: numLetters = 6; break; case TUESDAY: numLetters = 7; break; case THURSDAY: case SATURDAY: numLetters = 8; break; case WEDNESDAY: numLetters = 9; break; default: throw new IllegalStateException("Wat: " + day); }
现在的写法是这样的:
int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY -> 6; case TUESDAY -> 7; case THURSDAY, SATURDAY -> 8; case WEDNESDAY -> 9; };
API变化与废弃
xml
Jaxb被移除
Jaxb被移除,要导入:
<!-- ... --> </properties> <!-- ... --> <junit.version>4.12</junit.version> <jaxb-core.version>2.3.0</jaxb-core.version> <jaxb-api.version>2.3.0</jaxb-api.version> <jaxb-impl.version>2.3.0</jaxb-impl.version> <!-- ... --> </properties> <!-- ... --> <dependencyManagement> <!-- ... --> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>${jaxb-api.version}</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-core</artifactId> <version>${jaxb-core.version}</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>${jaxb-impl.version}</version> </dependency> <!-- ... --> </dependencyManagement>
网络相关
ssl
import java.security.cert.CertificateException; X509TrustManager x509tm = new X509TrustManager() { @Override public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { } @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[]{}; } }; newBuilder().sslSocketFactory(createSSLSocketFactory(), x509tm)
Java 13
文本直排
String html = """ <html> <body> <p>Hello, world</p> </body> </html> """;
switch表达式预览版
JDK 13中新增 switch 表达式beta 版本,这是对Java12 switch表达式功能的增强版本, 并且Java13版本的switch表达式的更新可以用于生产环境中。 switch 表达式扩展了 switch 语句,使其不仅可以作为语句(statement), 还可以作为表达式(expression),并且两种写法都可以使用传统的 switch 语法。
除了Java12的用法之外,Java13的更新引入一个新的关键字yield。大多数switch表达式在
case L ->
开关标签的右侧都有一个表达式。如果需要一个完整的块,
需要使用yield语句来产生一个值,该值是封闭switch表达式的值。示例:
int j = switch (day) { case MONDAY -> 0; case TUESDAY -> 1; default -> { int k = day.toString().length(); int result = f(k); yield result; } };
上例也可以使用传统的switch语句:
int result = switch (s) { case "Foo": yield 1; case "Bar": yield 2; default: System.out.println("Neither Foo nor Bar, hmmm..."); yield 0; };
switch表达的情况必须详细;对于所有可能的值,必须有一个匹配的switch标签。 (显然,switch声明并非必须详细。)这通常意味着需要一个default子句。 但是enum switch对于覆盖所有已知常量的表达式,default编译器会插入一个子句以指示该 enum定义在编译时和运行时之间已更改。
依靠这种隐式default子句的插入可以使代码更健壮。现在,当重新编译代码时, 编译器将检查所有情况是否得到明确处理。
此外,switch表达式必须以一个值正常完成,或者必须通过引发异常来突然完成。 这有许多后果。首先,编译器会检查每个开关标签是否匹配,然后产生一个值。
示例:
int i = switch (day) { case MONDAY -> { System.out.println("Monday"); // ERROR! Block doesn't contain a yield statement } default -> 1; }; i = switch (day) { case MONDAY, TUESDAY, WEDNESDAY: yield 0; default: System.out.println("Second half of the week"); // ERROR! Group doesn't contain a yield statement };
另一种后果是,控制语句,break,yield,return和continue,无法通过跳switch表达式,示例:
z: for (int i = 0; i < MAX_VALUE; ++i) { int k = switch (e) { case 0: yield 1; case 1: yield 2; default: continue z; // ERROR! Illegal jump through a switch expression }; // ... }
Java 15
https://my.oschina.net/waylau/blog/4633203
Java 17
ForkJoinPool在JDK17上挂起的bug
JDK17竟然有个大Bug:ForkJoinPool在单核机器上会挂起,平时开发都是4核8核,结果扔到AWS的单核跑就挂了:
https://bugs.openjdk.java.net/browse/JDK-8274349临时解决方案:加启动参数:
-Djava.util.concurrent.ForkJoinPool.common.parallelism=1
或者在main()
的第一行开始写:
if (Runtime.getRuntime().availableProcessors() <= 1) { System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "1"); }