JDK13,不如溫習下Java8

JDK13於9月17號正式GA,版本新特性可參考: https://www.oschina.net/news/109934/jdk-13-released
html

雖然JDK更新迅速,但開發者貌似並不買帳,據統計,目前仍以JDK8使用最多,預計可能還會延續好長一段時間。雖然JDK版本已至13,但對Java8的新特性,掌握程度如何呢?
本文對Java8的主要特性進行了梳理。供溫習參考。java

1. 接口默認方法

之前的接口只容許有抽象方法(沒有實現體),java8中提供了接口默認方法支持,便可以提供方法的默認實現,實現類能夠直接繼承,也能夠覆蓋。默認方法主要解決接口的修改致使現有實現類不兼容的問題。python

@RunWith(SpringRunner.class) @SpringBootTest public class InterfaceDefaultFunctionTest { public interface MyFunction<T> { T func(T t); //默認方法
        default int func2(T t){ return t.hashCode(); } //靜態方法
        static<T> void print(T t) { System.out.println(t); } } @Test public void testInterface(){ MyFunction<String> myFunction = new MyFunction<String>(){ @Override public String func(String s) { return s.toUpperCase(); } }; System.out.println(myFunction.func("abc")); System.out.println(myFunction.func2("abc")); LambdaTest.MyFunction.print("efg"); } }

 

默認方法經過關鍵字 default 聲明。同時也能夠在接口中定義靜態方法。 數組

2. 函數式接口

函數式接口就是有且僅有一個抽象方法的接口(能夠有其它非抽象方法),如1所示代碼中 MyFunction 就是一個函數式接口,只有一個抽象方法 func, 其它非抽象方法如默認方法 func2, 靜態方法 print 不影響其函數式接口的特性。安全

函數式接口可使用註解 @FunctionalInterface 標註,該註解會去檢查接口是否符合函數式接口的規範要求,不符合的話IDE會給出提示。微信

java中內置了一些函數式接口,app

函數式接口 描述
Consumer 包含方法 void accept(T t), 對類型爲T的對象t進行操做
Supplier 包含方法 T get(),返回類型爲T的對象
Function<T,R> 包含方法 R apply(T t),對類型爲T的對象進行操做,返回類型R的對象
Predicat 包含方法 boolean test(T t), 判斷類型爲T的對象是否知足條件

以及基於這些接口的其它變種或子接口,如BiConsumer<T,U>,BiFunction<T,U,R>等。還有如Runnable,Callable等接口,也屬於函數式接口 —— 都只有一個抽象方法。ide

@FunctionalInterface public interface BiConsumer<T, U> { void accept(T t, U u); default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) { Objects.requireNonNull(after); return (l, r) -> { accept(l, r); after.accept(l, r); }; } }

 

3. Lambda表達式

lambda表達式實質就是一個匿名函數,在python中很常見,java到了jdk8提供了支持。函數

lambda表達式的格式形如: (參數) -> {方法體語句},當參數只有一個時,左邊小括號能夠省略,當方法體語句只有一條時,右邊大括號能夠省略。工具

Java的lambda表達式基本上是對函數式接口實現的一種簡化 —— 用lambda表達式直接代替一個函數式接口的具體實現(抽象方法的實現)。當咱們使用jdk8在IDE中編寫1中代碼時,IDE會給出提示,
lambda-warn

匿名實現類能夠用lambda表達式替換。上述代碼使用lambda表達式替換可調整爲,

@Test public void testInterface(){ MyFunction<String> myFunction = s -> s.toUpperCase(); System.out.println(myFunction.func("abc")); System.out.println(myFunction.func2("abc")); }

 

 lambda表達式甚至可做爲方法參數傳入(實質也是做爲一個函數式接口的實現類實例)

@FunctionalInterface public interface MyFunction<T> { T func(T t); } public void print(MyFunction<String> function, String s){ System.out.println(function.func(s)); } @Test public void testInterface(){ //將lambda表達式做爲方法參數傳入
   print((String s) -> s.toUpperCase(), "abc"); }

 

局部變量在lambda表達式中是隻讀的,雖可不聲明爲final,但沒法修改。如

