1、引言
jdk1.8出來已經一段時間了,如今1.9也已經出來了,可是不少公司(咱們公司也同樣)不太願意升級到高版本的jdk,主要是有老的項目要維護,還有升級的話配套的框架也要升級,要考慮的細節事情太多。java
前段時間去面試,問到了jdk1.8的新特性,博主答得不是很好,今天抽了一段時間把這些都總結一下。面試
2、新特性
一、default關鍵字
在java裏面,咱們一般都是認爲接口裏面是隻能有抽象方法,不能有任何方法的實現的,那麼在jdk1.8裏面打破了這個規定,引入了新的關鍵字default,經過使用default修飾方法,可讓咱們在接口裏面定義具體的方法實現,以下。算法
public interface NewCharacter {編程
public void test1();數據結構
public default void test2(){
System.out.println("我是新特性1");
}多線程
}框架
那這麼定義一個方法的做用是什麼呢?爲何不在接口的實現類裏面再去實現方法呢?編程語言
其實這麼定義一個方法的主要意義是定義一個默認方法,也就是說這個接口的實現類實現了這個接口以後,不用管這個default修飾的方法, 也能夠直接調用,以下。ide
public class NewCharacterImpl implements NewCharacter{函數式編程
@Override
public void test1() {
}
public static void main(String[] args) {
NewCharacter nca = new NewCharacterImpl();
nca.test2();
}
}
因此說這個default方法是全部的實現類都不須要去實現的就能夠直接調用,那麼好比說jdk的集合List裏面增長了一個sort方法,那麼若是定義爲一個抽象方法,其全部的實現類如arrayList,LinkedList等都須要對其添加實現,那麼如今用default定義一個默認的方法以後,其實現類能夠直接使用這個方法了,這樣無論是開發仍是維護項目,都會大大簡化代碼量。
二、Lambda 表達式
Lambda表達式是jdk1.8裏面的一個重要的更新,這意味着java也開始認可了函數式編程,而且嘗試引入其中。
首先,什麼是函數式編程,引用廖雪峯先生的教程裏面的解釋就是說:函數式編程就是一種抽象程度很高的編程範式,純粹的函數式編程語言編寫的函數沒有變量,所以,任意一個函數,只要輸入是肯定的,輸出就是肯定的,這種純函數咱們稱之爲沒有反作用。而容許使用變量的程序設計語言,因爲函數內部的變量狀態不肯定,一樣的輸入,可能獲得不一樣的輸出,所以,這種函數是有反作用的。函數式編程的一個特色就是,容許把函數自己做爲參數傳入另外一個函數,還容許返回一個函數!
簡單的來講就是,函數也是一等公民了,在java裏面一等公民有變量,對象,那麼函數式編程語言裏面函數也能夠跟變量,對象同樣使用了,也就是說函數既能夠做爲參數,也能夠做爲返回值了,看一下下面這個例子。
//這是常規的Collections的排序的寫法,須要對接口方法重寫
public void test1(){
List<String> list =Arrays.asList("aaa","fsa","ser","eere");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
for (String string : list) {
System.out.println(string);
}
}
//這是帶參數類型的Lambda的寫法
public void testLamda1(){
List<String> list =Arrays.asList("aaa","fsa","ser","eere");
Collections.sort(list, (Comparator<? super String>) (String a,String b)->{
return b.compareTo(a);
}
);
for (String string : list) {
System.out.println(string);
}
}
//這是不帶參數的lambda的寫法
public void testLamda2(){
List<String> list =Arrays.asList("aaa","fsa","ser","eere");
Collections.sort(list, (a,b)->b.compareTo(a)
);
for (String string : list) {
System.out.println(string);
}
能夠看到不帶參數的寫法一句話就搞定了排序的問題,因此引入lambda表達式的一個最直觀的做用就是大大的簡化了代碼的開發,像其餘一些編程語言Scala,Python等都是支持函數式的寫法的。固然,不是全部的接口均可以經過這種方法來調用,只有函數式接口才行,jdk1.8裏面定義了好多個函數式接口,咱們也能夠本身定義一個來調用,下面說一下什麼是函數式接口。
三、函數式接口
定義:「函數式接口」是指僅僅只包含一個抽象方法的接口,每個該類型的lambda表達式都會被匹配到這個抽象方法。jdk1.8提供了一個@FunctionalInterface註解來定義函數式接口,若是咱們定義的接口不符合函數式的規範便會報錯。
@FunctionalInterface
public interface MyLamda {
public void test1(String y);
//這裏若是繼續加一個抽象方法便會報錯
// public void test1();
//default方法能夠任意定義
default String test2(){
return "123";
}
default String test3(){
return "123";
}
//static方法也能夠定義
static void test4(){
System.out.println("234");
}
}
看一下這個接口的調用,符合lambda表達式的調用方法。
MyLamda m = y -> System.out.println("ss"+y);
1
4.方法與構造函數引用
jdk1.8提供了另一種調用方式::,當 你 需 要使用 方 法 引用時 , 目 標引用 放 在 分隔符::前 ,方法 的 名 稱放在 後 面 ,即ClassName :: methodName 。例如 ,Apple::getWeight就是引用了Apple類中定義的方法getWeight。請記住,不須要括號,由於你沒有實際調用這個方法。方法引用就是Lambda表達式(Apple a) -> a.getWeight()的快捷寫法,以下示例。
//先定義一個函數式接口
@FunctionalInterface
public interface TestConverT<T, F> {
F convert(T t);
}
測試以下,能夠以::形式調用。
public void test(){
TestConverT<String, Integer> t = Integer::valueOf;
Integer i = t.convert("111");
System.out.println(i);
}
此外,對於構造方法也能夠這麼調用。
//實體類User和它的構造方法
public class User {
private String name;
private String sex;
public User(String name, String sex) {
super();
this.name = name;
this.sex = sex;
}
}
//User工廠
public interface UserFactory {
User get(String name, String sex);
}
//測試類
UserFactory uf = User::new;
User u = uf.get("ww", "man");
這裏的User::new就是調用了User的構造方法,Java編譯器會自動根據UserFactory.get方法的簽名來選擇合適的構造函數。
五、局部變量限制
Lambda表達式也容許使用自由變量(不是參數,而是在外層做用域中定義的變量),就像匿名類同樣。 它們被稱做捕獲Lambda。 Lambda能夠沒有限制地捕獲(也就是在其主體中引用)實例變量和靜態變量。但局部變量必須顯式聲明爲final,或事實上是final。
爲何局部變量有這些限制?
(1)實例變量和局部變量背後的實現有一個關鍵不一樣。實例變量都存儲在堆中,而局部變量則保存在棧上。若是Lambda能夠直接訪問局部變量,並且Lambda是在一個線程中使用的,則使用Lambda的線程,可能會在分配該變量的線程將這個變量收回以後,去訪問該變量。所以, Java在訪問自由局部變量時,其實是在訪問它的副本,而不是訪問原始變量。若是局部變量僅僅賦值一次那就沒有什麼區別了——所以就有了這個限制。
(2)這一限制不鼓勵你使用改變外部變量的典型命令式編程模式。
final int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2);
六、Date Api更新
1.8以前JDK自帶的日期處理類很是不方便,咱們處理的時候常常是使用的第三方工具包,好比commons-lang包等。不過1.8出現以後這個改觀了不少,好比日期時間的建立、比較、調整、格式化、時間間隔等。這些類都在java.time包下。比原來實用了不少。
6.1 LocalDate/LocalTime/LocalDateTime
LocalDate爲日期處理類、LocalTime爲時間處理類、LocalDateTime爲日期時間處理類,方法都相似,具體能夠看API文檔或源碼,選取幾個表明性的方法作下介紹。
now相關的方法能夠獲取當前日期或時間,of方法能夠建立對應的日期或時間,parse方法能夠解析日期或時間,get方法能夠獲取日期或時間信息,with方法能夠設置日期或時間信息,plus或minus方法能夠增減日期或時間信息;
6.2TemporalAdjusters
這個類在日期調整時很是有用,好比獲得當月的第一天、最後一天,當年的第一天、最後一天,下一週或前一週的某天等。
6.3DateTimeFormatter
之前日期格式化通常用SimpleDateFormat類,可是不怎麼好用,如今1.8引入了DateTimeFormatter類,默認定義了不少常量格式(ISO打頭的),在使用的時候通常配合LocalDate/LocalTime/LocalDateTime使用,好比想把當前日期格式化成yyyy-MM-dd hh:mm:ss的形式:
LocalDateTime dt = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
System.out.println(dtf.format(dt));
七、流
定義:流是Java API的新成員,它容許咱們以聲明性方式處理數據集合(經過查詢語句來表達,而不是臨時編寫一個實現)。就如今來講,咱們能夠把它們當作遍歷數據集的高級迭代器。此外,流還能夠透明地並行處理,也就是說咱們不用寫多線程代碼了。
Stream 不是集合元素,它不是數據結構並不保存數據,它是有關算法和計算的,它更像一個高級版本的 Iterator。原始版本的 Iterator,用戶只能顯式地一個一個遍歷元素並對其執行某些操做;高級版本的 Stream,用戶只要給出須要對其包含的元素執行什麼操做,好比 「過濾掉長度大於 10 的字符串」、「獲取每一個字符串的首字母」等,Stream 會隱式地在內部進行遍歷,作出相應的數據轉換。
Stream 就如同一個迭代器(Iterator),單向,不可往復,數據只能遍歷一次,遍歷過一次後即用盡了,就比如流水從面前流過,一去不復返。而和迭代器又不一樣的是,Stream 能夠並行化操做,迭代器只能命令式地、串行化操做。顧名思義,當使用串行方式去遍歷時,每一個 item 讀完後再讀下一個 item。而使用並行去遍歷時,數據會被分紅多個段,其中每個都在不一樣的線程中處理,而後將結果一塊兒輸出。Stream 的並行操做依賴於 Java7 中引入的 Fork/Join 框架(JSR166y)來拆分任務和加速處理過程。
流的操做類型分爲兩種:
Intermediate:一個流能夠後面跟隨零個或多個 intermediate 操做。其目的主要是打開流,作出某種程度的數據映射/過濾,而後返回一個新的流,交給下一個操做使用。這類操做都是惰性化的(lazy),就是說,僅僅調用到這類方法,並無真正開始流的遍歷。
Terminal:一個流只能有一個 terminal 操做,當這個操做執行後,流就被使用「光」了,沒法再被操做。因此這一定是流的最後一個操做。Terminal 操做的執行,纔會真正開始流的遍歷,而且會生成一個結果,或者一個 side effect。
在對於一個 Stream 進行屢次轉換操做 (Intermediate 操做),每次都對 Stream 的每一個元素進行轉換,並且是執行屢次,這樣時間複雜度就是 N(轉換次數)個 for 循環裏把全部操做都作掉的總和嗎?其實不是這樣的,轉換操做都是 lazy 的,多個轉換操做只會在 Terminal 操做的時候融合起來,一次循環完成。咱們能夠這樣簡單的理解,Stream 裏有個操做函數的集合,每次轉換操做就是把轉換函數放入這個集合中,在 Terminal 操做的時候循環 Stream 對應的集合,而後對每一個元素執行全部的函數。
構造流的幾種方式
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
因爲流這一塊博主目前使用的不是不少,因此有些新的用法也不是很熟悉,這一塊大概就寫到這裏,若是有比較懂得還請多多指教。
3、總結總的來講,jdk1.8的一些新特性主要仍是簡化了代碼的寫法,減小了部分開發量,可是須要一些時間來熟悉。挺慚愧的,如今1.9都已經出來了,1.8的新特性還不是很熟悉,因此仍是要繼續努力,多看些開源的東西。