1. 程式人生 > >Java8的新特性(一)

Java8的新特性(一)

主要內容

  1. Lambda表示式
  2. 函式式介面
  3. 方法引用和構造器引用
  4. Stream API
  5. 介面中預設的方法與靜態方法
  6. 新時間API

Java8新特性的簡介

  • 速度更快
  • 程式碼更少(增加了新的語法Lambda表示式)
  • 強大的Stream API
  • 便於並行
  • 最大化減少空指標的異常(Optional)

– 其中最重要的就是Lambda表示式和Stream API

1-Lambda表示式

為什麼要使用Lambda表示式

Lambda是一個匿名函式,我們可以把Lambda表示式理解為一段可以傳遞的程式碼(將程式碼像資料一樣可以進行傳遞)。可以寫出靈活、更簡潔的程式碼。作為一種更緊湊的程式碼風格,使Java的語言表達能力得到提升。


Java8新特性的初體驗:利用Lambda表示式可以解決匿名內部類的問題;

  • 原來的比較兩個數的大小可以這樣來寫:
    /**
     * 原來的匿名內部類
     */
    @Test
    public void test1() {
        Comparator<Integer> com = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return
Integer.compare(o1,o2); } }; TreeSet<Integer> ts = new TreeSet<>(com); }

上面的程式碼實際上最關鍵的程式碼就只有一句:

Integer.compare(o1,o2);

現在有了Lambda表示式就可以這樣來寫:

    /**
     * Java8的Lambda表示式就可以解決匿名內部類的問題
     */
    @Test
    public void test2() {
        Comparator<
Integer>
com = (x, y) -> Integer.compare(x, y); TreeSet<Integer> ts = new TreeSet<>(com); }

  • 我們還可以再舉一個栗子:
    • 之前我們寫上一個類去實現Runnable介面,然後重寫run方法,我們可以用匿名內部類去寫:
      程式碼如下:
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello World!");
        }
    };

現在我們有了Lambda表示式了,我們就可以這樣來寫,非常的簡便:

	Runnable runnable = () -> System.out.println("Hello World");

我們還可以舉一個栗子:

  • 我們經常會遇到這樣的需求:獲取當前公司中員工年齡大於35的員工的資訊
    原來我們可以這樣來寫:
    List<Employee> employees = Arrays.asList(
            new Employee("張三", 18, 9999.99),
            new Employee("李四", 38, 5555.55),
            new Employee("王五", 50, 6666.66),
            new Employee("趙六", 16, 3333.33),
            new Employee("田七", 8, 7777.77)
    );
    /** 需求:獲取當前公司中員工年齡大於35的員工的資訊 */
    @Test
    public List<Employee> filterEmployees(List<Employee> list) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : list) {
            if (emp.getAge() >= 35) {
                emps.add(emp);
            }
        }
        return emps;
    }

我們對上面的程式碼進行單元測試,如下圖,我們已經對當前公司中員工年齡大於35的員工的資訊進行了過濾:
在這裡插入圖片描述
假如這個時候,又來了一個需求:獲取當前公司員工工資大於5000的員工的資訊,這個時候,我又得重新寫個方法,其實我們對比一下方法,要改動的地方就一句,因為這一句又得重新寫上一個方法,非常的麻煩;
我們心裡的第一想法就是對這個方法進行優化,我們可以利用一些設計模式來對其進行優化;

  • 優化方式一:利用策略設計模式來進行優化
    我們可以寫上一個介面:
public interface MyPredicate<T> {
    public boolean test(T t);
}

我們寫上一個類來實現這個介面,如果員工的年齡大於35的話,我們就返回true:

public class FilterEmployeeByAge implements MyPredicate<Employee> {
    @Override
    public boolean test(Employee emp) {
        return emp.getAge()>=35;
    }
}

我們就可以這樣來用了:

    public List<Employee> filterEmployees(List<Employee> list,MyPredicate<Employee> mp) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : list) {
            if (mp.test(emp)) {
                emps.add(emp);
            }
        }
        return emps;
    }

然後,我們就可以對其進行測試了:
測試結果是正確的
這個時候,我們對介面的實現類進行擴充套件就可以了,給它什麼策略進行過濾,就可以按照什麼方式來進行過濾;但是這種方式不好的地方就是:每次實現一個策略的時候,還要單獨的寫上一個類;

  • 優化方式二:利用匿名內部類,利用匿名內部類來實現介面的方式來進行實現
    在這裡插入圖片描述
    這個時候,又回到了匿名內部類的程式碼冗餘的問題。
  • 優化方式三:Lambda表示式
    程式碼如下:
List<Employee> list = filterEmployees(this.employees, (e) -> e.getSalary() <= 5000)
list.forEach(System.out::println);

