java8十大特性
Java 8筆記
1、Lambda表示式
1.1、什麼叫Lambda表示式:
Lambda表示式就是一段帶有引數的可執行語句塊
1.2、Lambda表示式在java語言中引入了一個新的語法元素和操作符;
這個操作符為“->”,該操作符被稱為Lambda或箭頭操作符;它將Lambda
Fenwick兩個部分:
左側:指定了Lambda表示式需要的所有引數
右側:指定了Lambda體,即Lambda所需要執行的功能
1.3、Lambda表示式的語法:
1.3.1、語法格式一:無參、無返回值,Lambda只需要一條語句
Runnable r1=() ->System.out.println(“hello word”);
1.3.2、語法格式二:帶一個引數
Consumer<String> fun=(args) ->System.out.println(args);
1.3.3、語法格式三:Lambda只需要一個引數時,引數的小括號可以省略
Consumer<String> fun=args ->System.out.println(args);
1.3.4、語法格式四:Lambda只需要一個引數時,並且有返回值
BinaryOperator<Long> bo=(x,y) ->{
System.out.println(“實現函式式介面方法”);
return x+y;
};
1.3.5、語法格式五:當Lambda體只有一條語句是,大括號和return可以省略
BinaryOperator<Long> bo=(x,y) ->x+y
1.3.6、語法格式六:
BinaryOperator<Long> bo=(Long x,Long y) ->{
System.out.println(“實現函式式介面方法”);
return x+y;
};
//Lambda體引數型別可以省略,因為可有編譯器推斷的出來,稱為型別推斷
簡單的舉個例子:
*下面程式碼是老版本的Java中是如何排列字串的:
List<String> names=Arrays.asList(“peter”,”anna”,”mike”,”xenia”);
Collections.sort(names,new Comparator<String>);
@Override
Public int compare(String a,String b){
Return b.compareTo(a);
}
//只需要給靜態方法Collecyions.sort傳入一個List物件以及一個比較起來按指 定順序排列。通常做法都是建立一個匿名的比較器物件然後將其傳遞給sort 方法
*在java8中就沒必要使用這種傳統的匿名物件的方式,java8提供了更簡潔的 語法,lambda表示式:
Collections sort(names,(a,b) ->b.compare(a))
2、函式式介面
2.1、什麼是函式式介面:
2.1.1、只包含一個抽象方法的介面,稱為“函式式介面”;
2.1.2、你可以通過Lambda表示式來建立該介面的物件,(若Lambda表示式丟擲一 個受檢查時異常,那麼該異常需要在目標介面的抽象方法上進行宣告)
2.1.3、可以在任意函式式上使用@FunctionalInterface註解,該註解可以檢查它是否 是一個函式式介面,
2.2、java8中常用的全新介面
2.2.1、Predicate介面(斷言型介面)
Predicate介面只有一個引數,返回boolean型別,該介面包含多種預設方法 來將Predicate組合成其他複雜的邏輯(比如:與,或,非),包含方法boolean test(T t):
程式碼如下:
Predicate<String> predicate=(s) ->s.length()>0;
Predicate.test(“foo”);//true
Predicate.negate().test(“foo”);//false
2.2.2、Function<T,R>介面(函式型介面):T代表引數,R代表返回值
Function介面有一個引數並且返回一個結果,並附帶了一些可以和其他函 數組合的預設方法(compose,andThen),包含方法 R apply (T t);
程式碼如下:
Function<String,Integer> tolnteger=integer::valueOf;
Function<String,String> backToString=tointeger.andThen(String::valueOf);
backToString.apply(“123”);//123
2.2.3、Supplier介面(供給型介面):返回一個任意泛型的值,和Function不同的是該接 口沒有任何引數,包含方法T get();
程式碼如下:
Supplier<Person> personSupplier=person::new;
personSupplier.get();//new Person
2.2.4、Consumer<T>介面(消費型介面):對型別為T的物件應用操作,包含方法void accept(T t);
程式碼如下:
Consumer<Person> greeter=(p) ->System.out.println(“hello”+p.firstName);
Greeter.accept(new Person(“Luke”,”Skywalker”));
3、方法引用和構造引用
3.1、方法引用
*當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用!
(實現抽象方法的引數列表,必須與方法引用的引數列表保持一致!)
*方法引用:使用操作符”::” ,將方法名和物件或類的名字分開。
如下三種使用情況:
*物件::例項方法
*類::靜態方法
*類::例項方法
例1:
(x) ->System.out.println(x);
等同於
System.out::println;
例2:
Compare((x,y) ->x.equals(y),”abc”,”abc”)
等同於
Compare(String::equals,”abc”,”abc”)
3.2、建構函式引用
用函式式介面相結合,自動與函式式介面種方法相容,可以把構造器引用賦值給定 義的方法,構造器引數列表要與介面中抽象方法的引數列表一致。
例子:
Function<Integer,MyClass> fun=(n) ->new MyClass(n);
等同於
Function<Integer,MyClass> fun=MyClass::new;
3.3、陣列引用
格式:Type[]::new
例子:Function<Integer,MyClass> fun=(n) ->new Integer[n];
等同於
Function<Integer,MyClass> fun=Integer::new;
4、Stream API
4.1、流(Stream)到底是什麼
是資料渠道用於操作資料來源(集合,陣列等)所生成的元素序列
(集合講究的是資料,流講究的是計算)
注意:
*Stream自己不會儲存元素
*Stream不會改變源物件;相反,他們會反回持有結果的新流(Stream)
*Stream操作是延遲執行的,需要結果的時候才會執行
4.2、Stream操作的三個步驟
4.2.1、建立Stream
一個數據源(如:集合、陣列),獲取一個流
4.2.2、中間操作
一箇中間操作鏈,對資料來源的資料進行處理
4.2.3、終止操作(終端操作)
一個終止操作,執行中間操作鏈,併產生結果
------>一系列的中間處理操作(如map(),filter())------>終止操作
4.3、Stream怎麼用,
*Java8擴充套件了集合類,可以通過Collection.stream()或者Collection.parallelStream() 建立一個Stream
下面舉例子說明:
List<String > stringCollection=new ArrayList<>();
stringCollection.add(“ddd2”);
stringCollection.add(“aaa2”);
stringCollection.add(“bbb1”);
stringCollection.add(“aaa1”);
stringCollection.add(“bbb3”);
stringCollection.add(“ccc”);
stringCollection.add(“bbb2”);
stringCollection.add(“ddd1”);
4.3.1:filter方法過濾:
StringCollection.stream()
.filete((s) ->s.startsWith(“a”))
.forEach(System.out::println);//輸出結果”aaa2”,”aaa1”
4.3.2、sort排序
排序是個中間操作,返回的實拍序號後的stream,如果不指定一個字定義的 Comparator則會使用預設排序;
程式碼如下:
StringCollection.stream()
.sorted()
Filter((s) ->s.startsWith(“a”))
.forEach(System.out::println);//輸出結果”aaa1”,”aaa1”
//排序只建立一個排序好後的stream,而不會影響原來的資料來源,排序號之後源數 據StringCollection是不會修改的;
4.3.3、map對映
中間操作map會將元素根據指定的Function介面來依次將元素轉成另外的物件,
下面的示例展示了將字串轉換成大寫字串,你也可以通過map來將物件轉換 成其他型別,map返回的Stream型別是根據你map傳遞進去的函式的返回值決定的
例子:
StringCollection.stream()
.map(String::toUpperCase)
.sorted((a,b) ->b,compareTo(a))
.forEach(System.out::println);
//輸出結果:”DDD2”,”DDD1”,”CCC”,”BBB3”,”BBB2”,”AAA2”,”AAA1”
4.3.4、Match匹配
Stream提供了多種匹配操作,允許檢測制定的Predicate是否匹配整個Stream ,
所有的匹配操作都是最終操作,並且返回一個boolean型別的值
例子:
Boolean anyStartsWithA=
stringCollection.stream()
.anyMatch((s) ->s.startsWith(“a”))
System.out.println(anyStartsWithA);//true
4.3.5、count計數
技術是一個最終操作,返回Stream中元素的個數,返回值型別是long
例子:
Long startsWithB=
stringCollection.stream()
.filter((s)->s.startsWith(“b”))
.count();
System。Out.println(startsWithB);//3
4.3.6、規約
這是一個人最終操作,允許通過指定的函式來將stream中的多個元素規約為一個 元素,規約後的結果是通過Optional介面表示的
程式碼:
Optional<String> reduced=
stringCollection.stream()
.sorted()
.reduce((s1,s2) ->s1+”#”+s2);
Reduced.ifPresent(System.out::println);
//輸出結果:“aaa1#aaa2#bbb2#bbb3#ccc#ddd1#ddd2”
4.3.7、並行Streams
Stream有序列和並行兩種,序列Stream上的操作是在一個執行緒中依次完成,
而並行Stream則是在多個執行緒上同時執行
通過並行Stream可以提升效能,可以減少執行時間,他沒有空閒的執行緒核
一個執行緒執行完了,它會去從別的別的執行緒核末尾偷一個執行
5、介面中的預設方法與靜態方法
5.1、介面的預設方法
Java8允許我們給介面新增一個非抽象的方法實現,只需要使用default關鍵字即可,
這個特徵有叫做擴充套件方法
程式碼如下:
Interface Formula{
double calculate(int a );
default double sqrt(int a ){
Return Math.sqrt(a);
}
}
在上面Formula介面在擁有calculate方法之外同時還定義了sqrt方法,實現了 Formula介面的子類只需要實現一個calculate方法,預設方法sqrt將在子類上可以 直接使用。
*介面預設方法”類優先”原則:
若一個介面中定義了一個預設的方法,而另外一個父類或介面中又定義了一個同名 的方法時。
---選擇父類的方法,如果一個父類提供了具體的實現,那麼介面中具有相同名稱和 引數的預設方法會忽略
---介面衝突,如果一個父介面提供一個預設的方法,而另外一個介面也提供了一個 具有相同名稱和引數列表的方法(不管方法是不是預設方法),那麼必須覆蓋該方 法來解決衝突
簡單例子:
Interface MyFun(){
Defalut String getName()
Return “hello”;
}
}
Interface Named(){
Defalut String getName()
Return “hello word”;
}
}
Class MyClass implements MyFun, Named(){
Public String getName(){
Return Named.super.getName()
}
}
5.2靜態方法
介面中允許新增靜態方法
例子:
Interface Nmaed(){
Public Integer Myfun();
Defalut String getName(){
Return “hello”;
]
Static void show(){
System .out.println(“靜態方法引用”);
}
}
6、新時間日期API
6.1、在java8中,添加了一個新包:java.time,它提供了結構良好的API來處理時間和日 期;
新的API:java.time,由5個包組成:
.java.time --包含值物件的基礎包
.java.time.chrono --提供對不同的日曆系統的訪問
.java.time.format --格式化和解析時間日期
.java.time.temporal --包括底層框架和擴充套件特性
.java.time.zone --包含時區支援的類
*以前版本存在的問題
1、可變性:所有的日期類都是可變的,執行緒不安全
2、偏移性:Data中的年份是從1990開始的,而月份都是從0開始的
3、java的日期/時間類的定義並不一致,在Java.Util和Java.sql的包中都有日期類, 此為用於格式化和解析的類在java.text包中定義
4、java.util.Date 同時包含日期和時間,而java.sql.Date僅包含日期,將其納入java.sql
包並不合理,另外這連個類都有相同的名字,本身設計不合理
5、對於時間、時間戳、格式化以及解析,並沒有一些明確定義的類。對於格式化 和解析的需求,我們有java.text.DateFormat類被用於此類需求
6、日期類並不提供國際化,沒有時區支援,因此java引入了java.util.Calendar和 java.util.TimeZone類,但他們同樣存在上述所有的問題
*LocalDate類方法例子如下:
LocalDate date=LocalDate .of(2014,Month.JUNE,10);
Int year=date.getYear();//2014
Month month=date.getMonth();//6月
Int dom=date.getDayOfMonth();//10
DayOfWeek dow=date.getDayOfWeek();//星期二
Int len=date.lengthOfMonth();//30(6月份的天數)
bolean leap=date.isLeapYear();//false(不是閏年)
*LocalTime類的用法跟LacalDate相似:
LocalTime time=LocalTime.of(20,30);
Int hour=date.getHour();//20
Int minute=date.getMinute();//30
Time=time.withSecound(6);//20:30:06
Time=time.plusMinutes(3);//20:33:06
*LocalDateTime類是LocalDate和LocalTime的簡單組合,它表示一個跟時區無關的日期 和時間;LocalDateTime可以直接建立,或者組合時間和日期:
例子:
LocalDateTime dt1=LocalDateTime.of(2014,Month.JUNE,10,20,30);
LocalDateTime dt2=LocalDateTime .of(date,time);
LocalDateTime dt2=date.atTime(20,30);
LocalDateTime dt4=date.atTime(time);
7、型別註解與重複註解
7.1什麼是型別註解
7.1.1、Java8為ElementType列舉增加了TYPE_PARAMETER、TYPE_USE兩個列舉值,從而可以使 用@Target(ElementType_TYPE_USE)修飾註解定義,這種註解稱為型別註解,可以用在任何使 用到型別的地方;
* ElementType.TYPE_PARAMETE表示註解能寫在型別變數的的宣告語句中(如
class MyClass{...}).
*ElementType.TYPE_USE表示註解註解能寫在使用型別的任何語句中(例如宣告語
句、泛型和強制轉換語句中的型別)
7.1.2、在Java8之前,註解只能在宣告的地方使用,比如類、方法、屬性;
java8裡面,註解可以應用在任何地方;
簡單的舉幾個例子:
*建立類例項
New @interned MyObject();
*型別對映
myString=(@NonNull String) str;
@Encrypted String data;
* 泛型
List<@NonNull String> strings;//防止非空,java8裡面還沒有內建這個註解, 編譯時會報錯要配合框架使用(checker framework)
MyGRAph=(@Immutable Graph) tmpGraph ;
7.2什麼是可重複註解
*允許在同一申明型別(類、屬性、方法)的多次使用同一個註解
例子:
*Java8之前也有重複註解的解決方案,但可讀性不是很好,
Java8裡面的做法:
@Repeatable(MyAnnotations.class)
Public @interface MyAnnotation{
String role();
}
Public @interface MyAnnotations{
MyAnnotation[] role();
}
Public class Repeat{
@MyAnnotation(role=”Admin”)
@MyAnnotation(role=”Admin”)
public void TestAnnotations(){
}
}
*Java8不同地方的是,建立重複註解MyAnnotation時,加上@Repeatable,指向儲存
註解MyAnnotations(給註解建立一個容器),在使用時候,直接可以重複使用 MyAnnotation
註解
8、Optional類
8.1、*OPtional(java.util.Optional)是一個容器類,可以為null的容器物件,如果值存在則 isPresent()方法會返回true,呼叫get()方法會返回該物件
*OPtional類的引入很好的解決了空指標異常
8.2、of方法
可以用of方法通過工廠方法建立Optionnal類,建立物件時傳入的引數不能為null,
如果傳入引數為null,則丟擲NullPointerException
簡單例子:
//呼叫工廠方法建立Optional例項
Optional<String> name=Optional.of(“aaaa”);
//傳入引數為null,丟擲NullPointerException
Optional<String> someNull=Optional.pf(null);
8.3、ofNullable方法
為指定的值建立一個Optionnal,如果指定的值為null,則返回一個空 的 Optional;
ofNull與of方法相似,唯一的區別是可以接受引數為null的情況
示例如下:
//下面建立一個不包含任何值的Optional例項
//例如值為‘null’
Optional empty=Optional.ofNull(null);
8.4、isPresent方法
如果值存在返回true,否則返回false
例子:
//isPresent方法用來檢查Optional例項中是否包含值
If(name.isPresent()){
//在Optional例項類呼叫get()返回存在的值
System.out.println(name.get());
}
8.5、get方法
如果Optional有值則將其返回,否則丟擲NosuchElementException.
8.6、orElse方法
如果有值則將其返回,否則返回指定的其他值
8.7、orElesGet方法
orElesGet與orElse方法類似,區別在於得到的預設值。
orElse方法見傳入的字串作為預設值,orElesGet方法可以接受Supplier介面的實 現來生成預設值
8.8、orElseThrow方法
如果有值則將其返回,否則丟擲Supplier介面建立的異常
8.9、map方法
如果有值,則對其執行呼叫mapping函式得到返回值,如果返回值不為null,則創 建包含mapping返回值的Optional作為map方法的返回值,否則返回空Optional.
程式碼如下:
//map方法執行傳入的lambda表示式引數對Optional實力的值進行修改
//lambada表示式的返回值建立新的Optional例項作為map方法的返回值
Optional<String> upperName=name.map((value) ->value.toUpperCase());
System.out.println(upperName.orElse(“No value found”));
8.10、flatMap方法
如果有值,為其執行mapping函式返回Optional型別返回值,否則返回空Optional;
flatMap與map(Funtion)方法類似,區別在於flatMap中的mapper返回值必須是 Optional,呼叫結束時,flatMap不會對結果用Optional封裝
8.11、filter方法
通過傳入限定條件對Optional例項的值進行過濾
簡單的舉個例子:
//filter方法檢查給定的Option值是否滿足某些條件
//如果滿足則返回同一個Optional例項,否則返回空Optional
Optional<String> longName=name.filter((value) ->value.length()>6);
System.out.println(longName.orElse(“The name less than 6characters”));