Java8 新特性

LiuszZZ發表於2020-12-11

特性

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

Lambda表示式

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

public class TestLambda1 {
	
	//原來的匿名內部類
	@Test
	public void test1(){
		Comparator<String> com = new Comparator<String>(){
			@Override
			public int compare(String o1, String o2) {
				return Integer.compare(o1.length(), o2.length());
			}
		};
		
		TreeSet<String> ts = new TreeSet<>(com);
		
		TreeSet<String> ts2 = new TreeSet<>(new Comparator<String>(){
			@Override
			public int compare(String o1, String o2) {
				return Integer.compare(o1.length(), o2.length());
			}
			
		});
	}
	
	//現在的 Lambda 表示式
	@Test
	public void test2(){
		Comparator<String> com = (x, y) -> Integer.compare(x.length(), y.length());
		TreeSet<String> ts = new TreeSet<>(com);
	}
	
	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)
	);

	//需求:獲取公司中年齡小於 35 的員工資訊
	public List<Employee> filterEmployeeAge(List<Employee> emps){
		List<Employee> list = new ArrayList<>();
		
		for (Employee emp : emps) {
			if(emp.getAge() <= 35){
				list.add(emp);
			}
		}
		
		return list;
	}
	
	@Test
	public void test3(){
		List<Employee> list = filterEmployeeAge(emps);
		
		for (Employee employee : list) {
			System.out.println(employee);
		}
	}
	
	//需求:獲取公司中工資大於 5000 的員工資訊
	public List<Employee> filterEmployeeSalary(List<Employee> emps){
		List<Employee> list = new ArrayList<>();
		
		for (Employee emp : emps) {
			if(emp.getSalary() >= 5000){
				list.add(emp);
			}
		}
		
		return list;
	}
	
	//優化方式一:策略設計模式
	public List<Employee> filterEmployee(List<Employee> emps, MyPredicate<Employee> mp){
		List<Employee> list = new ArrayList<>();
		
		for (Employee employee : emps) {
			if(mp.test(employee)){
				list.add(employee);
			}
		}
		
		return list;
	}
	
	@Test
	public void test4(){
		List<Employee> list = filterEmployee(emps, new FilterEmployeeForAge());
		for (Employee employee : list) {
			System.out.println(employee);
		}
		
		System.out.println("------------------------------------------");
		
		List<Employee> list2 = filterEmployee(emps, new FilterEmployeeForSalary());
		for (Employee employee : list2) {
			System.out.println(employee);
		}
	}
	
	//優化方式二:匿名內部類
	@Test
	public void test5(){
		List<Employee> list = filterEmployee(emps, new MyPredicate<Employee>() {
			@Override
			public boolean test(Employee t) {
				return t.getId() <= 103;
			}
		});
		
		for (Employee employee : list) {
			System.out.println(employee);
		}
	}
	
	//優化方式三:Lambda 表示式
	@Test
	public void test6(){
		List<Employee> list = filterEmployee(emps, (e) -> e.getAge() <= 35);
		list.forEach(System.out::println);
		
		System.out.println("------------------------------------------");
		
		List<Employee> list2 = filterEmployee(emps, (e) -> e.getSalary() >= 5000);
		list2.forEach(System.out::println);
	}
	
	//優化方式四:Stream API
	@Test
	public void test7(){
		emps.stream()
			.filter((e) -> e.getAge() <= 35)
			.forEach(System.out::println);
		
		System.out.println("----------------------------------------------");
		
		emps.stream()
			.map(Employee::getName)
			.limit(3)
			.sorted()
			.forEach(System.out::println);
	}
}
複製程式碼
/*
 * 一、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);
 * 
 * 
 * 二、Lambda 表示式需要“函式式介面”的支援
 *只包含一個抽象方法的介面,稱為函式式介面。你可以通過 Lambda *表示式來建立該介面的物件。(若 Lambda
 *表示式丟擲一個受檢異常,那麼該異常需要在目標介面的抽象方
 *法上進行宣告)。
 *我們可以在任意函式式介面上使用 @FunctionalInterface 註解,
 *這樣做可以檢查它是否是一個函式式介面,同時 javadoc 也會包 *含一條宣告,說明這個介面是一個函式式介面。
 *作為引數傳遞 Lambda 表示式:為了將 Lambda 表示式作為引數傳遞,接收Lambda *表示式的引數型別必須是與該 Lambda 表示式相容的函式式介面的型別。
 */
public class TestLambda2 {
	
	@Test
	public void test1(){
		int num = 0;//jdk 1.7 前,必須是 final
		
		Runnable r = new Runnable() {
			@Override
			public void run() {
				System.out.println("Hello World!" + num);
			}
		};
		
		r.run();
		
		System.out.println("-------------------------------");
		
		Runnable r1 = () -> System.out.println("Hello Lambda!");
		r1.run();
	}
	
	@Test
	public void test2(){
		Consumer<String> con = x -> System.out.println(x);
		con.accept("hello world!");
	}
	
	@Test
	public void test3(){
		Comparator<Integer> com = (x, y) -> {
			System.out.println("函式式介面");
			return Integer.compare(x, y);
		};
	}
	
	@Test
	public void test4(){
		Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
	}
	
	@Test
	public void test5(){
//		String[] strs;
//		strs = {"aaa", "bbb", "ccc"};
		
		List<String> list = new ArrayList<>();
		
		show(new HashMap<>());
	}

	public void show(Map<String, Integer> map){
		
	}
	
	//需求:對一個數進行運算
	@Test
	public void test6(){
		Integer num = operation(100, (x) -> x * x);
		System.out.println(num);
		
		System.out.println(operation(200, (y) -> y + 200));
	}
	
	public Integer operation(Integer num, MyFun mf){
		return mf.getValue(num);
	}
}

複製程式碼
@FunctionalInterface
public interface MyFun {

	public Integer getValue(Integer num);
	
}

複製程式碼

函式式介面

java內建的四大核心函式式介面

java內建的四大核心函式式介面

其他介面

其他介面

/*
 * Java8 內建的四大核心函式式介面
 * 
 * Consumer<T> : 消費型介面
 * 		void accept(T t);
 * 
 * Supplier<T> : 供給型介面
 * 		T get(); 
 * 
 * Function<T, R> : 函式型介面
 * 		R apply(T t);
 * 
 * Predicate<T> : 斷言型介面
 * 		boolean test(T t);
 * 
 */
public class TestLambda3 {
	
	//Predicate<T> 斷言型介面:
	@Test
	public void test4(){
		List<String> list = Arrays.asList("Hello", "stream", "Lambda", "www", "ok");
		List<String> strList = filterStr(list, (s) -> s.length() > 3);
		
		for (String str : strList) {
			System.out.println(str);
		}
	}
	
