1. 程式人生 > >JDK1.8,Java8常用新特性

JDK1.8,Java8常用新特性

A.Lamdba表示式

1.Java8概述

Java8,也就是jdk1.8,是意義深遠的一個新版本

是Java5之後一個大的版本升級,讓Java語言和庫彷彿獲得了新生

新特性包含:

a.隨著大資料的興起,函數語言程式設計在處理大資料上的優勢開始體現,引入了Lambada函數語言程式設計

b.使用Stream徹底改變了集合使用方式:只關注結果,不關心過程

c.新的客戶端圖形化工具介面庫:JavaFX

d.良好設計的日期/時間API

e.增強的併發/並行API

f.Java與JS互動引擎 -nashorn

g.其他特性

2.什麼是Lambda表示式

帶有引數變數的表示式,是一段可以傳遞的程式碼,可以被一次或多次執行

是一種精簡的字面寫法,其實就是把匿名內部類中“一定”要做的工作省略掉

然後由JVM通過推導把簡化的表示式還原

格式:  (parameters引數) -> expression表示式或方法體

paramaters:

類似方法中的形參列表,這裡的引數是函式式接口裡的引數

->:可理解為“被用於”的意思

方法體:

可以是表示式也可以程式碼塊,是函式式接口裡方法的實現

如果負責運算的程式碼無法用表示式表示,可以使用編寫方法實現

但必須用{}包圍並按需明確使用 return語句

需求:對字串陣列按字串長度排序

package org.xxxx.demo01;
 
import java.util.Arrays;
import java.util.Comparator;
 
