Java8的新特性:
JAVA 8簡介:
Java8(又稱為jdk 1.8)是Java語言開發的一個主要版本,
Java8 是 Oracle公司于2014年3月發布,可以看成是自Java5以來最具革命性的版本,
Java8為Java語言、編譯器、類別庫、開發工具與JVM帶來了大量的新特性,
一、Java的新特性有哪些:

1.2 Java8新特性的作用:
- 速度更快
- 代碼更少(增加了新的語法:Lambda運算式 )
- 強大的 Stream API
- 便于并行
- 最大化減少空指標例外 :Optional
- Nashorn引擎,允許在JVM運行JS應用
1.1 并行流與串行流
并行流就是把一個內容分成多個資料塊,并用不同的執行緒分別處理每個資料塊流,相比較串行的流,并行的流可以很大程度上提高程式的執行效率,
Java 8 中將并行進行了優化,我們可以很容易的對資料進行并行操作,
Stream API 可以宣告性的通過 parallel() 與 sequential() 在并行流與順序流之間進行切換,
二、Lambda 運算式
2.1 為什么使用Lambda運算式?
Lambda 是一個匿名函式,我們可以把 Lambda 運算式理解為是一段可以傳遞的代碼 (將代碼像資料一樣進行傳遞) ,
使用它可以寫出更簡潔、更靈活的代碼,作為一種更緊湊的代碼風格,使Java的語言表達能力得到了提升,
2.2 Lambda運算式的使用
舉例:
(o1,o2) -> Integer.compare(o1,o2);
格式:
->:Lambda運算子 或 箭頭運算子
->:左邊:Lambda形參串列 (其實就是介面中的抽象方法的形參串列)
->:右邊:Lambda體 (其實就是重寫的抽象方法的方法體)
2.2.1 Lambda運算式的使用(分為六種情況)
① 語法格式一: 無參,無回傳值
Runnable r1 = () ->{System.out.println("Hello wxy");};
實體:Runnable介面
public class LambdaTest1 {
//語法格式一:無參,無回傳值
@Test
public void test1() {
Runnable r1 = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("無法停止轉動的地球人");
}
};
r1.run();
System.out.println("********Lambda的寫法*********");
Runnable r2 = () -> System.out.println("無法停止轉動的地球人");
r2.run();
}
②語法格式二:Lambda需要一個引數,但是沒有回傳值
Consumer<String> con = (String str) -> {System.out.println(str);};
實體:Consumer介面
//語法格式二:Lambda需要一個引數,但是沒有回傳值
public void test2() {
Consumer<String> con = new Consumer<String>(){
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("謊言和誓言的區別是什么呢?");
System.out.println("********Lambda的寫法*********");
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("一個是聽的人當真了,一個是說的人當真了");
}
③語法格式三:資料型別可省略,因為可由編譯器推斷得出,稱為型別推斷
Consumer<String> con = (str) -> {System.out.println(str)}
代碼示例:
//語法格式三:資料型別可以省略,因為可由編譯器推斷得出,稱為“型別推斷”
@Test
public void test3() {
//未使用Lambda運算式
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("你好啊Lambda!");
System.out.println("====================");
//使用Lambda運算式
Consumer<String> con1 = (s) -> {
System.out.println(s);
};
con1.accept("我是Lambda");
}
④語法格式四:Lambda 若只需要一個引數時,引數的小括號可以省略
Consumer<String> con = str -> {System.out.println(str)}
代碼示例:
//語法格式四:Lambda 若只需要一個引數時,引數的小括號可以省略
@Test
public void test4() {
//未使用Lambda運算式
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("你好啊Lambda!");
System.out.println("====================");
//使用Lambda運算式
Consumer<String> con1 = s -> {
System.out.println(s);
};
con1.accept("我是Lambda");
}
⑤語法格式五:Lambda 需要兩個或以上的引數,多條執行陳述句,并且可以有回傳值
@Test
public void test5() {
//未使用Lambda運算式
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return Integer.compare(o1, o2);
}
};
System.out.println(com1.compare(23, 45));
System.out.println("====================");
//使用Lambda運算式
Comparator<Integer> com2 = (o1, o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(23, 12));
}
⑥語法格式六:當Lamdba體只有一條陳述句時,return和大括號若有,都可以省略
Comparator<Integer>com = (o1,o1) -> Integer.compare(o1,o2);
語法格式:
//當Lamdba體只有一條陳述句時,return和大括號若有,都可以省略
@Test
public void test6(){
Comparator<Integer> com1 = (o1,o2) ->{
return o1.compareTo(o2);
};
System.out.println(com1.compare(13, 12));
System.out.println("********Lambda運算式***********");
Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
System.out.println(com2.compare(12, 21));
}
@Test
public void test7(){
Consumer<String> con2 = s -> {
System.out.println(s);
};
con2.accept("一個是聽得人當真了,一個是說的人當真了");
}
}
2.3 Lambda運算式使用總結
->左邊:Lambda形參串列的引數型別可以省略(型別推斷);如果Lambda形參串列只有一個引數,其一對 ( ) 也可以省略,->右邊:Lambda體應該使用一對{ }包裹;如果Lambda體只有一條執行陳述句(可能是return陳述句),可以省略這一對 { } 和 return關鍵字,
2.4 Lambda運算式總結
- Lambda運算式的本質:作為函式式介面的實體
- 當你需要使用函式式介面寫一個匿名實作類的物件的時候你可以用Lambda運算式
- 如果一個介面中, 只宣告了一個抽象方法,則此介面就稱為函式式介面,我們可以在一個介面上可以在一個介面上用
@FunctionalInterface注解, 這樣做可以檢查它是否是一個函式式介面, - 所以以前用匿名實作類表達的現在都可以用Lambda運算式
三、函式式介面
3.1 函式式介面概述
- 只包含一個抽象方法的介面,稱為函式式介面,
- 你可以通過Lambda運算式來創建該介面的物件,(若Lambda運算式拋出一個受檢例外(即:非運行時例外),那么該例外需要在目標介面的抽象方法上進行宣告),
- 可以在一個介面上使用
@FunctionalInterface注解,這樣做可以檢查它是否是一個函式式介面,同時 javadoc也會包含一條宣告,說明這個介面是一個函式式介面, - 在
java.util.function包下定義了Java 8的豐富的函式式介面
3. Java內置函式式介面
3.1.1四大核心函式式介面
(T.)里面的.請忽略掉,
| 函式式介面 | 引數型別 | 回傳型別 | 用途 |
|---|---|---|---|
| Consumer<T.> 消費型別介面 | T | void | 對型別為T的物件應用操作, 包含方法:void accept(T t) |
| Supplier 供給型介面 | 無 | T | 回傳型別為T的物件,包含方法:T get() |
| Function<T,R> 函式型介面 | T | R | 物件型別為T的物件應用操作,并回傳結果,結果是R型別的物件,包含方法:R apply(T t) |
| Predicate<T.> 斷定型介面 | T | boolean | 確定型別為T的物件是否滿足某約束,并回傳boolean值,包含方法:boolean test(T t) |
代碼示例:
public class LambdaTest3 {
//簡述:接受一個引數但是不回傳,(就像消費者,給他了東西但是不反回)
// 消費型介面 Consumer<T> void accept(T t)
@Test
public void test1() {
//未使用Lambda運算式
Learn("java", new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println("學習什么? " + s);
}
});
System.out.println("====================");
//使用Lambda表達
Learn("html", s -> System.out.println("學習什么? " + s));
}
private void Learn(String s, Consumer<String> stringConsumer) {
stringConsumer.accept(s);
}
//簡述:你不給他東西但是他回傳,
// 供給型介面 Supplier<T> T get()
@Test
public void test2() {
//未使用Lambdabiaodas
Supplier<String> sp = new Supplier<String>() {
@Override
public String get() {
return new String("我能提供東西");
}
};
System.out.println(sp.get());
System.out.println("====================");
//使用Lambda表達
Supplier<String> sp1 = () -> new String("我能通過lambda提供東西");
System.out.println(sp1.get());
}
//你放進去一個東西我給你回傳一個東西
//函式型介面 Function<T,R> R apply(T t)
@Test
public void test3() {
//使用Lambda運算式
Employee employee = new Employee(1001, "Tom", 45, 10000);
Function<Employee, String> func1 =e->e.getName();
System.out.println(func1.apply(employee));
System.out.println("====================");
//使用方法參考
Function<Employee,String>func2 = Employee::getName;
System.out.println(func2.apply(employee));
}
//斷定型介面 Predicate<T> boolean test(T t)
//傳進去是一個T 回傳一個結果是`true`還是`false`
@Test
public void test4() {
//使用匿名內部類
Function<Double, Long> func = new Function<Double, Long>() {
@Override
public Long apply(Double aDouble) {
return Math.round(aDouble);
}
};
System.out.println(func.apply(10.5));
System.out.println("====================");
//使用Lambda運算式
Function<Double, Long> func1 = d -> Math.round(d);
System.out.println(func1.apply(12.3));
System.out.println("====================");
//使用方法參考
Function<Double,Long>func2 = Math::round;
System.out.println(func2.apply(12.6));
}
}
使用總結:
何時使用lambda運算式?
當需要對一個函式式介面實體化的時候,可以使用lambda運算式,
何時使用給定的函式式介面?
如果我們開發中需要定義一個函式式介面,首先看看在已有的jdk提供的函式式介面是否提供了能滿足需求的函式式介面,
如果有,則直接呼叫即可,不需要自己再自定義了,
四、方法的參考
4.1 概述:
方法參考可以看做是Lambda運算式深層次的表達,
換句話說,方法參考就是Lambda運算式,也就是函式式介面的一個實體,通過方法的名字來指向一個方法,
使用情景:
當要傳遞給Lambda體的操作,已經實作的方法了,可以使用方法參考!
代碼格式:
格式:
類(或物件) :: 方法名
使用情況:
- 情況1 物件
::非靜態方法 - 情況2 類
::靜態方法 - 情況3 類
::非靜態方法
使用要求:
- 要求介面中的抽象方法的形參串列和回傳值型別與方法參考的方法的形參串列和回傳值型別相同!(針對于情況1和情況2)
- 當函式式介面方法的第一個引數是需要參考方法的呼叫者,并且第二個引數是需要參考方法的引數(或無引數)時:
ClassName::methodName(針對于情況3)
使用建議:
如果給函式式介面提供實體,恰好滿足方法參考的使用情境,就可以考慮使用方法參考給函式式介面提供實體,
如果不熟悉方法參考,那么還可以使用lambda運算式,
代碼實體:
public class MethodRefTest {
// 情況一:物件 :: 實體方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)
@Test
public void test1() {
//使用Lambda表達
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("中國");
System.out.println("====================");
//使用方法參考
PrintStream ps = System.out;
Consumer con2 = ps::println;
con2.accept("China");
}
//Supplier中的T get()
//Employee中的String getName()
@Test
public void test2() {
//使用Lambda表達
Employee emp = new Employee(1001, "Bruce", 34, 600);
Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());
System.out.println("====================");
//使用方法參考
Supplier sup2 = emp::getName;
System.out.println(sup2.get());
}
// 情況二:類 :: 靜態方法
//Comparator中的int compare(T t1,T t2)
//Integer中的int compare(T t1,T t2)
@Test
public void test3() {
//使用Lambda表達
Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2);
System.out.println(com1.compare(32, 45));
System.out.println("====================");
//使用方法參考
Comparator<Integer> com2 = Integer::compareTo;
System.out.println(com2.compare(43, 34));
}
//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4() {
//使用匿名內部類
Function<Double, Long> func = new Function<Double, Long>() {
@Override
public Long apply(Double aDouble) {
return Math.round(aDouble);
}
};
System.out.println(func.apply(10.5));
System.out.println("====================");
//使用Lambda運算式
Function<Double, Long> func1 = d -> Math.round(d);
System.out.println(func1.apply(12.3));
System.out.println("====================");
//使用方法參考
Function<Double, Long> func2 = Math::round;
System.out.println(func2.apply(12.6));
}
// 情況三:類 :: 實體方法
// Comparator中的int comapre(T t1,T t2)
// String中的int t1.compareTo(t2)
@Test
public void test5() {
//使用Lambda運算式
Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abd", "aba"));
System.out.println("====================");
//使用方法參考
Comparator<String> com2 = String::compareTo;
System.out.println(com2.compare("abd", "abc"));
}
//BiPredicate中的boolean test(T t1, T t2);
//String中的boolean t1.equals(t2)
@Test
public void test6() {
//使用Lambda運算式
BiPredicate<String, String> pre1 = (s1, s2) -> s1.equals(s2);
System.out.println(pre1.test("abc", "abc"));
System.out.println("====================");
//使用方法參考
BiPredicate<String, String> pre2 = String::equals;
System.out.println(pre2.test("abc", "abd"));
}
// Function中的R apply(T t)
// Employee中的String getName();
@Test
public void test7() {
//使用Lambda運算式
Employee employee = new Employee(1001, "Tom", 45, 10000);
Function<Employee, String> func1 =e->e.getName();
System.out.println(func1.apply(employee));
System.out.println("====================");
//使用方法參考
Function<Employee,String>func2 = Employee::getName;
System.out.println(func2.apply(employee));
}
}
五、構造器和陣列的參考
5.1 使用格式:
方法參考:
類名 ::new
陣列參考:陣列型別 [] :: new
5.2 使用要求:
5.2.1 構造器參考
和方法參考類似,函式式介面的抽象方法的形參串列和構造器的形參串列一致,抽象方法的回傳值型別即為構造器所屬的類的型別
5.2.2 陣列參考
可以把陣列看做是一個特殊的類,則寫法與構造器參考一致,
5.4 使用舉例
5.4.1 構造器的參考
//構造器參考
//Supplier中的T get()
@Test
public void test1() {
//使用匿名內部類
Supplier<Employee> sup = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println(sup.get());
//使用Lambda運算式
System.out.println("====================");
Supplier<Employee> sup1 = () -> new Employee(1001, "Tom", 43, 13333);
System.out.println(sup1.get());
//使用方法參考
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());
}
//Function中的R apply(T t)
@Test
public void test2() {
//使用Lambda運算式
Function<Integer, Employee> func1 = id -> new Employee(id);
Employee employee = func1.apply(1001);
System.out.println(employee);
System.out.println("====================");
//使用方法參考
Function<Integer, Employee> func2 = Employee::new;
Employee employee1 = func2.apply(1002);
System.out.println(employee1);
}
//BiFunction中的R apply(T t,U u)
@Test
public void test3() {
//使用Lambda運算式
BiFunction<Integer, String, Employee> func1 = (id, name) -> new Employee(id, name);
System.out.println(func1.apply(1001, "Tom"));
System.out.println("====================");
//使用方法參考
BiFunction<Integer, String, Employee> func2 = Employee::new;
System.out.println(func2.apply(1002, "Jarry"));
}
5.4.2 陣列參考
//陣列參考
//Function中的R apply(T t)
@Test
public void test4() {
Function<Integer, String[]> func1 = length -> new String[length];
String[] arr1 = func1.apply(5);
System.out.println(Arrays.toString(arr1));
System.out.println("====================");
//使用方法參考
Function<Integer,String[]>func2=String[]::new;
String[] arr2 = func2.apply(10);
System.out.println(Arrays.toString(arr2));
}
六、StreamAPI
6.1 Stream API概述
- Stream關注的是對資料的運算,與CPU打交道;集合關注的是資料的存盤,與記憶體打交道;
- Java8提供了一套api,使用這套api可以對記憶體中的資料進行過濾、排序、映射、歸約等操作,類似于sql對資料庫中表的相關操作,
- Stream是資料渠道,用于操作資料源(集合、陣列等)所生成的元素序列,“集合講的是資料, Stream講的是計算!”
使用注意點:
- Stream 自己不會存盤元素,
- Stream 不會改變源物件,相反,他們會回傳一個持有結果的新Stream,
- Stream 操作是延遲執行的,這意味著他們會等到需要結果的時候才執行,
Stream使用流程
- Stream的實體化
- 一系列的中間操作(過濾、映射、…)
- 終止操作

