1. 程式人生 > 實用技巧 >Java8新特性

Java8新特性

Java8新特性

一、介面的預設方法

在介面中新增了default方法和static方法,這兩種方法可以有方法體

1.1、static方法

介面中的static方法不能被繼承,也不能被實現類呼叫,只能被自身呼叫

示例程式碼:

static void staticMethod(){
    System.out.println("staticMethod方法");
}

1.2、default方法

default方法可以被子介面繼承也可被實現類呼叫

default void defaultMethod(){
    System.out.println("defaultMethod方法");
}
//實現類呼叫
public static void main(String[] args) {
    Test test = new Test();
    test.defaultMethod();	//呼叫default方法
}

default方法被繼承時,可以被子介面覆寫

//子介面覆寫
public interface TestNewInterfaceExtend extends TestNewInterface{
    @Override
    default void defaultMethod() {
        
    }
}

1.3、一個類實現了多個介面

如果一個類實現了多個介面

這些介面中無繼承關係

這些介面中若有相同的(同名,同參數)的default方法,則介面實現類會報錯

介面實現類必須通過特殊語法指定該實現類要實現那個介面的default方法
特殊語法:<介面>.super.<方法名>([引數])

TestNewInterface.super.defaultMethod();

二、Lambda 表示式

Lambda表示式可以看成是匿名內部類,使用Lambda表示式時,介面必須是函式式介面

2.1、語法

<函式式介面>  <變數名> = (引數1,引數2...) -> {
	//方法體
}

說明:

(引數1,引數2…)表示引數列表;->表示連線符;{}內部是方法體
1、=右邊的型別會根據左邊的函式式介面型別自動推斷;
2、如果形參列表為空,只需保留();
3、如果形參只有1個

()可以省略,只需要引數的名稱即可;
4、如果執行語句只有1句,且無返回值{}可以省略,若有返回值,則若想省去{},則必須同時省略return,且執行語句也保證只有1句
5、形參列表的資料型別自動推斷
6、lambda不會生成一個單獨的內部類檔案
7、lambda表示式若訪問了區域性變數,則區域性變數必須是final的,若是區域性變數沒有加final關鍵字,系統會自動新增,此後在修改該區域性變數,會報錯;

2.2、Lambda表示式其他特性

2.2.1、引用例項方法

語法:

<函式式介面>  <變數名> = <例項>::<例項方法名>
//呼叫
<變數名>.介面方法([實際引數...])
LambdaTest lt1 = s-> System.out.println(s);
lt1.print("原方式");
        
//改寫為:
LambdaTest lt2 = System.out::println;
lt2.print("例項引用方式");

將lt2呼叫時的實際引數傳遞給了PrintStream類中的println方法,並呼叫該方法

2.2.2、引用類方法

語法:

<函式式介面>  <變數名> = <類>::<類方法名稱>
//呼叫
<變數名>.介面方法([實際引數...])

現有陣列list,要給其排序:

LambdaTest lt = Collections::sort;
lt.sort(list, (a,b) -> {
    return a-b;
});

後面的(a,b)是Comparator介面的lambda表示式寫法

2.2.3、引用類的例項方法

定義、呼叫介面時,需要多傳遞一個引數,並且引數的型別與引用例項的型別一致
語法:

//定義介面
interface <函式式介面>{
    <返回值> <方法名>(<類><類名稱>,[其他引數...]); 
}
<函式式介面>  <變數名> = <類>::<類例項方法名>
//呼叫
<變數名>.介面方法(類的例項,[實際引數...])

將呼叫方法時的傳遞的實際引數,從第二個引數開始(第一個引數指定的類的例項),全部傳遞給引用的方法,執行引用的方法;

2.2.4、引用構造器方法

把方法的所有引數全部傳遞給引用的構造器,根據引數型別自動推斷呼叫的構造器方法;

語法:

<函式式介面>  <變數名> = <類>::<new>
//呼叫
<變數名>.介面方法([實際引數...])

根據傳入的引數型別,自動匹配建構函式

2.3、函式式介面

如果一個介面只有一個抽象方法,則該介面稱之為函式式介面,預設方法不算抽象方法
函式式介面可以使用Lambda表示式,lambda表示式會被匹配到這個抽象方法上

