java8 新特性精心整理(全)

  • 2019 年 10 月 3 日
  • 筆記

前言

越來越多的項目已經使用 Java 8 了,毫無疑問,Java 8 是Java自Java 5(發佈於2004年)之後的最重要的版本。這個版本包含語言、編譯器、庫、工具和 JVM 等方面的十多個新特性。在本文中我們將學習這些新特性,並用實際的例子說明在什麼場景下適合使用。

引用:本文參考了這兩篇文章,加以自己的理解,整理成一份最容易理解的 Java8 新特性文章,有少部分章節可能內容一致,但絕對不是抄襲,只是為了文章的完整性,大部分常用的地方加了我自己的理解和示例。

https://blog.csdn.net/yczz/article/details/50896975

https://blog.csdn.net/maosijunzi/article/details/38658095

適合讀者及目標

目標人群

  • 適合有用過 lambda 表達式的同學,想徹底了解清楚
  • 學習 Java8 的新特定

目標

  • 了解 java8 的函數式介面和 Lambda 表達式
  • 方法引用的使用
  • 介面的靜態方法和默認方法
  • Date/Time Api 的使用
  • Stream API 的使用

1. Java 語言的新特性

Java8 的 lambda 的使用確實方便了許多,但也使初次了解的人感覺到難以閱讀,其實是你不習慣的原因。很多語言從一開始就支援了 Lambda 表達式,像 Groovy,Scala 等。

1.1 Lambda 表達式和函數式介面

在 Java8 以前,我們想要讓一個方法可以與用戶進行交互,比如說使用方法內的局部變數;這時候就只能使用介面做為參數,讓用戶實現這個介面或使用匿名內部類的形式,把局部變數通過介面方法傳給用戶。

傳統匿名內部類缺點:程式碼臃腫,難以閱讀

Lambda 表達式

Lambda 表達式將函數當成參數傳遞給某個方法,或者把程式碼本身當作數據處理;

語法格式:

  • 用逗號分隔的參數列表
  • -> 符號
  • 和 語句塊 組成
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );

等價於

List<String> list = Arrays.asList( "a", "b", "d" );  for(String e:list){      System.out.println(e);  }

如果語句塊比較複雜,使用 {} 包起來

Arrays.asList( "a", "b", "d" ).forEach( e -> {      String m = "9420 "+e;      System.out.print( m );  });

Lambda 本質上是匿名內部類的改裝,所以它使用到的變數都會隱式的轉成 final

String separator = ",";  Arrays.asList( "a", "b", "d" ).forEach(      e -> System.out.print( e + separator ) );

等價於

final String separator = ",";  Arrays.asList( "a", "b", "d" ).forEach(      e -> System.out.print( e + separator ) );

Lambda 的返回值和參數類型由編譯器推理得出,不需要顯示定義,如果只有一行程式碼可以不寫 return 語句

Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );

等價於

List<String> list = Arrays.asList("a", "b", "c");  Collections.sort(list, new Comparator<String>() {      @Override      public int compare(String o1, String o2) {          return o1.compareTo(o2);      }  });

函數式介面

  • 介面中只能有一個介面方法
  • 可以有靜態方法和默認方法
  • 使用 @FunctionalInterface 標記
  • 默認方法可以被覆寫
@FunctionalInterface  public interface FunctionalDefaultMethods {      void method();        default void defaultMethod() {      }        static void staticMethod(){      }  }
private interface Defaulable {      // Interfaces now allow default methods, the implementer may or      // may not implement (override) them.      default String notRequired() {          return "Default implementation";      }  }    private static class DefaultableImpl implements Defaulable {  }    private static class OverridableImpl implements Defaulable {      @Override      public String notRequired() {          return "Overridden implementation";      }  }    // 也可以由介面覆蓋  public interface OverridableInterface extends Defaulable{      @Override      public String notRequired() {          return "interface Overridden implementation";      }  }

由於JVM上的默認方法的實現在位元組碼層面提供了支援,因此效率非常高。默認方法允許在不打破現有繼承體系的基礎上改進介面。該特性在官方庫中的應用是:給 java.util.Collection介面添加新方法,如 stream()parallelStream()forEach()removeIf() 等等。

已經存在的 Java8 定義的函數式介面

我們基本不需要定義自己的函數式介面,Java8 已經給我們提供了大量的默認函數式介面,基本夠用,在 rt.jar 包的 java.util.function 目錄下可以看到所有默認的函數式介面,大致分為幾類