	//需求:將滿足條件的字串,放入集合中
	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;
	}
	
	//Function<T, R> 函式型介面:
	@Test
	public void test3(){
		String newStr = strHandler("\t\t\t JAVA是世界上最好的語言   ", (str) -> str.trim());
		System.out.println(newStr);
		
		String subStr = strHandler("JAVA是世界上最好的語言", (str) -> str.substring(2, 5));
		System.out.println(subStr);
	}
	
	//需求:用於處理字串
	public String strHandler(String str, Function<String, String> fun){
		return fun.apply(str);
	}
	
	//Supplier<T> 供給型介面 :
	@Test
	public void test2(){
		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;
	}
	
	//Consumer<T> 消費型介面 :
	@Test
	public void test1(){
		happy(10000, (m) -> System.out.println("淘寶購物,每次消費:" + m + "元"));
	} 
	
	public void happy(double money, Consumer<Double> con){
		con.accept(money);
	}
}
複製程式碼

方法引用

當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用! (實現抽象方法的引數列表,必須與方法引用方法的引數列表保持一致!) 方法引用:使用操作符 “::” 將方法名和物件或類的名字分隔開來。 
複製程式碼
/*
 * 一、方法引用:若 Lambda 體中的功能,已經有方法提供了實現,可以使用方法引用
 * 			  (可以將方法引用理解為 Lambda 表示式的另外一種表現形式)
 * 
 * 1. 物件的引用 :: 例項方法名
 * 
 * 2. 類名 :: 靜態方法名
 * 
 * 3. 類名 :: 例項方法名
 * 
 * 注意:
 * 	 ①方法引用所引用的方法的引數列表與返回值型別,需要與函式式介面中抽象方法的引數列表和返回值型別保持一致!
 * 	 ②若Lambda 的引數列表的第一個引數,是例項方法的呼叫者,第二個引數(或無參)是例項方法的引數時,格式: ClassName::MethodName
 */
public class TestMethodRef {
	//類名 :: 例項方法名
	@Test
	public void test5(){
		BiPredicate<String, String> bp = (x, y) -> x.equals(y);
		System.out.println(bp.test("abcde", "abcde"));
		
		System.out.println("-----------------------------------------");
		
		BiPredicate<String, String> bp2 = String::equals;
		System.out.println(bp2.test("abc", "abc"));
		
		System.out.println("-----------------------------------------");
		
		
		Function<Employee, String> fun = (e) -> e.show();
		System.out.println(fun.apply(new Employee()));
		
		System.out.println("-----------------------------------------");
		
		Function<Employee, String> fun2 = Employee::show;
		System.out.println(fun2.apply(new Employee()));
		
	}
	
	//類名 :: 靜態方法名
	@Test
	public void test4(){
		Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
		
		System.out.println("-------------------------------------");
		
		Comparator<Integer> com2 = Integer::compare;
	}
	
	@Test
	public void test3(){
		BiFunction<Double, Double, Double> fun = (x, y) -> Math.max(x, y);
		System.out.println(fun.apply(1.5, 22.2));
		
		System.out.println("--------------------------------------------------");
		
		BiFunction<Double, Double, Double> fun2 = Math::max;
		System.out.println(fun2.apply(1.2, 1.5));
	}

	//物件的引用 :: 例項方法名
	@Test
	public void test2(){
		Employee emp = new Employee(101, "張三", 18, 9999.99);
		
		Supplier<String> sup = () -> emp.getName();
		System.out.println(sup.get());
		
		System.out.println("----------------------------------");
		
		Supplier<String> sup2 = emp::getName;
		System.out.println(sup2.get());
	}
	
	@Test
	public void test1(){
		PrintStream ps = System.out;
		Consumer<String> con = (str) -> ps.println(str);
		con.accept("Hello World!");
		
		System.out.println("--------------------------------");
		
		Consumer<String> con2 = ps::println;
		con2.accept("Hello Java8!");
		
		Consumer<String> con3 = System.out::println;
	}
	
}

複製程式碼

構造器引用

格式: ClassName::new 與函式式介面相結合,自動與函式式介面中方法相容。 可以把構造器引用賦值給定義的方法,與構造器引數。構造器的引數列表,需要與函式式介面中引數列表保持一致

//構造器引用
	@Test
	public void test7(){
		Function<String, Employee> fun = Employee::new;
		
		BiFunction<String, Integer, Employee> fun2 = Employee::new;
	}
	
	@Test
	public void test6(){
		Supplier<Employee> sup = () -> new Employee();
		System.out.println(sup.get());
		
		System.out.println("------------------------------------");
		
		Supplier<Employee> sup2 = Employee::new;
		System.out.println(sup2.get());
	}
複製程式碼

陣列引用

型別[] :: new;

	//陣列引用
	@Test
	public void test8(){
		Function<Integer, String[]> fun = (args) -> new String[args];
		String[] strs = fun.apply(10);
		System.out.println(strs.length);
		
		System.out.println("--------------------------");
		
		Function<Integer, Employee[]> fun2 = Employee[] :: new;
		Employee[] emps = fun2.apply(20);
		System.out.println(emps.length);
	}
複製程式碼

Stream API

Java8中有兩大最為重要的改變。第一個是 Lambda 表示式;另外一 個則是 Stream API(java.util.stream.*)。 Stream 是 Java8 中處理集合的關鍵抽象概念,它可以指定你希望對 集合進行的操作,可以執行非常複雜的查詢、過濾和對映資料等操作。 使用Stream API 對集合資料進行操作,就類似於使用 SQL 執行的資料庫查詢。也可以使用 Stream API 來並行執行操作。簡而言之, Stream API 提供了一種高效且易於使用的處理資料的方式。

什麼是Stream

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

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

Stream 操作的三個步驟

  • 建立 Stream。 一個資料來源(如:集合、陣列),獲取一個流
  • 中間操作。一箇中間操作鏈,對資料來源的資料進行處理。
  • 終止操作(終端操作)。一個終止操作,執行中間操作鏈,併產生結果

Stream操作流程

1.建立Steam

1.1 Java8 中的 Collection 介面被擴充套件,提供了兩個獲取流的方法:

  • default Stream stream() : 返回一個順序流
  • default Stream parallelStream() : 返回一個並行流

1.2 由陣列建立Stream。Java8 中的 Arrays 的靜態方法 stream() 可 以獲取陣列流:

  • static Stream stream(T[] array): 返回一個流

過載形式,能夠處理對應基本型別的陣列:

  • public static IntStream stream(int[] array)
  • public static LongStream stream(long[] array)
  • public static DoubleStream stream(double[] array)

1.3 由值建立Stream 可以使用靜態方法 Stream.of(), 通過顯示值,建立一個流。他可以接收任意數量的引數。

  • public static Stream of(T... values) : 返回一個流

