本文旨在介绍Java11相对Java8的新特性,包括Java9与Java10引入的特性。主要是新的语法特性,模块化开发,以及其余方面的一些新特性。html
本文涉及到的代码位于:https://github.com/zhaochuninhefei/study-czhao/tree/master/jdk11-test
或:https://gitee.com/XiaTangShaoBing/study/tree/master/jdk11-test
Java11相对Java8,在语法上的新特性并很少。主要有:java
Java10之后能够用var定义一个局部变量,不用显式写出它的类型。但要注意,被var定义的变量仍然是静态类型,编译器会试图去推断其类型。python
String strBeforeJava10 = "strBeforeJava10"; var strFromJava10 = "strFromJava10"; System.out.println(strBeforeJava10); System.out.println(strFromJava10);
所以,要注意:mysql
// 例以下面的语句编译会失败,"InCompatible types." strFromJava10 = 10;
// 例以下面这些都没法经过编译: var testVarWithoutInitial; var testNull = null; var testLamda = () -> System.out.println("test"); var testMethodByLamda = () -> giveMeString(); var testMethod2 = this::giveMeString;
而推荐使用类型推断的场景有:react
// 以下所示,Map <String,List <Integer >>类型,能够被简化为单个var关键字 var testList = new ArrayList<Map<String, List<Integer>>>(); for (var curEle : testList) { // curEle可以被推断出类型是 Map<String, List<Integer>> if (curEle != null) { curEle.put("test", new ArrayList<>()); } }
// 从Java 11开始,lambda参数也容许使用var关键字: Predicate<String> predNotNull = (var a) -> a != null && a.trim().length() > 0; String strAfterFilter = Arrays.stream((new String[]{"a", "", null, "x"})) .filter(predNotNull) .collect(Collectors.joining(",")); System.out.println(strAfterFilter);
完整的演示代码:git
package jdk11; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.function.Predicate; import java.util.stream.Collectors; /** * 本地变量类型推断 * * @author zhaochun */ public class TestCase01TypeInference { public static void main(String[] args) { TestCase01TypeInference me = new TestCase01TypeInference(); me.testVar(); } private void testVar() { // Java10之后能够用var定义一个局部变量,不用显式写出它的类型。 String strBeforeJava10 = "strBeforeJava10"; var strFromJava10 = "strFromJava10"; System.out.println(strBeforeJava10); System.out.println(strFromJava10); // 但要注意,被var定义的变量仍然是静态类型,编译器会试图去推断其类型。 // 所以不兼容的类型是不能从新赋值的。 // 例以下面的语句编译会失败,"InCompatible types." // strFromJava10 = 10; // 只要编译器没法推断出变量类型,就会编译错误! // 例以下面这些都没法经过编译: // var testVarWithoutInitial; // var testNull = null; // var testLamda = () -> System.out.println("test"); // var testMethodByLamda = () -> giveMeString(); // var testMethod2 = this::giveMeString; // 局部变量类型推断能够用于简化泛型声明。以下所示,Map <String,List <Integer >>类型,能够被简化为单个var关键字,从而避免大量样板代码: var testList = new ArrayList<Map<String, List<Integer>>>(); for (var curEle : testList) { // curEle可以被推断出类型是 Map<String, List<Integer>> if (curEle != null) { curEle.put("test", new ArrayList<>()); } } // 从Java 11开始,lambda参数也容许使用var关键字: Predicate<String> predNotNull = (var a) -> a != null && a.trim().length() > 0; String strAfterFilter = Arrays.stream((new String[]{"a", "", null, "x"})) .filter(predNotNull) .collect(Collectors.joining(",")); System.out.println(strAfterFilter); } private String giveMeString() { return "a string."; } }
Java 9开始引入HttpClient API来处理HTTP请求。 从Java 11开始,这个API正式进入标准库包。参考网址:http://openjdk.java.net/groups/net/httpclient/intro.html
程序员
HttpClient具备如下特性:github
要发送http请求,首先要使用其构建器建立一个HttpClient。这个构建器可以配置每一个客户端的状态:web
一旦构建完成,就可使用HttpClient发送多个请求。算法
HttpRequest是由它的构建器建立的。请求的构建器可用于设置:
HttpRequest构建以后是不可变的,但能够发送屡次。
请求既能够同步发送,也能够异步发送。固然同步的API会致使线程阻塞直到HttpResponse可用。异步API当即返回一个CompletableFuture,当HttpResponse可用时,它将获取HttpResponse并执行后续处理。
CompletableFuture是Java 8添加的新特性,用于可组合的异步编程。
请求和响应的主体做为响应式流(具备非阻塞背压的异步数据流)供外部使用。HttpClient其实是请求正文的订阅者和响应正文字节的发布者。BodyHandler接口容许在接收实际响应体以前检查响应代码和报头,并负责建立响应BodySubscriber。
HttpRequest和HttpResponse类型提供了许多便利的工厂方法,用于建立请求发布者和响应订阅者,以处理常见的主体类型,如文件、字符串和字节。这些便利的实现要么累积数据,直到能够建立更高级别的Java类型(如String),要么就文件流传输数据。BodySubscriber和BodyPublisher接口能够实现为自定义反应流处理数据。
HttpRequest和HttpResponse还提供了转换器,用于将 java.util.concurrent.Flow 的 Publisher/Subscriber 类型转换为 HTTP Client的 BodyPublisher/BodySubscriber 类型。
Java HTTP Client支持 HTTP/1.1 和 HTTP/2。默认状况下,客户端将使用 HTTP/2 发送请求。发送到尚不支持 HTTP/2 的服务器的请求将自动降级为 HTTP/1.1。如下是HTTP/2带来的主要改进:
因为HTTP/2是默认的首选协议,而且在须要的地方无缝地实现回退到HTTP/1.1,那么当HTTP/2被更普遍地部署时,Java HTTP客户端就无需修正它的应用代码。
https://docs.oracle.com/en/ja...
代码中请求的网址中,localhost:30001
的相关uri来自工程https://github.com/zhaochuninhefei/study-czhao/tree/master/jdk11-test
。
package jdk11; import com.fasterxml.jackson.databind.ObjectMapper; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.WebSocket; import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.TimeUnit; /** * HttpClient * * @author zhaochun */ public class TestCase02HttpClient { public static void main(String[] args) throws Exception { TestCase02HttpClient me = new TestCase02HttpClient(); me.testHttpClientGetSync(); me.testHttpClientGetAsync(); me.testHttpClientPost(); // 同一个HttpClient先登陆网站获取token,再请求受限制资源,从而爬取须要认证的资源 me.testLogin(); // HttpClient支持websocket me.testWebsocket(); } private void testHttpClientGetSync() { var url = "https://openjdk.java.net/"; var request = HttpRequest.newBuilder() .uri(URI.create(url)) .GET() .build(); var client = HttpClient.newHttpClient(); try { System.out.println(String.format("send begin at %s", LocalDateTime.now())); // 同步请求 HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(String.format("send end at %s", LocalDateTime.now())); System.out.println(String.format("receive response : %s", response.body().substring(0, 10))); } catch (Exception e) { e.printStackTrace(); } } private void testHttpClientGetAsync() { var url = "https://openjdk.java.net/"; var request = HttpRequest.newBuilder() .uri(URI.create(url)) .GET() .build(); var client = HttpClient.newHttpClient(); try { System.out.println(String.format("sendAsync begin at %s", LocalDateTime.now())); // 异步请求 client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) .thenApply(stringHttpResponse -> { System.out.println(String.format("receive response at %s", LocalDateTime.now())); return stringHttpResponse.body(); }) .thenAccept(s -> System.out.println(String.format("receive response : %s at %s", s.substring(0, 10), LocalDateTime.now()))); System.out.println(String.format("sendAsync end at %s", LocalDateTime.now())); // 为了防止异步请求还没有返回主线程就结束(jvm会退出),这里让主线程sleep 10秒 System.out.println("Main Thread sleep 10 seconds start..."); Thread.sleep(10000); System.out.println("Main Thread sleep 10 seconds stop..."); } catch (Exception e) { e.printStackTrace(); } } private void testHttpClientPost() { var url = "http://localhost:30001/jdk11/test/helloByPost"; var request = HttpRequest.newBuilder() .uri(URI.create(url)) .header("Content-Type", "text/plain") .POST(HttpRequest.BodyPublishers.ofString("zhangsan")) .build(); var client = HttpClient.newHttpClient(); try { HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.statusCode()); System.out.println(response.body()); } catch (Exception e) { e.printStackTrace(); } } private void testLogin() throws Exception { var client = HttpClient.newHttpClient(); // 某测试环境用户登陆URL var urlLogin = "http://x.x.x.x:xxxx/xxx/login"; var requestObj = new HashMap<String, Object>(); requestObj.put("username", "xxxxxx"); requestObj.put("password", "xxxxxxxxxxxxxxxx"); var objectMapper = new ObjectMapper(); var requestBodyJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(requestObj); var requestLogin = HttpRequest.newBuilder() .uri(URI.create(urlLogin)) .header("Content-Type", "application/json;charset=UTF-8") .POST(HttpRequest.BodyPublishers.ofString(requestBodyJson)) .build(); HttpResponse<String> responseLogin = client.send(requestLogin, HttpResponse.BodyHandlers.ofString()); // 这里的登陆网站使用token,而没有使用session,所以咱们须要从返回的报文主体中查找token信息; // 若是是使用session的网站,这里须要从响应的headers中查找"set-cookie"从而获取session id,并在后续请求中,将sid设置到header的Cookie中。 // 如: responseLogin.headers().map().get("set-cookie")获取cookies,再从中查找sid。 var loginResponse = responseLogin.body(); var mpLoginResponse = objectMapper.readValue(loginResponse, Map.class); var dataLogin = (Map<String, Object>) mpLoginResponse.get("data"); var token = dataLogin.get("token").toString(); // 测试环境获取某资源的URL var urlGetResource = "http://xxxx:xxxx/xxx/resource"; var requestRes = HttpRequest.newBuilder() .uri(URI.create(urlGetResource)) .header("Content-Type", "application/json;charset=UTF-8") // 注意,token并不是必定设置到header的Authorization中,这取决于网站验证的方式,也有可能token也放到cookie里。 // 但对于使用session的网站,sid都是设置在cookie里的。如: .header("Cookie", "JSESSIONID=" + sid) .header("Authorization", token) .GET() .build(); HttpResponse<String> responseResource = client.send(requestRes, HttpResponse.BodyHandlers.ofString()); var response = responseResource.body(); System.out.println(response); } private void testWebsocket() { var wsUrl = "ws://localhost:30001/ws/test"; var httpClient = HttpClient.newHttpClient(); WebSocket websocketClient = httpClient.newWebSocketBuilder() .buildAsync(URI.create(wsUrl), new WebSocket.Listener() { @Override public void onOpen(WebSocket webSocket) { System.out.println("onOpen : webSocket opened."); webSocket.request(1); } @Override public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) { System.out.println("onText"); webSocket.request(1); return CompletableFuture.completedFuture(data) .thenAccept(System.out::println); } @Override public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) { System.out.println("ws closed with status(" + statusCode + "). cause:" + reason); webSocket.sendClose(statusCode, reason); return null; } @Override public void onError(WebSocket webSocket, Throwable error) { System.out.println("error: " + error.getLocalizedMessage()); webSocket.abort(); } }).join(); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } // last参数用于指示websocketClient,本次发送的数据是不是完整消息的最后部分。 // 若是是false,则websocketClient不会把消息发送给websocket后台的listener,只会把数据缓存起来; // 当传入true时,会将以前缓存的数据和此次的数据拼接起来一块儿发送给websocket后台的listener。 websocketClient.sendText("test1", false); websocketClient.sendText("test2", true); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } websocketClient.sendText("org_all_request", true); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } websocketClient.sendText("employee_all_request", true); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } websocketClient.sendClose(WebSocket.NORMAL_CLOSURE, "Happy ending."); } }
List,Set,Map有了新的加强方法:of
与copyOf
。
List.of根据传入的参数列表建立一个新的不可变List集合;List.copyOf根据传入的list对象建立一个不可变副本。
var listImmutable = List.of("a", "b", "c"); var listImmutableCopy = List.copyOf(listImmutable);
因为拷贝的集合自己就是一个不可变对象,所以拷贝实际上并无建立新的对象,直接使用了原来的不可变对象。
// 结果为true System.out.println(listImmutable == listImmutableCopy); // 不可变对象不能进行修改 try { listImmutable.add("d"); } catch (Throwable t) { System.out.println("listImmutable can not be modified!"); } try { listImmutableCopy.add("d"); } catch (Throwable t) { System.out.println("listImmutableCopy can not be modified!"); }
若是想快速新建一个可变的集合对象,能够直接使用以前的不可变集合做为构造参数,建立一个新的可变集合。
var listVariable = new ArrayList<>(listImmutable); var listVariableCopy = List.copyOf(listVariable);
新建立的可变集合固然是一个新的对象,从这个新对象拷贝出来的不可变副本也是一个新的对象,并非以前的不可变集合。
System.out.println(listVariable == listImmutable); // false System.out.println(listVariable == listVariableCopy); // false System.out.println(listImmutable == listVariableCopy); // false // 新的可变集合固然是能够修改的 try { listVariable.add("d"); } catch (Throwable t) { System.out.println("listVariable can not be modified!"); } // 可变集合拷贝出来的副本依然是不可变的 try { listVariableCopy.add("d"); } catch (Throwable t) { System.out.println("listVariableCopy can not be modified!"); }
Set的of和copyOf与List相似。
var set = Set.of("a", "c", "r", "e"); var setCopy = Set.copyOf(set); System.out.println(set == setCopy);
但要注意,用of建立不可变Set时,要确保元素不重复,不然运行时会抛出异常: "java.lang.IllegalArgumentException: duplicate element"
try { var setErr = Set.of("a", "b", "a"); } catch (Throwable t) { t.printStackTrace(); }
固然建立可变set后添加剧复元素不会抛出异常,但会被去重
var setNew = new HashSet<>(set); setNew.add("c"); System.out.println(setNew.toString());
Map的of和copyOf与list,set相似,注意of方法的参数列表是依次传入key和value:
var map = Map.of("a", 1, "b", 2); var mapCopy = Map.copyOf(map); System.out.println(map == mapCopy);
固然也要注意建立不可变Map时,key不能重复
try { var mapErr = Map.of("a", 1, "b", 2, "a", 3); } catch (Throwable t) { t.printStackTrace(); }
package jdk11; import java.util.*; /** * Collection加强 * * @author zhaochun */ public class TestCase03Collection { public static void main(String[] args) { TestCase03Collection me = new TestCase03Collection(); me.test01_of_copyOf(); } private void test01_of_copyOf() { // List,Set,Map有了新的加强方法。 // List.of根据传入的参数列表建立一个新的不可变List集合; // List.copyOf根据传入的list对象建立一个不可变副本。 var listImmutable = List.of("a", "b", "c"); var listImmutableCopy = List.copyOf(listImmutable); // 因为拷贝的集合自己就是一个不可变对象,所以拷贝实际上并无建立新的对象,直接使用了原来的不可变对象。 System.out.println(listImmutable == listImmutableCopy); // 不可变对象不能进行修改 try { listImmutable.add("d"); } catch (Throwable t) { System.out.println("listImmutable can not be modified!"); } try { listImmutableCopy.add("d"); } catch (Throwable t) { System.out.println("listImmutableCopy can not be modified!"); } // 若是想快速新建一个可变的集合对象,能够直接使用以前的不可变集合做为构造参数,建立一个新的可变集合。 var listVariable = new ArrayList<>(listImmutable); var listVariableCopy = List.copyOf(listVariable); // 新建立的可变集合固然是一个新的对象,从这个新对象拷贝出来的不可变副本也是一个新的对象,并非以前的不可变集合。 System.out.println(listVariable == listImmutable); System.out.println(listVariable == listVariableCopy); System.out.println(listImmutable == listVariableCopy); // 新的可变集合固然是能够修改的 try { listVariable.add("d"); } catch (Throwable t) { System.out.println("listVariable can not be modified!"); } // 可变集合拷贝出来的副本依然是不可变的 try { listVariableCopy.add("d"); } catch (Throwable t) { System.out.println("listVariableCopy can not be modified!"); } // Set的of和copyOf与List相似。 var set = Set.of("a", "c", "r", "e"); var setCopy = Set.copyOf(set); System.out.println(set == setCopy); // 但要注意,用of建立不可变Set时,要确保元素不重复,不然运行时会抛出异常: "java.lang.IllegalArgumentException: duplicate element" try { var setErr = Set.of("a", "b", "a"); } catch (Throwable t) { t.printStackTrace(); } // 固然建立可变set后添加剧复元素不会抛出异常,但会被去重 var setNew = new HashSet<>(set); setNew.add("c"); System.out.println(setNew.toString()); // Map的of和copyOf与list,set相似 var map = Map.of("a", 1, "b", 2); var mapCopy = Map.copyOf(map); System.out.println(map == mapCopy); // 固然也要注意建立不可变Map时,key不能重复 try { var mapErr = Map.of("a", 1, "b", 2, "a", 3); } catch (Throwable t) { t.printStackTrace(); } } }
Java8开始引入stream,Java11提供了一些扩展:
注意null与""的区别:
long size1 = Stream.ofNullable(null).count(); System.out.println(size1); // 0 long size2 = Stream.ofNullable("").count(); System.out.println(size2); // 1
dropWhile,对于有序的stream,从头开始去掉知足条件的元素,一旦遇到不知足元素的就结束
List lst1 = Stream.of(1, 2, 3, 4, 5, 4, 3, 2, 1) .dropWhile(e -> e < 3) .collect(Collectors.toList()); System.out.println(lst1); // [3, 4, 5, 4, 3, 2, 1]
takeWhile,对于有序的stream,从头开始保留知足条件的元素,一旦遇到不知足的元素就结束
List lst2 = Stream.of(1, 2, 3, 4, 5, 4, 3, 2, 1) .takeWhile(e -> e < 3) .collect(Collectors.toList()); System.out.println(lst2); // [1, 2]
即便把剩下的元素都收集到了无序的set中,但在此以前,stream对象是有序的,所以结果包含了原来stream中最后的[a2]和[a1]:
Set set1 = Stream.of("a1", "a2", "a3", "a4", "a5", "a4", "a3", "a2", "a1") .dropWhile(e -> "a3".compareTo(e) > 0) .collect(Collectors.toSet()); System.out.println(set1); // [a1, a2, a3, a4, a5]
若是先建立一个无序不重复的set集合,set无序更准确的说法是不保证顺序不变,事实上是有顺序的。
所以这里会发现,dropWhile仍是按set当前的元素顺序断定的,一旦不知足条件就结束。
Set<String> set = new HashSet<>(); for (int i = 1; i <= 100 ; i++) { set.add("test" + i); } System.out.println(set); Set setNew = set.stream() .dropWhile(s -> "test60".compareTo(s) > 0) .collect(Collectors.toSet()); System.out.println(setNew);
java8里能够建立一个无限流,好比下面这个数列,起始值是1,后面每一项都在前一项的基础上 * 2 + 1,经过limit限制这个流的长度:
Stream<Integer> streamInJava8 = Stream.iterate(1, t -> 2 * t + 1); // 打印出该数列的前十个: 1,3,7,15,31,63,127,255,511,1023 System.out.println(streamInJava8.limit(10).map(Object::toString).collect(Collectors.joining(",")));
从Java9开始,iterate方法能够添加一个断定器,例如,限制数的大小不超过1000
Stream<Integer> streamFromJava9 = Stream.iterate(1, t -> t < 1000, t -> 2 * t + 1); // 这里打印的结果是 1,3,7,15,31,63,127,255,511 System.out.println(streamFromJava9.map(Objects::toString).collect(Collectors.joining(",")));
package jdk11; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; /** * Stream加强 * * @author zhaochun */ public class TestCase04Stream { public static void main(String[] args) { TestCase04Stream me = new TestCase04Stream(); me.test01_ofNullable(); me.test02_dropWhile_takeWhile(); me.test03_iterate(); } private void test01_ofNullable() { // 单个参数的Stream构造方法 long size1 = Stream.ofNullable(null).count(); System.out.println(size1); long size2 = Stream.ofNullable("").count(); System.out.println(size2); } private void test02_dropWhile_takeWhile() { // dropWhile 对于有序的stream,从头开始去掉知足条件的元素,一旦遇到不知足元素的就结束 List lst1 = Stream.of(1, 2, 3, 4, 5, 4, 3, 2, 1) .dropWhile(e -> e < 3) .collect(Collectors.toList()); System.out.println(lst1); // takeWhile 对于有序的stream,从头开始保留知足条件的元素,一旦遇到不知足的元素就结束 List lst2 = Stream.of(1, 2, 3, 4, 5, 4, 3, 2, 1) .takeWhile(e -> e < 3) .collect(Collectors.toList()); System.out.println(lst2); // 虽然这里最后把剩下的元素都收集到了无序的set中,但在此以前,stream对象是有序的,所以结果包含了原来stream中最后的[a2]和[a1] Set set1 = Stream.of("a1", "a2", "a3", "a4", "a5", "a4", "a3", "a2", "a1") .dropWhile(e -> "a3".compareTo(e) > 0) .collect(Collectors.toSet()); System.out.println(set1); // 这里先建立一个无序不重复的set集合,set无序更准确的说法是不保证顺序不变,事实上是有顺序的。 // 所以这里会发现,dropWhile仍是按set当前的元素顺序断定的,一旦不知足条件就结束。 Set<String> set = new HashSet<>(); for (int i = 1; i <= 100 ; i++) { set.add("test" + i); } System.out.println(set); Set setNew = set.stream() .dropWhile(s -> "test60".compareTo(s) > 0) .collect(Collectors.toSet()); System.out.println(setNew); } private void test03_iterate() { // java8里能够建立一个无限流,好比下面这个数列,起始值是1,后面每一项都在前一项的基础上 * 2 + 1 Stream<Integer> streamInJava8 = Stream.iterate(1, t -> 2 * t + 1); // 打印出该数列的前十个: 1,3,7,15,31,63,127,255,511,1023 System.out.println(streamInJava8.limit(10).map(Object::toString).collect(Collectors.joining(","))); // 从Java9开始,iterate方法能够添加一个断定器,能够用于限制数列范围不超过1000 Stream<Integer> streamFromJava9 = Stream.iterate(1, t -> t < 1000, t -> 2 * t + 1); // 这里打印的结果是 1,3,7,15,31,63,127,255,511 System.out.println(streamFromJava9.map(Objects::toString).collect(Collectors.joining(","))); } }
Optional.of("Hello openJDK11").stream() .flatMap(s -> Arrays.stream(s.split(" "))) .forEach(System.out::println);
System.out.println(Optional.empty() .or(() -> Optional.of("default")) .get());
package jdk11; import java.util.Arrays; import java.util.Optional; /** * Optional加强 * * @author zhaochun */ public class TestCase05Optional { public static void main(String[] args) { TestCase05Optional me = new TestCase05Optional(); me.test01_2stream_default(); } private void test01_2stream_default() { // 能够将Optional对象直接转为stream Optional.of("Hello openJDK11").stream() .flatMap(s -> Arrays.stream(s.split(" "))) .forEach(System.out::println); // 能够为Optional对象提供一个默认的Optional对象 System.out.println(Optional.empty() .or(() -> Optional.of("default")) .get()); } }
String方面,针对空白字符(空格,制表符,回车,换行等),提供了一些新的方法。
判断目标字符串是不是空白字符。如下结果所有为true
:
// 半角空格 System.out.println(" ".isBlank()); // 全角空格 System.out.println(" ".isBlank()); // 半角空格的unicode字符值 System.out.println("\u0020".isBlank()); // 全角空格的unicode字符值 System.out.println("\u3000".isBlank()); // 制表符 System.out.println("\t".isBlank()); // 回车 System.out.println("\r".isBlank()); // 换行 System.out.println("\n".isBlank()); // 各类空白字符拼接 System.out.println(" \t\r\n ".isBlank());
去除首尾的空白字符:
// 全角空格 + 制表符 + 回车 + 换行 + 半角空格 + <内容> + 全角空格 + 制表符 + 回车 + 换行 + 半角空格 var strTest = " \t\r\n 你好 jdk11 \t\r\n "; // strip 去除两边空白字符 System.out.println("[" + strTest.strip() + "]"); // stripLeading 去除开头的空白字符 System.out.println("[" + strTest.stripLeading() + "]"); // stripTrailing 去除结尾的空白字符 System.out.println("[" + strTest.stripTrailing() + "]");
重复字符串内容,拼接新的字符串:
var strOri = "jdk11"; var str1 = strOri.repeat(1); var str2 = strOri.repeat(3); System.out.println(str1); System.out.println(str2); // repeat传入参数为1时,不会建立一个新的String对象,而是直接返回原来的String对象。 System.out.println(str1 == strOri);
lines方法用 r 或 n 或 rn 对字符串切割并返回stream对象:
var strContent = "hello java\rhello jdk11\nhello world\r\nhello everyone"; // lines方法用 \r 或 \n 或 \r\n 对字符串切割并返回stream对象 strContent.lines().forEach(System.out::println); System.out.println(strContent.lines().count());
package jdk11; /** * String加强 * * @author zhaochun */ public class TestCase06String { public static void main(String[] args) { TestCase06String me = new TestCase06String(); me.test01_blank(); me.test02_strip(); me.test03_repeat(); me.test04_lines(); } private void test01_blank() { // 半角空格 System.out.println(" ".isBlank()); // 全角空格 System.out.println(" ".isBlank()); // 半角空格的unicode字符值 System.out.println("\u0020".isBlank()); // 全角空格的unicode字符值 System.out.println("\u3000".isBlank()); // 制表符 System.out.println("\t".isBlank()); // 回车 System.out.println("\r".isBlank()); // 换行 System.out.println("\n".isBlank()); // 各类空白字符拼接 System.out.println(" \t\r\n ".isBlank()); } private void test02_strip() { // 全角空格 + 制表符 + 回车 + 换行 + 半角空格 + <内容> + 全角空格 + 制表符 + 回车 + 换行 + 半角空格 var strTest = " \t\r\n 你好 jdk11 \t\r\n "; // strip 去除两边空白字符 System.out.println("[" + strTest.strip() + "]"); // stripLeading 去除开头的空白字符 System.out.println("[" + strTest.stripLeading() + "]"); // stripTrailing 去除结尾的空白字符 System.out.println("[" + strTest.stripTrailing() + "]"); } private void test03_repeat() { var strOri = "jdk11"; var str1 = strOri.repeat(1); var str2 = strOri.repeat(3); System.out.println(str1); System.out.println(str2); // repeat传入参数为1时,不会建立一个新的String对象,而是直接返回原来的String对象。 System.out.println(str1 == strOri); } private void test04_lines() { var strContent = "hello java\rhello jdk11\nhello world\r\nhello everyone"; // lines方法用 \r 或 \n 或 \r\n 对字符串切割并返回stream对象 strContent.lines().forEach(System.out::println); System.out.println(strContent.lines().count()); } }
InputStream提供了一个新的方法transferTo
,将输入流直接传输到输出流:
inputStream.transferTo(outputStream);
package jdk11; import java.io.*; /** * InputStream加强 * * @author zhaochun */ public class TestCase07InputStream { public static void main(String[] args) { TestCase07InputStream me = new TestCase07InputStream(); me.test01_transferTo(); } private void test01_transferTo() { var filePath = "/home/work/sources/test/jdk11-test/src/main/resources/application.yml"; var tmpFilePath = "/home/work/sources/test/jdk11-test/src/main/resources/application.yml.bk"; File tmpFile = new File(tmpFilePath); if (tmpFile.exists() && tmpFile.isFile()) { tmpFile.delete(); } try(InputStream inputStream = new FileInputStream(filePath); OutputStream outputStream = new FileOutputStream(tmpFilePath)) { // transferTo将 InputStream 的数据直接传输给 OutputStream inputStream.transferTo(outputStream); } catch (IOException e) { e.printStackTrace(); } } }
Java9引入了模块化,Java Platform Module System,java平台模块系统,简称JPMS。
这里使用IDEA说明如何基于module进行开发。
使用idea新建工程module-test-jdk11
,并删除根目录下的src
目录。
选择工程根目录,右键,选择 new -> module:
next,输入module名称:
这里新建了两个module,一个叫core
,一个叫main
。
这里选择core,在其src目录下,新建包com.czhao.test.module.core
,并新建classEmployee
:
package com.czhao.test.module.core; import java.util.Objects; /** * @author zhaochun */ public class Employee { private String name; private int level; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; return level == employee.level && name.equals(employee.name); } @Override public int hashCode() { return Objects.hash(name, level); } @Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", level=" + level + '}'; } }
而后在src目录下,新建module-info.java
(idea支持选择src -> 右键 -> new -> module-info.java):
module core { exports com.czhao.test.module.core; }
这里表示经过exports把包com.czhao.test.module.core
暴露出去。
core
的module首先,在idea的工程配置中,选择须要依赖core
的module,在其依赖中添加modulecore
:
而后,在modulemain
的src目录下新建module-info.java
:
module main { requires core; }
这里使用requires表示引入对modulecore
的依赖。
而后能够在main
的src目录下新建包和对应的类,并使用core
暴露出来的类:
package com.czhao.test.module.main; import com.czhao.test.module.core.Employee; /** * @author zhaochun */ public class AppMain { public static void main(String[] args) { Employee employee = new Employee(); employee.setName("kobe"); employee.setLevel(99); System.out.println(employee); } }
若是没有依赖core
,这里是没法使用core
里的类Employee
的。
java9以后,部分类库想在module中使用的话,也须要在module-info.java
引入它们,好比jdbc的相关类库。
main
的module-info.java
:
module main { requires core; requires java.sql; }
在modulemain
的依赖中添加jdbc驱动包:
而后能够在代码中使用JDBC开发:
package com.czhao.test.module.main; import com.czhao.test.module.core.Employee; import java.sql.*; /** * @author zhaochun */ public class AppMain { public static void main(String[] args) { Employee employee = new Employee(); employee.setName("kobe"); employee.setLevel(99); System.out.println(employee); try (Connection connection = DriverManager.getConnection( "jdbc:mysql://localhost:3306/db_jdk11_test?useUnicode=true&characterEncoding=UTF-8&useSSL=false", "root", "xxxxxx"); PreparedStatement ps = connection.prepareStatement("select * from tb_employee")) { ResultSet rs = ps.executeQuery(); while (rs.next()) { System.out.println(rs.getString("employee_name")); } } catch (SQLException e) { e.printStackTrace(); } } }
从Java9到Java11,陆续提供了一些新的工具或功能。
Java提供了一个新的工具jshell
,Java终于能够像python,scala等语言那样,交互式演示语法了。
$ /usr/java/jdk-11.0.7+10/bin/jshell | 欢迎使用 JShell -- 版本 11.0.7 | 要大体了解该版本, 请键入: /help intro jshell> var str1 = "hello world"; str1 ==> "hello world" jshell> System.out.println(str1); hello world jshell>
一个单文件源代码,即,单独的java文件,有main方法,且只依赖jdk类库以及本身文件内部定义的类,能够直接用java
执行而无需先编译再执行编译后的class文件了。
这对于一些简单的脚本开发是个利好。
zhaochun@zhaochun-T480:/home/work/sources/test/jdk11-test/src/test/java/jdk11$ ll 总用量 44 drwxrwxr-x 2 zhaochun zhaochun 4096 5月 14 15:27 ./ drwxrwxr-x 3 zhaochun zhaochun 4096 5月 12 14:25 ../ -rw-rw-r-- 1 zhaochun zhaochun 2323 5月 14 14:48 TestCase01TypeInference.java -rw-rw-r-- 1 zhaochun zhaochun 9803 5月 14 10:56 TestCase02HttpClient.java -rw-rw-r-- 1 zhaochun zhaochun 3384 5月 14 10:55 TestCase03Collection.java -rw-rw-r-- 1 zhaochun zhaochun 2896 5月 14 15:27 TestCase04Stream.java -rw-rw-r-- 1 zhaochun zhaochun 717 5月 14 11:04 TestCase05Optional.java -rw-rw-r-- 1 zhaochun zhaochun 2220 5月 14 12:54 TestCase06String.java -rw-rw-r-- 1 zhaochun zhaochun 1009 5月 14 13:23 TestCase07InputStream.java zhaochun@zhaochun-T480:/home/work/sources/test/jdk11-test/src/test/java/jdk11$ /usr/java/jdk-11.0.7+10/bin/java TestCase01TypeInference.java strBeforeJava10 strFromJava10 a,x zhaochun@zhaochun-T480:/home/work/sources/test/jdk11-test/src/test/java/jdk11$ ll 总用量 44 drwxrwxr-x 2 zhaochun zhaochun 4096 5月 14 15:27 ./ drwxrwxr-x 3 zhaochun zhaochun 4096 5月 12 14:25 ../ -rw-rw-r-- 1 zhaochun zhaochun 2323 5月 14 14:48 TestCase01TypeInference.java -rw-rw-r-- 1 zhaochun zhaochun 9803 5月 14 10:56 TestCase02HttpClient.java -rw-rw-r-- 1 zhaochun zhaochun 3384 5月 14 10:55 TestCase03Collection.java -rw-rw-r-- 1 zhaochun zhaochun 2896 5月 14 15:27 TestCase04Stream.java -rw-rw-r-- 1 zhaochun zhaochun 717 5月 14 11:04 TestCase05Optional.java -rw-rw-r-- 1 zhaochun zhaochun 2220 5月 14 12:54 TestCase06String.java -rw-rw-r-- 1 zhaochun zhaochun 1009 5月 14 13:23 TestCase07InputStream.java zhaochun@zhaochun-T480:/home/work/sources/test/jdk11-test/src/test/java/jdk11$
能够看到,该目录下并未生成class文件。
在Docker容器中运行Java应用程序一直存在一个问题,那就是在容器中运行的JVM程序在设置内存大小和CPU使用率后,会致使应用程序的性能降低。这是由于Java应用程序没有意识到它正在容器中运行。随着Java10的发布,这个问题总算得以解诀,JVM如今能够识别由容器控制组(cgroups) 设置的约束,能够在容器中使用内存和CPU约束来直接管理Java应用程序,其中包括:
Unicode 10新增了8518个字符,总计达到了136690个字符。包括56个新的emoji表情符号。
JDK11在java.lang
下增长了4个类来处理:
Java实现了RFC7539中指定的ChaCha20和Poly1305两种加密算法,代替RC4。
RFC7748定义的密钥协商方案更高效,更安全,JDK增长了两个新的接口XECPublicKey
和XECPrivateKey
。
免费的低耗能飞行记录仪和堆分析仪。
经过JVMTI的SampledObjectAlloc回调提供了一个开销低的heap分析方式提供一个低开销的,为了排错java应用问题,以及JVM问题的数据收集框架。
但愿达到的目标以下:
Flight Recorder 源自飞机的黑盒子。 Flight Recorder 之前是商业版的特性,在java11当中开源出来,它能够导出事件到文件中,以后能够用Java Mission Control 来分析。
两种启动方式:
java -XX:StartFlightRecording
$ jcmd <pid> JFR.start # 启动记录仪 $ jcmd <pid> JFR.dump.filename=recording.jfr # 将记录内容保存到文件里 $ jcmd <pid> JFR.stop # 中止记录仪
查看jfr文件在java11里没有办法,不过在java12里已经加入了jfr命令,能够查看jfr文件
JFR是一套集成进入JDK、JVM内部的事件机制框架,经过良好架构和设计的框架,硬件层面的极致优化,生产环境的普遍验证,它能够作到极致的可靠和低开销。在SPECjbb2015等基准测试中,JFR的性能开销最大不超过1%,因此,工程师能够基本没有心理负担地在大规模分布式的生产系统使用,这意味着,咱们既能够随时主动开启JFR进行特定诊断,也可让系统长期运行JFR,用以在复杂环境中进行"After-the-fact"分析。还须要苦恼重现随机问题吗? JFR让问题简化了不少在保证低开销的基础上, JFR提供的能力也使人眼前一亮,例如:咱们无需BCI就能够进行Object Allocation Profiling, 终于不用担忧BTrace 之类把进程搞挂了。对锁竞争、阻塞、延迟,JVM GC、SafePoint 等领域,进行很是细粒度分析。甚至深刻JIT Compiler 内部,全面把握热点方法、内联、逆优化等等。JFR提供了标准的Java,C++ 等扩展API,能够与各类层面的应用进行定制、集成,为复杂的企业应用栈或者复杂的分布式应用,提供All-in-One 解决方案。而这一切都是内建在JDK和JVM内部的,并不须要额外的依赖,开箱即用。
Java11新增了两种垃圾回收器,并改善了Java8开始提供的G1垃圾回收器。
关于Java8到Java11的垃圾回收器,将在后续其余文章中详细介绍。
Experimental(实验性质),生产环境不建议使用
ZGC是Java11最引人瞩目的新特性。
启用方法:-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
说明:ZGC, A Scalable Low-Latency Garbage collector( Experimental) ,一个可伸缩的低延时的垃圾回收器。GC暂停时间不会超过10ms,既能处理几百兆的小堆,也能处理几个T的大堆。和G1相比,应用吞吐能力不会降低超过15%,为将来的GC功能和利用colord指针以及Load barriers 优化奠基了基础。初始只支持64位系统。
ZGC的设计目标是:支持TB级内存容量,暂停时间低(<10ms),对整个程序吞吐量的影响小于15%。未来还能够扩 展实现机制,以支持很多使人兴奋的功能,例如多层堆(即热对象置于DRAM和冷对象置于NVMe闪存),或压缩堆。
GC是java主要优点之一。然而,当GC停顿太长,就会开始影响应用的响应时间。消除或者减小GC停顿时长,java将有可能在更普遍的应用场景中成长为一个更有吸引力的平台。此外,现代系统中可用内存不断增加,用户和程序员但愿JVM可以以高效的方式充分利用这些内存,而且无需长时间的GC暂停时间。
ZGC是一个并发,基于region,压缩型的垃圾收集器,只有root扫描阶段会STW,所以GC停顿时间不会随着堆的增加和存活对象的增加而变长。
垃圾回收器 | 平均等待时间(ms) | 最大等待时间(ms) |
---|---|---|
ZGC | 1.091 | 1.681 |
G1 | 156.806 | 543.846 |
Experimental(实验性质),生产环境不建议使用
启用方法:-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
说明:开发一个处理内存分配但不实现任何实际内存回收机制的GC,一旦可用堆内存用完,JVM就会退出,若是有System.gc()
调用,实际上什么也不会发生(这种场景下和-XX:+DisableExplicitGC
效果同样), 由于没有内存回收,这个实现可能会警告用户尝试强制GC是徒劳的。
主要用途以下:
-Xmx1g -XX:+UseEpsilonGC
,若是程序有问题,则程序会崩溃。对于G1 GC,相比于JDK8,升级到JDK 11便可免费享受到:并行的Full GC,快速的CardTable扫描,自适应的堆占用比例调整(IHOP),在并发标记阶段的类型卸载等等。这些都是针对G1的不断加强,其中串行FullGC等甚至是曾经被普遍诟病的短板,你会发现GC配置和调优在JDK11中愈来愈方便。
Java9到Java11,陆续移除了一些类库或功能。
在java11中移除了不太使用的JavaEE模块和CORBA技术。
CORBA来自于二十世纪九十年代,Oracle认为,如今用CORBA开发现代Java应用程序已经没有意义了,维护CORBA的成本已经超过了保留它带来的好处。
可是删除CORBA将使得那些依赖于JDK提供部分CORBAAPI的CORBA实现没法运行。目前尚未第三方CORBA版本,也不肯定是否会有第三方愿意接手CORBA API的维护工做。
在java11中将java9标记废弃的Java EE及CORBA模块移除掉,具体以下:
只剩下java.xml, java.xml.crypto.jdk.xml.dom 这几个模块。
其中,使用java.lang.invoke.MethodHandles.Lookup.defineClass
来替代移除的sun.miss.Unsafe.defineClass
。
将Javascript引擎标记为Deprecate,后续版本会移除,有须要的能够考虑使用开源的GraalVM。
java11中将pack200以及unpack200工具以及java.tiljar中的Pack200 API标记为Deprecate。由于Pack200主要是用来压缩jar包的工具,因为网络下载速度的提高以及java9引入模块化系统以后再也不依赖Pack200,所以这个版本将其标记为Deprecate。