@FunctionalInterface 註解,這個介面多於一個抽象方法的時候會報錯的

示例程式碼:

@FunctionalInterface
interface MyFuncInterface<F, T> {
    T myMethod(F f);
}

2.4、Lambda 作用域

在lambda表示式中訪問外層作用域和老版本的匿名物件中的方式很相似。你可以直接訪問標記了final的外層區域性變數,或者例項的欄位以及靜態變數

2.5、四大函式式介面

2.5.1、Function函式式介面

public static void main(String[] args) {
    //Function<String, String> function = new Function<String, String>() {
    //    @Override
    //    public String apply(String s) {
    //        return s;
    //    }
    //};

    Function<String, String> function = (s)->{
        return s;
    };

    System.out.println(function.apply("AAA"));
}

2.5.2、斷定型介面

public static void main(String[] args) {
    //    Predicate<String> predicate = new Predicate<String>() {
    //        @Override
    //        public boolean test(String s) {
    //            return s.equals("AAA");
    //        }
    //    };
    Predicate<String> predicate = (s)->{
        return s.equals("AAA");
    };

    System.out.println(predicate.test("AAA"));
}

2.5.3、消費型介面

public static void main(String[] args) {
//    Consumer<String> consumer = new Consumer<String>() {
//        @Override
//        public void accept(String s) {
//            System.out.println(s);
//        }
//    };
    Consumer<String> consumer = (s) ->{
        System.out.println(s);
    };

    consumer.accept("AAA");
}

2.5.4、供給型介面

public static void main(String[] args) {
//    Supplier<String> supplier = new Supplier<String>() {
//        @Override
//        public String get() {
//            return "AAA";
//        }
//    };

    Supplier<String> supplier = ()->{
        return "AAA";
    };

    System.out.println(supplier.get());
}

三、Stream

3.1、什麼是Stream

Stream 不是集合元素,它不是資料結構並不儲存資料,它是有關演算法和計算的,它更像一個高階版本的Iterator。

3.2、建立stream方法

3.2.1、基本建立方法

// 1、of建立
Stream stream = Stream.of("a", "b", "c");