1.4 由函式建立流:建立無限流

可以使用靜態方法 Stream.iterate() 和 Stream.generate(), 建立無限流。

  • 迭代 public static Stream iterate(final T seed, final UnaryOperator f)
  • 生成 public static Stream generate(Supplier s)

2.Steam的中間操作

多箇中間操作可以連線起來形成一個流水線,除非流水 線上觸發終止操作,否則中間操作不會執行任何的處理! 而在終止操作時一次性全部處理,稱為“惰性求值”。

  • 2.1 篩選與切片

篩選與切片

  • 2.2 對映

對映

  • 2.3 排序

排序

Steam 的終止操作

  • 3.1 查詢與匹配 終端操作會從流的流水線生成結果。其結果可以是任何不是流的 值,例如:List、Integer,甚至是 void 。

查詢與匹配

Java8 新特性

  • 3.2 歸約

歸約
備註:map 和 reduce 的連線通常稱為 map-reduce 模式,因 Google 用它 來進行網路搜尋而出名。

  • 3.3 收集

Java8 新特性
Collector 介面中方法的實現決定了如何對流執行收集操作(如收 集到 List、Set、Map)。但是 Collectors 實用類提供了很多靜態 方法,可以方便地建立常見收集器例項,具體方法與例項如下表:

Java8 新特性

Java8 新特性

/*
 * 一、Stream API 的操作步驟:
 * 
 * 1. 建立 Stream
 * 
 * 2. 中間操作
 * 
 * 3. 終止操作(終端操作)
 */
public class TestStreamaAPI {
	
	//1. 建立 Stream
	@Test
	public void test1(){
		//1. Collection 提供了兩個方法  stream() 與 parallelStream()
		List<String> list = new ArrayList<>();
		//獲取一個順序流
		Stream<String> stream = list.stream(); 
		//獲取一個並行流
		Stream<String> parallelStream = list.parallelStream(); 
		
		//2. 通過 Arrays 中的 stream() 獲取一個陣列流
		Integer[] nums = new Integer[10];
		Stream<Integer> stream1 = Arrays.stream(nums);
		
		//3. 通過 Stream 類中靜態方法 of()
		Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);
		
		//4. 建立無限流
		//迭代
		Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
		stream3.forEach(System.out::println);
		
		//生成
		Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
		stream4.forEach(System.out::println);
		
	}
	
	//2. 中間操作
	List<Employee> emps = Arrays.asList(
			new Employee(102, "李四", 59, 6666.66),
			new Employee(101, "張三", 18, 9999.99),
			new Employee(103, "王五", 28, 3333.33),
			new Employee(104, "趙六", 8, 7777.77),
			new Employee(104, "趙六", 8, 7777.77),
			new Employee(104, "趙六", 8, 7777.77),
			new Employee(105, "田七", 38, 5555.55)
	);
	
	/*
	  篩選與切片
		filter——接收 Lambda , 從流中排除某些元素。
		limit——截斷流,使其元素不超過給定數量。
		skip(n) —— 跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素不足 n 個,則返回一個空流。與 limit(n) 互補
		distinct——篩選,通過流所生成元素的 hashCode() 和 equals() 去除重複元素
	 */
	
	//內部迭代:迭代操作 Stream API 內部完成
	@Test
	public void test2(){
		//所有的中間操作不會做任何的處理
		Stream<Employee> stream = emps.stream()
			.filter((e) -> {
				System.out.println("測試中間操作");
				return e.getAge() <= 35;
			});
		
		//只有當做終止操作時,所有的中間操作會一次性的全部執行,稱為“惰性求值”
		stream.forEach(System.out::println);
	}
	
	//外部迭代
	@Test
	public void test3(){
		Iterator<Employee> it = emps.iterator();
		
		while(it.hasNext()){
			System.out.println(it.next());
		}
	}
	
	@Test
	public void test4(){
		emps.stream()
			.filter((e) -> {
				System.out.println("短路!"); // &&  ||
				return e.getSalary() >= 5000;
			}).limit(3)
			.forEach(System.out::println);
	}
	
	@Test
	public void test5(){
		emps.parallelStream()
			.filter((e) -> e.getSalary() >= 5000)
			.skip(2)
			.forEach(System.out::println);
	}
	
	@Test
	public void test6(){
		emps.stream()
			.distinct()
			.forEach(System.out::println);
	}
}

複製程式碼
/*
 * 一、 Stream 的操作步驟
 * 
 * 1. 建立 Stream
 * 
 * 2. 中間操作
 * 
 * 3. 終止操作
 */
public class TestStreamAPI1 {
	
	List<Employee> emps = Arrays.asList(
			new Employee(102, "李四", 59, 6666.66),
			new Employee(101, "張三", 18, 9999.99),
			new Employee(103, "王五", 28, 3333.33),
			new Employee(104, "趙六", 8, 7777.77),
			new Employee(104, "趙六", 8, 7777.77),
			new Employee(104, "趙六", 8, 7777.77),
			new Employee(105, "田七", 38, 5555.55)
	);
	
	//2. 中間操作
	/*
		對映
		map——接收 Lambda , 將元素轉換成其他形式或提取資訊。接收一個函式作為引數,該函式會被應用到每個元素上,並將其對映成一個新的元素。
		flatMap——接收一個函式作為引數,將流中的每個值都換成另一個流,然後把所有流連線成一個流
	 */
	@Test
	public void test1(){
		Stream<String> str = emps.stream()
			.map((e) -> e.getName());
		
		System.out.println("-------------------------------------------");
		
		List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
		
		Stream<String> stream = strList.stream()
			   .map(String::toUpperCase);
		
		stream.forEach(System.out::println);
		
		Stream<Stream<Character>> stream2 = strList.stream()
			   .map(TestStreamAPI1::filterCharacter);
		
		stream2.forEach((sm) -> {
			sm.forEach(System.out::println);
		});
		
		System.out.println("---------------------------------------------");
		
		Stream<Character> stream3 = strList.stream()
			   .flatMap(TestStreamAPI1::filterCharacter);
		
		stream3.forEach(System.out::println);
	}

	public static Stream<Character> filterCharacter(String str){
		List<Character> list = new ArrayList<>();
		
		for (Character ch : str.toCharArray()) {
			list.add(ch);
		}
		
		return list.stream();
	}
	
	/*
		sorted()——自然排序
		sorted(Comparator com)——定製排序
	 */
	@Test
	public void test2(){
		emps.stream()
			.map(Employee::getName)
			.sorted()
			.forEach(System.out::println);
		
		System.out.println("------------------------------------");
		
		emps.stream()
			.sorted((x, y) -> {
				if(x.getAge() == y.getAge()){
					return x.getName().compareTo(y.getName());
				}else{
					return Integer.compare(x.getAge(), y.getAge());
				}
			}).forEach(System.out::println);
	}
}
複製程式碼
/*
 * 一、 Stream 的操作步驟
 * 
 * 1. 建立 Stream
 * 
 * 2. 中間操作
 * 
 * 3. 終止操作
 */
