1、Java 8
2014 年 3 月 18 日,Oracle 發布了 Java 8,為目前唯二的 LTS(長期支持)版本之一,另一個是 Java 11,目前最新的版本為 Java 15
- Java 8 為目前使用最多的發行版本

更多:Java版本歷史、Jetbrains Java編程
2、介面默認實作與靜態方法
Java 8 之后可以為介面方法提供一個默認實作,用 default 修飾符來標記方法,這樣就可以只關心需要的方法,而不用去實作不需要的方法
- 在 JVM 中,介面的默認實作是非常高效的,并且通過位元組碼指令為方法呼叫提供了支持
interface Animal {
// 默認實作
default void cao() {
System.out.println("animal 艸");
}
void ri();
}
class Human implements Animal {
// 默認方法可以不進行重寫,非默認方法必須重寫
@Override
public void ri() {
System.out.println("human 日");
}
}
public class Test {
public static void main(String[] args) {
Human human = new Human();
// 不需要重寫,直接呼叫默認方法
human.cao();
human.ri();
}
}
2.1 默認方法沖突
如果在一個介面中定義了一個默認方法,又在超類或另一個介面中定義了同樣的方法,就會產生沖突
- 超類優先:如果超類提供了一個具體方法,同名且有相同引數型別的默認方法會被忽略
- 介面沖突:如果一個介面提供了一個默認方法,另一個介面提供了一個同名而且引數型別(不論是否是默認引數)相同的方法,必須覆寫這個方法來解決沖突
// 超類優先
interface Animal {
default void cao() {
System.out.println("animal 艸");
}
}
class Mammal {
public void cao() {
System.out.println("mammal 艸");
}
}
class Human extends Mammal implements Animal {}
public class Test {
public static void main(String[] args) {
Human human = new Human();
human.cao(); // mammal 艸
}
}
// 介面沖突
interface Animal {
default void cao() {
System.out.println("animal 艸");
}
}
interface Mammal {
default void cao() {
System.out.println("mammal 艸");
}
}
class Human implements Mammal, Animal {
// 必須重寫方法
@Override
public void cao() {
System.out.println("human 艸");
}
}
public class Test {
public static void main(String[] args) {
Human human = new Human();
human.cao();
}
}
2.2 介面中的靜態方法
public class Test {
public static void main(String[] args) {
Animal.cao();
}
}
interface Animal {
// 必須要有方法體
static void cao() {
System.out.println("animal 艸");
}
}
3、函式式介面
并不是所有的介面都可以使用 Lambda 運算式來實作,只有函式式介面才能寫成 Lambda 運算式
- 函式式介面:要求介面中定義的必須要實作的抽象方法只能有一個
// 該注解修飾函式式介面,即意味著介面中的抽象方法只能有一個,否則編譯器會報錯
// default方法和靜態方法不會造成任何影響
@FunctionalInterface
interface Animal {
void cao();
default void ri() {
System.out.println("日");
}
static void shit() {
System.out.println("shit");
}
}
4、Lambda 運算式
Lambda 運算式是 Java 8 添加的一個新特性,可以認為 Lambda 是一個匿名函式(相似于匿名內部類),作用是回傳一個實作了介面的物件
- Lambda 運算式是一個匿名函式,主要關注方法的引數串列和方法體
():描述引數串列{}:描述方法體->:Lambda 運算子,讀作goes to
// 無參無回傳介面
@FunctionalInterface
interface 無參無返 {
void test();
}
// 單參無回傳值介面
@FunctionalInterface
interface 單參無返 {
void test(int i);
}
// 多參無回傳值介面
@FunctionalInterface
interface 多參無返 {
void test(int a, int b);
}
// 無參有回傳值介面
@FunctionalInterface
interface 無參有返 {
int test();
}
// 單參有回傳值介面
@FunctionalInterface
interface 單參有返 {
int test(int i);
}
// 多參有回傳值介面
@FunctionalInterface
interface 多參有返 {
int test(int a, int b);
}
public class Test {
public static void main(String[] args) {
無參無返 l1 = () -> {
System.out.println("無參無回傳值");
};
l1.test();
單參無返 l2 = (int i) -> {
System.out.println("單參無回傳值");
};
l2.test(2);
多參無返 l3 = (int a, int b) -> {
System.out.println("多參無回傳值");
};
l3.test(3, 3);
無參有返 l4 = () -> {
System.out.println("無參有回傳值");
return 4;
};
int l4Result = l4.test();
單參有返 l5 = (int i) -> {
System.out.println("單參有回傳值");
return 5;
};
int l5Result = l5.test(5);
多參有返 l6 = (int a, int b) -> {
System.out.println("多參有回傳值");
return 6;
};
int l6Result = l6.test(6, 6);
}
}
4.1 Lambda 運算式語法精簡
4.1.1 引數型別的省略
由于在介面中已經定義了引數,所以在 Lambda 運算式中引數的型別可以省略
- 如果省略引數的型別,則所有的引數的型別都要省略
多參無返 l3 = (int a, int b) -> {
System.out.println("多參無回傳值");
};
// 省略引數型別
多參無返 l3 = (a, b) -> {
System.out.println("多參無回傳值");
};
4.1.2 引數小括號的省略
如果引數串列中,引數的個數有且只有一個,那么小括號可以省略,且仍然可以省略引數的型別
單參無返 l2 = (i) -> {
System.out.println("單參無回傳值");
};
// 省略小括號
單參無返 l2 = i -> {
System.out.println("單參無回傳值");
};
4.1.3 方法體大括號的省略
如果方法體只有一條陳述句,那么此時大括號可以省略
單參無返 l2 = i -> {
System.out.println("單參無回傳值");
};
// 省略大括號
單參無返 l2 = i -> System.out.println("單參無回傳值");
4.1.4 return 的省略
如果方法體 只有一條陳述句,且是回傳陳述句,可以省略 return,且必須要省略大括號
單參有返 l5 = i -> {
return 5;
};
單參有返 l5 = i -> 5;
4.2 方法參考
方法參考是 Lambda 運算式一種簡寫的方式,提供了一種參考而不執行方法的方式,用來直接訪問類或者實體的已經存在的方法或者構造方法,使用時方法參考會創建函式式介面的一個實體
- 回傳值的型別和引數串列要與介面中定義的一致
public class Test {
public static void main(String[] args) {
// 每次使用都實作相同的方法,則非常冗余
單參有返 l1 = i -> i * 2;
單參有返 l2 = i -> i * 2;
// 一般的方法呼叫:將Lambda運算式的實作指向change方法
單參有返 l3 = i -> Method.change(i);
單參有返 l4 = i -> new Method().change2(i);
// 方法參考:參考方法隸屬者的change方法
// 方法隸屬者:靜態方法隸屬者為類,非靜態方法的隸屬者是物件
單參有返 l5 = Method::change;
單參有返 l6 = new Method()::change2;
}
}
class Method {
static int change(int i) {
return i * 2;
}
int change2(int i) {
return i * 2;
}
}
4.2.1 構造方法的參考 類名::new
public class Test {
public static void main(String[] args) {
// 無參構造方法呼叫
HumanCreate1 hc1 = () -> new Human();
hc1.getHuman();
// 無參構造方法參考
HumanCreate1 hc1$ = Human::new;
hc1$.getHuman();
// 有參構造方法呼叫
HumanCreate2 hc2 = (String name, int age) -> new Human(name, age);
hc2.getHuman("特朗普", 18);
// 有參構造方法參考
HumanCreate2 hc2$ = Human::new;
hc2$.getHuman("特朗普", 18);
}
}
class Human {
String name;
int age;
Human() {
System.out.println("無參");
}
public Human(String name, int age) {
this.name = name;
this.age = age;
System.out.println("有參");
}
}
interface HumanCreate1 {
Human getHuman();
}
interface HumanCreate2 {
Human getHuman(String name, int age);
}
更多:Java-Lambda運算式和“方法參考”的對比和詳解
5、擴展注解
Java 8 擴展了注解的背景關系,現在幾乎可以在任何地方添加注解
public @Deprecated class Test {
@Retention( RetentionPolicy.RUNTIME )
@Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
public @interface NonEmpty {
}
public @Deprecated void show() {}
private void say(@NonEmpty String s) {}
void see() throws @NonEmpty Exception {
List<@NonEmpty String> list = new @NonEmpty ArrayList<>();
throw new @NonEmpty Exception();
}
}
6、重復注解
Java 8 引入了重復注解機制,在之前使用相同的注解在同一位置只能宣告一次,不能宣告多次
- 重復注解機制本身必須用
@Repeatable注解,其實底層原理并沒有改變,更多的是編譯器的技巧
public class Test {
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Filters {
Filter[] value();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
// 重復注解
@Repeatable(Filters.class)
public @interface Filter {
String value();
}
@Filter("filter1")
@Filter("filter2")
class filter {}
}
更多:Java 8新特性終極指南
7、Optional
Java 8 為了解決 Null 值判斷問題,受到 Google Guava 的啟發,引入了 Optional 類別庫,可以避免顯式的 Null 值判斷
- Optional 實際上是個容器,可以包裹 Null 或非 Null 的物件
7.1 創建 Optional 物件
// 1、創建一個空的Optional物件
Optional<String> op1 = Optional.empty();
// 2、創建一個物件不可為null的Optional物件,否則報空指標錯誤
Optional<String> op2 = Optional.of("Fuck");
// 3、創建一個物件可以為null的Optional物件
Optional<String> op3 = Optional.ofNullable(null);
7.2 常用方法
public class Test {
public static void main(String[] args) {
Person person = new Person("特朗普");
// get:回傳物件值,如果物件為null則拋出例外
Person op1 = Optional.of(person).get();
System.out.println("Optional: " + Optional.of(person));
System.out.println("get: " + op1);
// isPresent:判斷物件是否為空,如果物件為null則拋出例外
boolean op2 = Optional.of(person).isPresent();
System.out.println("isPresent: " + op2);
// ifPresent:如果物件不為空,則執行Lambda運算式,否則不做任何處理,沒有回傳值
Optional.of(person).ifPresent(p -> System.out.println("ifPresent: " + p.name));
// filter:如果物件值存在并且滿足條件,則回傳滿足條件的Optional,否則回傳empty
Optional<Person> op3 = Optional.of(person).filter(p -> p.name.length() > 0);
System.out.println("filter: " + op3);
// map:如果物件值存在則對其進行提取或轉換值,若結果不為空則回傳Optional,否則回傳empty
Optional<String> op4 = Optional.of(person).map(p -> "hello " + p.name);
System.out.println("map: " + op4);
// flatMap:功能類似map,需要先封裝成Optional
Optional<String> op5 = Optional.of(person).flatMap(p -> Optional.of(p.name));
System.out.println("flatMap: " + op5);
// orElse:如果物件值存在則回傳物件值,否則回傳傳入的值(默認值)
Person op6 = Optional.of(person).orElse(new Person("安倍晉三"));
System.out.println("orElse: " + op6);
// orElseGet:功能類似orElse,可以使用Lambda運算式
Person op7 = Optional.of(person).orElseGet(() -> new Person("安倍晉三"));
System.out.println("orElseGet: " + op7);
}
}
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "name=" + name;
}
}
Optional: Optional[name=特朗普]
get: name=特朗普
isPresent: true
ifPresent: 特朗普
filter: Optional[name=特朗普]
map: Optional[hello 特朗普]
flatMap: Optional[特朗普]
orElse: name=特朗普
orElseGet: name=特朗普
更多:理解、學習與使用 Java 中的 Optional
8、Stream
Java 8 引入 Stream 流,讓開發者能夠以宣告的方式處理資料源
-
Stream 操作分為中間操作或者最終操作兩種
- 終端操作會回傳一個結果
- 中間操作會回傳一個 Stream 流
-
Stream 的資料源只能 Collection 的子類,List 或者 Set,不支持 Map
-
Stream 的操作可以串行執行(Stream)或者并行執行(parallelStream)
8.1 常用方法
public class Test {
public static void main(String[] args) {
List<String> list = getList();
// forEach:回圈遍歷,沒有回傳值
list.stream().forEach(s -> System.out.print(s + " "));
// filter:過濾,回傳符合條件的結果
Stream<String> st1 = list.stream().filter(s -> s.length() > 2);
// sorted:排序,可以傳入Comparator進行自定義排序
Stream<String> st2 = list.stream().sorted();
// map:中間操作,將集合中的所有元素進行處理
Stream<Integer> st3 = list.stream().map(String::length);
// 匹配
// anyMatch:任意一個元素符合條件
boolean anyMatch = list.stream().anyMatch(s -> s.length() == 3);
// allMatch:全部都符合該條件
boolean allMatch = list.stream().allMatch(s -> s.length() > 0);
// noneMatch:全部都不符合該條件
boolean noneMatch = list.stream().noneMatch(s -> s.length() > 4);
// count:計數
long count = list.stream().count();
// reduce:將集合合并為一個值
Optional<String> optional = list.stream().reduce((s1, s2) -> s1 + " / " + s2);
// distinct:去重
list.stream().distinct();
// limit:回傳前n個元素
list.stream().limit(3);
// collect:集合轉換
// 轉Set
Set<String> set = list.stream().collect(Collectors.toSet());
// 轉List
List<String> strs = list.stream().collect(Collectors.toList());
// 轉Map
Map<String, Integer> map = list.stream().collect(Collectors.toMap(Function.identity(),String::length));
// Arrays.stream(T[] array):陣列轉Stream
Stream<String> st4 = Arrays.stream(new String[]{"one", "two"});
}
static List<String> getList() {
List<String> list = new ArrayList<>();
list.add("特朗普");
list.add("默克爾");
list.add("莫迪");
list.add("馬克龍");
list.add("文在寅");
list.add("普京");
list.add("約翰遜");
list.add("安倍晉三");
return list;
}
}
更多:【java8新特性】Stream API詳解
8.2 并行流
Stream 流支持串行和并行的,串行流操作是單執行緒操作,并行流是多執行緒操作,能夠充分利用物理機多核 CPU 的優勢,同時處理速度更快
// 使用并行流很簡單,將stream改為parallelStream即可
public class Test {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 5000000; i++) {
UUID uuid = UUID.randomUUID();
list.add(uuid.toString());
}
long beforeStream = System.currentTimeMillis();
System.out.println(list.stream().sorted().count());
System.out.println(System.currentTimeMillis() - beforeStream);
long beforeParallelStream = System.currentTimeMillis();
System.out.println(list.parallelStream().sorted().count());
System.out.println(System.currentTimeMillis() - beforeParallelStream);
}
}
5000000
4849
5000000
2188
- 可以看出,并行流的效率比串行流快了一倍左右
9、Date API
新的 Date API 除了是不可變類,執行緒安全之外,還多了添加和優化了許多方法,但原有的時間處理方法已經能解決大部分問題,除非考慮執行緒安全的因素,原有的時間 API 已經足夠使用了,另外新的 Date API 用法簡單,使用時百度即可,不做贅述,附上一個寫的不錯的文章
更多:Java 8 新的日期時間 API
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/108197.html
標籤:其他
下一篇:整合mybatis與spring
