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的方法:
-
count()、max()、min()方法
-
Filter 過濾方法
過濾通過一個predicate介面來過濾並只保留符合條件的元素,該操作屬於中間操作。
list.stream() .filter(user -> user.getAge() % 2 == 0) .filter(user -> user.getAge() > 20)
-
distinct方法
去除重複
-
Sort 排序
排序是一箇中間操作,返回的是排序好後的Stream。如果你不指定一個自定義的Comparator則會使用預設排序。
需要注意的是,排序只建立了一個排列好後的Stream,而不會影響原有的資料來源,排序之後原資料是不會被修改的: -
limit:
對一個Stream進行截斷操作,獲取其前N個元素,如果原Stream中包含的元素個數小於N,那就獲取其所有的元素;
-
skip:
返回一個丟棄原Stream的前N個元素後剩下元素組成的新Stream,如果原Stream中包含的元素個數小於N,那麼返回空Stream;
-
Match 匹配
Stream提供了多種匹配操作,允許檢測指定的Predicate是否匹配整個Stream。所有的匹配操作都是最終操作,並返回一個boolean型別的值。
-
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中支援多重註解了