public class TestStreamAPI1 {
	
	List<Employee> emps = Arrays.asList(
			new Employee(102, "李四", 59, 6666.66),
			new Employee(101, "張三", 18, 9999.99),
			new Employee(103, "王五", 28, 3333.33),
			new Employee(104, "趙六", 8, 7777.77),
			new Employee(104, "趙六", 8, 7777.77),
			new Employee(104, "趙六", 8, 7777.77),
			new Employee(105, "田七", 38, 5555.55)
	);
	
	//2. 中間操作
	/*
		對映
		map——接收 Lambda , 將元素轉換成其他形式或提取資訊。接收一個函式作為引數,該函式會被應用到每個元素上,並將其對映成一個新的元素。
		flatMap——接收一個函式作為引數,將流中的每個值都換成另一個流,然後把所有流連線成一個流
	 */
	@Test
	public void test1(){
		Stream<String> str = emps.stream()
			.map((e) -> e.getName());
		
		System.out.println("-------------------------------------------");
		
		List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
		
		Stream<String> stream = strList.stream()
			   .map(String::toUpperCase);
		
		stream.forEach(System.out::println);
		
		Stream<Stream<Character>> stream2 = strList.stream()
			   .map(TestStreamAPI1::filterCharacter);
		
		stream2.forEach((sm) -> {
			sm.forEach(System.out::println);
		});
		
		System.out.println("---------------------------------------------");
		
		Stream<Character> stream3 = strList.stream()
			   .flatMap(TestStreamAPI1::filterCharacter);
		
		stream3.forEach(System.out::println);
	}

	public static Stream<Character> filterCharacter(String str){
		List<Character> list = new ArrayList<>();
		
		for (Character ch : str.toCharArray()) {
			list.add(ch);
		}
		
		return list.stream();
	}
	
	/*
		sorted()——自然排序
		sorted(Comparator com)——定製排序
	 */
	@Test
	public void test2(){
		emps.stream()
			.map(Employee::getName)
			.sorted()
			.forEach(System.out::println);
		
		System.out.println("------------------------------------");
		
		emps.stream()
			.sorted((x, y) -> {
				if(x.getAge() == y.getAge()){
					return x.getName().compareTo(y.getName());
				}else{
					return Integer.compare(x.getAge(), y.getAge());
				}
			}).forEach(System.out::println);
	}
}

複製程式碼
public class TestStreamAPI2 {
	
	List<Employee> emps = Arrays.asList(
			new Employee(102, "李四", 59, 6666.66, Status.BUSY),
			new Employee(101, "張三", 18, 9999.99, Status.FREE),
			new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
			new Employee(104, "趙六", 8, 7777.77, Status.BUSY),
			new Employee(104, "趙六", 8, 7777.77, Status.FREE),
			new Employee(104, "趙六", 8, 7777.77, Status.FREE),
			new Employee(105, "田七", 38, 5555.55, Status.BUSY)
	);
	
	//3. 終止操作
	/*
		allMatch——檢查是否匹配所有元素
		anyMatch——檢查是否至少匹配一個元素
		noneMatch——檢查是否沒有匹配的元素
		findFirst——返回第一個元素
		findAny——返回當前流中的任意元素
		count——返回流中元素的總個數
		max——返回流中最大值
		min——返回流中最小值
	 */
	@Test
	public void test1(){
			boolean bl = emps.stream()
				.allMatch((e) -> e.getStatus().equals(Status.BUSY));
			
			System.out.println(bl);
			
			boolean bl1 = emps.stream()
				.anyMatch((e) -> e.getStatus().equals(Status.BUSY));
			
			System.out.println(bl1);
			
			boolean bl2 = emps.stream()
				.noneMatch((e) -> e.getStatus().equals(Status.BUSY));
			
			System.out.println(bl2);
	}
	
	@Test
	public void test2(){
		Optional<Employee> op = emps.stream()
			.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
			.findFirst();
		
		System.out.println(op.get());
		
		System.out.println("--------------------------------");
		
		Optional<Employee> op2 = emps.parallelStream()
			.filter((e) -> e.getStatus().equals(Status.FREE))
			.findAny();
		
		System.out.println(op2.get());
	}
	
	@Test
	public void test3(){
		long count = emps.stream()
						 .filter((e) -> e.getStatus().equals(Status.FREE))
						 .count();
		
		System.out.println(count);
		
		Optional<Double> op = emps.stream()
			.map(Employee::getSalary)
			.max(Double::compare);
		
		System.out.println(op.get());
		
		Optional<Employee> op2 = emps.stream()
			.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
		
		System.out.println(op2.get());
	}
	
	//注意:流進行了終止操作後,不能再次使用
	@Test
	public void test4(){
		Stream<Employee> stream = emps.stream()
		 .filter((e) -> e.getStatus().equals(Status.FREE));
		
		long count = stream.count();
		
		stream.map(Employee::getSalary)
			.max(Double::compare);
	}
}
複製程式碼
public class TestStreamAPI3 {
	
	List<Employee> emps = Arrays.asList(
			new Employee(102, "李四", 79, 6666.66, Status.BUSY),
			new Employee(101, "張三", 18, 9999.99, Status.FREE),
			new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
			new Employee(104, "趙六", 8, 7777.77, Status.BUSY),
			new Employee(104, "趙六", 8, 7777.77, Status.FREE),
			new Employee(104, "趙六", 8, 7777.77, Status.FREE),
			new Employee(105, "田七", 38, 5555.55, Status.BUSY)
	);
	
	//3. 終止操作
	/*
		歸約
		reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以將流中元素反覆結合起來,得到一個值。
	 */
	@Test
	public void test1(){
		List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
		
		Integer sum = list.stream()
			.reduce(0, (x, y) -> x + y);
		
		System.out.println(sum);
		
		System.out.println("----------------------------------------");
		
		Optional<Double> op = emps.stream()
			.map(Employee::getSalary)
			.reduce(Double::sum);
		
		System.out.println(op.get());
	}
	
	//需求:搜尋名字中 “六” 出現的次數
	@Test
	public void test2(){
		Optional<Integer> sum = emps.stream()
			.map(Employee::getName)
			.flatMap(TestStreamAPI1::filterCharacter)
			.map((ch) -> {
				if(ch.equals('六'))
					return 1;
				else 
					return 0;
			}).reduce(Integer::sum);
		
		System.out.println(sum.get());
	}
	
