<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          萬字總結(jié)Java 9~15新特性,真香!

          共 22591字,需瀏覽 46分鐘

           ·

          2021-09-10 19:56

          別人家的特性都用了幾年了,我 Java 才出來,哈哈!真實(shí)!

          Java9

          發(fā)布于 2017 年 9 月 21 日 。作為 Java8 之后 3 年半才發(fā)布的新版本,Java 9 帶 來了很多重大的變化其中最重要的改動(dòng)是 Java 平臺(tái)模塊系統(tǒng)的引入,其他還有諸如集合、Stream 流

          Java 平臺(tái)模塊系統(tǒng)

          Java 平臺(tái)模塊系統(tǒng)是Jigsaw Project的一部分,把模塊化開發(fā)實(shí)踐引入到了 Java 平臺(tái)中,可以讓我們的代碼可重用性更好!

          什么是模塊系統(tǒng)?官方的定義是:A uniquely named, reusable group of related packages, as well as resources (such as images and XML files) and a module descriptor.

          簡(jiǎn)單來說,你可以將一個(gè)模塊看作是一組唯一命名、可重用的包、資源和模塊描述文件(module-info.java)。

          任意一個(gè) jar 文件,只要加上一個(gè) 模塊描述文件(module-info.java),就可以升級(jí)為一個(gè)模塊。

          532f9a15b87501b2764cb7b6663db126.webp

          在引入了模塊系統(tǒng)之后,JDK 被重新組織成 94 個(gè)模塊。Java 應(yīng)用可以通過新增的 jlink 工具,創(chuàng)建出只包含所依賴的 JDK 模塊的自定義運(yùn)行時(shí)鏡像。這樣可以極大的減少 Java 運(yùn)行時(shí)環(huán)境的大小。

          我們可以通過 exports 關(guān)鍵詞精準(zhǔn)控制哪些類可以對(duì)外開放使用,哪些類只能內(nèi)部使用。

          module?my.module?{
          ????//exports?公開指定包的所有公共成員
          ????exports?com.my.package.name;
          }

          module?my.module?{
          ?????//exports…to?限制訪問的成員范圍
          ????export?com.my.package.name?to?com.specific.package;
          }

          Java 9 模塊的重要特征是在其工件(artifact)的根目錄中包含了一個(gè)描述模塊的 module-info.java 文 件。工件的格式可以是傳統(tǒng)的 JAR 文件或是 Java 9 新增的 JMOD 文件。

          想要深入了解 Java 9 的模塊化,參見:

          • 《Project Jigsaw: Module System Quick-Start Guide》
          • 《Java 9 Modules: part 1》

          Jshell

          jshell 是 Java 9 新增的一個(gè)實(shí)用工具。為 Java 提供了類似于 Python 的實(shí)時(shí)命令行交互工具。

          在 Jshell 中可以直接輸入表達(dá)式并查看其執(zhí)行結(jié)果。

          1d4310429e7304fa6f702e6d42cbf433.webp

          集合增強(qiáng)

          增加 了 List.of()、Set.of()Map.of()Map.ofEntries()等工廠方法來創(chuàng)建不可變集合(這部分內(nèi)容有點(diǎn)參考 Guava 的味道)

          List.of("Java",?"C++");
          Set.of("Java",?"C++");
          Map.of("Java",?1,?"C++",?2);

          使用 of() 創(chuàng)建的集合為不可變集合,不能進(jìn)行添加、刪除、替換、 排序等操作,不然會(huì)報(bào) java.lang.UnsupportedOperationException 異常。

          Collectors 中增加了新的方法 filtering()flatMapping()

          Collectorsfiltering() 方法類似于 Stream 類的 filter() 方法,都是用于過濾元素。

          Java 8 為 Collectors 類引入了 groupingBy 操作,用于根據(jù)特定的屬性將對(duì)象分組。

          List<String>?list?=?List.of("x","www",?"yy",?"zz");
          Map<Integer,?List<String>>?result?=?list.stream()
          ????????.collect(Collectors.groupingBy(String::length,
          ????????????????Collectors.filtering(s?->?!s.contains("z"),
          ????????????????????????Collectors.toList())));

          System.out.println(result);?//?{1=[x],?2=[yy],?3=[www]}

          Stream & Optional 增強(qiáng)

          Stream 中增加了新的方法 ofNullable()dropWhile()、takeWhile() 以及 iterate() 方法的重載方法。

          Java 9 中的 ofNullable() 方 法允許我們創(chuàng)建一個(gè)單元素的 Stream,可以包含一個(gè)非空元素,也可以創(chuàng)建一個(gè)空 Stream。而在 Java 8 中則不可以創(chuàng)建空的 Stream

          Stream<String>?stringStream?=?Stream.ofNullable("Java");
          System.out.println(stringStream.count());//?1
          Stream<String>?nullStream?=?Stream.ofNullable(null);
          System.out.println(nullStream.count());//0

          takeWhile() 方法可以從 Stream 中依次獲取滿足條件的元素,直到不滿足條件為止結(jié)束獲取。

          List<Integer>?integerList?=?List.of(11,?33,?66,?8,?9,?13);
          integerList.stream().takeWhile(x?->?x?<?50).forEach(System.out::println);//?11?33

          dropWhile() 方法的效果和 takeWhile() 相反。

          List<Integer>?integerList2?=?List.of(11,?33,?66,?8,?9,?13);
          integerList2.stream().dropWhile(x?->?x?<?50).forEach(System.out::println);//?66?8?9?13

          iterate() 方法的新重載方法提供了一個(gè) Predicate 參數(shù) (判斷條件)來決定什么時(shí)候結(jié)束迭代

          public?static<T>?Stream<T>?iterate(final?T?seed,?final?UnaryOperator<T>?f)?{
          }
          //?新增加的重載方法
          public?static<T>?Stream<T>?iterate(T?seed,?Predicate<??super?T>?hasNext,?UnaryOperator<T>?next)?{

          }

          兩者的使用對(duì)比如下,新的 iterate() 重載方法更加靈活一些。

          //?使用原始?iterate()?方法輸出數(shù)字?1~10
          Stream.iterate(1,?i?->?i?+?1).limit(10).forEach(System.out::println);
          //?使用新的?iterate()?重載方法輸出數(shù)字?1~10
          Stream.iterate(1,?i?->?i?<=?10,?i?->?i?+?1).forEach(System.out::println);

          Optional 類中新增了 ifPresentOrElse()、or()stream() 等方法

          ifPresentOrElse() 方法接受兩個(gè)參數(shù) ConsumerRunnable ,如果 Optional 不為空調(diào)用 Consumer 參數(shù),為空則調(diào)用 Runnable 參數(shù)。

          public?void?ifPresentOrElse(Consumer<??super?T>?action,?Runnable?emptyAction)

          Optional<Object>?objectOptional?
          =?Optional.empty();
          objectOptional.ifPresentOrElse(System.out::println,?()?->?System.out.println("Empty!!!"));//?Empty!!!

          or() 方法接受一個(gè) Supplier 參數(shù) ,如果 Optional 為空則返回 Supplier 參數(shù)指定的 Optional 值。

          public?Optional<T>?or(Supplier<??extends?Optional<??extends?T>>?supplier)

          Optional<Object>?objectOptional?
          =?Optional.empty();
          objectOptional.or(()?->?Optional.of("java")).ifPresent(System.out::println);//java

          String 存儲(chǔ)結(jié)構(gòu)變更

          JDK 8 及之前的版本,String 一直是用 char[] 存儲(chǔ)。在 Java 9 之后,String 的實(shí)現(xiàn)改用 byte[] 數(shù)組存儲(chǔ)字符串。

          進(jìn)程 API

          Java 9 增加了 ProcessHandle 接口,可以對(duì)原生進(jìn)程進(jìn)行管理,尤其適合于管理長(zhǎng)時(shí)間運(yùn)行的進(jìn)程。

          System.out.println(ProcessHandle.current().pid());
          System.out.println(ProcessHandle.current().info());
          cd2763ed6edc02a44c5ad21a7106fd17.webp

          平臺(tái)日志 API 和服務(wù)

          Java 9 允許為 JDK 和應(yīng)用配置同樣的日志實(shí)現(xiàn)。新增了 System.LoggerFinder 用來管理 JDK 使 用的日志記錄器實(shí)現(xiàn)。JVM 在運(yùn)行時(shí)只有一個(gè)系統(tǒng)范圍的 LoggerFinder 實(shí)例。

          我們可以通過添加自己的 System.LoggerFinder 實(shí)現(xiàn)來讓 JDK 和應(yīng)用使用 SLF4J 等其他日志記錄框架。

          反應(yīng)式流 ( Reactive Streams )

          在 Java9 中的 java.util.concurrent.Flow 類中新增了反應(yīng)式流規(guī)范的核心接口 。

          Flow 中包含了 Flow.Publisher、Flow.SubscriberFlow.SubscriptionFlow.Processor 等 4 個(gè)核心接口。Java 9 還提供了SubmissionPublisher 作為Flow.Publisher 的一個(gè)實(shí)現(xiàn)。

          變量句柄

          變量句柄是一個(gè)變量或一組變量的引用,包括靜態(tài)域,非靜態(tài)域,數(shù)組元素和堆外數(shù)據(jù)結(jié)構(gòu)中的組成部分等

          變量句柄的含義類似于已有的方法句柄 MethodHandle ,由 Java 類 java.lang.invoke.VarHandle 來表示,可以使用類 java.lang.invoke.MethodHandles.Lookup 中的靜態(tài)工廠方法來創(chuàng)建 VarHandle 對(duì)象。

          VarHandle 的出現(xiàn)替代了 java.util.concurrent.atomicsun.misc.Unsafe 的部分操作。并且提供了一系列標(biāo)準(zhǔn)的內(nèi)存屏障操作,用于更加細(xì)粒度的控制內(nèi)存排序。在安全性、可用性、性能上都要優(yōu)于現(xiàn)有的 API。

          改進(jìn)方法句柄(Method Handle)

          方法句柄從 Java7 開始引入,Java9 在類java.lang.invoke.MethodHandles 中新增了更多的靜態(tài)方法來創(chuàng)建不同類型的方法句柄。

          接口私有方法

          Java 9 允許在接口中使用私有方法。

          public?interface?MyInterface?{
          ????private?void?methodPrivate(){

          ????}
          }

          Java9 其它新特性

          • try-with-resources 增強(qiáng) :在 try-with-resources 語句中可以使用 effectively-final 變量(什么是 effectively-final 變量,見這篇文章:《Effectively Final Variables in Java》
          • CompletableFuture 中增加了幾個(gè)新的方法(completeAsync ,orTimeout 等)
          • Nashorn 引擎的增強(qiáng) :Nashorn 從 Java8 開始引入的 JavaScript 引擎,Java9 對(duì) Nashorn 做了些增強(qiáng),實(shí)現(xiàn)了一些 ES6 的新特性(Java 11 中已經(jīng)被棄用)。
          • I/O 流的新特性 :增加了新的方法來讀取和復(fù)制 InputStream 中包含的數(shù)據(jù)
          • 改進(jìn)應(yīng)用的安全性能 :Java 9 新增了 4 個(gè) SHA- 3 哈希算法,SHA3-224、SHA3-256、SHA3-384 和 SHA3-512
          • ......

          Java10

          發(fā)布于 2018 年 3 月 20 日,最知名的特性應(yīng)該是 var 關(guān)鍵字(局部變量類型推斷)的引入了,其他還有垃圾收集器改善、GC 改進(jìn)、性能提升、線程管控等一批新特性

          var(局部變量推斷)

          由于太多 Java 開發(fā)者希望 Java 中引入局部變量推斷,于是 Java 10 的時(shí)候它來了,也算是眾望所歸了!

          Java 10 提供了 var 關(guān)鍵字聲明局部變量。

          Scala 和 Kotlin 中有 val 關(guān)鍵字 ( final var 組合關(guān)鍵字),Java10 中并沒有引入。

          Java 10 只引入了 var,而

          var?id?=?0;
          var?codefx?=?new?URL("https://mp.weixin.qq.com/");
          var?list?=?new?ArrayList<>();
          var?list?=?List.of(1,?2,?3);
          var?map?=?new?HashMap<String,?String>();
          var?p?=?Paths.of("src/test/java/Java9FeaturesTest.java");
          var?numbers?=?List.of("a",?"b",?"c");
          for?(var?n?:?list)
          ????System.out.print(n+?"?");

          var 關(guān)鍵字只能用于帶有構(gòu)造器的局部變量和 for 循環(huán)中。

          var?count=null;?//?編譯不通過,不能聲明為?null
          var?r?=?()?->?Math.random();//?編譯不通過,不能聲明為?Lambda表達(dá)式
          var?array?=?{1,2,3};//?編譯不通過,不能聲明數(shù)組

          var 并不會(huì)改變 Java 是一門靜態(tài)類型語言的事實(shí),編譯器負(fù)責(zé)推斷出類型。

          相關(guān)閱讀:《Java 10 新特性之局部變量類型推斷》。

          集合增強(qiáng)

          list,setmap 提供了靜態(tài)方法copyOf()返回入?yún)⒓系囊粋€(gè)不可變拷貝。

          以下為 JDK 的源碼:

          static?<E>?List<E>?copyOf(Collection<??extends?E>?coll)?{
          ????return?ImmutableCollections.listCopy(coll);
          }

          使用 copyOf() 創(chuàng)建的集合為不可變集合,不能進(jìn)行添加、刪除、替換、 排序等操作,不然會(huì)報(bào) java.lang.UnsupportedOperationException 異常。IDEA 也會(huì)有相應(yīng)的提示。

          74d252afbdda824e11fefc5b54585c0b.webp

          java.util.stream.Collectors 中新增了靜態(tài)方法,用于將流中的元素收集為不可變的集合。

          var?list?=?new?ArrayList<>();
          list.stream().collect(Collectors.toUnmodifiableList());
          list.stream().collect(Collectors.toUnmodifiableSet());

          Optional

          新增了orElseThrow()方法來在沒有值時(shí)拋出指定的異常。

          Optional.ofNullable(cache.getIfPresent(key))
          ????????.orElseThrow(()?->?new?PrestoException(NOT_FOUND,?"Missing?entry?found?for?key:?"?+?key));

          并行全垃圾回收器 G1

          從 Java9 開始 G1 就了默認(rèn)的垃圾回收器,G1 是以一種低延時(shí)的垃圾回收器來設(shè)計(jì)的,旨在避免進(jìn)行 Full GC,但是 Java9 的 G1 的 FullGC 依然是使用單線程去完成標(biāo)記清除算法,這可能會(huì)導(dǎo)致垃圾回收期在無法回收內(nèi)存的時(shí)候觸發(fā) Full GC。

          為了最大限度地減少 Full GC 造成的應(yīng)用停頓的影響,從 Java10 開始,G1 的 FullGC 改為并行的標(biāo)記清除算法,同時(shí)會(huì)使用與年輕代回收和混合回收相同的并行工作線程數(shù)量,從而減少了 Full GC 的發(fā)生,以帶來更好的性能提升、更大的吞吐量。

          應(yīng)用程序類數(shù)據(jù)共享(擴(kuò)展 CDS 功能)

          在 Java 5 中就已經(jīng)引入了類數(shù)據(jù)共享機(jī)制 (Class Data Sharing,簡(jiǎn)稱 CDS),允許將一組類預(yù)處理為共享歸檔文件,以便在運(yùn)行時(shí)能夠進(jìn)行內(nèi)存映射以減少 Java 程序的啟動(dòng)時(shí)間,當(dāng)多個(gè) Java 虛擬機(jī)(JVM)共享相同的歸檔文件時(shí),還可以減少動(dòng)態(tài)內(nèi)存的占用量,同時(shí)減少多個(gè)虛擬機(jī)在同一個(gè)物理或虛擬的機(jī)器上運(yùn)行時(shí)的資源占用。CDS 在當(dāng)時(shí)還是 Oracle JDK 的商業(yè)特性。

          Java 10 在現(xiàn)有的 CDS 功能基礎(chǔ)上再次拓展,以允許應(yīng)用類放置在共享存檔中。CDS 特性在原來的 bootstrap 類基礎(chǔ)之上,擴(kuò)展加入了應(yīng)用類的 CDS 為 (Application Class-Data Sharing,AppCDS) 支持,大大加大了 CDS 的適用范圍。其原理為:在啟動(dòng)時(shí)記錄加載類的過程,寫入到文本文件中,再次啟動(dòng)時(shí)直接讀取此啟動(dòng)文本并加載。設(shè)想如果應(yīng)用環(huán)境沒有大的變化,啟動(dòng)速度就會(huì)得到提升。

          Java10 其他新特性

          • 線程-局部管控:Java 10 中線程管控引入 JVM 安全點(diǎn)的概念,將允許在不運(yùn)行全局 JVM 安全點(diǎn)的情況下實(shí)現(xiàn)線程回調(diào),由線程本身或者 JVM 線程來執(zhí)行,同時(shí)保持線程處于阻塞狀態(tài),這種方式使得停止單個(gè)線程變成可能,而不是只能啟用或停止所有線程
          • 備用存儲(chǔ)裝置上的堆分配:Java 10 中將使得 JVM 能夠使用適用于不同類型的存儲(chǔ)機(jī)制的堆,在可選內(nèi)存設(shè)備上進(jìn)行堆內(nèi)存分配
          • 統(tǒng)一的垃圾回收接口:Java 10 中,hotspot/gc 代碼實(shí)現(xiàn)方面,引入一個(gè)干凈的 GC 接口,改進(jìn)不同 GC 源代碼的隔離性,多個(gè) GC 之間共享的實(shí)現(xiàn)細(xì)節(jié)代碼應(yīng)該存在于輔助類中。統(tǒng)一垃圾回收接口的主要原因是:讓垃圾回收器(GC)這部分代碼更加整潔,便于新人上手開發(fā),便于后續(xù)排查相關(guān)問題。
          • ......

          Java11

          Java11 于 2018 年 9 月 25 日正式發(fā)布,這是很重要的一個(gè)版本!Java 11 和 2017 年 9 月份發(fā)布的 Java 9 以及 2018 年 3 月份發(fā)布的 Java 10 相比,其最大的區(qū)別就是:在長(zhǎng)期支持(Long-Term-Support)方面,Oracle 表示會(huì)對(duì) Java 11 提供大力支持,這一支持將會(huì)持續(xù)至 2026 年 9 月。這是據(jù) Java 8 以后支持的首個(gè)長(zhǎng)期版本。

          e076c3823d9cf14f5124c3bd5461e821.webp

          String

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

          Guide:說白點(diǎn)就是多了層封裝,JDK 開發(fā)組的人沒少看市面上常見的工具類框架啊!

          //判斷字符串是否為空
          "?".isBlank();//true
          //去除字符串首尾空格
          "?Java?".strip();//?"Java"
          //去除字符串首部空格
          "?Java?".stripLeading();???//?"Java?"
          //去除字符串尾部空格
          "?Java?".stripTrailing();??//?"?Java"
          //重復(fù)字符串多少次
          "Java".repeat(3);?????????????//?"JavaJavaJava"

          //返回由行終止符分隔的字符串集合。
          "A\nB\nC".lines().count();????//?3
          "A\nB\nC".lines().collect(Collectors.toList());

          Optional

          新增了empty()方法來判斷指定的 Optional 對(duì)象是否為空。

          var?op?=?Optional.empty();
          System.out.println(op.isEmpty());//判斷指定的?Optional?對(duì)象是否為空

          ZGC(可伸縮低延遲垃圾收集器)

          ZGC 即 Z Garbage Collector,是一個(gè)可伸縮的、低延遲的垃圾收集器。

          ZGC 主要為了滿足如下目標(biāo)進(jìn)行設(shè)計(jì):

          • GC 停頓時(shí)間不超過 10ms
          • 即能處理幾百 MB 的小堆,也能處理幾個(gè) TB 的大堆
          • 應(yīng)用吞吐能力不會(huì)下降超過 15%(與 G1 回收算法相比)
          • 方便在此基礎(chǔ)上引入新的 GC 特性和利用 colored 針以及 Load barriers 優(yōu)化奠定基礎(chǔ)
          • 當(dāng)前只支持 Linux/x64 位平臺(tái)

          ZGC 目前 處在實(shí)驗(yàn)階段,只支持 Linux/x64 平臺(tái)。

          與 CMS 中的 ParNew 和 G1 類似,ZGC 也采用標(biāo)記-復(fù)制算法,不過 ZGC 對(duì)該算法做了重大改進(jìn)。

          在 ZGC 中出現(xiàn) Stop The World 的情況會(huì)更少!

          詳情可以看 :《新一代垃圾回收器 ZGC 的探索與實(shí)踐》

          標(biāo)準(zhǔn) HTTP Client 升級(jí)

          Java 11 對(duì) Java 9 中引入并在 Java 10 中進(jìn)行了更新的 Http Client API 進(jìn)行了標(biāo)準(zhǔn)化,在前兩個(gè)版本中進(jìn)行孵化的同時(shí),Http Client 幾乎被完全重寫,并且現(xiàn)在完全支持異步非阻塞。

          并且,Java11 中,Http Client 的包名由 jdk.incubator.http 改為java.net.http,該 API 通過 CompleteableFuture 提供非阻塞請(qǐng)求和響應(yīng)語義。使用起來也很簡(jiǎn)單,如下:

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

          var(Lambda 參數(shù)的局部變量語法)

          從 Java 10 開始,便引入了局部變量類型推斷這一關(guān)鍵特性。類型推斷允許使用關(guān)鍵字 var 作為局部變量的類型而不是實(shí)際類型,編譯器根據(jù)分配給變量的值推斷出類型。

          Java 10 中對(duì) var 關(guān)鍵字存在幾個(gè)限制

          • 只能用于局部變量上
          • 聲明時(shí)必須初始化
          • 不能用作方法參數(shù)
          • 不能在 Lambda 表達(dá)式中使用

          Java11 開始允許開發(fā)者在 Lambda 表達(dá)式中使用 var 進(jìn)行參數(shù)聲明。

          //?下面兩者是等價(jià)的
          Consumer<String>?consumer?=?(var?i)?->?System.out.println(i);
          Consumer<String>?consumer?=?(String?i)?->?System.out.println(i);

          啟動(dòng)單文件源代碼程序

          JEP 330:啟動(dòng)單文件源代碼程序(aunch Single-File Source-Code Programs) 可以讓我們運(yùn)行單一文件的 Java 源代碼。此功能允許使用 Java 解釋器直接執(zhí)行 Java 源代碼。源代碼在內(nèi)存中編譯,然后由解釋器執(zhí)行,不需要在磁盤上生成 .class 文件了。

          唯一的約束在于所有相關(guān)的類必須定義在同一個(gè) Java 文件中。

          對(duì)于 Java 初學(xué)者并希望嘗試簡(jiǎn)單程序的人特別有用,并且能和 jshell 一起使用

          一定能程度上增強(qiáng)了使用 Java 來寫腳本程序的能力。

          Java11 其他新特性

          • 新的垃圾回收器 Epsilon :一個(gè)完全消極的 GC 實(shí)現(xiàn),分配有限的內(nèi)存資源,最大限度的降低內(nèi)存占用和內(nèi)存吞吐延遲時(shí)間
          • 低開銷的 Heap Profiling :Java 11 中提供一種低開銷的 Java 堆分配采樣方法,能夠得到堆分配的 Java 對(duì)象信息,并且能夠通過 JVMTI 訪問堆信息
          • TLS1.3 協(xié)議 :Java 11 中包含了傳輸層安全性(TLS)1.3 規(guī)范(RFC 8446)的實(shí)現(xiàn),替換了之前版本中包含的 TLS,包括 TLS 1.2,同時(shí)還改進(jìn)了其他 TLS 功能,例如 OCSP 裝訂擴(kuò)展(RFC 6066,RFC 6961),以及會(huì)話散列和擴(kuò)展主密鑰擴(kuò)展(RFC 7627),在安全性和性能方面也做了很多提升
          • 飛行記錄器(Java Flight Recorder) :飛行記錄器之前是商業(yè)版 JDK 的一項(xiàng)分析工具,但在 Java 11 中,其代碼被包含到公開代碼庫(kù)中,這樣所有人都能使用該功能了。
          • ......

          Java12

          String

          Java 11 增加了兩個(gè)的字符串處理方法,如以下所示。

          indent() 方法可以實(shí)現(xiàn)字符串縮進(jìn)。

          String?text?=?"Java";
          //?縮進(jìn)?4?格
          text?=?text.indent(4);
          System.out.println(text);
          text?=?text.indent(-10);
          System.out.println(text);

          輸出:

          ?????Java
          Java

          transform() 方法可以用來轉(zhuǎn)變指定字符串。

          String?result?=?"foo".transform(input?->?input?+?"?bar");
          System.out.println(result);?//?foo?bar

          文件比較

          Java 12 添加了以下方法來比較兩個(gè)文件:

          public?static?long?mismatch(Path?path,?Path?path2)?throws?IOException

          mismatch() 方法用于比較兩個(gè)文件,并返回第一個(gè)不匹配字符的位置,如果文件相同則返回 -1L。

          代碼示例(兩個(gè)文件內(nèi)容相同的情況):

          Path?filePath1?=?Files.createTempFile("file1",?".txt");
          Path?filePath2?=?Files.createTempFile("file2",?".txt");
          Files.writeString(filePath1,?"Java?12?Article");
          Files.writeString(filePath2,?"Java?12?Article");

          long?mismatch?=?Files.mismatch(filePath1,?filePath2);
          assertEquals(-1,?mismatch);

          代碼示例(兩個(gè)文件內(nèi)容不相同的情況):

          Path?filePath3?=?Files.createTempFile("file3",?".txt");
          Path?filePath4?=?Files.createTempFile("file4",?".txt");
          Files.writeString(filePath3,?"Java?12?Article");
          Files.writeString(filePath4,?"Java?12?Tutorial");

          long?mismatch?=?Files.mismatch(filePath3,?filePath4);
          assertEquals(8,?mismatch);

          數(shù)字格式化工具類

          NumberFormat 新增了對(duì)復(fù)雜的數(shù)字進(jìn)行格式化的支持

          NumberFormat?fmt?=?NumberFormat.getCompactNumberInstance(Locale.US,?NumberFormat.Style.SHORT);
          String?result?=?fmt.format(1000);
          ?System.out.println(result);?//?輸出為 1K,計(jì)算工資是多少K更方便了。。。

          Shenandoah GC

          Redhat 主導(dǎo)開發(fā)的 Pauseless GC 實(shí)現(xiàn),主要目標(biāo)是 99.9% 的暫停小于 10ms,暫停與堆大小無關(guān)等

          和 Java11 開源的 ZGC 相比(需要升級(jí)到 JDK11 才能使用),Shenandoah GC 有穩(wěn)定的 JDK8u 版本,在 Java8 占據(jù)主要市場(chǎng)份額的今天有更大的可落地性。

          G1 收集器提升

          Java12 為默認(rèn)的垃圾收集器 G1 帶來了兩項(xiàng)更新:

          • 可中止的混合收集集合 :JEP344 的實(shí)現(xiàn),為了達(dá)到用戶提供的停頓時(shí)間目標(biāo),JEP 344 通過把要被回收的區(qū)域集(混合收集集合)拆分為強(qiáng)制和可選部分,使 G1 垃圾回收器能中止垃圾回收過程。G1 可以中止可選部分的回收以達(dá)到停頓時(shí)間目標(biāo)
          • 及時(shí)返回未使用的已分配內(nèi)存 :JEP346 的實(shí)現(xiàn),增強(qiáng) G1 GC,以便在空閑時(shí)自動(dòng)將 Java 堆內(nèi)存返回給操作系統(tǒng)

          預(yù)覽新特性

          作為預(yù)覽特性加入,需要在javac編譯和java運(yùn)行時(shí)增加參數(shù)--enable-preview 。

          增強(qiáng) Switch

          傳統(tǒng)的 switch 語法存在容易漏寫 break 的問題,而且從代碼整潔性層面來看,多個(gè) break 本質(zhì)也是一種重復(fù)

          Java12 增強(qiáng)了 swtich 表達(dá)式,使用類似 lambda 語法條件匹配成功后的執(zhí)行塊,不需要多寫 break 。

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

          instanceof 模式匹配

          instanceof 主要在類型強(qiáng)轉(zhuǎn)前探測(cè)對(duì)象的具體類型。

          之前的版本中,我們需要顯示地對(duì)對(duì)象進(jìn)行類型轉(zhuǎn)換。

          Object?obj?=?"我是字符串";
          if(obj?instanceof?String){
          ???String?str?=?(String)?obj;
          ?System.out.println(str);
          }

          新版的 instanceof 可以在判斷是否屬于具體的類型同時(shí)完成轉(zhuǎn)換。

          Object?obj?=?"我是字符串";
          if(obj?instanceof?String?str){
          ?System.out.println(str);
          }

          Java13

          增強(qiáng) ZGC(釋放未使用內(nèi)存)

          在 Java 11 中是實(shí)驗(yàn)性的引入的 ZGC 在實(shí)際的使用中存在未能主動(dòng)將未使用的內(nèi)存釋放給操作系統(tǒng)的問題。

          ZGC 堆由一組稱為 ZPages 的堆區(qū)域組成。在 GC 周期中清空 ZPages 區(qū)域時(shí),它們將被釋放并返回到頁面緩存 ZPageCache 中,此緩存中的 ZPages 按最近最少使用(LRU)的順序,并按照大小進(jìn)行組織。

          在 Java 13 中,ZGC 將向操作系統(tǒng)返回被標(biāo)識(shí)為長(zhǎng)時(shí)間未使用的頁面,這樣它們將可以被其他進(jìn)程重用。

          SocketAPI 重構(gòu)

          Java Socket API 終于迎來了重大更新!

          Java 13 將 Socket API 的底層進(jìn)行了重寫, NioSocketImpl 是對(duì) PlainSocketImpl 的直接替代,它使用 java.util.concurrent 包下的鎖而不是同步方法。如果要使用舊實(shí)現(xiàn),請(qǐng)使用 -Djdk.net.usePlainSocketImpl=true。

          并且,在 Java 13 中是默認(rèn)使用新的 Socket 實(shí)現(xiàn)。

          public?final?class?NioSocketImpl?extends?SocketImpl?implements?PlatformSocketImpl?{
          }

          FileSystems

          FileSystems 類中添加了以下三種新方法,以便更容易地使用將文件內(nèi)容視為文件系統(tǒng)的文件系統(tǒng)提供程序:

          • newFileSystem(Path)
          • newFileSystem(Path, Map<String, ?>)
          • newFileSystem(Path, Map<String, ?>, ClassLoader)

          動(dòng)態(tài) CDS 存檔

          Java 13 中對(duì) Java 10 中引入的應(yīng)用程序類數(shù)據(jù)共享(AppCDS)進(jìn)行了進(jìn)一步的簡(jiǎn)化、改進(jìn)和擴(kuò)展,即:允許在 Java 應(yīng)用程序執(zhí)行結(jié)束時(shí)動(dòng)態(tài)進(jìn)行類歸檔,具體能夠被歸檔的類包括所有已被加載,但不屬于默認(rèn)基層 CDS 的應(yīng)用程序類和引用類庫(kù)中的類。

          這提高了應(yīng)用程序類數(shù)據(jù)共享(AppCDS)的可用性。無需用戶進(jìn)行試運(yùn)行來為每個(gè)應(yīng)用程序創(chuàng)建類列表。

          $?java?-XX:ArchiveClassesAtExit=my_app_cds.jsa?-cp?my_app.jar
          $?java?-XX:SharedArchiveFile=my_app_cds.jsa?-cp?my_app.jar

          預(yù)覽新特性

          文本塊

          解決 Java 定義多行字符串時(shí)只能通過換行轉(zhuǎn)義或者換行連接符來變通支持的問題,引入三重雙引號(hào)來定義多行文本。

          Java 13 支持兩個(gè) """ 符號(hào)中間的任何內(nèi)容都會(huì)被解釋為字符串的一部分,包括換行符。

          未支持文本塊之前的 HTML 寫法:

          String?json?="{\n"?+
          ??????????????"???\"name\":\"mkyong\",\n"?+
          ??????????????"???\"age\":38\n"?+
          ??????????????"}\n";

          支持文本塊之后的 HTML 寫法:

          ?String?json?=?"""
          ????????????????{
          ????????????????????"
          name":"mkyong",
          ????????????????????"
          age":38
          ????????????????}
          ????????????????"
          "";

          未支持文本塊之前的 SQL 寫法:

          String?query?=?"SELECT?`EMP_ID`,?`LAST_NAME`?FROM?`EMPLOYEE_TB`\n"?+
          ???????????????"
          WHERE?`CITY`?=?'INDIANAPOLIS'\n"?+
          ???????????????"
          ORDER?BY?`EMP_ID`,?`LAST_NAME`;\n";

          支持文本塊之后的 SQL 寫法:

          String?query?=?"""
          ???????????????SELECT?`EMP_ID`,?`LAST_NAME`?FROM?`EMPLOYEE_TB`
          ???????????????WHERE?`CITY`?=?'INDIANAPOLIS'
          ???????????????ORDER?BY?`EMP_ID`,?`LAST_NAME`;
          ???????????????""";

          另外,String 類新增加了 3 個(gè)新的方法來操作文本塊:

          • formatted(Object... args) :它類似于 Stringformat()方法。添加它是為了支持文本塊的格式設(shè)置。
          • stripIndent() :用于去除文本塊中每一行開頭和結(jié)尾的空格。
          • translateEscapes() :轉(zhuǎn)義序列如 “\\t” 轉(zhuǎn)換為 “\t”

          由于文本塊是一項(xiàng)預(yù)覽功能,可以在未來版本中刪除,因此這些新方法被標(biāo)記為棄用。

          @Deprecated(forRemoval=true,?since="13")
          public?String?stripIndent()?{
          }
          @Deprecated(forRemoval=true,?since="13")
          public?String?formatted(Object...?args)?{

          }
          @Deprecated(forRemoval=true,?since="13")
          public?String?translateEscapes()?{
          }

          增強(qiáng) Switch(引入 yield 關(guān)鍵字到 Switch 中)

          Switch 表達(dá)式中就多了一個(gè)關(guān)鍵字用于跳出 Switch 塊的關(guān)鍵字 yield,主要用于返回一個(gè)值

          yieldreturn 的區(qū)別在于:return 會(huì)直接跳出當(dāng)前循環(huán)或者方法,而 yield 只會(huì)跳出當(dāng)前 Switch 塊,同時(shí)在使用 yield 時(shí),需要有 default 條件

          ?private?static?String?descLanguage(String?name)?{
          ????????return?switch?(name)?{
          ????????????case?"Java":?yield?"object-oriented,?platform?independent?and?secured";
          ????????????case?"Ruby":?yield?"a?programmer's?best?friend";
          ????????????default:?yield?name?+"?is?a?good?language";
          ????????};
          ?}

          Java14

          空指針異常精準(zhǔn)提示

          通過 JVM 參數(shù)中添加-XX:+ShowCodeDetailsInExceptionMessages,可以在空指針異常中獲取更為詳細(xì)的調(diào)用信息,更快的定位和解決問題。

          a.b.c.i?=?99;?//?假設(shè)這段代碼會(huì)發(fā)生空指針

          Java 14 之前:

          Exception?in?thread?"main"?java.lang.NullPointerException
          ????at?NullPointerExample.main(NullPointerExample.java:5)

          Java 14 之后:

          ?//?增加參數(shù)后提示的異常中很明確的告知了哪里為空導(dǎo)致
          Exception?in?thread?"main"?java.lang.NullPointerException:
          ????????Cannot?read?field?'c'?because?'a.b'?is?null.
          ????at?Prog.main(Prog.java:5)

          switch 的增強(qiáng)(轉(zhuǎn)正)

          Java12 引入的 switch(預(yù)覽特性)在 Java14 變?yōu)檎桨姹荆恍枰黾訁?shù)來啟用,直接在 JDK14 中就能使用。

          Java12 為 switch 表達(dá)式引入了類似 lambda 語法條件匹配成功后的執(zhí)行塊,不需要多寫 break ,Java13 提供了 yield 來在 block 中返回值。

          String?result?=?switch?(day)?{
          ????????????case?"M",?"W",?"F"?->?"MWF";
          ????????????case?"T",?"TH",?"S"?->?"TTS";
          ????????????default?->?{
          ????????????????if(day.isEmpty())
          ????????????????????yield?"Please?insert?a?valid?day.";
          ????????????????else
          ????????????????????yield?"Looks?like?a?Sunday.";
          ????????????}

          ????????};
          System.out.println(result);

          預(yù)覽新特性

          record 關(guān)鍵字

          簡(jiǎn)化數(shù)據(jù)類的定義方式,使用 record 代替 class 定義的類,只需要聲明屬性,就可以在獲得屬性的訪問方法,以及 toString(),hashCode(), equals()方法

          類似于使用 class 定義類,同時(shí)使用了 lombok 插件,并打上了@Getter,@ToString,@EqualsAndHashCode注解

          /**
          ?*?這個(gè)類具有兩個(gè)特征
          ?*?1.?所有成員屬性都是final
          ?*?2.?全部方法由構(gòu)造方法,和兩個(gè)成員屬性訪問器組成(共三個(gè))
          ?*?那么這種類就很適合使用record來聲明
          ?*/

          final?class?Rectangle?implements?Shape?{
          ????final?double?length;
          ????final?double?width;

          ????public?Rectangle(double?length,?double?width)?{
          ????????this.length?=?length;
          ????????this.width?=?width;
          ????}

          ????double?length()?{?return?length;?}
          ????double?width()?{?return?width;?}
          }
          /**
          ?*?1.?使用record聲明的類會(huì)自動(dòng)擁有上面類中的三個(gè)方法
          ?*?2.?在這基礎(chǔ)上還附贈(zèng)了equals(),hashCode()方法以及toString()方法
          ?*?3.?toString方法中包括所有成員屬性的字符串表示形式及其名稱
          ?*/

          record?Rectangle(float?length,?float?width)?{?}

          文本塊

          Java14 中,文本塊依然是預(yù)覽特性,不過,其引入了兩個(gè)新的轉(zhuǎn)義字符:

          • \ : 表示行尾,不引入換行符
          • \s :表示單個(gè)空格
          String?str?=?"凡心所向,素履所往,生如逆旅,一葦以航。";

          String?str2?=?"""
          ????????凡心所向,素履所往,?\
          ????????生如逆旅,一葦以航。"
          "";
          System.out.println(str2);//?凡心所向,素履所往,?生如逆旅,一葦以航。
          String?text?=?"""
          ????????java
          ????????c++\sphp
          ????????"
          "";
          System.out.println(text);
          //輸出:
          java
          c++?php

          instanceof 增強(qiáng)

          依然是預(yù)覽特性 ,Java 12 新特性中介紹過。

          Java14 其他特性

          • 從 Java11 引入的 ZGC 作為繼 G1 過后的下一代 GC 算法,從支持 Linux 平臺(tái)到 Java14 開始支持 MacOS 和 Window(個(gè)人感覺是終于可以在日常開發(fā)工具中先體驗(yàn)下 ZGC 的效果了,雖然其實(shí) G1 也夠用)
          • 移除了 CMS(Concurrent Mark Sweep) 垃圾收集器(功成而退)
          • 新增了 jpackage 工具,標(biāo)配將應(yīng)用打成 jar 包外,還支持不同平臺(tái)的特性包,比如 linux 下的debrpm,window 平臺(tái)下的msiexe

          Java15

          CharSequence

          CharSequence 接口添加了一個(gè)默認(rèn)方法 isEmpty() 來判斷字符序列為空,如果是則返回 true。

          public?interface?CharSequence?{
          ??default?boolean?isEmpty()?{
          ??????return?this.length()?==?0;
          ??}
          }

          TreeMap

          TreeMap 新引入了下面這些方法:

          • putIfAbsent()
          • computeIfAbsent()
          • computeIfPresent()
          • compute()
          • merge()

          ZGC(轉(zhuǎn)正)

          Java11 的時(shí)候 ,ZGC 還在試驗(yàn)階段。

          當(dāng)時(shí),ZGC 的出現(xiàn)讓眾多 Java 開發(fā)者看到了垃圾回收器的另外一種可能,因此備受關(guān)注。

          經(jīng)過多個(gè)版本的迭代,不斷的完善和修復(fù)問題,ZGC 在 Java 15 已經(jīng)可以正式使用了!

          不過,默認(rèn)的垃圾回收器依然是 G1。你可以通過下面的參數(shù)啟動(dòng) ZGC:

          $?java?-XX:+UseZGC?className

          EdDSA(數(shù)字簽名算法)

          新加入了一個(gè)安全性和性能都更強(qiáng)的基于 Edwards-Curve Digital Signature Algorithm (EdDSA)實(shí)現(xiàn)的數(shù)字簽名算法。

          雖然其性能優(yōu)于現(xiàn)有的 ECDSA 實(shí)現(xiàn),不過,它并不會(huì)完全取代 JDK 中現(xiàn)有的橢圓曲線數(shù)字簽名算法( ECDSA)。

          KeyPairGenerator?kpg?=?KeyPairGenerator.getInstance("Ed25519");
          KeyPair?kp?=?kpg.generateKeyPair();

          byte[]?msg?=?"test_string".getBytes(StandardCharsets.UTF_8);

          Signature?sig?=?Signature.getInstance("Ed25519");
          sig.initSign(kp.getPrivate());
          sig.update(msg);
          byte[]?s?=?sig.sign();

          String?encodedString?=?Base64.getEncoder().encodeToString(s);
          System.out.println(encodedString);

          輸出:

          0Hc0lxxASZNvS52WsvnncJOH/mlFhnA8Tc6D/k5DtAX5BSsNVjtPF4R4+yMWXVjrvB2mxVXmChIbki6goFBgAg==

          文本塊(轉(zhuǎn)正)

          在 Java 15 ,文本塊是正式的功能特性了。

          隱藏類(Hidden Classes)

          隱藏類是為框架(frameworks)所設(shè)計(jì)的,隱藏類不能直接被其他類的字節(jié)碼使用,只能在運(yùn)行時(shí)生成類并通過反射間接使用它們。

          預(yù)覽新特性

          record 關(guān)鍵字

          Java 15 對(duì) Java 14 中引入的預(yù)覽新特性進(jìn)行了增強(qiáng),主要是引入了一個(gè)新的概念 密封類(Sealed Classes)。

          密封類可以對(duì)繼承或者實(shí)現(xiàn)它們的類進(jìn)行限制。

          比如抽象類 Person 只允許 EmployeeManager 繼承。

          public?abstract?sealed?class?Person
          ????permits?Employee,?Manager?
          {

          ????//...
          }

          另外,任何擴(kuò)展密封類的類本身都必須聲明為 sealed、non-sealedfinal

          public?final?class?Employee?extends?Person?{
          }

          public?non-sealed?class?Manager?extends?Person?{
          }
          5da31b12e2b7a7e0a108177a7520b225.webp

          java.lang.Class 增加了兩個(gè)公共方法用于獲取 Record 類信息:

          1. RecordComponent[] getRecordComponents()
          2. boolean isRecord()

          instanceof 模式匹配

          Java 15 并沒有對(duì)此特性進(jìn)行調(diào)整,繼續(xù)預(yù)覽特性,主要用于接受更多的使用反饋。

          在未來的 Java 版本中,Java 的目標(biāo)是繼續(xù)完善 instanceof 模式匹配新特性。

          Java15 其他新特性

          • Nashorn JavaScript 引擎徹底移除 :Nashorn 從 Java8 開始引入的 JavaScript 引擎,Java9 對(duì) Nashorn 做了些增強(qiáng),實(shí)現(xiàn)了一些 ES6 的新特性。在 Java 11 中就已經(jīng)被棄用,到了 Java 15 就徹底被刪除了。
          • DatagramSocket API 重構(gòu)
          • 禁用和廢棄偏向鎖(Biased Locking) :偏向鎖的引入增加了 JVM 的復(fù)雜性大于其帶來的性能提升。不過,你仍然可以使用 -XX:+UseBiasedLocking 啟用偏向鎖定,但它會(huì)提示 這是一個(gè)已棄用的 API。
          • ......

          總結(jié)

          關(guān)于預(yù)覽特性

          先貼一段 oracle 官網(wǎng)原文:This is a preview feature, which is a feature whose design, specification, and implementation are complete, but is not permanent, which means that the feature may exist in a different form or not at all in future JDK releases. To compile and run code that contains preview features, you must specify additional command-line options.

          這是一個(gè)預(yù)覽功能,該功能的設(shè)計(jì),規(guī)格和實(shí)現(xiàn)是完整的,但不是永久性的,這意味著該功能可能以其他形式存在或在將來的 JDK 版本中根本不存在。要編譯和運(yùn)行包含預(yù)覽功能的代碼,必須指定其他命令行選項(xiàng)。

          就以switch的增強(qiáng)為例子,從 Java12 中推出,到 Java13 中將繼續(xù)增強(qiáng),直到 Java14 才正式轉(zhuǎn)正進(jìn)入 JDK 可以放心使用,不用考慮后續(xù) JDK 版本對(duì)其的改動(dòng)或修改

          一方面可以看出 JDK 作為標(biāo)準(zhǔn)平臺(tái)在增加新特性的嚴(yán)謹(jǐn)態(tài)度,另一方面?zhèn)€人認(rèn)為是對(duì)于預(yù)覽特性應(yīng)該采取審慎使用的態(tài)度。特性的設(shè)計(jì)和實(shí)現(xiàn)容易,但是其實(shí)際價(jià)值依然需要在使用中去驗(yàn)證

          JVM 虛擬機(jī)優(yōu)化

          每次 Java 版本的發(fā)布都伴隨著對(duì) JVM 虛擬機(jī)的優(yōu)化,包括對(duì)現(xiàn)有垃圾回收算法的改進(jìn),引入新的垃圾回收算法,移除老舊的不再適用于今天的垃圾回收算法等

          整體優(yōu)化的方向是高效,低時(shí)延的垃圾回收表現(xiàn)

          對(duì)于日常的應(yīng)用開發(fā)者可能比較關(guān)注新的語法特性,但是從一個(gè)公司角度來說,在考慮是否升級(jí) Java 平臺(tái)時(shí)更加考慮的是JVM 運(yùn)行時(shí)的提升

          參考資料

          • JDK Project Overview :<https://openjdk.java.net/projects/jdk/ >
          • IBM Developer Java9 https://www.ibm.com/developerworks/cn/java/the-new-features-of-Java-9/
          • Guide to Java10 https://www.baeldung.com/java-10-overview
          • Java 10 新特性介紹https://www.ibm.com/developerworks/cn/java/the-new-features-of-Java-10/index.html
          • IBM Devloper Java11 https://www.ibm.com/developerworks/cn/java/the-new-features-of-Java-11/index.html
          • Java 11 – Features and Comparison:https://www.geeksforgeeks.org/java-11-features-and-comparison/
          • Oracle Java12 ReleaseNote https://www.oracle.com/technetwork/java/javase/12all-relnotes-5211423.html#NewFeature
          • Oracle Java13 ReleaseNote https://www.oracle.com/technetwork/java/javase/13all-relnotes-5461743.html#NewFeature
          • New Features in Java 12 https://www.baeldung.com/java-12-new-features
          • New Java13 Features https://www.baeldung.com/java-13-new-features
          • Java13 新特性概述 https://www.ibm.com/developerworks/cn/java/the-new-features-of-Java-13/index.html
          • Oracle Java14 record https://docs.oracle.com/en/java/javase/14/language/records.html
          • java14-features https://www.techgeeknext.com/java/java14-features
          • Java 14 Features : https://www.journaldev.com/37273/java-14-features
          • What is new in Java 15: https://mkyong.com/java/what-is-new-in-java-15/
          < END >卷的小伙伴可以繼續(xù)學(xué)習(xí)????:

          ????萬字Java知識(shí)地圖助你成為Offer收割機(jī)

          ????2021年Java工程師飛升成神之路

          一起卷吧,『Java八股文』項(xiàng)目上線啦

          作者簡(jiǎn)介:

          博主從華中科技大學(xué)碩士畢業(yè),是一個(gè)對(duì)技術(shù)有追求,對(duì)生活有激情的程序員。

          幾年間浪跡于多個(gè)一線互聯(lián)網(wǎng)大廠,具有多年開發(fā)實(shí)戰(zhàn)經(jīng)驗(yàn)。如果你熱愛技術(shù)或者你也不滿足現(xiàn)狀喜歡搞事情,那你不妨關(guān)注我,讓我們一路同行,一起分享技術(shù)干貨、交流面試技巧,吐槽職場(chǎng)故事。

          我有技術(shù)和故事,你來嗎?

          小giegie 們,三連支持一下a0714bff1749c01b588625890acc649a.webp

          瀏覽 40
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本中文字幕网站 | 神马午夜三级片 | 久久久久久久久久97 | 黄a片免费观看 | www.一区二区三区四区 |