java8 新特性精心整理(全)

来源:https://www.cnblogs.com/sanri1993/archive/2019/09/22/11569863.html
-Advertisement-
Play Games

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


前言

越來越多的項目已經使用 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.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 Files\Java\jdk1.8.0\jre\lib\rt.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

一點小推廣

創作不易,希望可以支持下我的開源軟體,及我的小工具,歡迎來 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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 除了基本的docker pull、docker image、docker ps,還有一些命令及參數也很重要,在此記錄下來避免遺忘。 環境信息 以下是本次操作的環境: 1. 操作系統:CentOS Linux release 7.7.1908 2. Docker:19.03.2 假設當前環境正運行著兩 ...
  • PHP開啟目錄引索 一. 前言 不知為何對nginx情有獨鍾, 最近練習php, 為了方便寫代碼, 便想要開啟nginx的目錄索引功能, 顯然不如Apache開啟的方便, 幾次嘗試都崩了... 我這個小白確實有點看不懂nginx的配置文件. 不過最後還是成功了, 記錄一下, 萬一哪天忘了, 回來看看 ...
  • 今天開始改變寫博客風格,其他不多說. 今天題目如下: 我先寫自己的寫程式的方法,先直接看正確完整的代碼直接往下看 一開始看了題目,我發現的規律是"alex"、"name"、"hobby"由多個變成一個 因此我想到了用set集合去重 我是想要把user_list列表的鍵收集起來變成列表,然後通過set ...
  • Spring Boot 項目中使用 JSP: 項目結構:需要添加webapp文件夾用來存放目錄 jsp 文件 在配置文件application.properties中指定 jsp 的位置和尾碼。spring.mvc.view.prefix=/WEB-INF/jsp/spring.mvc.view.s ...
  • 上篇文章 "SpringBoot自動裝配原理解析" 中,我們分析了SpringBoot的自動裝配原理以及 註解的原理,本篇文章則繼續基於上篇文章中的main方法來分析 這個類 點擊 方法一路跟蹤下來,發現首先做的是實例化 對象實例 1. 首先看一下 方法 大抵意思就是根據當前項目中是否存在上方的幾個 ...
  • 最近將萬方數據的爬取代碼進行了重構,速度大概有10w每小時吧,因為屬於公司項目,代碼暫時就不開源了,所以在這裡先說說思路和一些註意事項吧,順帶吐槽一下萬方。 先上圖: 其實邏輯也蠻簡單的,醫學類的期刊分了16個大類,那麼首先手動將這16大類所對應的唯一id拿下來拼接出該類型的url,然後翻頁請求它就 ...
  • 函數概述 qsort 為quick sort的簡寫,意為快速排序,主要用於對各種數組的排序,在頭文件stdlib.h中。 因為數組的元素可能是任何類型的,甚至是結構或者聯合,所以必須高數函數qsort如何確定兩個數組元素哪一個“更小”,這就需要我們給出比較的規則,即什麼算大,什麼算小。 通過編寫比較 ...
  • 在 Spring Cloud 微服務系統中,一種常見的負載均衡方式是,客戶端的請求首先經過負載均衡(Ngnix),再到達服務網關(Zuul 集群),然後再到具體的服務。服務統一註冊到高可用的服務註冊中心集群,服務的所有的配置文件由配置服務管理,配置服務的配置文件放在 GIT 倉庫,方便開發人員隨時改 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...