	//collect——將流轉換為其他形式。接收一個 Collector介面的實現,用於給Stream中元素做彙總的方法
	@Test
	public void test3(){
		List<String> list = emps.stream()
			.map(Employee::getName)
			.collect(Collectors.toList());
		
		list.forEach(System.out::println);
		
		System.out.println("----------------------------------");
		
		Set<String> set = emps.stream()
			.map(Employee::getName)
			.collect(Collectors.toSet());
		
		set.forEach(System.out::println);

		System.out.println("----------------------------------");
		
		HashSet<String> hs = emps.stream()
			.map(Employee::getName)
			.collect(Collectors.toCollection(HashSet::new));
		
		hs.forEach(System.out::println);
	}
	
	@Test
	public void test4(){
		Optional<Double> max = emps.stream()
			.map(Employee::getSalary)
			.collect(Collectors.maxBy(Double::compare));
		
		System.out.println(max.get());
		
		Optional<Employee> op = emps.stream()
			.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
		
		System.out.println(op.get());
		
		Double sum = emps.stream()
			.collect(Collectors.summingDouble(Employee::getSalary));
		
		System.out.println(sum);
		
		Double avg = emps.stream()
			.collect(Collectors.averagingDouble(Employee::getSalary));
		
		System.out.println(avg);
		
		Long count = emps.stream()
			.collect(Collectors.counting());
		
		System.out.println(count);
		
		System.out.println("--------------------------------------------");
		
		DoubleSummaryStatistics dss = emps.stream()
			.collect(Collectors.summarizingDouble(Employee::getSalary));
		
		System.out.println(dss.getMax());
	}
	
	//分組
	@Test
	public void test5(){
		Map<Status, List<Employee>> map = emps.stream()
			.collect(Collectors.groupingBy(Employee::getStatus));
		
		System.out.println(map);
	}
	
	//多級分組
	@Test
	public void test6(){
		Map<Status, Map<String, List<Employee>>> map = emps.stream()
			.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
				if(e.getAge() >= 60)
					return "老年";
				else if(e.getAge() >= 35)
					return "中年";
				else
					return "成年";
			})));
		
		System.out.println(map);
	}
	
	//分割槽
	@Test
	public void test7(){
		Map<Boolean, List<Employee>> map = emps.stream()
			.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
		
		System.out.println(map);
	}
	
	//
	@Test
	public void test8(){
		String str = emps.stream()
			.map(Employee::getName)
			.collect(Collectors.joining("," , "----", "----"));
		
		System.out.println(str);
	}
	
	@Test
	public void test9(){
		Optional<Double> sum = emps.stream()
			.map(Employee::getSalary)
			.collect(Collectors.reducing(Double::sum));
		
		System.out.println(sum.get());
	}

}

複製程式碼
public class TestStreamAPI {
	
	/*
	  	1.	給定一個數字列表,如何返回一個由每個數的平方構成的列表呢?
		,給定【1,2,3,4,5】, 應該返回【1,4,9,16,25】。
	 */
	@Test
	public void test1(){
		Integer[] nums = new Integer[]{1,2,3,4,5};
		
		Arrays.stream(nums)
			  .map((x) -> x * x)
			  .forEach(System.out::println);
	}

	/*
	 2.	怎樣用 map 和 reduce 方法數一數流中有多少個Employee呢?
	 */
	List<Employee> emps = Arrays.asList(
			new Employee(102, "李四", 59, 6666.66, Status.BUSY),
			new Employee(101, "張三", 18, 9999.99, Status.FREE),
			new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
			new Employee(104, "趙六", 8, 7777.77, Status.BUSY),
			new Employee(104, "趙六", 8, 7777.77, Status.FREE),
			new Employee(104, "趙六", 8, 7777.77, Status.FREE),
			new Employee(105, "田七", 38, 5555.55, Status.BUSY)
	);
	
	@Test
	public void test2(){
		Optional<Integer> count = emps.stream()
			.map((e) -> 1)
			.reduce(Integer::sum);
		
		System.out.println(count.get());
	}
}

複製程式碼
//交易員類
public class Trader {

	private String name;
	private String city;

	public Trader() {
	}

	public Trader(String name, String city) {
		this.name = name;
		this.city = city;
	}

	public String getName() {
		return name;
	}

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

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	@Override
	public String toString() {
		return "Trader [name=" + name + ", city=" + city + "]";
	}

}
//交易類
public class Transaction {

	private Trader trader;
	private int year;
	private int value;

	public Transaction() {
	}

	public Transaction(Trader trader, int year, int value) {
		this.trader = trader;
		this.year = year;
		this.value = value;
	}

	public Trader getTrader() {
		return trader;
	}

	public void setTrader(Trader trader) {
		this.trader = trader;
	}

	public int getYear() {
		return year;
	}

	public void setYear(int year) {
		this.year = year;
	}

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}

	@Override
	public String toString() {
		return "Transaction [trader=" + trader + ", year=" + year + ", value="
				+ value + "]";
	}

}

public class TestTransaction {
	
	List<Transaction> transactions = null;
	
	@Before
	public void before(){
		Trader raoul = new Trader("Raoul", "Cambridge");
		Trader mario = new Trader("Mario", "Milan");
		Trader alan = new Trader("Alan", "Cambridge");
		Trader brian = new Trader("Brian", "Cambridge");
		
		transactions = Arrays.asList(
				new Transaction(brian, 2011, 300),
				new Transaction(raoul, 2012, 1000),
				new Transaction(raoul, 2011, 400),
				new Transaction(mario, 2012, 710),
				new Transaction(mario, 2012, 700),
				new Transaction(alan, 2012, 950)
		);
	}
	
	//1. 找出2011年發生的所有交易, 並按交易額排序(從低到高)
	@Test
	public void test1(){
		transactions.stream()
					.filter((t) -> t.getYear() == 2011)
					.sorted((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()))
					.forEach(System.out::println);
	}
	
	//2. 交易員都在哪些不同的城市工作過?
	@Test
	public void test2(){
		transactions.stream()
					.map((t) -> t.getTrader().getCity())
					.distinct()
					.forEach(System.out::println);
	}
	
	//3. 查詢所有來自劍橋的交易員,並按姓名排序
	@Test
	public void test3(){
		transactions.stream()
					.filter((t) -> t.getTrader().getCity().equals("Cambridge"))
					.map(Transaction::getTrader)
					.sorted((t1, t2) -> t1.getName().compareTo(t2.getName()))
					.distinct()
					.forEach(System.out::println);
	}
	