  • Function<T,R> T 作為輸入,返回的 R 作為輸出
  • Predicate<T> T 作為輸入 ,返回 boolean 值的輸出
  • Consumer<T> T 作為輸入 ,沒有輸出
  • Supplier<R> 沒有輸入 , R 作為輸出
  • BinaryOperator<T> 兩個 T 作為輸入 ,T 同樣是輸出
  • UnaryOperator<T>Function 的變種 ,輸入輸出者是 T

其它的都是上面幾種的各種擴展,只為更方便的使用,下面演示示例,你可以把其當成正常的介面使用,由用戶使用 Lambda 傳入。

// hello world 示例  Function<String,String> function = (x) -> {return x+"Function";};  System.out.println(function.apply("hello world"));  // hello world Function    UnaryOperator<String> unaryOperator = x -> x + 2;  System.out.println(unaryOperator.apply("9420-"));   // 9420-2    // 判斷輸入值是否為偶數示例  Predicate<Integer> predicate = (x) ->{return x % 2 == 0 ;};  System.out.println(predicate.test(1));              // false    // 這個沒有返回值  Consumer<String> consumer = (x) -> {System.out.println(x);};  consumer.accept("hello world ");                    // hello world    // 這個沒有輸入  Supplier<String> supplier = () -> {return "Supplier";};  System.out.println(supplier.get());                 // Supplier    // 找出大數  BinaryOperator<Integer> bina = (x, y) ->{return x > y ? x : y;};  bina.apply(1,2);                                    // 2 

1.2 方法引用

方法引用使得開發者可以直接引用現存的方法、Java類的構造方法或者實例對象。方法引用和Lambda表達式配合使用,使得java類的構造方法看起來緊湊而簡潔,沒有很多複雜的模板程式碼。

public static class Car {      public static Car create( final Supplier< Car > supplier ) {          return supplier.get();      }        public static void collide( final Car car ) {          System.out.println( "Collided " + car.toString() );      }        public void follow( final Car another ) {          System.out.println( "Following the " + another.toString() );      }        public void repair() {          System.out.println( "Repaired " + this.toString() );      }  }

第一種方法引用的類型是構造器引用,語法是Class::new,或者更一般的形式:Class::new。注意:這個構造器沒有參數。

final Car car = Car.create( Car::new );

等價於

Car car = Car.create(() -> new Car());

第二種方法引用的類型是靜態方法引用,語法是Class::static_method。注意:這個方法接受一個Car類型的參數。

cars.forEach( Car::collide );

forEach 原型為 forEach(Consumer<? super T> action) 使用的是 Consumer 只有參數,沒有返回值;這個參數 T 就是 car 類型,因為是 cars.forEach 嘛,所以上面的方法引用等價於

cars.forEach(car -> Car.collide(car));

第三種方法引用的類型是某個類的成員方法的引用,語法是Class::method,注意,這個方法沒有定義入參:

cars.forEach( Car::repair );

它等價於

cars.forEach(car -> car.repair());

1.3 重複註解

自從Java 5中引入註解以來,這個特性開始變得非常流行,並在各個框架和項目中被廣泛使用。不過,註解有一個很大的限制是:在同一個地方不能多次使用同一個註解。Java 8打破了這個限制,引入了重複註解的概念,允許在同一個地方多次使用同一個註解。

在Java 8中使用 @Repeatable 註解定義重複註解,實際上,這並不是語言層面的改進,而是編譯器做的一個trick,底層的技術仍然相同。可以利用下面的程式碼說明:

@Target( ElementType.TYPE )  @Retention( RetentionPolicy.RUNTIME )  @Repeatable( Filters.class )  public @interface Filter {      String value();  };    @Filter( "filter1" )  @Filter( "filter2" )  public interface Filterable {  }    public static void main(String[] args) {      for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {          System.out.println( filter.value() );      }  }    

正如我們所見,這裡的Filter類使用 @Repeatable(Filters.class) 註解修飾,而Filters是存放Filter註解的容器,編譯器盡量對開發者屏蔽這些細節。這樣,Filterable介面可以用兩個Filter註解注釋(這裡並沒有提到任何關於Filters的資訊)。

另外,反射API提供了一個新的方法:getAnnotationsByType(),可以返回某個類型的重複註解,例如Filterable.class.getAnnoation(Filters.class)將返回兩個Filter實例。

1.4 更好的類型推斷

Java 8編譯器在類型推斷方面有很大的提升,在很多場景下編譯器可以推導出某個參數的數據類型,從而使得程式碼更為簡潔。例子程式碼如下:

public class Value< T > {      public static< T > T defaultValue() {          return null;      }        public T getOrDefault( T value, T defaultValue ) {          return ( value != null ) ? value : defaultValue;      }  }
public class TypeInference {      public static void main(String[] args) {          final Value< String > value = new Value<>();          value.getOrDefault( "22", Value.defaultValue() );      }  }

參數 Value.defaultValue() 的類型由編譯器推導得出,不需要顯式指明。在Java 7中這段程式碼會有編譯錯誤,除非使用Value.<String>defaultValue()

1.5 拓寬註解的應用場景

Java 8拓寬了註解的應用場景。現在,註解幾乎可以使用在任何元素上:局部變數、介面類型、超類和介面實現類,甚至可以用在函數的異常定義上。下面是一些例子:

package com.javacodegeeks.java8.annotations;    import java.lang.annotation.ElementType;  import java.lang.annotation.Retention;  import java.lang.annotation.RetentionPolicy;  import java.lang.annotation.Target;  import java.util.ArrayList;  import java.util.Collection;    public class Annotations {      @Retention( RetentionPolicy.RUNTIME )      @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )      public @interface NonEmpty {      }        public static class Holder< @NonEmpty T > extends @NonEmpty Object {          public void method() throws @NonEmpty Exception {          }      }        @SuppressWarnings( "unused" )      public static void main(String[] args) {          final Holder< String > holder = new @NonEmpty Holder< String >();          @NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();      }  }