public class Demo01 {
	public static void main(String[] args) {
		// 定義字串陣列
		String[] strArr = { "abc", "cd", "abce", "a" };
 
		// 傳統方法
		// 匿名內部類
		Arrays.sort(strArr, new Comparator<String>() {
 
			@Override
			public int compare(String s1, String s2) {
				return Integer.compare(s2.length(), s1.length());
			}
		});
 
		// 輸出排序結果
		for (String s : strArr) {
			System.out.println(s);
		}
		System.out.println("---------------------");
 
		// Lambda表示式
		Arrays.sort(strArr, (s1, s2) -> Integer.compare(s2.length(), s1.length()));
 
		// 輸出
		for (String s : strArr) {
			System.out.println(s);
		}

需求:用Lambda實現多執行緒


package org.xxxx.demo01;
 
public class Demo01 {
	public static void main(String[] args) {
		// Lambda表示式
		new Thread(() -> System.out.println(1 + "hello world")).start();
 
		System.out.println("----------------");
 
		// 方法體
		new Thread(() -> {
			for (int i = 0; i < 10; i++) {
				System.out.println(2 + "hello world");
			}
		}).start();
	}
}

3.何時使用

通過上面的兩個需求,發現Lamdba表示式很簡單,那何時使用呢

需要顯示建立函式式介面物件的地方,都可以使用

實際上函式式介面的轉換是Lambda表示式唯一能做的事情

即lambda必須和Functional Interface配套使用

主要用於替換以前廣泛使用的內部匿名類,各種回撥

比如事件響應器、傳入Thread類的Runnable等

4.函式式介面分類

a.系統與定義函式介面(Comparator, Runnable)

b.使用者自定義函式介面(註解必須有,表示式是直接通過引數列表來實現的,只能有一個有效方法)


@FunctionalInterface
public interface MyInterface {
	String info(String tip);
}

5.公共定義的函式式介面

從jdk1.8開始為了方便使用者開發專門提供了一個新的包:java.util.function

在這個包裡面針對於使用者有可能做的函式式介面做了一個公共定義

最為核心的有四個介面:

a.功能性介面:Function<T, R>

有輸入引數,有返回值

是對接收一個T型別引數,返回R型別的結果的方法的抽象

通過呼叫apply方法執行內容

需求:給定一個字串,返回字串長度

package org.xxxx.demo01;
 
import java.util.function.Function;
 
public class Demo01 {
	public static void main(String[] args) {
		// 定義字串
		String str = "helloworld";
		
		// 呼叫方法
		// 在呼叫的時候寫方法體,方法比較靈活
		int length = testFun(str, (s) -> s.length());
		
		System.out.println(length);
	}
	
	// 方法
	/**
	 * 
	 * @param str 輸入引數
	 * @param fun 表示式 String 為輸入型別,Integer為輸出型別
	 * @return 返回字串長度
	 */
	public static int testFun(String str, Function<String, Integer> fun) {
		// 執行
		Integer length = fun.apply(str);
		
		return length;
	}
}

b.消費型介面:Consumer<T>

有輸入引數,沒返回值

對應的方法型別為接收一個引數,沒有返回值

一般來說使用Consumer介面往往伴隨著一些期望狀態的改變

或者事件的發生,典型的forEach就是使用的Consumer介面

雖然沒有任何的返回值,但是向控制檯輸出結果

Consumer 使用accept對引數執行行為

需求:輸出字串


package org.xxxx.demo01;
 
import java.util.function.Consumer;
 
public class Demo01 {
	public static void main(String[] args) {
		// 建立字串
		String str = "hello world";
		
		// 呼叫
		testCon(str, (s) -> System.out.println(s));
	}
	
	/**
	 * 
	 * @param str 傳入引數
	 * @param con
	 */
	public static void testCon(String str, Consumer<String> con) {
		// 執行
		con.accept(str);
	}
	
}

c.供給型介面:Supplier<T>

無傳入引數,有返回值

該介面對應的方法型別不接受引數,但是提供一個返回值

使用get()方法獲得這個返回值

package org.xxxx.demo01;
 
import java.util.function.Supplier;
 
public class Demo01 {
	public static void main(String[] args) {
		// 建立字串
		String str = "hello world";
		
		// 呼叫
		String sup = testSup(() -> str);
		
		System.out.println(sup);
	}
	
	/**
	 * 
	 * @param sup
	 * @return
	 */
	public static String testSup(Supplier<String> sup) {
		// 執行
		String s = sup.get();
		return s;
	}
	
}

d.斷言型介面:Predicate<T>

有傳入引數,有返回值Boolean

該介面對應的方法為接收一個引數,返回一個Boolean型別值

多用於判斷與過濾,使用test()方法執行這段行為

需求:輸入字串,判斷長度是否大於0

package org.xxxx.demo01;
 
import java.util.function.Predicate;
 
public class Demo01 {
	public static void main(String[] args) {
		// 建立字串
		String str = "hello world";
		
		// 呼叫
		boolean flag = testPre(str, (s) -> s.length() > 0);
		
		System.out.println(flag);
	}
	
	/**
	 * 
	 * @param str
	 * @param pre
	 * @return
	 */
	public static boolean testPre(String str, Predicate<String> pre) {
		// 執行
		boolean flag = pre.test(str);
		
		return flag;
	}
	
}

6.Lambda的優點

a.極大的減少程式碼冗餘,同時可讀性也好過冗長的匿名內部類

b.與集合類批處理操作結合,實現內部迭代,並充分利用現代多核CPU進行平行計算。之前集合類的迭代都是外部的,即客戶程式碼。而內部迭代意味著由Java類庫來進行迭代,而不是客戶程式碼

7.和匿名內部類的區別

a.在lambda中,this不是指向lambda表示式產生的那個物件,而是它的外部物件

b.Java 編譯器編譯 Lambda 表示式並將他們轉化為類裡面的私有函式,它使用 Java 7 中新加的 invokedynamic 指令動態繫結該方法,但每一個匿名內部類編譯器會為其建立一個類檔案

B.方法引用

1.概述

某些lambda表示式裡面僅僅是呼叫了一個已存在的方法,在這種情況下

直接通過方法名稱引用方法的形式可讀性更高一些,這種形式就是方法引用

方法引用是一種更簡潔易懂的lambda 表示式替換

其唯一用途就是支援Lambda的簡寫

函式介面中抽象方法的引數列表,必須與方法引用方法的引數列表保持一致

方法引用中::後只是方法名,不能加();

forEach()也是jdk8的新特性

比如:list.forEach((s) -> System.out.println(s));---list.forEach(Syetem.out::println);

2.類靜態方法引用

求絕對值,使用Function實現

package org.xxxx.demo01;
 
import java.util.function.Function;
 
public class Demo01 {
	public static void main(String[] args) {
		// 呼叫
		long l1 = testAbs(-10L, (s) -> Math.abs(s));
		
		// 改進
		long l2 = testAbs(-10, Math::abs);
		
		System.out.println(l1);
		System.out.println(l2);
	}
	
	public static long testAbs(long s, Function<Long, Long> fun) {
		Long l = fun.apply(s);
		return l;
	}
}

3.例項方法引用

迴圈集合中元素,使用forEach方法

(s) -> System.out.println(s)的型別是Consumer型別

其accept接受引數和println一致


package org.xxxx.demo01;
 
import java.util.ArrayList;
import java.util.List;
 
public class Demo01 {
	public static void main(String[] args) {
		// 建立集合
		List<String> list = new ArrayList<>();
		
		// 新增元素
		list.add("e");
		list.add("c");
		list.add("a");
		list.add("d");
		list.add("b");
		
		// 排序
		list.sort((s1, s2) -> s1.compareTo(s2));
		
		// 遍歷
		list.forEach((s) -> System.out.println(s));
		list.forEach(System.out::println);
	}
}

4.構造方法引用

在引用構造器的時候,構造器引數列表要與介面中抽象方法的引數列表一致

格式為 類名::new


package org.xxxx.demo01;
 
import java.util.function.Supplier;
 
public class Demo01 {
	public static void main(String[] args) {
		getString(String :: new);
	}
	
	public static void getString(Supplier<String> su) {
		String s = su.get();
		System.out.println(s == null);
	}
}

5.任意物件的例項方法

若Lambda表示式的引數列表的第一個引數,是例項方法的呼叫者

第二個引數(或無參)是例項方法的引數時,就可以使用這種方法

參考第3條中的排序

C.Stream流

1.思考

計算字串List中長度大於2的元素的數量

分析:使用增強for迴圈

a.程式碼冗餘

b.實現平行計算很麻煩

c.程式碼無法及時傳遞程式設計師的意圖 ,必須讀完程式碼

2.外部迭代

 forEach工作原理:程式碼底層使用Iterator進行迭代的,是序列化操作

3.內部迭代

4.Stream概述

是用函數語言程式設計方式在集合類上進行復雜操作的工具,更像一個高階版本的 Iterator

原始版本的 Iterator,使用者只能顯式地一個一個遍歷元素並對其執行某些操作

高階版本的 Stream,使用者只要給出需要對其包含的元素執行什麼操作

Stream 會隱式地在內部進行遍歷,做出相應的資料轉換

而和迭代器又不同的是,Stream 可以並行化操作

藉助於 Lambda 表示式,極大的提高程式設計效率和程式可讀性

5.常用操作

a.forEach

迭代集合中元素。接收一個 Lambda 表示式

然後在 Stream 的每一個元素上執行該表示式

此操作是及早求值方法

b.collect(toList()) 

由Stream 裡的值生成一個列表,是一個及早求值操作

很多Stream 操作都是惰性求值,因此呼叫Stream 一系列方法之後

還需最後再呼叫一個類似collect 的及早求值方法返回集合

開篇的例子:(再將符合要求的字串放到一個新的集合裡)

使用filter過濾器:遍歷資料並檢查其中的元素

package org.xxxx.demo01;
 
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
 
public class Demo01 {
	public static void main(String[] args) {
		// 建立集合
		List<String> list = new ArrayList<>();
 
		// 新增元素
		list.add("sdf");
		list.add("a");
		list.add("asdf");
		list.add("d");
		list.add("basdfgh");
 
		// 統計長度大於2的
		long count = list.stream().filter((s) -> s.length() > 2).count();
 
		// 將符合要求的放入集合
		List<String> list2 = list.stream().filter((s) -> s.length() > 2).collect(Collectors.toList());
 
		System.out.println(count);
		list2.forEach(System.out :: println);
	}
 
}

6.map

如果有一個函式可以將一種型別的值轉換成另外一種型別

map 操作就可以使用該函式,將一個流中的值轉換成一個新的流

需求:將字串全轉換成大寫

package org.xxxx.demo01;
 
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
 
public class Demo01 {
	public static void main(String[] args) {
		// 建立集合
		List<String> list = new ArrayList<>();
 
		// 新增元素
		list.add("sdf");
		list.add("a");
		list.add("asdf");
		list.add("d");
		list.add("basdfgh");
		
		// 轉換為大寫
		List<String> list2 = list.stream().map((s) -> s.toUpperCase()).collect(Collectors.toList());
		
		list2.forEach(System.out :: println);
 
	}
 
}

7.filter

遍歷資料並檢查其中的元素。例如獲取字串List中以數字開始的字元集合

package org.xxxx.demo01;
 
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
 
public class Demo01 {
	public static void main(String[] args) {
		// 建立集合
		List<String> list = new ArrayList<>();
 
		// 新增元素
		list.add("1sdf");
		list.add("a");
		list.add("2asdf");
		list.add("d");
		list.add("basdfgh");
 
		// 獲取數字開頭的
		List<String> list2 = list.stream().filter((s) -> Character.isDigit(s.charAt(0))).collect(Collectors.toList());
 
		list2.forEach(System.out::println);
	}
 
}

8.flatMap 

可用Stream 替換值, 然後將多個Stream 連線成一個Stream

map 操作可用一個新的值代替Stream 中的值

若使用者希望讓map操作有點變化

生成一個新的Stream 物件取而代之則flatMap適用

假設有一個包含多個列表的流,現在希望得到所有數字的序列

package org.xxxx.demo01;
 
import java.util.Arrays;
import java.util.stream.Stream;
 
public class Demo01 {
	public static void main(String[] args) {
 
		Stream<?> flatMap = Stream.of(Arrays.asList("a", "b"), Arrays.asList(1, 2, 3)).flatMap((s) -> s.stream());
		flatMap.forEach(System.out :: println);
	}
 
}

9.max和min

獲取Stream中最大值或最小值,獲取字串List中長度最長的字串長度

package org.xxxx.demo01;
 
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
 
public class Demo01 {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>();
		
		list.add("abc");
		list.add("ab");
		list.add("abcd");
		list.add("abcde");
		
		// 獲取最大值
		int max = list.stream().map((s) -> s.length()).max(Integer :: compareTo).get();
		System.out.println(max);
		
		// 獲取最小值,另一種方法
		int min = list.stream().min(Comparator.comparing((s) -> s.length())).get().length();
		System.out.println(min);
	}
 
}

10.reduce

通過指定的函式把stream中的多個元素匯聚為一個元素,min max等是它的特例

格式:reduce(初始值,(r, i) -> r + i) 或者 reduce((r, i) -> r + "*" i)

計算1~100的和

package org.xxxx.demo01;
 
import java.util.ArrayList;
import java.util.List;
 
public class Demo01 {
	public static void main(String[] args) {
		List<Long> list = new ArrayList<>();
		
		// 封裝到集合
		for (long i = 1; i <= 100; i++) {
			list.add(i);
		}
		
		// 計算
		// reduce:參1,和的初始值
		Long sum = list.stream().parallel().reduce(0L, (r, l) -> r + l);
		
		System.out.println(sum);
	}
 

11.練習

a.獲取Student集合中年齡小於20歲的集合中年齡最大的學生資訊

Student類省略

package org.xxxx.demo01;
 
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
 
public class Demo01 {
	public static void main(String[] args) {
		List<Student> list = new ArrayList<>();
 
		Student s1 = new Student("張三", 21);
		Student s2 = new Student("李四", 19);
		Student s3 = new Student("王五", 18);
		Student s4 = new Student("程六", 17);
		Student s5 = new Student("趙七", 20);
 
		list.add(s1);
		list.add(s2);
		list.add(s3);
		list.add(s4);
		list.add(s5);
 
		// 篩選
		Student student = list.stream().filter((s) -> s.getAge() < 20).max(Comparator.comparing((s) -> s.getAge()))
				.get();
		System.out.println(student);
	}
 
}