@Test public void testInterface(){ int i = 1; //lambda表達式中沒法修改局部變量i,將報編譯錯誤
    print((String s) -> {i = i+10; return s.toUpperCase();}, "abc"); }

 

4. 方法引用 

當須要使用lambda表達式時,若是已經有了相同的實現方法,則可使用方法引用來替代lambda表達式,幾種場景示例以下。

@RunWith(SpringRunner.class) @SpringBootTest public class FunctionReferenceTest { @Test public void testFunctionReference() { // 實例::實例方法
        Consumer<String> consumer = s -> System.out.println(s); //lambda表達式
        Consumer<String> consumer2 = System.out::println; //方法引用
        consumer.accept("abc"); consumer2.accept("abc"); //類::靜態方法
        Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y); //lambda表達式
        Comparator<Integer> comparator2 = Integer::compare; //方法引用
        System.out.println(comparator.compare(10, 8)); System.out.println(comparator2.compare(10, 8)); //類::實例方法, 當引用方法是形如 a.func(b)時,用類::實例方法的形式
        BiPredicate<String, String> biPredicate = (a, b) -> a.equals(b); //lambda表達式
        BiPredicate<String, String> biPredicate2 = String::equals; //方法引用
        System.out.println(biPredicate.test("abc", "abb")); System.out.println(biPredicate2.test("abc","abb")); //type[]::new 數組引用
        Function<Integer,Integer[]> fun= n-> new Integer[n]; //lambda表達式
        Function<Integer,Integer[]> fun2=Integer[]::new; //方法引用
        System.out.println(fun.apply(10)); System.out.println(fun2.apply(10)); //構造器引用
        Function<String,String> func = n-> new String(n); //lambda表達式
        Function<String,String> func2 = String::new; //方法引用
        System.out.println(func.apply("aaa")); System.out.println(func2.apply("aaa")); } }

 

5. Stream API 

Stream與lambda應該是java8最重要的兩大特性。Stream 對集合的處理進行了抽象,能夠對集合進行很是複雜的查找、過濾和映射等操做。提供了一種高效的且易於使用的處理數據的方式。
Stream的三個特性:

  • Stream自己不會存儲元素
  • Stream不會改變操做對象(即集合),會返回一個新的Stream
  • Stream的中間操做不會馬上執行,而是會等到須要結果的時候才執行

Java8 的Collection接口包含了兩個方法 stream(), parallelStream(), 分別返回一個順序流與一個並行流,全部Collection類型(如List, )的對象能夠調用這兩個方法生成流。
Java8 的Arrays類也提供了 stream(T[] array)等方法用以生成流。也可使用靜態方法 Stream.iterate() 和 Stream.generate() 來建立無限流。

Stream的中間操做包括

操做 描述
filter(Predicate p) 接收 Lambda , 從流中過濾出知足條件的元素
distinct() 經過hashCode() 和 equals() 去除重複元素
limit(long maxSize) 截斷流,使元素的個數不超過給定數量
skip(long n) 跳過前面的n個元素,若流中元素不足n個,則返回一個空流
map(Function f) 將每一個元素使用函數f執行,將其映射成一個新的元素
mapToDouble(ToDoubleFunction f) 將每一個元素使用f執行,產生一個新的DoubleStream流
mapToInt(ToIntFunction f) 將每一個元素使用f執行,產生一個新的IntStream流
mapToLong(ToLongFunction f) 將每一個元素使用f執行,產生一個新的LongStream流
flatMap(Function f) 將流中的每一個值都經過f轉換成另外一個流,而後把全部流鏈接成一個流
sorted() 按天然順序排序,產生一個新流
sorted(Comparator comp) 根據比較器排序,產生一個新流
allMatch(Predicate p) 判斷是否匹配全部元素
anyMatch(Predicate p) 判斷是否匹配至少一個元素
noneMatch(Predicate p) 判斷是否沒有匹配任意元素
findFirst() 返回第一個元素
findAny() 返回任意一個元素
reduce(T iden, BinaryOperator b) 對流中的元素進行reduce操做,返回T類型對象
reduce(BinaryOperator b) 對流中的元素進行reduce操做,返回Optional對象

Stream的終止操做包括