ElementType.TYPE_USER ElementType.TYPE_PARAMETER 是Java 8新增的兩個註解,用於描述註解的使用場景。Java 語言也做了對應的改變,以識別這些新增的註解。

2. Java 編譯器的新特性

Java 8 開始正式支援參數名稱,終於不需要讀 class 位元組碼來獲取參數名稱了,這對於經常使用反射的人特別有用。

在 Java8 這個特性默認是關閉的,需要開啟參數才能獲取參數名稱:

<plugin>      <groupId>org.apache.maven.plugins</groupId>      <artifactId>maven-compiler-plugin</artifactId>      <version>3.1</version>      <configuration>          <compilerArgument>-parameters</compilerArgument>          <source>1.8</source>          <target>1.8</target>      </configuration>  </plugin>

3. JVM 的新特性

使用MetaspaceJEP 122)代替持久代(PermGen space)。在JVM參數方面,使用-XX:MetaSpaceSize-XX:MaxMetaspaceSize代替原來的-XX:PermSize-XX:MaxPermSize

4. Java 官方庫的新特性

Java 8增加了很多新的工具類(date/time類),並擴展了現存的工具類,以支援現代的並發編程、函數式編程等,本章節參考原文,並提取出常用功能。

4.1 Streams

Streams 操作分為中間操作和晚期操作,中間操作會返回一個新的 Stream ,只是把要做的操作記錄起來而已,並不會真的執行,晚期操作才會真的遍歷列表並執行所有操作