// 2、Arrays
String[] strArray = new String[]{"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);

// 3、Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
stream.forEach(System.out::println);

3.2.2、數值流的構造

對於基本數值型,目前有三種對應的包裝型別 Stream:IntStream、LongStream、DoubleStream。

3.2.3、流轉換為其他資料結構

// 3、流轉換為其他資料結構
Stream<String> stream1 = Stream.of(new String[]{"1", "2", "3"});
//        List<String> list1 = stream1.collect(Collectors.toList());
//        List<String> list2 = stream1.collect(Collectors.toCollection(ArrayList::new));
// 一個 Stream 只可以使用一次
String str = stream1.collect(Collectors.joining());
System.out.println(str);

3.3、Stream的操作型別

  • 中間操作(Intermediate Operation):一個流可以後面跟隨零個或多個 intermediate 操作。其目的主要是開啟流,做出某種程度的資料對映/過濾,然後返回一個新的流,交給下一個操作使用。這類操作都是惰性化的(lazy),就是說,僅僅呼叫到這類方法,並沒有真正開始流的遍歷。

    • 無狀態操作(Stateless Operation):操作是無狀態的,不需要知道集合中其他元素的狀態,每個元素之間是相互獨立的,比如map()、filter()等操作。

    • 有狀態操作(Stateful Operation):有狀態操作,操作是需要知道集合中其他元素的狀態才能進行的,比如sort()、distinct()。

  • 終止操作(Terminal Operation):一個流只能有一個 terminal 操作,當這個操作執行後,流就被使用“光”了,無法再被操作。所以這必定是流的最後一個操作。Terminal 操作的執行,才會真正開始流的遍歷,並且會生成一個結果。

    • 短路操作(short-circuiting):短路操作是指不需要處理完所有元素即可結束整個過程
    • 非短路操作(non-short-circuiting):非短路操作是需要處理完所有元素之後才能結束整個過程

3.4、stream的方法:

  1. count()、max()、min()方法

  2. Filter 過濾方法

    過濾通過一個predicate介面來過濾並只保留符合條件的元素,該操作屬於中間操作。

    list.stream()
        .filter(user -> user.getAge() % 2 == 0)
        .filter(user -> user.getAge() > 20)
    
  3. distinct方法

    去除重複

  4. Sort 排序

    排序是一箇中間操作,返回的是排序好後的Stream。如果你不指定一個自定義的Comparator則會使用預設排序。
    需要注意的是,排序只建立了一個排列好後的Stream,而不會影響原有的資料來源,排序之後原資料是不會被修改的:

  5. limit:

    對一個Stream進行截斷操作,獲取其前N個元素,如果原Stream中包含的元素個數小於N,那就獲取其所有的元素;

  6. skip:

    返回一個丟棄原Stream的前N個元素後剩下元素組成的新Stream,如果原Stream中包含的元素個數小於N,那麼返回空Stream;

  7. Match 匹配

    Stream提供了多種匹配操作,允許檢測指定的Predicate是否匹配整個Stream。所有的匹配操作都是最終操作,並返回一個boolean型別的值。

  8. Count 計數

    計數是一個最終操作,返回Stream中元素的個數,返回值型別是long。

四、Date API

Java 8通過釋出新的Date-Time API (JSR 310)來進一步加強對日期與時間的處理。對日期與時間的操作一直是Java程式設計師最痛苦的地方之一。標準的 java.util.Date以及後來的java.util.Calendar一點沒有改善這種情況(可以這麼說,它們一定程度上更加複雜)。
這種情況直接導致了Joda-Time——一個可替換標準日期/時間處理且功能非常強大的Java API的誕生。Java 8新的Date-Time API (JSR 310)在很大程度上受到Joda-Time的影響,並且吸取了其精髓。

4.1、LocalDate類

LocalDate date = LocalDate.now(); // 當前日期

date = date.plusDays(1); // 增加一天
date = date.plusMonths(1); // 增加一個月
System.out.println(date);
date = date.minusDays(1); // 減少一天
date = date.minusMonths(1); // 減少一個月
System.out.println(date);

/*
結果如下:
================
2020-08-23
2020-07-22
================
*/

4.2、LocalTime類

LocalTime time = LocalTime.now(); // 當前時間
time = time.plusHours(1);// 增加一小時
time = time.plusMinutes(1); // 增加一分鐘
time = time.plusSeconds(1); // 增加一秒
System.out.println(time);
time = time.minusHours(1);  //減小一小時
time = time.minusMinutes(1); // 減少一分鐘
time = time.minusSeconds(1); // 減少一秒
System.out.println(time);

/*
結果如下:
================
22:27:55.349
21:26:54.349
================
*/

4.3、LocalDateTime類

LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime); // UTC格式
System.out.println(
    localDateTime.format(
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
    )
); // 自定義格式
/*
結果如下:
================
2020-07-22T21:29:03.781
2020-07-22 21:29:03
================
*/

4.4、ZoneDateTime類

ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println(zonedDateTime);

ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
System.out.println(zonedDatetimeFromZone);

ZoneId zoneId = ZoneId.systemDefault();
System.out.println(zoneId);
/*
結果如下:
================
2020-07-22T21:37:26.233+08:00[Asia/Shanghai]
2020-07-22T06:37:26.235-07:00[America/Los_Angeles]
Asia/Shanghai
================
*/

4.5、Clock類

//它通過指定一個時區,然後就可以獲取到當前的時刻,日期與時間。
// Clock可以替換System.currentTimeMillis()與TimeZone.getDefault()

Clock utc = Clock.systemUTC(); // 世界標準時間
System.out.println(LocalDateTime.now(utc));

Clock shanghai = Clock.system(ZoneId.of("Asia/Shanghai")); // 上海時間
System.out.println(LocalDateTime.now(shanghai));
/*
結果如下:
================
2020-07-22T13:32:17.832
2020-07-22T21:32:17.895
================
*/

4.6、Duration類

計算兩個日期之間差的天數

LocalDateTime from = LocalDateTime.parse("2020-07-21 20:35:50", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
LocalDateTime to = LocalDateTime.parse("2020-07-22 21:35:50", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Duration duration = Duration.between(from, to);
System.out.println("Duration in days: " + duration.toDays());
System.out.println("Duration in hours: " + duration.toHours());

五、Annotation 註解

在Java 8中支援多重註解了