 b.查詢集合中以a開頭的字元的長度集合

package org.xxxx.demo01;
 
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
 
public class Demo01 {
	public static void main(String[] args) {
		List<Integer> list = Stream.of("abc", "b", "a", "abcd").filter((s) -> s.startsWith("a")).map((s) -> s.length())
				.collect(Collectors.toList());
		System.out.println(list);
	}
 
}

D.並行API

1.概述

在Java7之前,並行處理資料基本是通過多執行緒來解決

a.將資料分成部分

b.給每個子部分分配一個子執行緒

c.等待所有的子執行緒全部結束

d.合併子執行緒

這樣的並行資料處理不穩定、易出錯,在Java8中Stream介面應用分支/合併框架

將一個數據內容分成多個數據塊,並用不同的執行緒分別處理每個資料塊流

Stream有序列和並行兩種,序列Stream上的操作是在一個執行緒中依次完成

而並行Stream則是在多個執行緒上同時執行

並行是每個cpu執行一個程式

2.使用方法

通常編寫並行程式碼很難而且容易出錯,

但使用 Stream API 無需編寫一行多執行緒的程式碼

就可以很方便地寫出高效能的併發程式 

a.呼叫Stream的parallel()方法

b.呼叫Collection的parallelStream()方法

c.parallel() 與 sequential() 可在並行流與順序流之間切換

package org.xxxx.demo01;
 
import java.util.ArrayList;
import java.util.List;
 
public class Demo01 {
	public static void main(String[] args) {
		List<Long> list = new ArrayList<>();
		
		// 封裝到集合
		for (long i = 1; i <= 100; i++) {
			list.add(i);
		}
		
		// 計算
		// reduce:參1,和的初始值
		Long sum = list.stream().parallel().reduce(0L, (r, l) -> r + l);
		
		System.out.println(sum);
	}
 
}

3.使用並行的建議

a.儘量使用基本型別的流  IntStream, LongStream, and DoubleStream

b.有些操作使用併發流的效能會比順序流的效能更差,比如limit,findFirst,依賴元素順序的操作在併發流中是極其消耗效能的.findAny的效能就會好很多,因為不依賴順序

c.考慮流中計算的效能(Q)和操作的效能(N)的對比, Q表示單個處理所需的時間, N表示需要處理的數量,如果Q的值越大, 使用併發流的效能就會越高

d.資料量不大時使用併發流,效能得不到提升

e.考慮資料結構:併發流需要對資料進行分解,不同的資料結構被分解的效能時不一樣的

f.傳遞給並行流的函式都是執行緒安全的,無副作用

4.何時需要並行程式設計

a.是否需要並行

弄清楚你要解決的問題是什麼,資料量有多大,計算的特點是什麼

並不是所有的問題都適合使用併發,比如當資料量不大時

順序執行往往比並行執行更快。

準備執行緒池和其它相關資源也是需要時間的

但當任務涉及到I/O操作並且任務之間不互相依賴時,那麼適合並行化

b.任務之間是否是獨立的?是否會引起任何競態條件

如果任務之間是獨立的

並且程式碼中不涉及到對同一個物件的某個狀態或者某個變數的更新操作

那麼就表明程式碼是可以被並行化的

c.結果是否取決於任務的呼叫順序

由於在並行環境中任務的執行順序是不確定的

因此對於依賴於順序的任務而言,並行化也許不能給出正確的結果

E.Time日期時間

1.舊的日期時間缺點

a.非執行緒安全