Stream 的另一個價值就是支援了並行處理 parallel 方法。

Stream API 簡化了集合的操作,並擴展了集合的分組,求和,mapReduce,flatMap ,排序等功能,下面列出項目中經常用到的功能,會以使用頻率排序。

  1. 準備一個用於下面例子測試的對象
import lombok.AllArgsConstructor;  import lombok.Data;  import lombok.NoArgsConstructor;    @Data  @NoArgsConstructor  @AllArgsConstructor  public class Vehicle {      //車架號      private String vin;      // 車主手機號      private String phone;      // 車主姓名      private String name;      // 所屬車租車公司      private Integer companyId;      // 個人評分      private Double score;      //安裝的設備列表imei,使用逗號分隔      private String deviceNos;  }
  1. 準備一些車輛數據
static List<Vehicle> vehicles = new ArrayList<>();    @Before  public void init(){      List<String> imeis = new ArrayList<>();      for (int i = 0; i <5 ; i++) {          List<String> singleVehicleDevices = new ArrayList<>();          for (int j = 0; j < 3; j++) {              String imei = RandomStringUtils.randomAlphanumeric(15);              singleVehicleDevices.add(imei);          }          imeis.add(StringUtils.join(singleVehicleDevices,','));      }      vehicles.add(new Vehicle("KPTSOA1K67P081452","17620411498","9420",1,4.5,imeis.get(0)));      vehicles.add(new Vehicle("KPTCOB1K18P057071","15073030945","張玲",2,1.4,imeis.get(1)));      vehicles.add(new Vehicle("KPTS0A1K87P080237","19645871598","sanri1993",1,3.0,imeis.get(2)));      vehicles.add(new Vehicle("KNAJC526975740490","15879146974","李種",1,3.9,imeis.get(3)));      vehicles.add(new Vehicle("KNAJC521395884849","13520184976","袁紹",2,4.9,imeis.get(4)));  }

4.1.1 forEach 遍歷Collection 數據

vehicles.forEach(vehicle -> System.out.println(vehicle));    //這樣就可以遍歷列印  vehicles.forEach(System.out::println);

4.1.2 forEach 遍歷 Map 數據

Map<String,Integer> map = new HashMap<>();  map.put("a",1);map.put("b",2);map.put("c",3);    map.forEach((k,v) -> System.out.println("key:"+k+",value:"+v));

4.1.3 filter 數據過濾

// 去掉評分為 3 分以下的車  List<Vehicle> collect = vehicles.stream().filter(vehicle -> vehicle.getScore() >= 3).collect(Collectors.toList());

4.1.4 map 對象映射

對一個 List<Object> 大部分情況下,我們只需要列表中的某一列,或者需要把裡面的每一個對象轉換成其它的對象,這時候可以使用 map 映射,示例:

// 取出所有的車架號列表   List<String> vins = vehicles.stream().map(Vehicle::getVin).collect(Collectors.toList());

4.1.5 groupBy 按照某個屬性進行分組

// 按照公司 Id 進行分組  Map<Integer, List<Vehicle>> companyVehicles = vehicles.stream().collect(Collectors.groupingBy(Vehicle::getCompanyId));    // 按照公司分組求司機打分和  Map<Integer, Double> collect = vehicles.stream().collect(Collectors.groupingBy(Vehicle::getCompanyId, Collectors.summingDouble(Vehicle::getScore)));

4.1.6 sort 按照某個屬性排序 ,及多列排序

// 單列排序  vehicles.sort((v1,v2) -> v2.getScore().compareTo(v1.getScore()));    // 或使用 Comparator 類來構建比較器,流處理不會改變原列表,需要接收返回值才能得到預期結果   List<Vehicle> collect = vehicles.stream().sorted(Comparator.comparing(Vehicle::getScore).reversed()).collect(Collectors.toList());    // 多列排序,score 降序,companyId 升序排列  List<Vehicle> collect = vehicles.stream().sorted(Comparator.comparing(Vehicle::getScore).reversed()                  .thenComparing(Comparator.comparing(Vehicle::getCompanyId)))                  .collect(Collectors.toList());

4.1.7 flatMap 扁平化數據處理

// 查出所有車綁定的所有設備  List<String> collect = vehicles.stream().map(vehicle -> {      String deviceNos = vehicle.getDeviceNos();      return StringUtils.split(deviceNos,',');  }).flatMap(Arrays::stream).collect(Collectors.toList());

flatMap 很適合 List<List>List<object []> 這種結構,可以當成一個列表來處理;像上面的設備列表,在資料庫中存儲的結構就是以逗號分隔的數據,而車輛列表又是一個列表數據。

4.1.8 mapReduce 數據處理

// 對所有司機的總分求和  Double reduce = vehicles.stream().parallel().map(Vehicle::getScore).reduce(0d, Double::sum);

4.1.9 綜合處理示例

// 總的分值  Double totalScore = vehicles.stream().parallel().map(Vehicle::getScore).reduce(0d, Double::sum);    // 查看每一個司機占的分值比重  List<String> collect = vehicles.stream()      .mapToDouble(vehicle -> vehicle.getScore() / totalScore)      .mapToLong(weight -> (long) (weight * 100))      .mapToObj(percentage -> percentage + "%")      .collect(Collectors.toList());

原文的 boxed 不知道是什麼意思,希望有大神能幫忙解答下,不用 boxed 也是可以的

4.2 Optional

Optional 用來解決 Java 中經常出現的 NullPointerException ,從而避免源碼被各種空檢查污染,使源碼更加簡潔和更加容易閱讀

// 假設有一個對象 obj ,你不知道它是不是為空的,但是你想用它的方法,可以這麼玩  Optional<T> canUseObj = Optional.ofNullable(obj);  canUseObj.ifPresent(System.out::println);       //如果 obj 不為空,則可以使用 obj 的方法,這裡做個簡單輸出 

4.3 Date/Time API(JSR 310)

新的日期時間工具全部都在 java.time 及其子包中。

4.3.1 新 Date/Time API 設計原則

Java 8日期/時間API是 JSR-310 規範的實現,它的目標是克服舊的日期/時間API實現中所有的缺陷,新的日期/時間API的一些設計原則如下:

  • 不變性:新的日期/時間API中,所有的類都是不可變的,這種設計有利於並發編程。
  • 關注點分離:新的API將人可讀的日期時間和機器時間(unix timestamp)明確分離,它為日期(Date)、時間(Time)、日期時間(DateTime)、時間戳(unix timestamp)以及時區定義了不同的類。
  • 清晰:在所有的類中,方法都被明確定義用以完成相同的行為。舉個例子,要拿到當前實例我們可以使用now()方法,在所有的類中都定義了format()和parse()方法,而不是像以前那樣專門有一個獨立的類。為了更好的處理問題,所有的類都使用了工廠模式和策略模式,一旦你使用了其中某個類的方法,與其他類協同工作並不困難。
  • 實用操作:所有新的日期/時間API類都實現了一系列方法用以完成通用的任務,如:加、減、格式化、解析、從日期/時間中提取單獨部分等操作。
  • 可擴展性:新的日期/時間API是工作在ISO-8601日曆系統上的,但我們也可以將其應用在非IOS的日曆上。

4.3.2 常用類及其使用

時間大致可以分為三個部分:日期、時間、時區

其中日期又細分為年、月、日;時間又細分為時、分、秒

一般機器時間用從 1970-01-01T00:00 到現在的秒數來表示時間; 這裡糾正大部分人犯的一個錯誤概念,時間戳指的是秒數,而不是毫秒數。

幾乎所有的時間對象都實現了 Temporal 介面,所以介面參數一般都是 Temporal

  • Instant: 表示時間線上的一個點,參考點是標準的Java紀元(epoch),即1970-01-01T00:00:00Z(1970年1月1日00:00 GMT)

  • LocalDate: 日期值對象如 2019-09-22

  • LocalTime:時間值對象如 21:25:36

  • LocalDateTime:日期+時間值對象

  • ZoneId:時區

  • ZonedDateTime:日期+時間+時區值對象

  • DateTimeFormatter:用於日期時間的格式化

  • Period:用於計算日期間隔

  • Duration:用於計算時間間隔

4.3.2.1 Instant 表示時間線上的一個點(瞬時)
// 測試執行一個 new 操作使用的時間(納秒值)  Instant begin = Instant.now();  StreamMain streamMain = new StreamMain();  Instant end = Instant.now();  System.out.println(Duration.between(begin,end).toNanos());
4.3.2.2 LocalDateLocalTimeLocalDateTimeZonedDateTime 可以規為一組,用於表示時間的
// 可以使用 of 方法構建它們的實例,如下面創建了一個 2019-9-22 21:42:59 東八區 的時間對象  LocalDate localDate = LocalDate.of(2019, Month.SEPTEMBER, 22);  LocalTime localTime = LocalTime.of(21, 42, 59);  LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);  ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.systemDefault());    // 獲取現在的時間,這是一個靜態方法  LocalDate now = LocalDate.now();    // 每個實例可以獲取它們的 part 資訊,如獲取年  int year = localDate.getYear();    // 可以修改 part 資訊,這將返回一個新對象,如增加一年  LocalDate localDatePlus = localDate.plusYears(1);    // 設置 part 資訊,也會返回新的對象,如設置為 2017 年  LocalDate localDateWithYear = localDate.withYear(2017);    // 比較兩個日期 isAfter,isBefore  boolean after = localDate.isAfter(LocalDate.now());    // 格式化日期時間  // yyyy-MM-dd  System.out.println(now.format(DateTimeFormatter.ISO_DATE));  // yyyy-MM-ddTHH:mm:ss  System.out.println(now.format(DateTimeFormatter.ISO_DATE_TIME));  // yyyy-MM-dd HH:mm:ss  System.out.println(now.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)));    // 日期解析  System.out.println(LocalDate.parse("2019-09-22"));  System.out.println(LocalDateTime.parse("2019-09-22T21:05:22"));  System.out.println(LocalDateTime.parse("2019-09-22 21:05:22",DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)));
4.3.2.3 ZoneId 用來操作時區,它提供了獲取所有時區和本地時區的方法
ZoneId zoneId = ZoneId.systemDefault();  Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
4.3.2.4 PeriodDuration 可以視為一組,用於計算時間間隔
// 創建一個兩周的間隔  Period periodWeeks = Period.ofWeeks(2);    // 一年三個月零二天的間隔  Period custom = Period.of(1, 3, 2);    // 一天的時長  Duration duration = Duration.ofDays(1);    // 計算2015/6/16 號到現在 2019/09/22 過了多久,它這個把間隔分到每個 part 了  LocalDate now = LocalDate.now();  LocalDate customDate = LocalDate.of(2015, 6, 16);  Period between = Period.between(customDate, now);  // 結果為 4:3:6 即過去了 4年3個月6天了  System.out.println(between.getYears()+":"+between.getMonths()+":"+between.getDays());    // 比較兩個瞬時的時間間隔  Instant begin = Instant.now();  Instant end = Instant.now();  Duration.between(begin,end);    // 同樣可以修改 part 資訊和設置 part 資訊,都是返回新的對象來表示設置過的值,原來的對象不變  Period plusDays = between.plusDays(1);  Period withDays = between.withDays(4);  

4.3.3 與 Date,Calendar 的轉換

雖然說,這個新的時間工具很好用,但如果不能與以前的舊的 api 兼容的話,一樣是沒有用的;還好新的工具類能很好的與以前的工具類進行相互轉換。

通過 Instant做中間轉換實現DateCalendarLocalDateTimeZonedDateTimeLocalDate 的互相轉換

// LocalDateTime 轉 Date  Date localDateTimeDate = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());  // LocalDateTime 轉 Calendar  Calendar localDateTimeCalendar = GregorianCalendar.from(ZonedDateTime.of(localDateTime, ZoneId.systemDefault()));    // Date 轉 LocalDateTime  LocalDateTime dateLocalDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());  // Calendar 轉 LocalDateTime  LocalDateTime calendarLocalDateTime = LocalDateTime.ofInstant(calendar.toInstant(), ZoneOffset.systemDefault());

4.4 Base64

對於 Base64 終於不用引用第三方包了,使用 java 庫就可以完成

// 編碼  final String encoded = Base64.getEncoder().encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );  // 解碼  final String decoded = new String( Base64.getDecoder().decode( encoded ),StandardCharsets.UTF_8 );

4.5 JUC 工具包擴充

基於新增的lambda表達式和steam特性,為Java 8中為java.util.concurrent.ConcurrentHashMap類添加了新的方法來支援聚焦操作;另外,也為java.util.concurrentForkJoinPool類添加了新的方法來支援通用執行緒池操作(更多內容可以參考我們的並發編程課程)。

Java 8還添加了新的java.util.concurrent.locks.StampedLock類,用於支援基於容量的鎖——該鎖有三個模型用於支援讀寫操作(可以把這個鎖當做是java.util.concurrent.locks.ReadWriteLock的替代者)。

java.util.concurrent.atomic包中也新增了不少工具類,列舉如下:

  • DoubleAccumulator
  • DoubleAdder
  • LongAccumulator
  • LongAdder

5. 新的工具

Java 8提供了一些新的命令行工具,這部分會講解一些對開發者最有用的工具。

5.1 類依賴分析器:jdeps

deps是一個相當棒的命令行工具,它可以展示包層級和類層級的Java類依賴關係,它以.class文件、目錄或者Jar文件為輸入,然後會把依賴關係輸出到控制台。

我們可以利用jedps分析下Spring Framework庫,為了讓結果少一點,僅僅分析一個JAR文件:org.springframework.core-3.0.5.RELEASE.jar

jdeps org.springframework.core-3.0.5.RELEASE.jar

這個命令會輸出很多結果,我們僅看下其中的一部分:依賴關係按照包分組,如果在classpath上找不到依賴,則顯示"not found".

org.springframework.core-3.0.5.RELEASE.jar -> C:Program FilesJavajdk1.8.0jrelibrt.jar     org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)        -> java.io        -> java.lang        -> java.lang.annotation        -> java.lang.ref        -> java.lang.reflect        -> java.util        -> java.util.concurrent        -> org.apache.commons.logging                         not found        -> org.springframework.asm                            not found        -> org.springframework.asm.commons                    not found     org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)        -> java.lang        -> java.lang.annotation        -> java.lang.reflect        -> java.util

相關源碼位置

https://gitee.com/sanri/example/tree/master/testjava8

一點小推廣

創作不易,希望可以支援下我的開源軟體,及我的小工具,歡迎來 gitee 點星,fork ,提 bug 。

Excel 通用導入導出,支援 Excel 公式
部落格地址:https://blog.csdn.net/sanri1993/article/details/100601578
gitee:https://gitee.com/sanri/sanri-excel-poi

使用模板程式碼 ,從資料庫生成程式碼 ,及一些項目中經常可以用到的小工具
部落格地址:https://blog.csdn.net/sanri1993/article/details/98664034
gitee:https://gitee.com/sanri/sanri-tools-maven