測試結果如下圖:
在這裡插入圖片描述

  • 優化方式四:利用Stream API
    程式碼如下:
    /** 僅僅就是對上面資料進行過濾,其他的什麼之前的介面都沒有 */
    @Test
    public void test7() {
        employees.stream()
                .filter((e) -> e.getSalary()>=5000)
                .forEach(System.out::println);
    }

測試結果如下:
在這裡插入圖片描述
如果我們只想取這四條資料的前面兩條:我們還可以這樣來寫:
在這裡插入圖片描述
我們還可以舉一個常見的操作的栗子:把員工資訊裡面的所有的員工的名字給提取出了,我們就可以這樣來寫:

    /** 把員工資訊裡面的所有的名字給提取出來 */
    @Test
    public void test8() {
        employees.stream()
                .map(Employee::getName)
                .forEach(System.out::println);
    }

效果如下:
在這裡插入圖片描述


Lambda基礎語法

一、Lambda表示式的基本語法:
Java8中引入了一個新的操作符"->"該操作符也稱為箭頭操作符或Lambda操作符;箭頭操作符將Lambda表示式拆分成兩部分;

  • 左側:Lambda表示式的引數列表;
  • 右側:Lambda表示式所需要執行的功能:即Lambda體;

  1. 語法格式一:無參,無返回值,Lambda體只需要一條語句
 () -> System.out.println("Hello World!");

舉例如下:這是兩種不同的實現方式:

    @Test
    public void test1() {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World!");
            }
        };
        runnable.run();

        Runnable runnable1 = () -> System.out.println("Hello World!");
        runnable1.run();
    }

有一個小的注意事項:在區域性內部類中,引用了一個同級別的區域性變數時,在Jdk1.7的時候是會報錯的,但在Jdk1.8是可以的,實際上它是省略了final關鍵字,預設給我們加上了:
在這裡插入圖片描述

  1. 語法格式二:有一個引數,並且無返回值
(x) -> System.out.println(x);

舉例,程式碼如下:

    @Test
    public void test2() {
        Consumer<String> con = (x) -> System.out.println(x);
        con.accept("我愛Java");
    }

分析如下:對介面中抽象方法的實現
在這裡插入圖片描述
3. 語法格式三:若只有一個引數的話,小括號可以省略不寫,但是一般我們通常都給它寫上。

x -> System.out.println(x);

舉例程式碼如下:

    @Test
    public void test3() {
        Consumer<String> con = x -> System.out.println(x);
        con.accept("我愛Java");
    }
  1. 語法格式四:有兩個以上的引數,有返回值,並且Lambda體中有多條語句,如果有多條語句,那麼大括號是不能省略的;
    @Test
    public void test4() {
        Comparator<Integer> com  = (x,y)->{
            System.out.println("函式式介面");
            return Integer.compare(x, y);
        };
    }
  1. 語法格式五:若Lambda體中只有一條語句,return和大括號都可以省略不寫
    @Test
    public void test5() {
        Comparator<Integer> com  = (x,y)->Integer.compare(x, y);
    }
  1. 語法格式六:Lambda表示式的引數列表的資料型別可以省略不寫,因為JVM編譯器可以通過上下文推斷出資料型別,我們稱為型別推斷;
  • 這裡的引數列表的資料型別是可以不寫的:
    @Test
    public void test6() {
        Comparator<Integer> com  = (Integer x,Integer y)->Integer.compare(x, y);
    }

總結
(1)左右遇一括號省:"->“左邊只有一個引數時,括號可以省;”->"右邊只有一條語句時,大括號可以省;
(2)左側推斷型別省:左側的引數列表裡面的引數可以根據上下文進行型別的推斷,可以省略資料型別不寫;


二、Lambda表示式需要函式式介面的支援:
函式式介面:介面中只有一個抽象方法的介面,稱為函式式介面。可以使用一個註解:@FunctionalInterface來進行修飾一下,可以檢查當前的介面是否為函式式介面;


簡單使用一下:
需求:對一個數進行運算
1)定義一個介面:

	@FunctionalInterface
	public interface MyFunction {
	    public Integer getValue(Integer num);
	}

2)定義一個方法:傳入一個數和一個介面,介面在呼叫的時候利用Lambda進行實現:

    public Integer operation(Integer num, MyFunction myFun) {
        return myFun.getValue(num);
    }

3)利用Lambda表示式對介面進行實現,並且重寫裡面的方法:

    @Test
    public void test(){
        Integer num = operation(100, x -> x * x);
        System.out.println(num);
    }

如果我們想要進行加法的運算,我們就可以這樣來寫:

    @Test
    public void test9(){
        System.out.println(operation(200, (y)->y+200));
    }