使用流程中的注意點:
- 一個中間操作鏈,對資料源的資料進行處理
- 一旦執行終止操作,就執行中間操作鏈,并產生結果,之后,不會再被使用
6.2 使用方法
6.2.1 步驟一 創建Stream
① 創建方式一:通過集合
Java 8的Collection介面被擴展,提供了兩個獲取流的方法:
default Stream\<E> stream(): 回傳一個順序流default Stream\<E> parallelStream() :回傳一個并行流
②創建方式二:通過陣列
Java 8中的Arrays的靜態方法stream()可以獲取陣列流
- 呼叫Arrays類的
static\<T> Stream\<T> stream(T[] array): 回傳一個流 - 多載形式,能夠處理對應基本型別的陣列:
-public static IntStream stream(int[] array)
-public static LongStream stream(long[] array)
-public static DoubleStream stream(double[] array)
③ 創建方式三:通過Stream的of()方法
可以呼叫Stream類靜態方法of(),通過顯示值創建一個流,可以用于接收任意數量的引數
public static \<T>Stream\<T> of(T...values):回傳一個流
④創建方式四:創建無限流
- 迭代:
public static\<T> Stream\<T> iterate(final T seed, final UnaryOperator\<T> f) - 生成:
public static\<T> Stream\<T> generate(Supplier\<T> s)
代碼示例:
public class StreamAPITest1 {
//創建 Stream方式一:通過集合
@Test
public void test1() {
List<Employee> employees = EmployeeData.getEmployees();
//efault Stream<E> stream() : 回傳一個順序流
Stream<Employee> stream = employees.stream();
//default Stream<E> parallelStream() : 回傳一個并行流
Stream<Employee> employeeStream = employees.parallelStream();
}
//創建 Stream方式二:通過陣列
@Test
public void test2() {
int[] arrs = {1, 2, 3, 6, 2};
//呼叫Arrays類的static <T> Stream<T> stream(T[] array): 回傳一個流
IntStream stream = Arrays.stream(arrs);
Employee e1 = new Employee(1001, "Tom");
Employee e2 = new Employee(1002, "Jerry");
Employee[] employees = {e1, e2};
Stream<Employee> stream1 = Arrays.stream(employees);
}
//創建 Stream方式三:通過Stream的of()
@Test
public void test3() {
Stream<Integer> integerStream = Stream.of(12, 34, 45, 65, 76);
}
//創建 Stream方式四:創建無限流
@Test
public void test4() {
//迭代
//public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
//遍歷前10個偶數
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
//生成
//public static<T> Stream<T> generate(Supplier<T> s)
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/276638.html
標籤:其他
上一篇:對Vue生命周期的一些簡單見解
