jdk1.8新特性
解決的問題:
1.開發者可以使用Java8編寫複雜的處理算法,只需要簡單修改一個方法,就能讓代碼在多核CPU上高效運行。增加lambda表達式。
2.使用函數式編程的思想,面向對象編程是對數據進行抽象,而函數式編程是對行爲進行抽象。現實世界中,數據和行爲存,程序也是如此,因此這兩種編程方式我們都得學。通過函數式編程,程序員能編寫出更容易閱讀的代碼——這種代碼更多地表達了業務邏輯,而不是從機制上如何實現。易讀的代碼也易於維護、更可靠、更不容易出錯。
lambda 函數編程
3.Java 8還讓集合類可以擁有一些額外的方法:
default(默認)方法。程序員在維護自己的類庫時,可以使用這些方法。
在接口中可以定義默認方法:
接口{
default(){
很多代碼
}
規範2();
規範1();
}
可以在不修改 以前代碼的 情況下 添加新功能
接口{
規範2();
規範1();
default 新規範(){
}
}
實現類:
default默認方法可以重寫.
1.接口中方法
通過在方法前加上default關鍵字就可以在接口中寫方法的默認實現
大家熟知的List接口,如果需要新添加一個功能,但是又不想對已有的實現類代碼修改, 怎麼實現呢?
思考:接口中的默認方法出現會帶來什麼好處?
答: 對接口中的功能 增強
這使得我們不需要在接口中增加方法後對已經存在的每個實現類進行實現該方法,做到了平滑的增強。
Java 8中的接口現在支持在聲明方法的同時提供實現,通過兩種方式可以完成這種操作。
Collections :集合工具類
Collection :接口 :Set List 父類
2.接口內聲明靜態方法
同時定義接口以及工具輔助類(companion class)是Java語言常用的一種模式,工具類定義了與接口實例協作的很多靜態方法
比如,Collections就是處理Collection對象的輔助類。
由於靜態方法可以存在於接口內部,
你代碼中的這些輔助類就沒有了存在的必要,
你可以把這些靜態方法轉移到接口內部。
爲了保持後向的兼容性,這些類依然會存在於Java應用程序的接口之中。
在接口中可以提供靜態的方法
例:
public interface InterfaceTest {
public void add();
public static void show() {
System.out.println("這個是接口中的靜態方法");
}
public static void minus() {
System.out.println("這個也是接口中的靜態方法");
}
}
調用接口中靜態方法:
接口名.方法名(參數);
public static void main(String[] args) {
InterfaceTest.minus();
InterfaceTest.minus();
}
3.接口中默認方法
接口包含的方法在它的實現類中也可以不提供實現
efault的方法實現會作爲接口的一部分由實現類繼承(所以命名爲默認實現),而無需由實現類提供。
默認方法由default修飾符修飾,並像類中聲明的其他方法一樣包含方法體。
例:
public interface DefaultTest {
public default void show1() {
System.out.println("這個是默認方法 1");
}
public default void show2() {
System.out.println("這個是默認方法 2");
}
public default void show3() {
System.out.println("這個是默認方法 3");
}
}
interface DD extends DefaultTest{
//覆蓋父接口中的show2方法
public default void show2() {
System.out.println("這個是我重寫以後的 方法 2");
}
}
//實現類
class Test implements DD{
public void show3(){
//在實現類中調用父接口中的默認方法show3
DD.super.show3();
System.out.println("...");
}
}
測試:
public static void main(String[] args) {
Test t = new Test();
t.show1();
t.show2();
t.show3();
}
1.函數式編程
以函數思維做爲核心,在這種思維的角度去思考問題,最重要的基礎是函數的輸入和輸出,使用不可變值和函數,
【函數對一個值進行處理,映射成另任意函數式接口類型的一個值】
Comparator t = 函數(值);
1: 函數式接口
函數式接口就是隻定義一個抽象方法的接口。
注:哪怕有很多默認方法,
只要接口只定義了一個抽象方法,它就仍然是一個函數式接口。
2: 函數式接口的作用
Lambda表達式允許你直接以內聯的形式爲函數式接口的抽象方法提供實現,並把整個表達式作爲函數式接口的實例 (具體說來,是函數式接口一個具體實現的實例)。你用匿名內部類也可以完成同樣的事情,只不過比較笨拙。
3: 函數描述符
函數式接口中的抽象方法的簽名基本上就是Lambda表達式的簽名,我們將這種抽象方法叫作函數描述符。
lambda:表達式
() -> {}
4,聲明函數式接口 @FunctionalInterface
這個標註用於表示該接口會設計成一個函數式接口
2.lambda表達式
可以把Lambda表達式理解爲簡潔地表示可傳遞的匿名函數的一種方式:它沒有名稱,但它有參數列表、函數主體、返回類型,可能還有一個可以拋出的異常列表。
匿名——我們說匿名,是因爲它不像普通的方法那樣有一個明確的名稱。
函數——我們說它是函數,是因爲Lambda函數不像方法那樣屬於某個特定的類。但和方法一樣,Lambda有參數列表、函數主體、返回類型,還可能有可以拋出的異常列表。
傳遞——Lambda表達式可以作爲參數傳遞給方法或存儲在變量中。(行爲參數)
簡潔——無需像匿名類那樣寫很多模板代碼。
總結:快速實現接口中的方法【lambda簡單化的匿名函數】
1.基本語法
函數式接口類型 變量名 = ()->{}
使用lambda表達式實現配合Collections完成排序功能。
對比,匿名內部類
感受:lambda是快速實現接口中的方法
自定義接口:裏面只有一個抽象方法
自定義測試類: 裏面的某一個方法 參數爲 接口類型
main : 進行測試。
2.lambda表達式用法:
1.沒有參數,沒有返回值
()->{}
注:如果大括號裏只有一句代碼 ()->代碼 該代碼不要;號結尾
2.沒有參數,有返回值
()->{return 值;}
注:如果大括號裏只有一句代碼 ()->返回值 該代碼不要;號結尾
3.有一個參數,沒有返回值
(a)->{System.out.println(a)}
注:如果大括號裏只有一句代碼 (a)->代碼 該代碼不要;號結尾
4.有一個參數,有返回值
(a)->{a++;return a;}
注:如果大括號裏只有一句代碼 (a)->返回值 該代碼不要;號結尾
注:如果只有一個參數,()也可以不寫。
a->{};
5.有兩個參數,沒有返回值
(a,b)->{a++;System.out.println(b);}
注:如果大括號裏只有一句代碼 (a,b)->代碼 該代碼不要;號結尾
6.有兩個參數,有返回值
(a,b)->{a++;return a;}
注:如果大括號裏只有一句代碼 (a,b)->返回值 該代碼不要;號結尾
注:大括號裏的代碼需要打分號。就是普通正常代碼。
注:參數裏面是不用謝參數類型的。
2.5,lambda中變量的問題:
lambda中可以使用成員變量,局部變量,但是使用過局部變量以後,該局部變量就必須爲final,所以不能再修改了。
3:在函數式接口上使用lambda表達式
jdk8中新引入了幾個函數式接口:
Predicate<T>接口 test(T t)
接口定義了一個名叫test的抽象方法,它接受泛型T對象,並返回一個boolean。
boolean test(T t);表示:t進行斷言,返回true或者false,可以配合其他的方法使用,如filter();
Predicate接口是用來支持java函數式編程新增的一個接口,使用這個接口和lambda表達式就可以以更少的代碼爲API方法添加更多的動態行爲。
還有兩個默認方法 and or
/*
@FunctionalInterface
interface Predicate<T>{
boolean test(T t);
}
*/
public class LambdaTest4 {
public static void filter(List<String> languages, Predicate<String> condition) {
for(String name: languages) {
if(condition.test(name)) {
System.out.println(name + " ");
}
}
}
public static void main(String[] args) {
List<String> languages = Arrays.asList("Java", "html5","JavaScript", "C++", "hibernate", "PHP");
//開頭是J的語言
filter(languages,(String name)->name.startsWith("J"));
//5結尾的
filter(languages,(String name)->name.endsWith("5"));
//所有的語言
filter(languages,(name)->true);
//一個都不顯示
filter(languages,(name)->false);
//顯示名字長度大於4
filter(languages,(name)->name.length()>4);
System.out.println("-----------------------");
//名字以J開頭並且長度大於4的
Predicate<String> c1 = (name)->name.startsWith("J");
Predicate<String> c2 = (name)->name.length()>4;
filter(languages,c1.and(c2));
//名字不是以J開頭
Predicate<String> c3 = (name)->name.startsWith("J");
filter(languages,c3.negate());
//名字以J開頭或者長度小於4的
Predicate<String> c4 = (name)->name.startsWith("J");
Predicate<String> c5 = (name)->name.length()<4;
filter(languages,c4.or(c5));
//名字爲Java的
filter(languages,Predicate.isEqual("Java"));
//判斷倆個字符串是否相等
boolean test = Predicate.isEqual("hello").test("world");
System.out.println(test);
}
源代碼解析:
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
Function<T, R> 接口 R apply(T t); 有參數有返回值
參數 返回值
接收T對象,返回R對象
表示接受一個參數併產生結果的函數。
/*@FunctionalInterface
interface Function<T, R>{
R apply(T t);
}*/
public class LambdaTest6 {
public static <T, R> List<R> map(List<T> list,Function<T, R> f) {
List<R> result = new ArrayList<>();
for(T s: list){
result.add(f.apply(s));
}
return result;
}
public static void main(String[] args) {
List<Integer> list = map(
Arrays.asList("lambdas","in","action"),
(String s) -> s.length()
);
System.out.println(list);// [7, 2, 6]
}
}
Supplier<T> 接口 T get(); 沒參數有返回值
返回值
代表結果供應商。
沒有要求每次調用供應商時都會返回新的或不同的結果。
/*@FunctionalInterface
interface Supplier<T>{
T get();
}*/
public class LambdaTest7 {
public static void main(String[] args) {
//生成一個八位的隨機字符串
Supplier<String> f = ()->{
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 8; i++) {
//生成[0,base.length)之間的隨機數
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
};
System.out.println(f.get());
}
}
Consumer<T> 接口 void accept(T t); 有參數沒返回值
參數 對給定的參數執行此操作
這個接口,接收一個泛型的參數T,然後調用accept,對這個參數做一系列的操作,沒有返回值;
public class LambdaTest5 {
public static <T> void forEach(List<T> list, Consumer<T> c){
for(T i: list){
c.accept(i);
}
}
public static void main(String[] args) {
forEach(
Arrays.asList(1,2,3,4,5),
(Integer i) -> System.out.println(i)
);
}
}
另外需要注意的接口: 其用法和上面介紹的接口使用方式類同
BinaryOperator<T>接口 T apply(T t, T t) 將兩個T作爲輸入,返回一個T作爲輸出
BiFunction<T, U, R>接口 R apply(T t, U u) 將一個T和一個U輸入,返回一個R作爲輸出
BinaryOperator接口繼承了BiFunction接口
public interface BinaryOperator<T> extends BiFunction<T,T,T>
BiConsumer<T, U>接口 void accept(T t, U u) 將倆個參數傳入,沒有返回值
4.java常用的函數式編程接口使用:
1.比較器
2.實現線程
3.綁定事件
4.集合數據排序