 java.util.Date不是執行緒安全的

因此開發者必須在使用日期處理併發性問題

新的日期時間API是不可變的,並且沒有setter方法

b.設計不佳

預設的開始日期從1900年,開始每月從1天從0開始,所以沒有統一

不直接使用方法操作日期。新的API提供了這樣操作實用方法

c. 困難的時區處理

開發人員必須編寫大量的程式碼來處理時區的問題

新的API設計開發保持特定領域設計

2.Instat

a.概述

時間戳,即時間軸上的一個點

從元年1970-1-1 00:00:00到現在的nanosecond數

b. Instant.now();獲取當前時間

package org.xxxx.demo01;
 
import java.time.Instant;
 
public class Demo01 {
	public static void main(String[] args) {
		// 獲取當前時間
		Instant now = Instant.now();
		
		// T代表東西經0°經線區時:倫敦時間
		System.out.println(now);// 2017-11-25T14:06:57.079Z
	}
 
}

 c.Instant.ofEpochMilli(new Date().getTime()); 舊日期轉為新日期

package org.xxxx.demo01;
 
import java.time.Instant;
import java.util.Date;
 
public class Demo01 {
	public static void main(String[] args) {
		// 獲取當前時間
		Instant instant = Instant.ofEpochMilli(new Date().getTime());
		System.out.println(instant);
	}
 
}

d. Instant.parse();日期文字格式轉換為時間格式

package org.xxxx.demo01;
 
import java.time.Instant;
 
public class Demo01 {
	public static void main(String[] args) {
		// 獲取當前時間
		Instant instant = Instant.parse("1993-02-06T10:12:35Z");
		System.out.println(instant);
	}
 
}

3.LocalDate

表示不帶時區的日期,比如2014-01-14

a.LocalDateTime.now();獲取當前日期時間

b.now.minusDays(2);將當前日期時間減去兩天

c.LocalDateTime.of(2016, 10, 23);構造一個指定日期時間的物件

package org.xxxx.demo01;
 
import java.time.LocalDate;
 
public class Demo01 {
	public static void main(String[] args) {
		// 當前時間
		LocalDate now = LocalDate.now();
		System.out.println(now);
		
		// 往前推兩天
		LocalDate date = now.minusDays(2);
		System.out.println(date);
		
		// 制定一個日期
		LocalDate localDate = LocalDate.of(1993, 2, 6);
		System.out.println(localDate);
	}
 
}

4.LocalTime

表示不帶時區的時間

a. LocalTime.now();當前時間

b. LocalTime.of(22, 33);確定的時間

c.LocalTime.ofSecondOfDay(4503); 一天中的第4503秒

package org.xxxx.demo01;
 
import java.time.LocalTime;
 
public class Demo01 {
	public static void main(String[] args) {
		// 當前時間
		LocalTime now = LocalTime.now();
		System.out.println(now);
		
		// 22:33
		LocalTime localTime = LocalTime.of(22, 33);
		System.out.println(localTime);
		
		// 一天中的4503秒
		LocalTime ofDay = LocalTime.ofSecondOfDay(4503);
		System.out.println(ofDay);
	}
 
}

5.LocalDateTim