	//4. 返回所有交易員的姓名字串,按字母順序排序
	@Test
	public void test4(){
		transactions.stream()
					.map((t) -> t.getTrader().getName())
					.sorted()
					.forEach(System.out::println);
		
		System.out.println("-----------------------------------");
		
		String str = transactions.stream()
					.map((t) -> t.getTrader().getName())
					.sorted()
					.reduce("", String::concat);
		
		System.out.println(str);
		
		System.out.println("------------------------------------");
		
		transactions.stream()
					.map((t) -> t.getTrader().getName())
					.flatMap(TestTransaction::filterCharacter)
					.sorted((s1, s2) -> s1.compareToIgnoreCase(s2))
					.forEach(System.out::print);
	}
	
	public static Stream<String> filterCharacter(String str){
		List<String> list = new ArrayList<>();
		
		for (Character ch : str.toCharArray()) {
			list.add(ch.toString());
		}
		
		return list.stream();
	}
	
	//5. 有沒有交易員是在米蘭工作的?
	@Test
	public void test5(){
		boolean bl = transactions.stream()
					.anyMatch((t) -> t.getTrader().getCity().equals("Milan"));
		
		System.out.println(bl);
	}
	
	
	//6. 列印生活在劍橋的交易員的所有交易額
	@Test
	public void test6(){
		Optional<Integer> sum = transactions.stream()
					.filter((e) -> e.getTrader().getCity().equals("Cambridge"))
					.map(Transaction::getValue)
					.reduce(Integer::sum);
		
		System.out.println(sum.get());
	}
	
	
	//7. 所有交易中,最高的交易額是多少
	@Test
	public void test7(){
		Optional<Integer> max = transactions.stream()
					.map((t) -> t.getValue())
					.max(Integer::compare);
		
		System.out.println(max.get());
	}
	
	//8. 找到交易額最小的交易
	@Test
	public void test8(){
		Optional<Transaction> op = transactions.stream()
					.min((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()));
		
		System.out.println(op.get());
	}

}


複製程式碼

並行流與序列流

並行流就是把一個內容分成多個資料塊,並用不同的執行緒分別處理每個資料塊的流。

Java 8 中將並行進行了優化,我們可以很容易的對資料進行並行操作。Stream API 可以宣告性地通過 parallel() 與 sequential() 在並行流與順序流之間進行切換。

瞭解 Fork/Join 框架

Fork/Join 框架:就是在必要的情況下,將一個大任務,進行拆分(fork)成若干個 小任務(拆到不可再拆時),再將一個個的小任務運算的結果進行 join 彙總.

Java8 新特性

Fork/Join 框架與傳統執行緒池的區別

採用 “工作竊取”模式(work-stealing): 當執行新的任務時它可以將其拆分,分成更小的任務執行,並將小任務加到線 程佇列中,然後再從一個隨機執行緒的佇列中偷一個並把它放在自己的佇列中。

相對於一般的執行緒池實現,fork/join框架的優勢體現在對其中包含的任務的 處理方式上.在一般的執行緒池中,如果一個執行緒正在執行的任務由於某些原因 無法繼續執行,那麼該執行緒會處於等待狀態。而在fork/join框架實現中,如果 某個子問題由於等待另外一個子問題的完成而無法繼續執行,那麼處理該子 問題的執行緒會主動尋找其他尚未執行的子問題來執行.這種方式減少了執行緒 的等待時間,提高了效能。

public class ForkJoinCalculate extends RecursiveTask<Long>{


	private static final long serialVersionUID = -1L;
	
	private long start;
	private long end;
	
	private static final long THRESHOLD = 10000L; //臨界值
	
	public ForkJoinCalculate(long start, long end) {
		this.start = start;
		this.end = end;
	}
	
	@Override
	protected Long compute() {
		long length = end - start;
		
		if(length <= THRESHOLD){
			long sum = 0;
			
			for (long i = start; i <= end; i++) {
				sum += i;
			}
			
			return sum;
		}else{
			long middle = (start + end) / 2;
			
			ForkJoinCalculate left = new ForkJoinCalculate(start, middle);
			left.fork(); //拆分,並將該子任務壓入執行緒佇列
			
			ForkJoinCalculate right = new ForkJoinCalculate(middle+1, end);
			right.fork();
			
			return left.join() + right.join();
		}
		
	}

}
複製程式碼
public class TestForkJoin {
	
	@Test
	public void test1(){
		long start = System.currentTimeMillis();
		
		ForkJoinPool pool = new ForkJoinPool();
		ForkJoinTask<Long> task = new ForkJoinCalculate(0L, 10000000000L);
		
		long sum = pool.invoke(task);
		System.out.println(sum);
		
		long end = System.currentTimeMillis();
		
		System.out.println("耗費的時間為: " + (end - start)); //112-1953-1988-2654-2647-20663-113808
	}
	
	@Test
	public void test2(){
		long start = System.currentTimeMillis();
		
		long sum = 0L;
		
		for (long i = 0L; i <= 10000000000L; i++) {
			sum += i;
		}
		
		System.out.println(sum);
		
		long end = System.currentTimeMillis();
		
		System.out.println("耗費的時間為: " + (end - start)); //34-3174-3132-4227-4223-31583
	}
	
	@Test
	public void test3(){
		long start = System.currentTimeMillis();
		
		Long sum = LongStream.rangeClosed(0L, 10000000000L)
							 .parallel()
							 .sum();
		
		System.out.println(sum);
		
		long end = System.currentTimeMillis();
		
		System.out.println("耗費的時間為: " + (end - start)); //2061-2053-2086-18926
	}

}
複製程式碼

Optional 類

Optional 類(java.util.Optional) 是一個容器類,代表一個值存在或不存在, 原來用 null 表示一個值不存在,現在 Optional 可以更好的表達這個概念。並且 可以避免空指標異常。

常用方法: Optional.of(T t) : 建立一個 Optional 例項

Optional.empty() : 建立一個空的 Optional 例項

Optional.ofNullable(T t):若 t 不為 null,建立 Optional 例項,否則建立空例項

isPresent() : 判斷是否包含值

orElse(T t) : 如果呼叫物件包含值,返回該值,否則返回t

orElseGet(Supplier s) :如果呼叫物件包含值,返回該值,否則返回 s 獲取的值

map(Function f): 如果有值對其處理,並返回處理後的Optional,否則返回 Optional.empty() flatMap(Function mapper):與 map 類似,要求返回值必須是Optional

public class Godness {

	private String name;

	public Godness() {
	}

	public Godness(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

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

	@Override
	public String toString() {
		return "Godness [name=" + name + "]";
	}

}


public class Man {

	private Godness god;

	public Man() {
	}

	public Man(Godness god) {
		this.god = god;
	}

	public Godness getGod() {
		return god;
	}

	public void setGod(Godness god) {
		this.god = god;
	}

	@Override
	public String toString() {
		return "Man [god=" + god + "]";
	}

}

//注意:Optional 不能被序列化
public class NewMan {

