Java|Java8 新特性
- 2019 年 11 月 7 日
- 筆記
前言 Java8已經出現了很久了,但是呢,還是有很多人都還在習慣使用以前的來版本JDK,或者是以前老版本的編程方式。通過一段時間對Java8新特性的學習之後,自己也深受感觸,原來java8版本寫程式碼可以這麼簡潔。Java8最主要的就是給我們編程的過程帶來了很多便利,不僅僅是程式碼量少。更多的還是讓程式更簡潔,減少程式碼冗餘。Java8新特性中有幾個比較主要的特性,Lambda 表達式、函數式介面、方法引用和幾個新增庫Stream API、Optional類等。 接下來就是對自己的學習內容做一個總結。 Lambda 表達式 2.1 Lambda表達式簡介 Lambda 表達式其實就是一種匿名函數,在這之前大家可能還接觸過匿名內部類,雖然也是一種匿名函數,但是它並沒有Lambda表達式那麼簡潔,Lambda表達式的特點就是可以傳遞,在使用Lambda表達式的時候我們可以將程式碼像數據一樣傳遞。使用Lambda表達式能夠極大的減少我們程式碼的冗餘,而且使用也相當的方便。熟練之後會大大加快我們寫程式碼的速度。 2.2 java8的方法與老版本的對比 首先我們來嘗試著找出公司員工年齡大於35歲的所有員工,這個問題都不是很難吧。我們用不同的方法來實現一下,我先給出幾個不同的方法直接看看效果。 首先我們應該做的是創建員工的數據,先創建一個Staff類來代表員工,並且使用構造函數來創建員工的集合。第一種方法來看看我使用的最常規的方法: public List<Staff> getStaffs(List<Staff> staffs) { List<Staff> stas=new ArrayList<>(); for(Staff sta:staffs){ if (sta.getAge()>=35){ stas.add(sta); } } return stas; } @Test public void test1(){ List<Staff> list = getStaffs(staffs); for (Staff staf:list) { System.out.println(staf); } } 只看一種方法可能沒有什麼感覺,那麼再看看第二種方法試試呢。 這種方法也叫策略設計模式,我們需要寫一個介面,然後實行這個介面,最後是調用介面。這種方法看起來就比前面的那種更有層次,而且假如又需要查找員工工資大於5000的員工的話,我們就可以重新寫一個類來實現介面就好了,下面來看看程式碼。 public List<Staff> filterStaff(List<Staff> list,MyPredicate<Staff> mp){ List<Staff> staffs=new ArrayList<>(); for (Staff staff:list) { if (mp.test(staff)){ staffs.add(staff); } } return staffs; } @Test public void test2(){ List<Staff> list = filterStaff(staffs,new filterStaffByAge()); for (Staff staf:list) { System.out.println(staf); } } 第三種方法是使用匿名內部類的方法,其實也是和第二種方法是差不多的。 public void test3(){ List<Staff> list=filterStaff(staffs, new MyPredicate<Staff>() { @Override public boolean test(Staff staff) { return staff.getAge()>=35; } }); for (Staff staf:list) { System.out.println(staf); } } 這些方法都是java8之前可以使用的一些方法,那麼java8以後有哪些方法呢?前面的方法看了之後有沒有覺得很麻煩呢?要麼是重新寫方法,要麼就是重新實現介面,這樣就感覺很繁瑣。 Java8之後就不用再重新實現介面啦。 public void test4(){ List<Staff> list=filterStaff(staffs,(e)->e.getAge()>=35); list.forEach(System.out::println); } 不知道你有沒有發現什麼問題呢?是不是有一部分程式碼直接代替了介面實現的類啊。那就是我們的Lambda表達式,代替介面實現的程式碼如下所示,非常的簡短。 (e)->e.getAge()>=35) 有沒有發現我們前面的方法都建立在了同一個filterStaff函數上面呢? 那麼如果我們現在只有員工數據,沒有filterStaff函數java8可不可以實現找出年齡大於35的員工呢?那答案是肯定的,那就要用到java8中的Stream API啦。 程式碼如下: public void test5(){ staffs.stream() .filter((e)->e.getAge()>=35) .forEach(System.out::println);} 沒錯,就短短的4行程式碼就能實現我們前面寫的一大堆程式碼。 看到這裡我相信大家已經提起學習java8新特性的興趣來了吧。 2.3 Lambda表達式語法 Lambda表達式的語法基礎有哪些? (1) Lambda 表達式的語法基礎: Java8中引入了一個新的操作符「->」該操作符稱為箭頭操作符或Lambda 操作符。 箭頭操作符將Lambda表達式拆分成兩部分: 左側:Lambda 表達式參數列表。 右側:Lambda 表達式中所需要執行的功能,即Lambda 體。 語法格式一:無參數,無返回值。 () ->System.out.println("Hello"); 語法格式二:有一個參數,並且無返回值。 (e) -> System.out.println(e); 語法格式三:若只有一個參數,參數的小括弧可以省略。 e -> System.out.println(e); 語法格式四:有兩個以上的參數,又返回值,有多條執行語句。 (x,y)-> { System.out.println("Lambda"); return Integer.compare(x,y); }; 語法格式五:如果有兩個以上的參數,又返回值,只有一條執行語句,Lambda 體的大括弧和return可以省略。 Comparator<Integer>con=(x,y)->Integer.compare(x,y); 語法格式六:Lambda表達式的參數列表的數據類型可以省略不寫,因為JVM編譯器可以通過上下文推斷出數據類型,即「類型推斷」。 (Integer x,Integery)->Integer.compare(x,y); (2) Lambda 表達式需要「函數式介面」的支援。 函數式介面:介面中只有一個抽象方法,稱為函數式介面。可以使用註解@FunctionalInterface修飾,可以檢測是否為函數式介面。 2.4 Java8 四大內置核心函數式介面 上面有提到,Lambda表達式需要「函數式表達式」的支援,我們都已經知道,函數式介面只能有一個抽象方法。是不是每次我們都需要創建一個介面呢?當然不是,其實java8給我們準備了現成的函數式介面讓我們使用,除了很特殊的時候需要自己寫函數式介面外,大部分地方我們都可以使用內置的函數式介面。來看看四大內置介面及其使用方法。 (1) Consumer<T>:消費型介面 void accep(T t); (2) Supplier<T>:供給型介面 T get(); (3) Function<T,R>:函數型介面 R apply(T t); (4) Predicate<T>:斷言型介面 boolean Test(T t); List<Integer> numlist=getNumList(10,()-> (int)Math.random()100); for (Integer num:numlist) { System.out.println(num); } } public List<Integer> getNumList(int num, Supplier<Integer> sup){ List<Integer> list=new ArrayList<>(); for (int i = 0; i < num; i++) { Integer n=sup.get(); list.add(n); } return list; } //Function<T,R>:函數型介面 //用於處理字元串 public String StrHandler(String str, Function<String,String> fun){ return fun.apply(str); } @Test public void test3(){ String str=StrHandler("ttt 就是對快睡覺",(x)->x.trim()); System.out.println(str); String uper=StrHandler("ttt 就是對快睡覺",(x)->x.toUpperCase()); System.out.println(uper); String newStr=StrHandler("ttt 就是對快睡覺",(x)->x.substring(2,5)); System.out.println(newStr); } //Predicate<T>:斷言型介面 //將滿足條件的字元串放入集合中 public List<String> FilterStr(List<String> list, Predicate<String> pre){ List<String> strList=new ArrayList<>(); for (String str:list) { if (pre.test(str)){ strList.add(str); } } return strList; } @Test public void test4(){ List<String> list= Arrays.asList("aaa","bbb","ccc","bbbb","jahdgja"); List<String> strList=FilterStr(list,s -> s.length()>3); for (String str:strList) { System.out.println(str); } } 2.5 使用」::」方法引用 (1) 方法引用:若Lambda 體中的內容有方法已經實現了,我們可以使用「方法引用」(可以理解為方法引用是Lambda表達式的另外一種表現形式) 主要有三種語法格式: ①對象::實例方法名 ②類::靜態方法名 ③類::實例方法名 注意:Lambda 體中調用方法的參數列表和返回值類型要與函數式介面中抽象方法的參數列表的返回值一致。若Lambda 參數列表中的第一個參數是實例方法的調用者,而第二個參數是實例方法的參數時,可以使用ClassNane::nethod (2) 構造器引用 格式:ClassName::new 注意:需要調用的構造器的參數列表要與函數式介面中抽象方法的參數列表保持一致! (3) 數組引用 Type::new Stream API 3.1簡介 Java8中有兩大最為重要的改變。第-一個是Lambda表達式;另外一個則是Stream API(java. util. stream. *)。 Stream是Java8中處理集合的關鍵抽象概念,它可以指定你希望對集合進行的操作,可以執行非常複雜的查找、過濾和映射數據等操作。使用Stream API對集合數據進行操作,就類似於使用SQL 執行的資料庫查詢。也可以使用Stream API來並行執行操作。簡而言之,StreamAPI提供了一種高效且易於使用的處理數據的方式。說實話,Stream學起來要比Lambda表達式簡單多了,畢竟這只是API。 3.2 創建Stream 創建Stream其實就幾種,我直接上程式碼吧。 public void test1(){ //1.可以通過Collection系列集合提供的stream()或parallelStream() List<String> list=new ArrayList<>(); Stream<String> stream1 = list.stream(); //2.通過Arrays中的靜態方法stream()獲取數組流 Staff[] staffs=new Staff[10]; Stream<Staff> stream2 = Arrays.stream(staffs); //3.通過Steam類中的靜態方法of() Stream<String> stream3 = Stream.of("aaa","bbb","ccc"); //4.創建無限流 //迭代 Stream<Integer> stream4 = Stream.iterate(0,(x)->x+2); stream4.forEach(System.out::println); //生成 Stream.generate(()->Math.random()); } 3.3 Stream的使用 Stream的作用主要就是對數據的操作,了解了他下面的一些方法之後用起來就很方便。其中重要的有以下幾個: (1) forEach:Stream 提供了新的方法 'forEach' 來迭代流中的每個數據。 (2) map:map 方法用於映射每個元素到對應的結果。 (3) filter:filter 方法用於通過設置的條件過濾出元素。 (4) limit:limit 方法用於獲取指定數量的流。
(5) sorted:sorted 方法用於對流進行排序。
(6) 並行(parallel)程式:parallelStream 是流並行處理程式的代替方法。
(7) Collectors:Collectors 類實現了很多歸約操作,例如將流轉換成集合和聚合元素。Collectors可用於返回列表或字元串。
(8) 另外,一些產生統計結果的收集器也非常有用。它們主要用於int、double、long等基本類型上。如:getMax()、getMin()、getSum()、getAverage()等等。
結語
總的來說java8新特性的難點還是在Lambda表達式上,並且這也是重點。因此,我建議在學習java8新特性時應該在Lambda表達式上多下功夫,這是一個全新的編程方式,因此會出現很多錯誤。起初需要很細心才能慢慢學好。不得不承認Java8的魅力確實很大。