 是LocalDate和LocalTime的組合體,表示的是不帶時區的日期及時間

a.LocalDateTime.now();當前時間

b.localDateTime.plusHours(25).plusMinutes(3); 當前時間加上25小時3分鐘

c.LocalDateTime.of(2014, 1, 20, 3, 30, 20);轉換

package org.xxxx.demo01;
 
import java.time.LocalDateTime;
 
public class Demo01 {
	public static void main(String[] args) {
		// 當前時間
		LocalDateTime now = LocalDateTime.now();
		System.out.println(now);
 
		// 當前時間加上25小時3分鐘
		LocalDateTime plusMinutes = now.plusHours(25).plusMinutes(3);
		System.out.println(plusMinutes);
 
		// 轉換
		LocalDateTime of = LocalDateTime.of(1993, 2, 6, 11, 23, 30);
		System.out.println(of);
	}
 
}

6.ZoneDateTime

含有時區資訊的時間

a.ZonedDateTime.now();獲取包含時區的當前日期時間

b.ZonedDateTime.of(LocalDateTime.of(2014, 1, 20, 3, 30, 20), ZoneId.of("+08"));建立時區的日期時間物件


package org.xxxx.demo01;
 
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
 
public class Demo01 {
	public static void main(String[] args) {
		// 當前時間
		ZonedDateTime now = ZonedDateTime.now();
		System.out.println(now);
 
		// 建立時區的日期時間物件
		ZonedDateTime of = ZonedDateTime.of(LocalDateTime.of(1993, 2, 6, 11, 23, 30), ZoneId.of("+08"));
		System.out.println(of);
	}
 
}

7.DateTimeFormatter

格式化日期和解析日期格式字串。DateTimeFormatter是不可變類

a.格式化:日期物件轉換為格式字串

package org.xxxx.demo01;
 
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
 
public class Demo01 {
	public static void main(String[] args) {
		// 格式化
		String time = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss").format(LocalDateTime.now());
		System.out.println(time);
	}
 
}

