????????????????????????java8新特性
????????????????????????原創(chuàng)者:文思
一、特性簡介
速度更快
代碼更少,增加了Lambda?????????
強大的Stream API
便于并行
最大化減少空指針異常
二、Lambda表達式
1、Lambda簡介
Lambda是一個匿名函數(shù),可以理解為一段可以傳遞的代碼。
Lambda解決了什么?
沒有Lambda之前,靠匿名內(nèi)部類解決的需求
package java8;
import java.util.ArrayList;
import java.util.Arrays;
importjava.util.Comparator;
import java.util.List;
importjava.util.TreeSet;
importcom.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader.Array;
public classTestLamabda {
??? Listlist= Arrays.asList(
?????????? new Employee("zhangsan",56,19999),
?????????? new Employee("lisi",36,169999),
?????????? new Employee("wangwu",66,369999));
??? public static void main(String[] args) {
?????? TestLamabdat = new TestLamabda();
?????? t.test();
??? }
??? public void test(){
?????? ListEmployees = getList(list,newFilterEmployeeInterface<Employee>(){
?????????? @Override
?????????? public boolean test(Employee t) {
????????????? return t.getSalary()>19999;
?????????? }
?????? });
?????? for(Employee e:Employees){
?????????? System.out.println(e);
?????? }
??? }
??? public List<Employee>
getList(List<Employee> list,FilterEmployeeInterfaceemp){
?????? ListempList= newArrayList<Employee>();
?????? for(Employee e:this.list){
?????????? if(emp.test(e)){
????????????? empList.add(e);
?????????? }
?????? }
?????? return empList;
??? }
}
運行輸出:
java8.Employee@15db9742
而用Lambda表達式:
publicListgetList(Listlist,FilterEmployeeInterfaceemp){
?????? ListempList = newArrayList();
?????? for(Employee e:this.list){
?????????? if(emp.test(e)){
????????????? empList.add(e);
?????????? }
?????? }
?????? return empList;
??? }
??? public void test1(){
?????? ListempList = getList(list,(e)->((Employee)e).getSalary()>5619999);
?????? list.forEach(System.out::println);
??? }
2、Lambda表達式
->左側(cè):表達式的參數(shù)列表。接口中抽象方法的的形參
->右側(cè):表達式中所需執(zhí)行的功能。接口中抽象方法的實現(xiàn)
語法格式1:無參數(shù)無返回值
() -> System.out.println();
語法格式2:有參數(shù)無返回值
() -> System.out.println(e);
語法格式3:若只有一個參數(shù),小括號可以不寫
語法格式4:有兩個以上參數(shù),有返回值
Comparator<Integer> com = (x,y)->{….;….
}
Lambda表達式需要函數(shù)式接口的支持。
三、函數(shù)式接口
什么是函數(shù)式接口?
只包含一個抽象方法的接口,稱為函數(shù)式接口。
可以通過Lambda 表達式來創(chuàng)建該接口的對象。(若Lambda 表達式拋出一個受檢異常,那么該異常需要在目標(biāo)接口的抽象方法上進行聲明)
我們可以在任意函數(shù)式接口上使用@FunctionalInterface注解,這樣做可以檢查它是否是一個函數(shù)式接口,同時javadoc也會包含一條聲明,說明這個接口是一個函數(shù)式接口。
自定義函數(shù)式接口:
@FunctionalInterface
public interfaceMyFunctionInterface<T> {
??? public T getValue(T t);
}
package java8;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
?* java8內(nèi)置的四大核心函數(shù)式接口
?*
?*Consumer<T>:消費型接口
?*???? void accept(T t)
?*Supplier<T>:供給型接口
?*???? T get()
?*Function:函數(shù)型接口
?*???? Rapply(T t)
?*Predicate<T>:斷言型接口
?*???? booleantest(T t)
?*/
public class?TestLambda3 {
??? //消費類型接口
??? public void xiaofei(double money,Consumer<Double> con){
?????? con.accept(money);
??? }
??? public void test1(){
?????? xiaofei(10000,(m) -> System.out.println("旅游消費:"+m+"元"));
??? }
??? //供給類型接口
??? public List<Integer> getNumList(intnum,Supplier<Integer>sup){
?????? List?list = new?ArrayList<Integer>();
?????? for(int i=0;i<num;i++){
?????????? Integer n = sup.get();
?????????? list.add(n);
?????? }
?????? return list;
??? }
??? public void test2(){
List<Integer>list= getNumList(10,()->(int)(Math.random()*100));
?????? for(Integer i:list){
?????????? System.out.print(i+",");
?????? }
?????? System.out.println();
??? }
??? //函數(shù)型接口
??? public String strHandler(String str,Function<String,String>fun){?
?????? return fun.apply(str);
??? }
??? public void test3(){
?????? String newStr = strHandler("acb",(str) -> str.toUpperCase());
?????? System.out.println(newStr+",");
??? }
??? //斷言型
??? public boolean isTest(String str,Predicate<String>pre){
?????? return pre.test(str);
??? }
??? public void test4(){
?????? boolean f = isTest("is",(m)->m.equals("is"));
?????? if(f){
?????????? System.out.println(f);
?????? }
??? }
??? public static void main(String[] args) {
?????? System.out.println("消費型:");
?????? new TestLambda3().test1();
?????? System.out.println("供給型:");
?????? new TestLambda3().test2();
?????? System.out.println("函數(shù)型:");
?????? new TestLambda3().test3();
?????? System.out.println("斷言型:");
?????? newTestLambda3().test4();?
?????? System.out.println("自定義:");
?????? new TestLambda3().test5();
??? }
??? //自定義供給型接口
public Integer testCusomerFunctionInterface(Integer i,MyFunctionInterface<Integer> customerFun ){
?????? return customerFun.getValue(i);
??? }
??? public void test5(){
?????? Integeri = testCusomerFunctionInterface(1,(m)->(m+1));
?????? System.out.println(i);
??? }
}
運行輸出:
消費型:
旅游消費:10000.0元
消費型:
旅游消費:10000.0元
供給型:
76,62,27,7,25,83,18,69,19,5,
函數(shù)型:
ACB,
斷言型:
true
自定義:
2

