JDK8 新特性學習筆記

ZGYSYY發表於2018-07-04

java8新特性學習

java8的特點

  1. 速度更快(修改了HasMap、HasSet、CurrentHasMap等儲存結構)
  2. 程式碼更少(增加了新的語法Lambda表示式)
  3. 強大的Stream API
  4. 便於並行
  5. 最大化減少空指標異常

1.Lambda表示式

1.1、Lambda表示式是什麼?

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

1.2、Lambda表示式的基礎語法

Java8中引入了一個新的操作符“->”,該操作符稱為箭頭操作符或者Lambda操作符,該操作符將Lambda表示式拆分成兩個部分:

左側:Lambda表示式的引數列表。

右側:Lambda表示式中需要執行的功能,即Lambda體。

語法格式一:無引數,無返回值

() -> System.out.println("hello Lambda");
複製程式碼

語法格式二:有一個引數,並且無返回值

(x) -> System.out.println(x);
複製程式碼

語法格式三:若只有一個引數,小括號可以省略不寫

x -> System.out.println(x);
複製程式碼

語法格式四:有兩個以上的引數,有返回值,並且Lambda體中有多條語句

Comparator<Integer> com = (x,y) -> {
    System.out.println("函式式介面");
    return Integer.compare(x,y);
};
複製程式碼

語法格式五:若Lambda體中只有一條語句,return和大括號都可以省略不寫

Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
複製程式碼

語法格式六:Lambda表示式的引數列表的資料型別可以省略不寫,因為JVM編譯器通過上下文推斷出資料型別

(Integer x,Integer y) -> Integer.compare(x,y);
複製程式碼

原始碼

package com.zgy.deom1;

import java.util.Comparator;
import java.util.function.Consumer;

import org.junit.jupiter.api.Test;

public class TestLambda2 {

	/*
	 * Lambda基本語法:引數列表 -> 方法體
	 */
	
	/*
	 * 語法格式一:無引數,無返回值
	 * 	() -> System.out.println("Hello World");
	 */
	@Test
	public void test1() {
		int num = 0; //jdk8中當匿名內部類中使用的變數,會自動隱式新增final關鍵字,匿名內部類中依然不能將變數進行加減操作。
		Runnable r = new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("hello world"+num);
			}
		};
		r.run();

		System.out.println("=============================");
		
		Runnable r1 = () -> System.out.println("hello lambda"+num);
		r1.run();
	}
	
	/*
	 * 語法格式二:有一個引數,沒有返回值
	 */
	@Test
	public void test2() {
		Consumer<String> c = (x) -> System.out.println(x);
		c.accept(1+"");
		
		//Lambda表示式中如果引數只有一個,那麼括號可以省略不寫
		Consumer<Object> c1 = x -> System.out.println("哈哈哈");
		c1.accept("ABC");
	}
	
	/*
	 * 語法格式三:有兩個引數,有返回值,Lambda體中有多條語句
	 */
	@Test
	public void test3() {
		Comparator<Integer> com = (x,y) -> {
			System.out.println("好好學習,天天向上!");
			return x+y;
		};
		
		int result = com.compare(20, 30);
		System.out.println("result:"+result);
		
		//如果Lambda體中只有一條語句,可以省略“{}”和return
		Comparator<Integer> c = (Integer x,Integer y) -> Integer.compare(x, y);
		int num = c.compare(30, 30);
		System.out.println("maxNub"+num);
		
		//由於JVM的型別推斷,所以Lambda引數列表的資料型別,可以省略不寫
		Comparator<Integer> c1 = (x,y) -> Integer.compare(x, y);
		int num1 = c1.compare(30, 31);
		System.out.println("maxNub"+num1);
	}
	
	/*
	 * 呼叫自定的函式式介面
	 */
	@Test
	public void test4() {
		MyFun myFun = x -> x+100;
		int value = myFun.getValue(100);
		System.out.println("value:"+value);
	}
	
}
複製程式碼
package com.zgy.deom1;

@FunctionalInterface
public interface MyFun {

	int getValue(int x);
}
複製程式碼

練習程式碼

package com.zgy.deom1;


import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.junit.jupiter.api.Test;