	private Optional<Godness> godness = Optional.empty();
	
	private Godness god;
	
	public Optional<Godness> getGod(){
		return Optional.of(god);
	}

	public NewMan() {
	}

	public NewMan(Optional<Godness> godness) {
		this.godness = godness;
	}

	public Optional<Godness> getGodness() {
		return godness;
	}

	public void setGodness(Optional<Godness> godness) {
		this.godness = godness;
	}

	@Override
	public String toString() {
		return "NewMan [godness=" + godness + "]";
	}

}
/*
 * 一、Optional 容器類:用於儘量避免空指標異常
 * 	Optional.of(T t) : 建立一個 Optional 例項
 * 	Optional.empty() : 建立一個空的 Optional 例項
 * 	Optional.ofNullable(T t):若 t 不為 null,建立 Optional 例項,否則建立空例項
 * 	isPresent() : 判斷是否包含值
 * 	orElse(T t) :  如果呼叫物件包含值,返回該值,否則返回t
 * 	orElseGet(Supplier s) :如果呼叫物件包含值,返回該值,否則返回 s 獲取的值
 * 	map(Function f): 如果有值對其處理,並返回處理後的Optional,否則返回 Optional.empty()
 * 	flatMap(Function mapper):與 map 類似,要求返回值必須是Optional
 */
public class TestOptional {
	
	@Test
	public void test4(){
		Optional<Employee> op = Optional.of(new Employee(101, "張三", 18, 9999.99));
		
		Optional<String> op2 = op.map(Employee::getName);
		System.out.println(op2.get());
		
		Optional<String> op3 = op.flatMap((e) -> Optional.of(e.getName()));
		System.out.println(op3.get());
	}
	
	@Test
	public void test3(){
		Optional<Employee> op = Optional.ofNullable(new Employee());
		
		if(op.isPresent()){
			System.out.println(op.get());
		}
		
		Employee emp = op.orElse(new Employee("張三"));
		System.out.println(emp);
		
		Employee emp2 = op.orElseGet(() -> new Employee());
		System.out.println(emp2);
	}
	
	@Test
	public void test2(){
		/*Optional<Employee> op = Optional.ofNullable(null);
		System.out.println(op.get());*/
		
//		Optional<Employee> op = Optional.empty();
//		System.out.println(op.get());
	}

	@Test
	public void test1(){
		Optional<Employee> op = Optional.of(new Employee());
		Employee emp = op.get();
		System.out.println(emp);
	}
	
	@Test
	public void test5(){
		Man man = new Man();
		
		String name = getGodnessName(man);
		System.out.println(name);
	}
	
	//需求:獲取一個男人心中女神的名字
	public String getGodnessName(Man man){
		if(man != null){
			Godness g = man.getGod();
			
			if(g != null){
				return g.getName();
			}
		}
		
		return "蒼老師";
	}
	
	//運用 Optional 的實體類
	@Test
	public void test6(){
		Optional<Godness> godness = Optional.ofNullable(new Godness("林志玲"));
		
		Optional<NewMan> op = Optional.ofNullable(new NewMan(godness));
		String name = getGodnessName2(op);
		System.out.println(name);
	}
	
	public String getGodnessName2(Optional<NewMan> man){
		return man.orElse(new NewMan())
				  .getGodness()
				  .orElse(new Godness("蒼老師"))
				  .getName();
	}
}


複製程式碼

介面中的預設方法與靜態方法

預設方法

Java 8中允許介面中包含具有具體實現的方法,該方法稱為 “預設方法”,預設方法使用 default 關鍵字修飾。

介面預設方法的”類優先”原則 若一個介面中定義了一個預設方法,而另外一個父類或介面中 又定義了一個同名的方法時

  • 選擇父類中的方法。如果一個父類提供了具體的實現,那麼 介面中具有相同名稱和引數的預設方法會被忽略。
  • 介面衝突。如果一個父介面提供一個預設方法,而另一個接 口也提供了一個具有相同名稱和引數列表的方法(不管方法 是否是預設方法),那麼必須覆蓋該方法來解決衝突

靜態方法

Java8 中,介面中允許新增靜態方法。

public interface MyInterface1 {
	
	default String getName(){
		return "哈哈哈";
	}

}

public interface MyInterface2 {
	
	default String getName(){
		return "呵呵呵";
	}
	
	public static void show(){
		System.out.println("介面中的靜態方法");
	}

}

public class MyClass {
	
	public String getName(){
		return "嘿嘿嘿";
	}

}


public class SubClass /*extends MyClass*/ implements MyFun, MyInterface{

	@Override
	public String getName() {
		return MyInterface.super.getName();
	}

}

public class TestDefaultInterface {
	
	public static void main(String[] args) {
		SubClass sc = new SubClass();
		System.out.println(sc.getName());
		
		MyInterface.show();
	}

}
複製程式碼

新的時間日期API

使用 LocalDate、LocalTime、LocalDateTime

LocalDate、LocalTime、LocalDateTime 類的實 例是不可變的物件,分別表示使用 ISO-8601日 歷系統的日期、時間、日期和時間。它們提供 了簡單的日期或時間,並不包含當前的時間信 息。也不包含與時區相關的資訊。

注:ISO-8601日曆系統是國際標準化組織制定的現代公民的日期和時間的表示法

Java8 新特性

Instant 時間戳

用於“時間戳”的運算。它是以Unix元年(傳統 的設定為UTC時區1970年1月1日午夜時分)開始 所經歷的描述進行運算

Duration 和 Period

  • Duration:用於計算兩個“時間”間隔
  • Period:用於計算兩個“日期”間隔

日期的操縱

  • TemporalAdjuster : 時間校正器。有時我們可能需要獲 取例如:將日期調整到“下個週日”等操作。
  • TemporalAdjusters : 該類通過靜態方法提供了大量的常 用 TemporalAdjuster 的實現。

Java8 新特性

解析與格式化

java.time.format.DateTimeFormatter 類:該類提供了三種 格式化方法:

  • 預定義的標準格式
  • 語言環境相關的格式
  • 自定義的格式

時區的處理

Java8 中加入了對時區的支援,帶時區的時間為分別為: ZonedDate、ZonedTime、ZonedDateTime 其中每個時區都對應著 ID,地區ID都為 “{區域}/{城市}”的格式 例如 :Asia/Shanghai 等

ZoneId:該類中包含了所有的時區資訊 getAvailableZoneIds() : 可以獲取所有時區時區資訊 of(id) : 用指定的時區資訊獲取 ZoneId 物件

與傳統日期處理的轉換

Java8 新特性

public class DateFormatThreadLocal {
	
	private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
		
		protected DateFormat initialValue(){
			return new SimpleDateFormat("yyyyMMdd");
		}
		
	};
	
