Java 8於14年發佈到如今已經有5年時間了,通過時間的磨練,毫無疑問,Java 8是繼Java 5(發佈於2004年)以後的又一個很是最重要的版本。由於Java 8裏面出現了很是多新的特徵,這些特徵主要包含語言、編譯器、庫、工具和JVM等方面,具體以下:html
以上最值得咱們學習的應該就是Lambda表達式、Stream API和新的日期處理類。並非說其餘的就不用去學了,仍是要去了解一下的,而這三個對咱們來講很重要因此必須學習。java
Lambda表達式本質上是一個匿名函數(方法),它沒有方法名,沒有權限修飾符,沒有返回值聲明。看起來就是一個箭頭(->)從左邊指向右邊。咱們能夠把Lambda表達式理解爲一段能夠傳遞的代碼(將代碼像數據同樣進行傳遞),它的核心思想是將面向對象中的傳遞數據變成傳遞行爲。Lambda表達式的出現就是爲了簡化匿名內部類,讓匿名內部類在方法中做爲參數的使用更加方便(這裏我的理解,可能有誤!)。因此使用Lambda表達式可讓咱們的代碼更少,看上去更簡潔,代碼更加靈活。而Lambda表達式做爲一種更緊湊的代碼風格,使得Java的語言表達能力獲得了提高。但也有它的缺點所在,若是Lambda表達式用的很差的話,調試運行和後期維護很是的麻煩。express
Lambda表達式在Java語言中引入了一個新的語法元素和操做符。這個操做符爲"->",該操做符被稱爲Lambda操做符或箭頭操做符,它將Lambda分爲兩個部分:數組
Java8中的Lambda表達式的基本語法爲:app
(params) -> expression
(params) -> statement
(params) -> { statements }dom
上面只是基本的語法而已,因此看起來比較的簡單,其中的具體使用方法有不少,以下:編輯器
①、無參數,無返回值 void。
() -> System.out.print(「Lambda…」) ;ide②、有一個參數,但無返回值 void 。函數
(String s) -> System.out.print(「Lambda…」) ;工具
③、有參數,可是參數數據類型省略,由編譯器推斷,稱爲‘類型推斷’。
(s) –> System.out.print(「Lambda…」) ;
④、若只有一個參數,方法的括號能夠省略,若是多個參數則必須寫上 。
s–> System.out.print(「Lambda…」) ;
⑤、有參數,且有返回值,若是顯式返回語句時就必須使用花括號「{}」。
(s,t) –> s+t ;
或
(s,t) –> {return s+t;};
⑥、若是有兩個或兩個以上的參數,而且有多條語句則須要加上「{}」,一條執行語句能夠省略。
(s,t) –> {
System.out.print(s) ;
System.out.print(t) ;
return s+t;
};
因此到目前爲止,咱們對Lambda表達式有了基本的認識,而前面講了那麼多的理論,就是爲了接下來的快樂時光(寫代碼),用幾個簡單的例子來讓咱們好好理解一下Lambda表達式的使用:
public class LambdaTest { public static void main(String[] args) { //一、建立線程舉例 //普通寫法 Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("Ordinary Writing"); } }); thread.start(); //Lambda寫法。無參無返回void Thread thread1 = new Thread(() -> System.out.println("Lambda Writing")); thread1.start(); //二、排序舉例 //普通寫法,默認升序 List<Integer> list = Arrays.asList(26, 65, 13, 79, 6, 123); Collections.sort(list,new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1, o2); } }); System.out.println(list.toString()); //Lambda表達式寫法。有參有返回,「類型推斷」 Collections.sort(list,(o1,o2)-> Integer.compare(o1,o2)); System.out.println(list.toString()); //三、遍歷list集合 //普通寫法 list.forEach(new Consumer<Integer>() { @Override public void accept(Integer integer) { System.out.println(integer); } }); //Lambda表達式寫法。遍歷List集合,forEach方法中的參數是Consumer<? super T> action //其中Consumer是Java8新的新出現的函數式接口,下面會講到函數式接口 list.forEach(alist-> System.out.println(alist));//只有一個參數可省略括號 } }
注意:要使用Lambda表達式的前提是函數式接口,因此接下來學習一下函數式接口。
函數式接口(Functional Interface)也是Java8中的新特徵。
函數式接口就是隻能有一個抽象方法,同時能夠有多個非抽象方法的接口(Java8中接口能夠定義普通方法)。
這樣接口就能夠被隱式轉換爲Lambda表達式。
若是咱們須要自定義一個函數式接口,就須要用到Java 8提供的一個特殊的註解@FunctionalInterface,該註解會檢查它是不是一個函數式接口,簡單的舉個定義函數式接口的例子,代碼以下:
//定義函數式接口註解 @FunctionalInterface public interface MyInterface { //抽象方法 void method(); //void method1();不能再定義 //默認方法,必須用default修飾 default void defaultMethod(){ System.out.println("默認方法..."); } //靜態方法方法 static void staticMethod(){ System.out.println("靜態方法..."); } }
上面的例子能夠很容易的轉換成以下Lambda表達式:
MyInterface myInterface = () -> System.out.println("MyInterface...");
我須要注意的一點是,接口中的默認方法和靜態方法並不會破壞函數式接口的定義,既不會影響到Lambda表達式。同時也正由於Lambda表達式的引入,因此函數式接口也變得流行起來。
其實早在Java8以前就有不少接口是函數式接口,只是在Java8才正式提出之一特性,例如:
除了以上這些,在Java 8中還增長了一個新的包:java.util.function。它們裏面包含了經常使用的函數式接口,該包下定義的函數式接口很是多,這裏只列舉比較重要的四個,以下:
(博客園的表格編輯器真的無力吐槽,實在太垃圾了,而後從其餘編輯器編輯好了在截圖過來!)
如下是這四個函數式接口的簡單舉例,理解Lambda表達式以後很容易寫出來:
public class FunctionInterfaceTest { public static void main(String[] args) { //Consumer<T> : void accept(T t); Consumer<Integer> consumer = (a) -> System.out.println("消費型接口..."+a); consumer.accept(1000); //Function<T, R> : R apply(T t); Function<String,String> function = (b) -> b; Object apply = function.apply("函數型接口..."); System.out.println(apply); //Predicate<T> : boolean test(T t); Predicate predicate = (c) -> c.equals(10); boolean test = predicate.test(10); System.out.println("判定型接口..."+test); //Supplier<T> : T get(); Supplier supplier=()->(int)(Math.random() * 50); Object o = supplier.get(); System.out.println("供給型接口..."+o); } }
除了上面的這些基本的函數式接口,java.util.function包下還提供了一些針對多個參數的函數式接口,例如BiFunction<T,U,R>,它接收類型爲T和U的對象,而後返回R對象。後面還有BiConsumer<T,U>、BiPredicate<T,U>等。一樣還提供一些針對基本數據類型的特化函數式接口,例如XXXFunction:表示只接收XXX數據類型、XXXToXXXFunction:接收前一個XXX類型,而後返回後一個XXX類型、ToXXXFunction:表示返回值爲XXX類型等等不少這樣的類。(其中XXX只能是Int、Double和Long這三個基本數據類型)
若是有須要學習這些API的,能夠自行去java.util.function包下查看學習,這裏很少作描述。
經過上面Lambda表達式的學習,若是你認爲Lambda表達式已經讓代碼夠簡潔了,那麼這裏還有一個更加簡潔的方法——方法引用。
簡單來講,方法引用就是進一步的簡化Lambda表達式聲明的一種語法糖。也正是由於方法引用實在太簡潔了,因此學習方法引用前必需要對Lambda表達式很是的熟悉,不然學習方法引用會有點吃力。
方法引用使用操做符 「::」 將對象或類的名字和方法名分隔開來。方法引用有不少種,它們的語法以下(注意後面是不要寫括號的):
在使用方法引用時要注意一點:引用的函數一定與定義的接口形參和返回值類型一致。
先用System.out.println()簡單舉例:
//System.out.println()簡單舉例 @Test public void test() { Consumer<String> consumer = (str) -> System.out.println(str); consumer.accept("Lambda表達式"); PrintStream out = System.out; Consumer consumer1 = out::println; consumer1.accept("方法引用"); }
其中Consumer中的accept(T t)方法中是一個參數,返回值是void,PrintStream中的println(Object x)也是一個參數,返回值也是void 。
①、靜態方法引用。語法格式:ClassName::staticMethodName。
//一、靜態方法用——ClassName::staticMethodName @Test public void test1() { //Lambda表達式 BiFunction<Double, Double, Double> biFunction = (x, y) -> Math.max(x, y); System.out.println(biFunction.apply(11.1, 22.2)); System.out.println("-------------"); //方法引用 BiFunction<Double, Double, Double> biFunction1 = Math::max; System.out.println(biFunction1.apply(33.3, 44.4)); //另一組例子,其中c1與c2是同樣的 Comparator<Integer> c1 = (x, y) -> Integer.compare(x, y); Comparator<Integer> c2 = Integer::compare; }
②、實例上的實例方法引用。語法格式:instanceName::methodName。
//二、實例上的實例方法引用——instanceName::methodName @Test public void test2(){ Consumer<String> consumer = (str) -> System.out.println(str); Consumer consumer1 = System.out::println; Person person = new Person("唐浩榮", 20, "China"); Supplier supplier = () -> person.getName(); Supplier supplier1 = person::getName; }
③、類上的實例方法引用。語法格式:ClassName::methodName 。
//三、類上的實例方法引用——ClassName::methodName public void test3(){ BiPredicate<String, String> biPredicate = (x, y) -> x.equals(y); BiPredicate<String, String> biPredicate1 = String::equals; Function<Person, String> fun = (p) -> p.getName(); Function<Person, String> fun2 = Person::getName; }
④、父類上的實例方法引用。語法格式:super::methodName。
//四、父類上的實例方法引用——super::methodName @Test public void test4(){ Person person=new Person(); Supplier supplier = () -> super.toString(); Supplier supplier1 =super::toString; }
⑤、構造方法引用。語法格式:ClassName::new。
//五、構造方法引用——ClassName::new @Test public void test5() { Function<String, String> function = (n) -> new String(n); String apply = function.apply("Lambda構造方法"); System.out.println(apply); Function<String, String> function1 = String::new; String apply1 = function.apply("構造方法引用"); System.out.println(apply1); }
⑥、數組構造方法引用:TypeName[]::new。
//六、數組構造方法引用——TypeName[]::new @Test public void test6() { Function<Integer, Integer[]> function = (n) -> new Integer[n]; //Integer integer[]=new Integer[20]; Integer[] apply = function.apply(3); apply[0] = 1; for (Integer integer : apply) { System.out.println(integer); } System.out.println("-----------------"); Function<Integer, Integer[]> function1 = Integer[]::new; Integer[] apply1 = function1.apply(5); apply1[0] = 11; apply1[1] = 22; for (Integer integer : apply1) { System.out.println(integer); } }