public class TestLambda {

	
	List<Employee> emps = Arrays.asList(
			new Employee(101,"張三",18,9999.99),
			new Employee(102,"李四",59,6666.66),
			new Employee(103,"王五",28,3333.33),
			new Employee(104,"趙六",8,7777.77),
			new Employee(105,"田七",38,5555.55)
			);
	
	/*
	 * 使用Collections對員工列表進行排序,按工資從多到少比
	 * 關於Collections.sort()自定義排序規則,兩個引數x和y,如果要按照升序,就是x在前面;相反如果要按照降序,就是y在前面。
	 */
	@Test
	public void test1() {
		
		Collections.sort(emps, (x,y) -> Double.compare(y.getSalary(), x.getSalary()));
		for (Employee employee : emps) {
			System.out.println(employee);
		}
	}
	
	/*
	 * 定義一個函式式介面,傳如一個引數,返回該引數的大寫形式
	 */
	@Test
	public void test2() {
		
		String str = strHandler("zgy", x -> x.toUpperCase());
		System.out.println("str:"+str);
	}
	
	//小寫轉大寫的方法
	public String strHandler(String str,MyFunction mf) {
		return mf.getValue(str);
	}
	
	/*
	 * 宣告一個帶兩個泛型的函式式介面,泛型型別為<T,R> T為引數,R為返回值,介面中宣告對應的抽象方法
	 * 再使用介面作為引數,計算兩個long型引數的和
	 * 再計算兩個long型引數的積
	 */
	@Test
	public void test3() {
		
		//計算和
		System.out.println(getValue(100L, 200L, (x,y) -> x+y));
		
		//計算積
		System.out.println(getValue(500L, 900L, (x,y) -> x*y));
	}
	
	public long getValue(Long x,Long y,MyFunction2<Long, Long> mf) {
		
		return mf.getValue(x, y);
	}
}
複製程式碼
package com.zgy.deom1;

public class Employee {

	private int id;
	private String name;
	private int age;
	private double salary;
	
	public Employee() {}
	
	public Employee(int id, String name, int age, double salary) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
		this.salary = salary;
	}

	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}
	
}
複製程式碼
package com.zgy.deom1;

@FunctionalInterface
public interface MyFunction {

	public String getValue(String x);
}
複製程式碼
package com.zgy.deom1;

@FunctionalInterface
public interface MyFunction2<T,R> {

	public R getValue(T t1, T t2);
}
複製程式碼

1.3、函式式介面

函式式介面:介面中只有一個抽象方法的介面,稱為函式式介面,可以使用註解@FunctionalInterface修飾,該註解的作用是可以檢查該介面是否為函式式介面。

1.4、Java8內建四大核心函式式介面

JDK8 新特性學習筆記

Consumer:消費型介面

​ void accept(T t);

Supplier:供給型介面

​ T get();

Function<T, R>:函式型介面

​ R apply(T t);

Predicate:斷言型介面

​ boolean test(T t);

其它子介面如下圖

JDK8 新特性學習筆記

原始碼

package com.zgy.deom1;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

import org.junit.jupiter.api.Test;

/*
 * JDK8 內建四大核心函式式介面
 */
public class TestLambda3 {

	/*
	 * 消費式介面:傳一個引數,沒有返回值
	 * Consumer<T>  void accept(T t);
	 */
	@Test
	public void test1() {
		say("好好學習,天天向上!", x -> System.out.println(x+new Date()));
	}
	
	public void say(String str, Consumer<String> c) {
		c.accept(str);
	}
	
	/*
	 * 供給式介面:沒有引數,返回一個結果
	 * Supplier<T>  T get();
	 */
	@Test
	public void test2() {
		List<Integer> listResult = getNumberArr(() -> {
			List<Integer> list = new ArrayList<>();
			for(int i=0; i<10; i++) {				
				list.add(new Random().nextInt());
			}
			return list;
		});
		
		for (Integer integer : listResult) {
			System.out.println("DATA:"+integer);
		}
	}
	
	public List<Integer> getNumberArr(Supplier<List<Integer>> s){
		return s.get();
	} 
	
	/*
	 * 函式式介面:傳入一個引數,返回一個結果
	 * Function<T,R>  R apply(T t);
	 */
	@Test
	public void test3() {
		System.out.println(apply("ZGY:", (x) -> x+new Date()));
	}
	