	public static final Date convert(String source) throws ParseException{
		return df.get().parse(source);
	}

}
public class TestSimpleDateFormat {
	
	public static void main(String[] args) throws Exception {
		
		/*SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
		
		Callable<Date> task = new Callable<Date>() {

			@Override
			public Date call() throws Exception {
				return sdf.parse("20161121");
			}
			
		};

		ExecutorService pool = Executors.newFixedThreadPool(10);
		
		List<Future<Date>> results = new ArrayList<>();
		
		for (int i = 0; i < 10; i++) {
			results.add(pool.submit(task));
		}
		
		for (Future<Date> future : results) {
			System.out.println(future.get());
		}
		
		pool.shutdown();*/
		
		//解決多執行緒安全問題
		/*Callable<Date> task = new Callable<Date>() {

			@Override
			public Date call() throws Exception {
				return DateFormatThreadLocal.convert("20161121");
			}
			
		};

		ExecutorService pool = Executors.newFixedThreadPool(10);
		
		List<Future<Date>> results = new ArrayList<>();
		
		for (int i = 0; i < 10; i++) {
			results.add(pool.submit(task));
		}
		
		for (Future<Date> future : results) {
			System.out.println(future.get());
		}
		
		pool.shutdown();*/
		
		DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
		
		Callable<LocalDate> task = new Callable<LocalDate>() {

			@Override
			public LocalDate call() throws Exception {
				LocalDate ld = LocalDate.parse("20161121", dtf);
				return ld;
			}
			
		};

		ExecutorService pool = Executors.newFixedThreadPool(10);
		
		List<Future<LocalDate>> results = new ArrayList<>();
		
		for (int i = 0; i < 10; i++) {
			results.add(pool.submit(task));
		}
		
		for (Future<LocalDate> future : results) {
			System.out.println(future.get());
		}
		
		pool.shutdown();
	}

}
public class TestLocalDateTime {
	
	//6.ZonedDate、ZonedTime、ZonedDateTime : 帶時區的時間或日期
	@Test
	public void test7(){
		LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
		System.out.println(ldt);
		
		ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
		System.out.println(zdt);
	}
	
	@Test
	public void test6(){
		Set<String> set = ZoneId.getAvailableZoneIds();
		set.forEach(System.out::println);
	}

	
	//5. DateTimeFormatter : 解析和格式化日期或時間
	@Test
	public void test5(){
//		DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
		
		DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
		
		LocalDateTime ldt = LocalDateTime.now();
		String strDate = ldt.format(dtf);
		
		System.out.println(strDate);
		
		LocalDateTime newLdt = ldt.parse(strDate, dtf);
		System.out.println(newLdt);
	}
	
	//4. TemporalAdjuster : 時間校正器
	@Test
	public void test4(){
	LocalDateTime ldt = LocalDateTime.now();
		System.out.println(ldt);
		
		LocalDateTime ldt2 = ldt.withDayOfMonth(10);
		System.out.println(ldt2);
		
		LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
		System.out.println(ldt3);
		
		//自定義:下一個工作日
		LocalDateTime ldt5 = ldt.with((l) -> {
			LocalDateTime ldt4 = (LocalDateTime) l;
			
			DayOfWeek dow = ldt4.getDayOfWeek();
			
			if(dow.equals(DayOfWeek.FRIDAY)){
				return ldt4.plusDays(3);
			}else if(dow.equals(DayOfWeek.SATURDAY)){
				return ldt4.plusDays(2);
			}else{
				return ldt4.plusDays(1);
			}
		});
		
		System.out.println(ldt5);
		
	}
	
	//3.
	//Duration : 用於計算兩個“時間”間隔
	//Period : 用於計算兩個“日期”間隔
	@Test
	public void test3(){
		Instant ins1 = Instant.now();
		
		System.out.println("--------------------");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
		}
		
		Instant ins2 = Instant.now();
		
		System.out.println("所耗費時間為:" + Duration.between(ins1, ins2));
		
		System.out.println("----------------------------------");
		
		LocalDate ld1 = LocalDate.now();
		LocalDate ld2 = LocalDate.of(2011, 1, 1);
		
		Period pe = Period.between(ld2, ld1);
		System.out.println(pe.getYears());
		System.out.println(pe.getMonths());
		System.out.println(pe.getDays());
	}
	
	//2. Instant : 時間戳。 (使用 Unix 元年  1970年1月1日 00:00:00 所經歷的毫秒值)
	@Test
	public void test2(){
		Instant ins = Instant.now();  //預設使用 UTC 時區
		System.out.println(ins);
		
		OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
		System.out.println(odt);
		
		System.out.println(ins.getNano());
		
		Instant ins2 = Instant.ofEpochSecond(5);
		System.out.println(ins2);
	}
	
	//1. LocalDate、LocalTime、LocalDateTime
	@Test
	public void test1(){
		LocalDateTime ldt = LocalDateTime.now();
		System.out.println(ldt);
		
		LocalDateTime ld2 = LocalDateTime.of(2016, 11, 21, 10, 10, 10);
		System.out.println(ld2);
		
		LocalDateTime ldt3 = ld2.plusYears(20);
		System.out.println(ldt3);
		
		LocalDateTime ldt4 = ld2.minusMonths(2);
		System.out.println(ldt4);
		
		System.out.println(ldt.getYear());
		System.out.println(ldt.getMonthValue());
		System.out.println(ldt.getDayOfMonth());
		System.out.println(ldt.getHour());
		System.out.println(ldt.getMinute());
		System.out.println(ldt.getSecond());
	}

}

複製程式碼

重複註解與型別註解

Java 8對註解處理提供了兩點改進:可重複的註解及可用於類 型的註解。

Java8 新特性

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})//可以修飾的目標
@Retention(RetentionPolicy.RUNTIME)//生命週期
public @interface MyAnnotations {
    MyAnnotation[] value();
}

@Repeatable(MyAnnotations.class)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE,TYPE_PARAMETER})//可以修飾的目標
@Retention(RetentionPolicy.RUNTIME)//生命週期
public @interface MyAnnotation {
    String value() default "hello Annotation";
}
public class TestAnnotation {

    //checker framework框架提供此註解
    private /*@NonNull*/ Object obj=null;

    @Test
    public void test1() throws NoSuchMethodException, SecurityException{
        Class<TestAnnotation> clazz=TestAnnotation.class;
        Method m1=clazz.getMethod("show");
        MyAnnotation[] mas=m1.getAnnotationsByType(MyAnnotation.class);
        for(MyAnnotation myAnnotation:mas){
            System.out.println(myAnnotation.value());
        }
    }

    @MyAnnotation("Hello")
    @MyAnnotation("world")
    public void show(@MyAnnotation("abc")String str){

    }
}
複製程式碼

相關文章