 b.解析:格式字串轉換為日期物件

package org.xxxx.demo01;
 
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
 
public class Demo01 {
	public static void main(String[] args) {
		// 格式化
		LocalDateTime parse = LocalDateTime.parse("2017.01.01 08:08:08", DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss"));
		System.out.println(parse);
	}
 
}

8. 遺留程式碼操作

a.Date --> Instant

b.Calendar-->Instant

package org.xxxx.demo01;
 
import java.time.Instant;
import java.util.Calendar;
import java.util.Date;
 
public class Demo01 {
	public static void main(String[] args) {
		Instant timestamp = new Date().toInstant(); 
		
		Instant instant = Calendar.getInstance().toInstant();
		
		System.out.println(timestamp);
		System.out.println(instant);
	}
 
}

F.其他

1.預設方法

介面和它的實現類保持相容非常重要

因為你開發的類庫可能正在被多個開發者廣泛的使用著

若上層介面需要做改變的時候(如增加新的空方法)

下層介面就需要實現新增的方法,在某些情況下,變得不靈活

因此:使用預設方法

介面中定義的default修飾的有具體實現的方法,是給介面增加的公用方法

是物件方法,需要使用物件來進行訪問

新增預設方法,對以前程式碼不會有影響,避免其實現類都做出相應的改動

且default方法通常使用已經實現類已存在的方法進行定義

2.問:

如果一個介面中定義一個預設方法

另外一個父類或介面中定義同名方法(不管是否default)時候,子類如何處理

a.選擇父類方法。當父類提供具體實現時,介面同名同參方法被忽略

b.覆蓋父類方法。當一個父介面提供預設方法,另一個父介面提供同名同參方法時候

3.靜態方法

介面中定義的static修飾的有具體實現的方法

類似於default方法,但在實現類中不能覆蓋(override)該方法

介面的static方法只對接口裡的方法可見,和其他靜態方法類似

可以使用介面名來呼叫介面中的靜態方法

介面的static方法有利於提供通用的方法,比如判空檢查,集合排序等

可以移除工具類中方法到介面static方法中

例如:sList.sort((s,t)->Integer.compare(s.length(), t.length()));

轉換為靜態方法:sList.sort(Comparator.comparing(String::length));

4.String新增join(字串集合或陣列)方法

使用指定連線符連線多個字串

package org.xxxx.demo01;
 
public class Demo01 {
	public static void main(String[] args) {
		String[] str = {"a", "b", "c"};
		
		String join = String.join("-", str);
		System.out.println(join);
	}
}

5.可能錯過的Java7特性

try--with-resources

在 JDK 7 之前,檔案資源需要手動finally中關閉

try-with-resources 能夠很容易地關閉在 try-catch 語句塊中使用的資源

所謂的資源(resource)是指在程式完成後,必須關閉的物件

try-with-resources 語句確保了每個資源在語句結束時關閉

所有實現了 java.lang.AutoCloseable 介面的物件可以使用作為資源

在try( ...)裡宣告的資源,會在try-catch程式碼塊結束後自動關閉掉