作為參數(shù)傳遞Lambda 表達式:為了將Lambda 表達式作為參數(shù)傳遞,接收Lambda 表達式的參數(shù)類型必須是與該Lambda 表達式兼容的函數(shù)式接口的類型。
其它子接口:

四、方法引用與構(gòu)造器引用
package java8;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;
/*
?*一、方法引用:若 Lambda 體中的功能,已經(jīng)有方法提供了實現(xiàn),可使用方法引用
?*??????? ??(可以將方法引用理解為 Lambda 表達式的另外一種表現(xiàn)形式)
?* 1.對象的引用 :: 實例方法名
?* 2.類名 :: 靜態(tài)方法名
?* 3.類名 :: 實例方法名
?*注意:
?* ?①方法引用所引用的方法的參數(shù)列表與返回值類型,需要與函數(shù)式接口中抽象方法的參數(shù)列表和返回值類型保持一致!
?* ?②若Lambda 的參數(shù)列表的第一個參數(shù),是實例方法的調(diào)用者,第二個參數(shù)(或無參)是實例方法的參數(shù)時,格式: ClassName::MethodName
?*二、構(gòu)造器引用 :構(gòu)造器的參數(shù)列表,需要與函數(shù)式接口中參數(shù)列表保持一致!
?* 1.類名 :: new
?*三、數(shù)組引用
?* 類型[] :: new;
?*/
public classTestMethodRef {
??? //對象::實例方法名
??? public void test1(){
?????? PrintStream ps = System.out;
?????? Consumer?con1= ps::println;
?????? con1.accept("abc");
??? }
??? //類::靜態(tài)方法
??? public void test2(){
?????? Comparator?com = Integer::compare;
??? }
??? //類::實例方法名。(如果第一個參數(shù)是實例方法的調(diào)用者,且第二個參數(shù)是實例方法的參數(shù)時才能使用)
??? public void test3(){
??? //? BiPredicate?bp = (x,y)->x.equals(y);????
?????? BiPredicate?bp = String::equals;
??? }
??? //構(gòu)造器引用
??? public void test4(){
??? //? Suppliersup = ()->new Employee();
?????? Supplier?sup = Employee::new;
?????? Employee emp = sup.get();
?????? System.out.println(emp);
??? }
}
五、Stream API
此流非彼流
Java8中有兩大最為重要的改變。第一個是Lambda 表達式;另外一個則是Stream API(java.util.stream.*)。
Stream 是Java8 中處理集合的關(guān)鍵抽象概念,它可以指定你希望對集合進行的操作,可以執(zhí)行非常復(fù)雜的查找、過濾和映射數(shù)據(jù)等操作。使用Stream API 對集合數(shù)據(jù)進行操作,就類似于使用SQL 執(zhí)行的數(shù)據(jù)庫查詢。也可以使用Stream API 來并行執(zhí)行操作。簡而言之,Stream API 提供了一種高效且易于使用的處理數(shù)據(jù)的方式。
流(Stream) 到底是什么呢?
是數(shù)據(jù)渠道,用于操作數(shù)據(jù)源(集合、數(shù)組等)所生成的元素序列?!凹现v的是數(shù)據(jù),流講的是計算!”
注意:
①Stream 自己不會存儲元素。
②Stream 不會改變源對象。相反,他們會返回一個持有結(jié)果的新Stream。
③Stream 操作是延遲執(zhí)行的。這意味著他們會等到需要結(jié)果的時候才執(zhí)行
Stream 的操作三個步驟:
創(chuàng)建Stream一個數(shù)據(jù)源(如:集合、數(shù)組),獲取一個流中間操作
一個中間操作鏈,對數(shù)據(jù)源的數(shù)據(jù)進行處理終止操作(終端操作)
一個終止操作,執(zhí)行中間操作鏈,并產(chǎn)生結(jié)果