	public String apply(String a, Function<String, String> f) {
		return f.apply(a);
	};
	
	/*
	 * 斷言型介面:傳入一個引數,返回一個布林值
	 * Predicate<T>  boolean test(T t);
	 */
	@Test
	public void test4() {
		System.out.println(test(40, x -> x>0));
	}
	
	public String test(int x, Predicate<Integer> p) {
		if(p.test(x)) {
			return "大於0";
		}else {
			return "小於0";
		}
	}
}
複製程式碼

1.5、方法引用與構造器引用

  1. 方法引用:若Lambda體中的內容有方法已經實現了,我們可以使用“方法引用”(可以理解為方法引用是Lambda表示式的另外一種表現形式)

    主要有三種語法格式:

    1. 物件 :: 例項方法名
    2. 類 :: 靜態方法名
    3. 類 :: 例項方法名

    注意:

    1. Lambda體中呼叫方法的引數列表與返回值型別,要與函式式介面中抽象方法的函式列表和返回值型別保持一致。
    2. 若Lambda引數列表中的第一個引數是例項方法的呼叫者,第二個引數是例項方法的引數,可以用使用ClassName :: method
  2. 構造器引用:

    格式:ClassName :: new

    注意:需要呼叫的構造器引數列表要與函式式介面中抽象方法的引數列表保持一致

  3. 陣列引用:

    格式:Type :: new;

原始碼

package com.zgy.deom1;

import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.junit.jupiter.api.Test;

public class TestMethodRef {

	/*
	 * 方法引用和構造器引用
	 */
	
	@Test
	public void test1() {
		//物件 :: 例項方法名
		Consumer<String> c = System.out::println;
		c.accept("ZGY");
		
		Employee e = new Employee(111,"ZGY",22,1000);
		Supplier<String> s = e::getName;
		System.out.println(s.get());
	}
	
	@Test
	public void test2() {
		//類名::方法名
		Comparator<Integer> c = Integer::compare;
		System.out.println(c.compare(800, 210));
	}
	
	@Test
	public void test3() {
		//類名::例項方法名
		BiPredicate<String, String> bp = String::equals;
		System.out.println(bp.test("AAA", "AAA"));
	}
	
	@Test
	public void test4() {
		//構造器引用
		Supplier<Employee> e = Employee::new;
		Employee employee = e.get();
		employee.setName("ZGY");
		System.out.println(employee);
	}
}
複製程式碼

2、Stream API

2.1、什麼是Stream

流(Stream)到底是什麼?

流,是資料渠道,用於運算元據源(集合、陣列等)所生成的元素序列。集合講的是資料,而流講的是計算!

注意:

  1. Stream自己不會儲存元素。
  2. Stream不會改變源物件,相反他們會返回一個持有結果的新Stream。
  3. Stream是延遲執行的,這意味著它們會等到需要結果的時候才會執行。

2.2、Stream的操作三個步驟

JDK8 新特性學習筆記

  1. 建立Stream原始碼

    package com.zgy.deom1;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Stream;
    
    import org.junit.jupiter.api.Test;
    
    /*
     * 建立Stream的幾種方式
     */
    public class TestStreamAPI1 {
    
    	@Test
    	public void test1() {
    		//1.可以通過Collection系列集合提供的stream()或parallelStream()
    		List<String> list = new ArrayList<>();
    		Stream<String> stream = list.stream();
    		
    		//2.通過Arrays中的靜態方法stream()獲取陣列流
    		Employee[] employees = new Employee[10];
    		Stream<Employee> stream2 = Arrays.stream(employees);
    		
    		//3.通過Stream類中的靜態方法of()
    		Stream<String> stream3 = Stream.of("AA","BB","CC","DD");
    		
    		//4.通過迭代的方式建立無限流
    		Stream<Integer> stream4 = Stream.iterate(0, x -> x+2);
    		stream4.limit(5).forEach(System.out::println);
    		
    		//5.通過生成的方式建立無限流
    		Stream<Double> stream5 = Stream.generate(() -> Math.random());
    		//下面limit、forEach分別是
    		stream5.limit(20).forEach(System.out::println);
    	}
    }
    複製程式碼

相關文章