操做 描述
count() 返回元素總數
max(Comparator c) 返回最大值
min(Comparator c) 返回最小值
forEach(Consumer c) 內部迭代調用Consumer操做
collect(Collector c) 將流轉換爲其餘形式,通常經過Collectors來實現

Stream使用示例

@Test public void testStream() { List<User> list = new ArrayList<>(); //轉換爲List,這裏沒啥意義,僅作示範
    List<User> users = list.stream().collect(Collectors.toList()); //轉換爲Set
    Set<User> users1 = list.stream().collect(Collectors.toSet()); //轉換爲Collection
    Collection<User> users2 = list.stream().collect(Collectors.toCollection(ArrayList::new)); //計數
    long count = list.stream().collect(Collectors.counting()); //求和
    int total = list.stream().collect(Collectors.summingInt(User::getAge)); //求平均值
    double avg= list.stream().collect(Collectors.averagingInt(User::getAge)); //獲取統計對象,經過該統計對象可獲取最大值,最小值之類的數據
    IntSummaryStatistics iss= list.stream().collect(Collectors.summarizingInt(User::getAge)); //將值經過","拼接
    String str= list.stream().map(User::getName).collect(Collectors.joining(",")); //最大值
    Optional<User> max= list.stream().collect(Collectors.maxBy(Comparator.comparingInt(User::getAge))); //最小值
    Optional<User> min = list.stream().collect(Collectors.minBy(Comparator.comparingInt(User::getAge))); //從累加器開始,對指定的值,這裏是年齡,進行sum的reduce操做
    int t =list.stream().collect(Collectors.reducing(0, User::getAge, Integer::sum)); //對轉換的結果再進行處理
    int how = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)); //分組
    Map<String, List<User>> map= list.stream().collect(Collectors.groupingBy(User::getName)); //根據條件進行分區
    Map<Boolean,List<User>> vd= list.stream().collect(Collectors.partitioningBy(u -> u.getName().startsWith("W"))); }

 

6. Optional類 

Optional是一個容器類,能夠避免顯式的null判斷,基本使用示例以下

@RunWith(SpringRunner.class) @SpringBootTest public class OptionalTest { @Test public void testOptional(){ // of 不容許傳入null值,不然拋出NPE
        Optional<Integer> optional = Optional.of(new Integer(10)); System.out.println(optional.get()); // ofNullable 容許傳入null,可是直接調用get會拋出NoSuchElementException異常, // 可經過isPresent判斷是否存在值
        Optional<Integer> optional1 = Optional.ofNullable(null); if(optional1.isPresent()) { System.out.println(optional1.get()); }else{ System.out.println("optional1 is empty"); } // orElse 判斷是否存在值,存在則返回,不存在則返回參數裏的值
        Integer value = optional1.orElse(new Integer(0)); // map方法,若是optional有值,則對值進行處理返回新的Optional, // 若是沒有值則返回Optional.empty()
        optional = optional.map(x -> x*x); System.out.println(optional.get()); // 與map相似,只是要求返回值必須是Optional,進一步避免空指針
        optional = optional.flatMap(x ->Optional.of(x*x)); System.out.println(optional.get()); } }

 

7. Base64 

在java8中,Base64成爲了java類庫的標準,可直接使用

import java.util.Base64; @RunWith(SpringRunner.class) @SpringBootTest public class Base64Test { @Test public void testBase64(){ //base64編碼
        String encode = Base64.getEncoder().encodeToString("abc".getBytes()); System.out.println(encode); //base64解碼
        System.out.println(new String(Base64.getDecoder().decode(encode))); } }

 

8. 日期時間類 

之前的Date類是非線程安全的,而且一些經常使用的日期時間運算須要本身編寫util工具類。java8推出了java.time包,裏面包含了如 LocalDate, LocalTime, LocalDateTime等類,可方便地進行日期時間的運算,如日期間隔、時間間隔,日期時間的加減,格式化等等。

 

—————————————————————————————
做者:空山新雨
歡迎關注個人微信公衆號:jboost-ksxy
微信公衆號

原文出處:https://www.cnblogs.com/spec-dog/p/11555352.html

相關文章
相關標籤/搜索