package java8;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/*
?*一、Stream API 的操作步驟:
?* 1.創(chuàng)建 Stream
?* 2.中間操作
?* 3.終止操作(終端操作)
?*/
public classTestSteamAPI {
??? public void test(){
?????? //1、可以通過Collection系列集合提供的stream()或parallStream()
?????? Listlist= newArrayList<>();
?????? Streamstream= list.stream();
?????? //2通過Arrays中的靜態(tài)方法stream()獲取
?????? Employee[]emps= newEmployee[10];
?????? Streamstream1= Arrays.stream(emps);
?????? //3通過Stream類中的靜態(tài)方法of()
?????? Streamstream2= Stream.of("a","b","c");
?????? //4創(chuàng)建無限流
?????? Streamstream3= Stream.iterate(1,(x->x+2));
?????? stream3.limit(10).forEach(System.out::println);
??? }
}
Stream的中間操作
多個中間操作可以連接起來形成一個流水線,除非流水線上觸發(fā)終止操作,否則中間操作不會執(zhí)行任何的處理!
而在終止操作時一次性全部處理,稱為“惰性求值”。
篩選與切片:

映射:

排序:

importjava.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public classTestStreamAPI2 {
??? List?list = Arrays.asList(
?????????? new Employee("zhangsan",56,19999),
?????????? new Employee("lisi",36,169999),
?????????? new Employee("wangwu",66,369999));
??? /**
??? ?*中間操作
??? ?*/
??? //filter舉例
??? public void test1(){
?????? //中間操作:每次循環(huán)不會執(zhí)行任何操作
?????? Stream?es = list.stream().filter((e)->
?????????? {
????????????? System.out.print("fasfa");
????????????? return e.getAge()>30;
?????????? });
?????? //終止操作:一次執(zhí)行全部內(nèi)容(惰性求值)
?????? es.forEach(System.out::println);
??? }
??? //skip舉例
??? public void test2(){
?????? list.stream().filter((e)->{
?????????? return e.getAge()>30;
?????? }).skip(2).forEach(System.out::println);
??? }
??? //映射,map-接收lambda,將元素轉(zhuǎn)換成其它形式或提取信息。并接收一個函數(shù)作為參數(shù),該函數(shù)會作用于每一個元素上
public void?test3(){???????
?????list.stream().map((e)->e.getName().toUpperCase()).forEach(System.out::println);?????????
? ?}
??? //flatMap針對Stream<Stream>,Stream嵌套Stream這種情況
??? public void test4(){
?????? /* Stream<Stream<String>> stream
?????? ?* stream.forEach((sm)->{sm.forEach(System.out::println)})
?????? ?*/
??? }
??? //排序,sorted()自然排序,sorted(Comparatorcom)定制排序
??? public void test5(){
?????? list.stream().sorted((e,ee)->{
?????????? if(e.getAge()>ee.getAge()){
????????????? return e.getName().compareTo(ee.getName());
?????????? }else{
????????????? return e.getName().compareTo(ee.getName());
?????????? }
?????? }).forEach(System.out::println);
??? }
}
查找與匹配:


規(guī)約:

收集:

Collector接口中方法的實現(xiàn)決定了如何對流執(zhí)行收集操作(如收集到List、Set、Map)。但是Collectors 實用類提供了很多靜態(tài)方法,可以方便地創(chuàng)建常見收集器實例,具體方法與實例:


import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.junit.Test;
public classTestStreamAPI3 {
??? List<employees> employeesList= Arrays.asList(
?????????? new Employee("zhangsan",56,19999),
?????????? new Employee("lisi",36,169999),
?????????? new Employee("wangwu",66,369999));
??? //規(guī)約:reduce(T identity,BinaryOperator)/reduce(BinaryOperator)
??? //可以將流中元素反復(fù)結(jié)合起來得到一個值
??? @Test
??? public void test(){
?????? List?list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
?????? Integer sum = list.stream().reduce(0,(x,y)->x+y);
?????? System.out.println(sum);
?????? Optional?op = employeesList.stream()
????????????? .map((e)->e.getAge())
????????????? .reduce(Integer::sum);
?????? System.out.println(op.get());
??? }
??? //收集:collect將流轉(zhuǎn)換為其它形式。接收一個Collector接口的實現(xiàn),用于給Stream元素做匯總的方法
??? @Test
??? public void test1(){
?????? List?list = employeesList.stream().map(Employee::getName).collect(Collectors.toList());
?????? list.forEach(System.out::println);
?????? Long count = employeesList.stream().collect(Collectors.counting());
?????? System.out.println(count);
??? }
}
六、并行流與串行流
并行流就是把一個內(nèi)容分成多個數(shù)據(jù)塊,并用不同的線程分別處理每個數(shù)據(jù)塊的流。
Java 8 中將并行進行了優(yōu)化,我們可以很容易的對數(shù)據(jù)進行并行操作。Stream API 可以聲明性地通過parallel() 與sequential() 在并行流與順序流之間進行切換。
Fork-Join框架的封裝實現(xiàn)。
七、Optional類
非重點了解。
Optional<T> 類(java.util.Optional) 是一個容器類,代表一個值存在或不存在,原來用null 表示一個值不存在,現(xiàn)在Optional 可以更好的表達這個概念。且可以避免空指針異常。
常用方法:
Optional.of(T t) : 創(chuàng)建一個Optional 實例
Optional.empty() : 創(chuàng)建一個空的Optional 實例
Optional.ofNullable(T t):若t 不為null,創(chuàng)建Optional 實例,否則創(chuàng)建空實例
isPresent() : 判斷是否包含值
orElse(T t) : 如果調(diào)用對象包含值,返回該值,否則返回t
orElseGet(Supplier s) :如果調(diào)用對象包含值,返回該值,否則返回s 獲取的值
map(Function f): 如果有值對其處理,并返回處理后的Optional,否則返回Optional.empty()
flatMap(Function mapper):與map 類似,要求返回值必須是Optional
import java.util.Optional;
import org.junit.Test;
/*
?*一、Optional 容器類:用于盡量避免空指針異常
?* Optional.of(Tt) :創(chuàng)建一個 Optional 實例
?* Optional.empty():創(chuàng)建一個空的 Optional 實例
?* Optional.ofNullable(Tt):若 t 不為 null,創(chuàng)建 Optional 實例,否則創(chuàng)建空實例
?* isPresent():判斷是否包含值
?* orElse(Tt) :? 如果調(diào)用對象包含值,返回該值,否則返回t
?* orElseGet(Suppliers) :如果調(diào)用對象包含值,返回該值,否則返回 s 獲取的值
?* map(Functionf):如果有值對其處理,并返回處理后的Optional,否則返回 Optional.empty()
?* flatMap(Functionmapper):與 map 類似,要求返回值必須是Optional
?*/
public classTestOptional {
??? @Test
??? public void test(){
?????? Optional?op = Optional.of(new Employee());
?????? Employee e = op.get();
?????? System.out.println(e);
??? }
}
八、接口中的默認方法與靜態(tài)方法
Java 8中允許接口中包含具有具體實現(xiàn)的方法,該方法稱為“默認方法”,默認方法使用default關(guān)鍵字修飾.
接口默認方法的”類優(yōu)先”原則
若一個接口中定義了一個默認方法,而另外一個父類或接口中又定義了一個同名的方法時
1:選擇父類中的方法。如果一個父類提供了具體的實現(xiàn),那么接口中具有相同名稱和參數(shù)的默認方法會被忽略。
2:接口沖突。如果一個父接口提供一個默認方法,而另一個接口也提供了一個具有相同名稱和參數(shù)列表的方法(不管方法是否是默認方法),那么必須覆蓋該方法來解決沖突
public interface?TestNewInterface {
??? default String getName(){
?????? return "this is testNewInterface";
??? }
??? public static void test(){
?????? System.out.println("thi is testNewInterface function : test");
??? }
}
public interface?TestNewInterface2 {
??? default String getName(){
?????? return "this is testNewInterface2";
??? }
??? public static void test(){
?????? System.out.println("thi is testNewInterface function : test2");
??? }
}
public classTestNewClass {
??? public String getName(){
?????? return "this is testNewClass";
??? }
}
??? public static void main(String[] args) {
?????? TestSubClass t = new TestSubClass();
?????? String str = t.getName();
?????? System.out.println(str);
?????? TestNewInterface.test();
??? }
}
運行結(jié)果:
this is testNewClass
thi is testNewInterface function : test
九、傳統(tǒng)時間格式化安全問題與新時間格式
新時間日期API
使用LocalDate、LocalTime、LocalDateTime、LocalTime、LocalDateTime 類的實例是不可變的對象,分別表示使用ISO-8601日歷系統(tǒng)的日期、時間、日期和時間。它們提供了簡單的日期或時間,并不包含當(dāng)前的時間信息。也不包含與時區(qū)相關(guān)的信息。
public classTestLocalDateTime {
??? @Test
??? public void test(){
?????? LocalDateTime ldt = LocalDateTime.now();
?????? System.out.println(ldt);
??? }
}
Instant 時間戳
?用于“時間戳”的運算。它是以Unix元年(傳統(tǒng)的設(shè)定為UTC時區(qū)1970年1月1日午夜時分)開始所經(jīng)歷的描述進行運算。
@Test
??? public void test1(){
?????? Instant inst = Instant.now();//默認獲取UTC時區(qū)
?????? System.out.println(inst);
?????? System.out.println(inst.toEpochMilli());//轉(zhuǎn)成毫秒時間戳
??? }
Duration:用于計算兩個“時間”間隔,計算兩個時間之間的間隔,示例:
@Test
??? public void test2(){
?????? Instant inst1 = Instant.now();
?????? Instant inst2 = Instant.now();
?????? Duration duration= Duration.between(inst1,inst2);
?????? System.out.println(duration.toMillis());
?????? LocalTime lt1 =LocalTime.now();
?????? LocalTime lt2 = LocalTime.now();
?????? Duration duration2 = Duration.between(lt1,lt2);
?????? System.out.println(duration2.toMillis());
??? }
Period:用于計算兩個“日期”間隔,示例:
@Test
??? public void test3(){
?????? LocalDate ld1 = LocalDate.of(2018, 9, 23);
?????? LocalDate ld2 = LocalDate.now();
?????? Period period = Period.between(ld1,ld2);
?????? System.out.println(period);
?????? System.out.println(period.getYears());
?????? System.out.println(period.getMonths());
?????? System.out.println(period.getDays());
??? }
TemporalAdjuster: 時間校正器。有時我們可能需要獲取例如:將日期調(diào)整到“下個周日”等操作。
TemporalAdjusters : 該類通過靜態(tài)方法提供了大量的常用TemporalAdjuster 的實現(xiàn)
自定義操作時間,時間校正器:
@Test
??? public void test4(){
?????? LocalDateTime ldt = LocalDateTime.now();
?????? LocalDateTime ldt5 = ldt.with((l) -> {
?????????? LocalDateTime ldt4 = (LocalDateTime)l;
?????????? DayOfWeek dow = ldt4.getDayOfWeek();
?????????? if(dow.equals(DayOfWeek.FRIDAY)){
????????????? return ldt4.plusDays(3);
?????????? }else{
????????????? return ldt4.plusDays(1);
?????????? }
?????? });
??? }
解析與格式化
java.time.format.DateTimeFormatter 類:該類提供了三種格式化方法:
1、預(yù)定義的標(biāo)準(zhǔn)格式。2、語言環(huán)境相關(guān)的格式。3、自定義的格式
@Test
??? public void test5(){
?????? //時間解析成字符串
?????? DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
?????? LocalDateTime ldt = LocalDateTime.now();
?????? String str = ldt.format(dtf);
?????? System.out.println(str);
?????? String str1 = dtf.format(ldt);
?????? System.out.println(str1);
?????? //字符串轉(zhuǎn)換成日期
?????? LocalDateTime ldt2 = LocalDateTime.parse(str,dtf);
?????? System.out.println(ldt2);
??? }
十、重復(fù)注解與類型注解
Java 8對注解處理提供了兩點改進:可重復(fù)的注解及可用于類型的注解,自行百度。