- 前言:
在遨遊了一番 Java Web 的世界以後,發現了本身的一些缺失,因此就着一篇深度好文:知名互聯網公司校招 Java 開發崗面試知識點解析 ,來好好的對 Java 知識點進行復習和學習一番,大部份內容參照自這一篇文章,有一些本身補充的,也算是從新學習一下 Java 吧。javascript
前序文章連接:css
對於 Java 各個版本的特性,特別是 Java 8 的新知識點,咱們都應該有所瞭解。
前排申明和好文推薦:閃爍之狐 » Java5新特性及使用 » Java6新特性及使用 » Java7新特性及使用 » Java8新特性及使用(一) » Java8新特性及使用(二)面試
參考文章:jdk 1.5新特性算法
答:加強 for 循環:foreach 語句,foreach 簡化了迭代器。sql
格式:// 加強for循環括號裏寫兩個參數,第一個是聲明一個變量,第二個就是須要迭代的容器shell
for( 元素類型 變量名 : Collection集合 & 數組 ) { … }
語法:
for ( type 變量名:集合變量名 ) { … }
數據庫
注意事項:
高級for循環和傳統for循環的區別:
高級for循環在使用時,必需要明確被遍歷的目標。這個目標,能夠是Collection集合或者數組,若是遍歷Collection集合,在遍歷過程當中還須要對元素進行操做,好比刪除,須要使用迭代器。
若是遍歷數組,還須要對數組元素進行操做,建議用傳統for循環由於能夠定義角標經過角標操做元素。若是隻爲遍歷獲取,能夠簡化成高級for循環,它的出現爲了簡化書寫。比起普通的for循環,高級for循環還有性能優點,由於它對數組索引的邊界值只計算一次(摘自《Effective Java》第46條)。
高級for循環能夠遍歷map集合嗎?
答:原則上map集合是沒法使用加強for循環來迭代的,由於加強for循環只能針對實現了Iterable接口的集合進行迭代;Iterable是jdk5中新定義的接口,就一個方法iterator方法,只有實現了Iterable接口的類,才能保證必定有iterator方法,java有這樣的限定是由於加強for循環內部仍是用迭代器實現的,而實際上,咱們能夠經過某種方式來使用加強for循環。
for(Object obj : map.entrySet()) { Map.Entry entry = (Entry) obj; // obj 依次表示Entry System.out.println(entry.getKey() + "=" + entry.getValue()); }
總之,for-each 循環在簡潔性和預防 Bug 方面有着傳統 for 循環沒法比擬的優點,而且沒有性能損失。應該儘量地使用 for-each 循環。遺憾的是,有三種常見的狀況是沒法使用 for-each 循環的:
過濾——若是須要遍歷集合,並刪除選定的元素,就須要使用顯式地迭代器,以即可以調用它的 remove 方法。
轉換——若是須要遍歷列表或者數組,並取代它部分或者所有的元素值(增刪、或對元素進行賦值),就須要列表迭代器或者數組索引,以便設定元素的值
平行迭代——若是須要並行地遍歷多個集合,就須要顯式地控制迭代器或者所因變量以便全部迭代器或者索引變量均可以獲得同步前移
解析:什麼意思呢?舉個例子:在 JDK 1.5 以前,當咱們要爲一個傳遞多個類型相同的參數時,咱們有兩種方法解決,1.直接傳遞一個數組過去,2.有多少個參數就傳遞多少個參數。
例如:
public void printColor(String red,String green,String yellow){ } // 或者 public void printColor(String[] colors){ }
這樣編寫方法參數雖然可以實現咱們想要的效果,可是,這樣是否是有點麻煩呢?再者,若是參數個數不肯定,咱們怎麼辦呢?Java JDK1.5爲咱們提供的可變參數就可以完美的解決這個問題
答:
可變參數(...):用到函數的參數上,當要操做的同一個類型元素個數不肯定的時候,但是用這個方式,這個參數能夠接受任意個數的同一類型的數據。
和之前接收數組不同的是:
之前定義數組類型,須要先建立一個數組對象,再將這個數組對象做爲參數傳遞給函數。如今,直接將數組中的元素做爲參數傳遞便可。底層實際上是將這些元素進行數組的封裝,而這個封裝動做,是在底層完成的,被隱藏了。因此簡化了用戶的書寫,少了調用者定義數組的動做。
若是在參數列表中使用了可變參數,可變參數必須定義在參數列表結尾(也就是必須是最後一個參數,不然編譯會失敗。)。
若是要獲取多個int數的和呢?可使用將多個int數封裝到數組中,直接對數組求和便可。
可變參數的特色:
Public int add(int x, int... args){//也能夠直接(int..args)就是說傳不傳均可以 Int sum = x; For(int i = 0; i<=args.lengrth;i++){ Sum+=args[i]; } return sum; }
實例:
public class VariableParameter { public static void main(String[] args) { System. out.println(add(1, 2)); System. out.println(add(1, 2, 3)); } public static int add(int x, int... args){ int sum = x; for(int i = 0; i < args.length; i++){ sum += args[i]; } return sum; } }
解析:關鍵字 enum
答:
問題:對象的某個屬性的值不能是任意的,必須爲固定的一組取值其中的某一個;
解決辦法:
1)在 setGrade 方法中作判斷,不符合格式要求就拋出異常;
2)直接限定用戶的選擇,經過自定義類模擬枚舉的方式來限定用戶的輸入,寫一個 Grade 類,私有構造函數,對外提供 5 個靜態的常量表示類的實例;
3)jdk5 中新定義了枚舉類型,專門用於解決此類問題;
4)枚舉就是一個特殊的java類,能夠定義屬性、方法、構造函數、實現接口、繼承類;
爲何要有枚舉?
問題:要定義星期幾或性別的變量,該怎麼定義?假設用1-7分別表示星期一到星期日,但有人可能會寫成int weekday = 0;或即便使用常量方式也沒法阻止意外。
枚舉就是要讓某個類型的變量的取值只能爲若干個固定值中的一個,不然,編譯器就會報錯。枚舉可讓編譯器在編譯時就能夠控制源程序中填寫的非法值,普通變量的方式在開發階段沒法實現這一目標。
用普通類如何實現枚舉功能,定義一個Weekday的類來模擬枚舉功能。
一、私有的構造方法。
二、每一個元素分別用一個公有的靜態成員變量表示。
能夠有若干公有方法或抽象方法。採用抽象方法定義nextDay就將大量的if.else語句轉移成了一個個獨立的類
示例:定義一個Weekday的類來模擬枚舉功能。
public class WeekDay { private WeekDay(){} public static final WeekDay SUN = new WeekDay(); public static final WeekDay MON = new WeekDay(); public WeekDay nextDay(){ if(this == SUN){ return MON ; } else{ return SUN ; } } public String toString(){ return this == SUN? "SUN":"MON" ; } } public class EnumTest { public static void main(String[] args) { WeekDay day = WeekDay.MON; System. out.println(day.nextDay()); //結果:SUN } }
使用枚舉類實現
public class EnumTest { public static void main(String[] args) { WeekDay day = WeekDay.FRI; System.out.println(day); //結果:FRI System.out.println(day.name()); //結果:FRI System.out.println(day.ordinal()); //結果:5 System.out.println(WeekDay. valueOf("SUN")); //結果:SUN System.out.println(WeekDay. values().length); //結果:7 } public enum WeekDay{ SUN,MON ,TUE,WED, THI,FRI ,SAT; } }
總結: 枚舉是一種特殊的類,其中的每一個元素都是該類的一個實例對象,例如能夠調用WeekDay.SUN.getClass().getName 和 WeekDay.class.getName()。
注意: 最後一個枚舉元素後面能夠加分號,也能夠不加分號。
實現帶有構造方法的枚舉
示例:
public class EnumTest { public static void main(String[] args) { WeekDay day = WeekDay.FRI; } public enum WeekDay{ SUN(1),MON (),TUE, WED,THI ,FRI,SAT; private WeekDay(){ System. out.println("first" ); } private WeekDay(int value){ System. out.println("second" ); } //結果: //second //first //first //first //first //first //first } }
實現帶有抽象方法的枚舉
定義枚舉TrafficLamp,實現抽象的nextTrafficLamp方法:每一個元素分別是由枚舉類的子類來生成的實例對象,這些子類採用相似內部類的方式進行定義。增長上表示時間的構造方法。
public class EnumTest { public static void main(String[] args) { TrafficLamp lamp = TrafficLamp.RED; System.out.println(lamp.nextLamp()); //結果:GREEN } public enum TrafficLamp { RED(30) { public TrafficLamp nextLamp() { return GREEN; } }, GREEN(45) { public TrafficLamp nextLamp() { return YELLOW; } }, YELLOW(5) { public TrafficLamp nextLamp() { return RED; } }; private int time; private TrafficLamp(int time) { this.time = time; } public abstract TrafficLamp nextLamp(); } }
注意:
一、枚舉只有一個成員時,就能夠做爲一種單例的實現方式。
二、查看生成的class文件,能夠看到內部類對應的class文件。
答:在 Java 中數據類型分爲兩種:基本數據類型、引用數據類型(對象)
自動裝箱:把基本類型變成包裝器類型,本質是調用包裝器類型的valueOf()方法
注意:基本數據類型的數組與包裝器類型數組不能互換
在 java程序中全部的數據都須要當作對象來處理,針對8種基本數據類型提供了包裝類,以下:
int → Integer
byte → Byte
short → Short
long → Long
char → Character
double → Double
float → Float
boolean → Boolean
在 jdk 1.5 之前基本數據類型和包裝類之間須要相互轉換:
基本---引用 Integer x = new Integer(x);
引用---基本 int num = x.intValue();
1)Integer x = 1; x = x + 1;
經歷了什麼過程?裝箱→拆箱→裝箱
2)爲了優化,虛擬機爲包裝類提供了緩衝池,Integer池的大小爲 -128~127 一個字節的大小。String池:Java 爲了優化字符串操做也提供了一個緩衝池;
→ 享元模式(Flyweight Pattern):享元模式的特色是,複用咱們內存中已經存在的對象,下降系統建立對象實例。
自動裝箱:
Integer num1 = 12;
自動拆箱:
System.out.println(num1 + 12);
基本數據類型的對象緩存:
Integer num1 = 12; Integer num2 = 12; System.out.println(num1 == num2);//ture
Integer num3 = 129; Integer num4 = 129; System.out.println(num3 == num4);//false
Integer num5 = Integer.valueOf(12); Integer num6 = Integer.valueOf(12); System.out.println(num5 == num6);//true
示例:
public class AutoBox { public static void main(String[] args) { //裝箱 Integer iObj = 3; //拆箱 System. out.println(iObj + 12); //結果:15 Integer i1 = 13; Integer i2 = 13; System. out.println(i1 == i2); //結果:true i1 = 137; i2 = 137; System. out.println(i1 == i2); //結果:false } }
注意:
若是有不少很小的對象,而且他們有相同的東西,那就能夠把他們做爲一個對象。
若是還有不少不一樣的東西,那就能夠做爲外部的東西,做爲參數傳入。
這就是享元設計模式(flyweight)。
例如示例中的Integer對象,在-128~127範圍內的Integer對象,用的頻率比較高,就會做爲同一個對象,所以結果爲true。超出這個範圍的就不是同一個對象,所以結果爲false。
答:引用泛型以後,容許指定集合裏元素的類型,免去了強制類型轉換,而且能在編譯時刻進行類型檢查的好處。Parameterized Type做爲參數和返回值,Generic是vararg、annotation、enumeration、collection的基石。
泛型能夠帶來以下的好處總結以下:
答:靜態導入:導入了類中的全部靜態成員,簡化靜態成員的書寫。
import語句能夠導入一個類或某個包中的全部類
import static語句導入一個類中的某個靜態方法或全部靜態方法
import static java.util.Collections.*; //導入了Collections類中的全部靜態成員
靜態導入能夠導入靜態方法,這樣就沒必要寫類名而能夠直接調用靜態方法了。
例子:
原來的:
public class Demo12 { public static void main(String[] args) { System.out.println(Math.max(12, 15)); System. out.println(Math.abs(3-6)); } }
使用靜態導入的:
import static java.lang.Math.max ; import static java.lang.Math.abs ; public class Demo12 { public static void main(String[] args) { System.out.println(max(12, 15)); System. out.println(abs(3-6)); } }
注意:
一、也能夠經過import static java.lang.Math.*;導入Math類下全部的靜態方法。
二、若是將javac設置爲了Java5如下,那麼靜態導入等jdk1.5的特性都會報告錯誤。
答: 最主要的就是引入了 java.util.concurrent 包,這個都是須要重點掌握的。
HashMap 的替代者 ConcurrentHashMap 和 ArrayList 的替代者 CopyOnWriteArrayList 在大併發量讀取時採用 java.util.concurrent 包裏的一些類會讓你們滿意 BlockingQueue、Callable、Executor、Semaphore
答:是 Java 語言對 Bean 類屬性、事件的一種缺省處理方法。例如類 A 中有屬性 name , 那咱們經過 getName,setName 來獲得其值或者設置新的值。經過 getName/setName 來訪問name屬性,這就是默認的規則。Java 中提供了一套 API 用來訪問某個屬性的 getter /setter 方法,經過這些 API 可使你不須要了解這個規則(但你最好仍是要搞清楚),這些 API 存放於包 java.beans 中。
通常的作法是經過類 Introspector 來獲取某個對象的 BeanInfo 信息,而後經過 BeanInfo 來獲取屬性的描述器 (PropertyDescriptor),經過這個屬性描述器就能夠獲取某個屬性對應的 getter/setter 方法,而後咱們就能夠經過反射機制來 調用這些方法。
答:
註解(Annotation)是一種應用於類、方法、參數、變量、構造器及包聲明中的特殊修飾符,它是一種由JSR-175標準選擇用來描述元數據的一種工具。Java從Java5開始引入了註解。在註解出現以前,程序的元數據只是經過java註釋和javadoc,可是註解提供的功能要遠遠超過這些。註解不只包含了元數據,它還能夠做用於程序運行過程當中、註解解釋器能夠經過註解決定程序的執行順序。
好比,下面這段代碼:
@Override public String toString() { return "This is String."; }
上面的代碼中,我重寫了toString()方法並使用了@Override註解。可是,即便咱們不使用@Override註解標記代碼,程序也可以正常執行。那麼,該註解表示什麼?這麼寫有什麼好處嗎?事實上,@Override告訴編譯器這個方法是一個重寫方法(描述方法的元數據),若是父類中不存在該方法,編譯器便會報錯,提示該方法沒有重寫父類中的方法。若是我不當心拼寫錯誤,例如將toString()寫成了toStrring(){double r},並且我也沒有使用@Override註解,那程序依然能編譯運行。但運行結果會和我指望的大不相同。如今咱們瞭解了什麼是註解,而且使用註解有助於閱讀程序。
爲何要引入註解?
使用註解以前(甚至在使用以後),XML被普遍的應用於描述元數據。不知什麼時候開始一些應用開發人員和架構師發現XML的維護愈來愈糟糕了。他們但願使用一些和代碼緊耦合的東西,而不是像XML那樣和代碼是鬆耦合的(在某些狀況下甚至是徹底分離的)代碼描述。若是你在Google中搜索「XML vs. annotations」,會看到許多關於這個問題的辯論。最有趣的是XML配置其實就是爲了分離代碼和配置而引入的。上述兩種觀點可能會讓你很疑惑,二者觀點彷佛構成了一種循環,但各有利弊。下面咱們經過一個例子來理解這二者的區別。
假如你想爲應用設置不少的常量或參數,這種狀況下,XML是一個很好的選擇,由於它不會同特定的代碼相連。若是你想把某個方法聲明爲服務,那麼使用註解會更好一些,由於這種狀況下須要註解和方法緊密耦合起來,開發人員也必須認識到這點。
另外一個很重要的因素是註解定義了一種標準的描述元數據的方式。在這以前,開發人員一般使用他們本身的方式定義元數據。例如,使用標記接口,註釋,transient關鍵字等等。每一個程序員按照本身的方式定義元數據,而不像註解這種標準的方式。
目前,許多框架將XML和Annotation兩種方式結合使用,平衡二者之間的利弊。
參考文章(更多註解戳這裏):Java註解的理解和應用
答:
ProcessBuilder
類是 Java5 在 java.lang
包中新添加的一個新類,此類用於建立操做系統進程,它提供一種啓動和管理進程(也就是應用程序)的方法。在此以前,都是由 Process
類處來實現進程的控制管理。每一個 ProcessBuilder
實例管理一個進程屬性集。它的 start()
方法利用這些屬性建立一個新的 Process
實例。start()
方法能夠從同一實例重複調用,以利用相同的或相關的屬性建立新的子進程。
ProcessBuilder
是一個 final
類,有兩個帶參數的構造方法,你能夠經過構造方法來直接建立 ProcessBuilder
的對象。而 Process
是一個抽象類,通常都經過 Runtime.exec()
和 ProcessBuilder.start()
來間接建立其實例。ProcessBuilder
爲進程提供了更多的控制,例如,能夠設置當前工做目錄,還能夠改變環境參數。而 Process
類的功能相對來講簡單的多。ProcessBuilder
類不是同步的。若是多個線程同時訪問一個 ProcessBuilder
,而其中至少一個線程從結構上修改了其中一個屬性,它必須保持外部同步。
若要使用 ProcessBuilder
建立一個進程,只須要建立 ProcessBuilder
的一個實例,指定該進程的名稱和所需參數。要執行此程序,調用該實例上的 start()
便可。下面是一個執行打開 Windows 記事本的例子。注意它將要編輯的文件名指定爲一個參數。
class PBDemo { public static void main(String args[]) { try { ProcessBuilder proc = new ProcessBuilder("notepad.exe", "testfile"); proc.start(); } catch (Exception e) { System.out.println("Error executing notepad."); } } }
參考文章:Java5新特性及使用
Formatter
類是Java5中新增的 printf-style
格式化字符串的解釋器,它提供對佈局和對齊的支持,提供了對數字,字符串和日期/時間數據的經常使用格式以及特定於語言環境的輸出。常見的 Java 類型,如 byte
,java.math.BigDecimal
和 java.util.Calendar
都支持。 經過 java.util.Formattable
接口提供了針對任意用戶類型的有限格式定製。
更詳細的介紹見這裏。主要使用方法的代碼示例以下:
import java.io.BufferedReader; import java.io.FileReader; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 格式化測試使用的示例類. * * @author blinkfox on 2017-11-28. */ public class FormatTester { private static final Logger log = LoggerFactory.getLogger(FormatTester.class); /** * 格式化. */ private static void formatter() { StringBuilder sb = new StringBuilder(); Formatter formatter = new Formatter(sb, Locale.US); // 可從新排序輸出. formatter.format("%n%4$2s %3$2s %2$2s %1$2s %n", "a", "b", "c", "d"); // -> " d c b a" formatter.format(Locale.FRANCE, "e = %+10.4f", Math.E); // -> "e = +2,7183" formatter.format("%nAmount gained or lost since last statement: $ %(,.2f", 6217.58); // -> "Amount gained or lost since last statement: $ 6,217.58" log.info("打印出格式化後的字符串:{}", formatter); formatter.close(); } /** * printf打印. */ private static void printf() { String filename = "testfile"; try (FileReader fileReader = new FileReader(filename)) { BufferedReader reader = new BufferedReader(fileReader); String line; int i = 1; while ((line = reader.readLine()) != null) { System.out.printf("Line %d: %s%n", i++, line); } } catch (Exception e) { System.err.printf("Unable to open file named '%s': %s", filename, e.getMessage()); } } /** * stringFormat使用. */ private static void stringFormat() { // 格式化日期. Calendar c = new GregorianCalendar(1995, Calendar.MAY, 23); String s = String.format("Duke's Birthday: %1$tm %1$te,%1$tY", c); // -> s == "Duke's Birthday: May 23, 1995" log.info(s); } /** * 格式化消息. */ private static void messageFormat() { String msg = "歡迎光臨,當前({0})等待的業務受理的顧客有{1}位,請排號辦理業務!"; MessageFormat mf = new MessageFormat(msg); String fmsg = mf.format(new Object[]{new Date(), 35}); log.info(fmsg); } /** * 格式化日期. */ private static void dateFormat() { String str = "2010-1-10 17:39:21"; SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); try { log.info("格式化後的日期:{}", format.format(format.parse(str))); } catch (Exception e) { log.error("日期格式化出錯!", e); } } public static void main(String[] args) { formatter(); stringFormat(); messageFormat(); dateFormat(); printf(); } }
參考文章:Java5新特性及使用
java.util.Scanner
是 Java5 的新特徵,主要功能是簡化文本掃描,但這個類最實用的地方仍是在獲取控制檯輸入。
(1).Scanner概述
能夠從字符串(Readable
)、輸入流、文件、Channel等來直接構造Scanner對象,有了Scanner了,就能夠逐段(根據正則分隔式)來掃描整個文本,並對掃描後的結果作想要的處理。
Scanner
默認使用空格做爲分割符來分隔文本,但容許你使用 useDelimiter(Pattern pattern)
或 useDelimiter(String pattern)
方法來指定新的分隔符。
主要API以下:
delimiter()
: 返回此 Scanner
當前正在用於匹配分隔符的 Pattern
。hasNext()
: 判斷掃描器中當前掃描位置後是否還存在下一段。hasNextLine()
: 若是在此掃描器的輸入中存在另外一行,則返回true。next()
: 查找並返回來自此掃描器的下一個完整標記。nextLine()
: 此掃描器執行當前行,並返回跳過的輸入信息。(2).掃描控制檯輸入
當經過 new Scanner(System.in)
建立了一個 Scanner
實例時,控制檯會一直等待輸入,直到敲回車鍵結束,把所輸入的內容傳給 Scanner
,做爲掃描對象。若是要獲取輸入的內容,則只須要調用 Scanner
的 nextLine()
方法便可。
/** * 掃描控制檯輸入. * * @author blinkfox 2017-11-28 */ public class ScannerTest { public static void main(String[] args) { Scanner s = new Scanner(System.in); System.out.println("請輸入字符串:"); while (true) { String line = s.nextLine(); if (line.equals("exit")) break; System.out.println(">>>" + line); } } }
(3).其它示例
該示例中會從 myNumbers
文件中讀取長整型 long
的數據。
Scanner sc = new Scanner(new File("myNumbers")); while (sc.hasNextLong()) { long aLong = sc.nextLong(); }
如下示例可使用除空格以外的分隔符來從一個字符串中讀取幾個條目:
String input = "1 fish 2 fish red fish blue fish"; Scanner s = new Scanner(input).useDelimiter("\\s*fish\\s*"); System.out.println(s.nextInt()); System.out.println(s.nextInt()); System.out.println(s.next()); System.out.println(s.next()); s.close();
將輸出:
1 2 red blue
參考文章:Java5新特性及使用
StringBuilder
也是 Java5 中新增的類,主要用來代替 +
號和 StringBuffer
來更加高效的拼接字符串。StringBuffer
與 StringBuilder
都是繼承於 AbstractStringBuilder
,主要的區別就是 StringBuffer
的函數上都有 synchronized
關鍵字,保證線程安全。
關於 StringBuilder
的使用這裏就再也不詳細介紹了,網上文章也有不少。總之,對於動態字符串的拼接推薦使用 StringBuilder
。靜態字符串的拼接直接使用 +
號或者字符串的 concat(String str)
方法,甚至也使用 StringBuilder
亦可。
參考文章:Java5新特性及使用
關於 JDK 1.6 的新特性,瞭解一下就能夠了...若是有興趣深刻研究的童鞋,右轉這裏:Java6新特性及使用
答:
在JDK6中 ,AWT新增長了兩個類:Desktop 和 SystemTray 。
前者能夠用來打開系統默認瀏覽器瀏覽指定的 URL,打開系統默認郵件客戶端給指定的郵箱發郵件,用默認應用程序打開或編輯文件(好比,用記事本打開以txt爲後綴名的文件),用系統默認的打印機打印文檔;
後者能夠用來在系統托盤區建立一個托盤程序.
答:
JAXB是Java Architecture for XML Binding的縮寫,能夠將一個Java對象轉變成爲XML格式,反之亦然。
咱們把對象與關係數據庫之間的映射稱爲ORM, 其實也能夠把對象與XML之間的映射稱爲OXM(Object XML Mapping). 原來JAXB是Java EE的一部分,在JDK6中,SUN將其放到了Java SE中,這也是SUN的一向作法。
JDK6中自帶的這個JAXB版本是2.0, 比起1.0(JSR 31)來,JAXB2(JSR 222)用JDK5的新特性Annotation來標識要做綁定的類和屬性等,這就極大簡化了開發的工做量。
實際上,在Java EE 5.0中,EJB和Web Services也經過Annotation來簡化開發工做。另外,JAXB2在底層是用StAX(JSR 173)來處理XML文檔。除了JAXB以外,咱們還能夠經過XMLBeans和Castor等來實現一樣的功能。
答:
StAX(JSR 173)是JDK6.0中除了DOM和SAX以外的又一種處理XML文檔的API。
StAX 的來歷 :在JAXP1.3(JSR 206)有兩種處理XML文檔的方法:DOM(Document Object Model)和SAX(Simple API for XML).
由 於JDK6.0中的JAXB2(JSR 222)和JAX-WS 2.0(JSR 224)都會用到StAX,因此Sun決定把StAX加入到JAXP家族當中來,並將JAXP的版本升級到1.4(JAXP1.4是JAXP1.3的維護版本). JDK6裏面JAXP的版本就是1.4. 。
StAX是The Streaming API for XML的縮寫,一種利用拉模式解析(pull-parsing)XML文檔的API.StAX經過提供一種基於事件迭代器(Iterator)的API讓 程序員去控制xml文檔解析過程,程序遍歷這個事件迭代器去處理每個解析事件,解析事件能夠看作是程序拉出來的,也就是程序促使解析器產生一個解析事件,而後處理該事件,以後又促使解析器產生下一個解析事件,如此循環直到碰到文檔結束符;
SAX也是基於事件處理xml文檔,但倒是用推模式解析,解析器解析完整個xml文檔後,才產生解析事件,而後推給程序去處理這些事件;DOM 採用的方式是將整個xml文檔映射到一顆內存樹,這樣就能夠很容易地獲得父節點和子結點以及兄弟節點的數據,但若是文檔很大,將會嚴重影響性能。
答:
如今咱們能夠用JDK6 的Compiler API(JSR 199)去動態編譯Java源文件,Compiler API結合反射功能就能夠實現動態的產生Java代碼並編譯執行這些代碼,有點動態語言的特徵。
這個特性對於某些須要用到動態編譯的應用程序至關有用,好比JSP Web Server,當咱們手動修改JSP後,是不但願須要重啓Web Server才能夠看到效果的,這時候咱們就能夠用Compiler API來實現動態編譯JSP文件,固然,如今的JSP Web Server也是支持JSP熱部署的,如今的JSP Web Server經過在運行期間經過Runtime.exec或ProcessBuilder來調用javac來編譯代碼,這種方式須要咱們產生另外一個進程去 作編譯工做,不夠優雅並且容易使代碼依賴與特定的操做系統;Compiler API經過一套易用的標準的API提供了更加豐富的方式去作動態編譯,並且是跨平臺的。
答:
JDK6 提供了一個簡單的Http Server API,據此咱們能夠構建本身的嵌入式Http Server,它支持Http和Https協議,提供了HTTP1.1的部分實現,沒有被實現的那部分能夠經過擴展已有的Http Server API來實現,程序員必須本身實現HttpHandler接口,HttpServer會調用HttpHandler實現類的回調方法來處理客戶端請求,在 這裏,咱們把一個Http請求和它的響應稱爲一個交換,包裝成HttpExchange類,HttpServer負責將HttpExchange傳給 HttpHandler實現類的回調方法.
答:
插入式註解處理API(JSR 269)提供一套標準API來處理Annotations(JSR 175)
實 際上JSR 269不只僅用來處理Annotation,我以爲更強大的功能是它創建了Java 語言自己的一個模型,它把method, package, constructor, type, variable, enum, annotation等Java語言元素映射爲Types和Elements(二者有什麼區別?), 從而將Java語言的語義映射成爲對象, 咱們能夠在javax.lang.model包下面能夠看到這些類. 因此咱們能夠利用JSR 269提供的API來構建一個功能豐富的元編程(metaprogramming)環境.
JSR 269用Annotation Processor在編譯期間而不是運行期間處理Annotation, Annotation Processor至關於編譯器的一個插件,因此稱爲插入式註解處理.若是Annotation Processor處理Annotation時(執行process方法)產生了新的Java代碼,編譯器會再調用一次Annotation Processor,若是第二次處理還有新代碼產生,就會接着調用Annotation Processor,直到沒有新代碼產生爲止.每執行一次process()方法被稱爲一個"round",這樣整個Annotation processing過程能夠看做是一個round的序列.
JSR 269主要被設計成爲針對Tools或者容器的API. 舉個例子,咱們想創建一套基於Annotation的單元測試框架(如TestNG),在測試類裏面用Annotation來標識測試期間須要執行的測試方法。
JDK6 中提供了java.io.Console 類專用來訪問基於字符的控制檯設備. 你的程序若是要與Windows下的cmd或者Linux下的Terminal交互,就能夠用Console類代勞. 但咱們不老是能獲得可用的Console, 一個JVM是否有可用的Console依賴於底層平臺和JVM如何被調用. 若是JVM是在交互式命令行(好比Windows的cmd)中啓動的,而且輸入輸出沒有重定向到另外的地方,那麼就能夠獲得一個可用的Console實例.
如: ruby, groovy, javascript.
Common annotations
本來是Java EE 5.0(JSR 244)規範的一部分,如今SUN把它的一部分放到了Java SE 6.0中.隨着Annotation元數據功能(JSR 175)加入到Java SE 5.0裏面,不少Java 技術(好比EJB,Web Services)都會用Annotation部分代替XML文件來配置運行參數(或者說是支持聲明式編程,如EJB的聲明式事務), 若是這些技術爲通用目的都單獨定義了本身的Annotations,顯然有點重複建設, 因此,爲其餘相關的Java技術定義一套公共的Annotation是有價值的,能夠避免重複建設的同時,也保證Java SE和Java EE 各類技術的一致性。
從 JDK6 開始,JDK 目錄中新增了一個名爲 db
的目錄。這即是 Java 6 的新成員:Java DB。這是一個純 Java 實現、開源的數據庫管理系統(DBMS),源於 Apache 軟件基金會(ASF)名下的項目 Derby
。它只有 2MB 大小,對比動輒上 G 的數據庫來講可謂袖珍。但這並不妨礙 Derby 功能齊備,支持幾乎大部分的數據庫應用所須要的特性。JDK6.0裏面帶的這個Derby的版本是10.2.1.7,支持存儲過程和觸發器;有兩種運行模式,一種是做爲嵌入式數據庫,另外一種是做爲網絡數據庫。前者的數據庫服務器和客戶端都在同一個JVM裏面運行,後者容許數據庫服務器端和客戶端不在同一個JVM裏面,並且容許這二者在不一樣的物理機器上。值得注意的是JDK6裏面的這個Derby支持JDK6的新特性 JDBC 4.0
規範(JSR 221)。
在 Java SE 6 所提供的諸多新特性和改進中,值得一提的是爲 Java 程序提供數據庫訪問機制的 JDBC 版本升級到了 4.0, 這個以 JSR-221 爲代號的版本,提供了更加便利的代碼編寫機制及柔性,而且支持更多的數據類型。JDBC4.0 主要有如下改進和新特性。
java.sql.Driver
,而不須要再調用 class.forName
;java.sql.RowId
數據類型用來能夠訪問 sql rowid
;National Character Set
的支持;BLOB
和 CLOB
的支持功能;SQL/XML
和 XML
支持;Wrapper Pattern
;SQLException
加強;Connection
和 Statement
接口加強;New Scalar Funtions
;JDBC API changes
。以前已經寫過一篇詳細介紹 Java 7 特性的文章了,這裏就直接黏了:Java 7新特性
類型判斷是一我的特殊的煩惱,入下面的代碼:
Map<String,List<String>> anagrams = new HashMap<String,List<String>>();
經過類型推斷後變成:
Map<String,List<String>> anagrams = new HashMap<>();
注:這個<>被叫作diamond(鑽石)運算符,Java 7後這個運算符從引用的聲明中推斷類型。
switch語句可使用原始類型或枚舉類型。Java引入了另外一種類型,咱們能夠在switch語句中使用:字符串類型。
說咱們有一個根據其地位來處理貿易的要求。直到如今,咱們使用if-其餘語句來完成這個任務。
private voidprocessTrade(Trade t){ String status = t.getStatus(); if(status.equalsIgnoreCase(NEW)) { newTrade(t); } else if(status.equalsIgnoreCase(EXECUTE)) { executeTrade(t); } else if(status.equalsIgnoreCase(PENDING)) { pendingTrade(t); } }
這種處理字符串的方法是粗糙的。在Java中,咱們可使用加強的switch語句來改進程序,該語句以String類型做爲參數。
public voidprocessTrade(Trade t) { String status = t.getStatus(); switch(status) { caseNEW: newTrade(t); break; caseEXECUTE: executeTrade(t); break; casePENDING: pendingTrade(t); break; default: break; } }
在上面的程序中,狀態字段老是經過使用 String.equals() 與案例標籤來進行比較。
Java中有一些資源須要手動關閉,例如Connections,Files,Input/OutStreams等。一般咱們使用 try-finally 來關閉資源:
public voidoldTry() { try{ fos= newFileOutputStream("movies.txt"); dos= newDataOutputStream(fos); dos.writeUTF("Java 7 Block Buster"); } catch(IOException e) { e.printStackTrace(); } finally{ try{ fos.close(); dos.close(); } catch(IOException e) { // log the exception } } }
然而,在Java 7中引入了另外一個很酷的特性,能夠自動管理資源。它的操做也很簡單,咱們所要作的就是在 try 塊中申明資源以下:
try(resources_to_be_cleant){ // your code }
以上方法與舊的 try-finally 能最終寫成下面的代碼:
public voidnewTry() { try(FileOutputStream fos = newFileOutputStream("movies.txt"); DataOutputStream dos = newDataOutputStream(fos)) { dos.writeUTF("Java 7 Block Buster"); } catch(IOException e) { // log the exception } }
上面的代碼也表明了這個特性的另外一個方面:處理多個資源。FileOutputStream 和 DataOutputStream 在try語句中一個接一個地含在語句中,每個都用分號(;)分隔符分隔開。咱們沒必要手動取消或關閉流,由於當空間存在try塊時,它們將自動關閉。
在後臺,應該自動關閉的資源必須試驗 java.lang.AutoCloseable 接口。
任何實現 AutoCloseable 接口的資源均可以做爲自動資源管理的候選。AutoCloseable 是 java.io.Closeable 接口的父類,JVM會在程序退出try塊後調用一個方法 close()。
數字文字絕對是對眼睛的一種考驗。我相信,若是你給了一個數字,好比說,十個零,你就會像我同樣數零。若是不計算從右到左的位置,識別一個文字的話,就很容易出錯,並且很麻煩。Not anymore。Java在識別位置時引入了下劃線。例如,您能夠聲明1000,以下所示:
int thousand = 1_000;
或1000000(一百萬)以下:
int million = 1_000_000
請注意,這個版本中也引入了二進制文字-例如「0b1」-所以開發人員沒必要再將它們轉換爲十六進制。
在異常處理區域有幾處改進。Java引入了多個catch功能,以使用單個抓到塊捕獲多個異常類型。
假設您有一個方法,它拋出三個異常。在當前狀態下,您將分別處理它們,以下所示:
public voidoldMultiCatch() { try{ methodThatThrowsThreeExceptions(); } catch(ExceptionOne e) { // log and deal with ExceptionOne } catch(ExceptionTwo e) { // log and deal with ExceptionTwo } catch(ExceptionThree e) { // log and deal with ExceptionThree } }
在一個catch塊中逐個捕獲一個連續的異常,看起來很混亂。我還看到了捕獲十幾個異常的代碼。這是很是低效和容易出錯的。Java爲解決這隻醜小鴨帶來了新的語言變化。請參閱下面的方法oldMultiCatch方法的改進版本:
public voidnewMultiCatch() { try{ methodThatThrowsThreeExceptions(); } catch(ExceptionOne | ExceptionTwo | ExceptionThree e) { // log and deal with all Exceptions } }
多個異常經過使用 「|」 操做符在一個catch塊中捕獲。這樣,您沒必要編寫數十個異常捕獲。可是,若是您有許多屬於不一樣類型的異常,那麼您也可使用「多個catch塊」塊。下面的代碼片斷說明了這一點:
public voidnewMultiMultiCatch() { try{ methodThatThrowsThreeExceptions(); } catch(ExceptionOne e) { // log and deal with ExceptionOne } catch(ExceptionTwo | ExceptionThree e) { // log and deal with ExceptionTwo and ExceptionThree } }
在上面的例子中,在和ExceptionThree屬於不一樣的層次結構,所以您但願以不一樣的方式處理它們,但使用一個抓到塊。
那些使用Java的人可能還記得框架引發的頭痛。在操做系統或多文件系統之間無縫地工做歷來都不是一件容易的事情.。有些方法,例如刪除或重命名,在大多數狀況下都是出乎意料的。使用符號連接是另外一個問題。實質上API須要大修。
爲了解決上述問題,Java引入了一個新的API,並在許多狀況下引入了新的api。
在NIO2.0提出了許多加強功能。在處理多個文件系統時,它還引入了新的類來簡化開發人員的生活。
Working With Path(使用路徑):
新的 java.nio.file 由包和接口組成例如:Path,Paths,FileSystem,FileSystems等等。
路徑只是對文件路徑的簡單引用。它與java.io.File等價(並具備更多的特性)。下面的代碼段顯示瞭如何獲取對「臨時」文件夾的路徑引用:
public voidpathInfo() { Path path= Paths.get("c:\Temp\temp"); System.out.println("Number of Nodes:"+ path.getNameCount()); System.out.println("File Name:"+ path.getFileName()); System.out.println("File Root:"+ path.getRoot()); System.out.println("File Parent:"+ path.getParent()); }
最終控制檯的輸出將是:
Number of Nodes:2 File Name:temp.txt File Root:c: File Parent:c:Temp
刪除文件或目錄就像在文件中調用delete方法(注意複數)同樣簡單。在類公開兩個刪除方法,一個拋出NoSuchFileException,另外一個不拋。
下面的delete方法調用拋出NoSuchFileException,所以您必須處理它:
Files.delete(path);
Where as Files.deleteIfExists(path) does not throw exception (as expected) if the file/directory does not exist.
使用 Files.deteleIfExists(path) 則不會拋出異常。
您可使用其餘實用程序方法,例如Files.copy(.)和Files.move(.)來有效地對文件系統執行操做。相似地,使用 createSymbolicLink(..) 方法使用代碼建立符號連接。
文件更改通知:
JDK 7中最好的改善算是File change notifications(文件更改通知)了。這是一個長期等待的特性,它最終被刻在NIO 2.0中。WatchService API 容許您在對主題(目錄或文件)進行更改時接收通知事件。
具體的建立步驟就不給了,總之它的功能就跟它的名字通常,當文件發生更改的時候,能及時做出反饋。
在一個 Java 程序中有效地使用並行內核一直是一個挑戰。不多有國內開發的框架將工做分配到多個核心,而後加入它們來返回結果集。Java已經將這個特性做爲Fork/Join框架結合了起來。
基本上,在把手頭的任務變成了小任務,直到小任務簡單到能夠不進一步分手的狀況下解決。這就像一個分而治之的算法.。在這個框架中須要注意的一個重要概念是,理想狀況下,沒有工做線程是空閒的。他們實現了一個 work-stealing 算法,在空閒的工人「偷」工做從那些工人誰是忙。
支持Fork-Join機制的核心類是 ForkJoinPool和ForkJoinTask。
什麼是Fork/Join框架:
Java7提供的一個用於並行執行任務的框架,是一個把大任務分割成若干個小任務,最終彙總每一個小任務結果後獲得大任務結果的框架。
Fork/Join的運行流程圖以下:
工做竊取算法:
工做竊取(work-stealing)算法是指某個線程從其餘隊列裏竊取任務來執行。工做竊取的運行流程圖以下:
工做竊取算法的優勢是充分利用線程進行並行計算,並減小了線程間的競爭,其缺點是在某些狀況下仍是存在競爭,好比雙端隊列裏只有一個任務時。而且消耗了更多的系統資源,好比建立多個線程和多個雙端隊列。
Fork/Join框架使用示例:
讓咱們經過一個簡單的需求來使用下 Fork/Join
框架,需求是:計算1 + 2 + 3 + 4
的結果。
使用 Fork/Join
框架首先要考慮到的是如何分割任務,若是咱們但願每一個子任務最多執行兩個數的相加,那麼咱們設置分割的閾值是2
,因爲是4
個數字相加,因此 Fork/Join
框架會把這個任務 fork
成兩個子任務,子任務一負責計算1 + 2
,子任務二負責計算3 + 4
,而後再 join
兩個子任務的結果。
由於是有結果的任務,因此必須繼承 RecursiveTask
,實現代碼以下:
import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Future; import java.util.concurrent.RecursiveTask; /** * CountTask. * * @author blinkfox on 2018-01-03. */ public class CountTask extends RecursiveTask<Integer> { /** 閾值. */ public static final int THRESHOLD = 2; /** 計算的開始值. */ private int start; /** 計算的結束值. */ private int end; /** * 構造方法. * * @param start 計算的開始值 * @param end 計算的結束值 */ public CountTask(int start, int end) { this.start = start; this.end = end; } /** * 執行計算的方法. * * @return int型結果 */ @Override protected Integer compute() { int sum = 0; // 若是任務足夠小就計算任務. if ((end - start) <= THRESHOLD) { for (int i = start; i <= end; i++) { sum += i; } } else { // 若是任務大於閾值,就分裂成兩個子任務來計算. int middle = (start + end) / 2; CountTask leftTask = new CountTask(start, middle); CountTask rightTask = new CountTask(middle + 1, end); // 等待子任務執行完,並獲得結果,再合併執行結果. leftTask.fork(); rightTask.fork(); sum = leftTask.join() + rightTask.join(); } return sum; } /** * main方法. * * @param args 數組參數 */ public static void main(String[] args) throws ExecutionException, InterruptedException { ForkJoinPool fkPool = new ForkJoinPool(); CountTask task = new CountTask(1, 4); Future<Integer> result = fkPool.submit(task); System.out.println("result:" + result.get()); } }
參考文章:Java7新特性及使用
這裏是Java 7的新特性一覽表:http://www.oschina.net/news/20119/new-features-of-java-7
關於 Java 8 中新知識點,面試官會讓你說說 Java 8 你瞭解多少,下面分享一下我收集的 Java 8 新增的知識點的內容,前排申明引用自:Java8新特性及使用
Java 8用默認方法與靜態方法這兩個新概念來擴展接口的聲明。與傳統的接口又有些不同,它容許在已有的接口中添加新方法,而同時又保持了與舊版本代碼的兼容性。
1.接口默認方法
默認方法與抽象方法不一樣之處在於抽象方法必需要求實現,可是默認方法則沒有這個要求。相反,每一個接口都必須提供一個所謂的默認實現,這樣全部的接口實現者將會默認繼承它(若是有必要的話,能夠覆蓋這個默認實現)。讓咱們看看下面的例子:
private interface Defaulable { // Interfaces now allow default methods, the implementer may or // may not implement (override) them. default String notRequired() { return "Default implementation"; } } private static class DefaultableImpl implements Defaulable { } private static class OverridableImpl implements Defaulable { @Override public String notRequired() { return "Overridden implementation"; } }
Defaulable
接口用關鍵字 default
聲明瞭一個默認方法 notRequired()
,Defaulable
接口的實現者之一 DefaultableImpl
實現了這個接口,而且讓默認方法保持原樣。Defaulable
接口的另外一個實現者 OverridableImpl
用本身的方法覆蓋了默認方法。
1.1 多重繼承的衝突說明:
因爲同一個方法能夠從不一樣的接口引入,天然而然的會有衝突的現象,規則以下:
public interface A { default void hello() { System.out.println("Hello A"); } }
public interface B extends A { default void hello() { System.out.println("Hello B"); } }
public class C implements A, B { public static void main(String[] args) { new C().hello(); // 輸出 Hello B } }
1.2 優缺點:
1.3 接口默認方法不能重載Object類的任何方法:
接口不能提供對Object類的任何方法的默認實現。簡單地講,每個java類都是Object的子類,也都繼承了它類中的 equals()/hashCode()/toString()
方法,那麼在類的接口上包含這些默認方法是沒有意義的,它們也歷來不會被編譯。
在 JVM 中,默認方法的實現是很是高效的,而且經過字節碼指令爲方法調用提供了支持。默認方法容許繼續使用現有的Java接口,而同時可以保障正常的編譯過程。這方面好的例子是大量的方法被添加到 java.util.Collection
接口中去:stream()
,parallelStream()
,forEach()
,removeIf()
等。儘管默認方法很是強大,可是在使用默認方法時咱們須要當心注意一個地方:在聲明一個默認方法前,請仔細思考是否是真的有必要使用默認方法。
2.接口靜態方法
Java 8 帶來的另外一個有趣的特性是接口能夠聲明(而且能夠提供實現)靜態方法。在接口中定義靜態方法,使用 static
關鍵字,例如:
public interface StaticInterface { static void method() { System.out.println("這是Java8接口中的靜態方法!"); } }
下面的一小段代碼是上面靜態方法的使用。
public class Main { public static void main(String[] args) { StaticInterface.method(); // 輸出 這是Java8接口中的靜態方法! } }
Java 支持一個實現類能夠實現多個接口,若是多個接口中存在一樣的 static
方法會怎麼樣呢?若是有兩個接口中的靜態方法如出一轍,而且一個實現類同時實現了這兩個接口,此時並不會產生錯誤,由於Java8中只能經過接口類調用接口中的靜態方法,因此對編譯器來講是能夠區分的。
Lambda
表達式(也稱爲閉包)是整個Java 8發行版中最受期待的在Java語言層面上的改變,Lambda容許把函數做爲一個方法的參數(即:行爲參數化,函數做爲參數傳遞進方法中)。
一個 Lambda
能夠由用逗號分隔的參數列表、–>
符號與函數體三部分表示。
首先看看在老版本的Java中是如何排列字符串的:
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia"); Collections.sort(names, new Comparator<String>() { @Override public int compare(String a, String b) { return b.compareTo(a); } });
只須要給靜態方法 Collections.sort
傳入一個List對象以及一個比較器來按指定順序排列。一般作法都是建立一個匿名的比較器對象而後將其傳遞給sort方法。 在Java 8 中你就不必使用這種傳統的匿名對象的方式了,Java 8提供了更簡潔的語法,lambda表達式:
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a); });
看到了吧,代碼變得更短且更具備可讀性,可是實際上還能夠寫得更短:
Collections.sort(names, (String a, String b) -> b.compareTo(a));
對於函數體只有一行代碼的,你能夠去掉大括號{}以及return關鍵字,可是你還能夠寫得更短點:
Collections.sort(names, (a, b) -> b.compareTo(a));
Java編譯器能夠自動推導出參數類型,因此你能夠不用再寫一次類型。
更多 Lambda 表達式的示例在這裏:Java8 lambda表達式10個示例
Lambda
表達式是如何在 Java 的類型系統中表示的呢?每個Lambda表達式都對應一個類型,一般是接口類型。而函數式接口是指僅僅只包含一個抽象方法的接口,每個該類型的Lambda表達式都會被匹配到這個抽象方法。由於默認方法不算抽象方法,因此你也能夠給你的函數式接口添加默認方法。
咱們能夠將Lambda表達式看成任意只包含一個抽象方法的接口類型,確保你的接口必定達到這個要求,你只須要給你的接口添加 @FunctionalInterface
註解,編譯器若是發現你標註了這個註解的接口有多於一個抽象方法的時候會報錯的。
示例以下:
@FunctionalInterface interface Converter<F, T> { T convert(F from); } Converter<String, Integer> converter = (from) -> Integer.valueOf(from); Integer converted = converter.convert("123"); System.out.println(converted); // 123
注意: 若是
@FunctionalInterface
若是沒有指定,上面的代碼也是對的。
更多參考: Java 8——Lambda表達式、Java8新特性及使用
1.概述:
在學習了Lambda表達式以後,咱們一般使用Lambda表達式來建立匿名方法。然而,有時候咱們僅僅是調用了一個已存在的方法。以下:
Arrays.sort(strArray, (s1, s2) -> s1.compareToIgnoreCase(s2));
在Java8中,咱們能夠直接經過方法引用來簡寫Lambda表達式中已經存在的方法。
Arrays.sort(strArray, String::compareToIgnoreCase);
這種特性就叫作方法引用(Method Reference
)。
方法引用是用來直接訪問類或者實例的已經存在的方法或者構造方法。方法引用提供了一種引用而不執行方法的方式,它須要由兼容的函數式接口構成的目標類型上下文。計算時,方法引用會建立函數式接口的一個實例。當Lambda表達式中只是執行一個方法調用時,不用Lambda表達式,直接經過方法引用的形式可讀性更高一些。方法引用是一種更簡潔易懂的Lambda表達式。
注意: 方法引用是一個Lambda表達式,其中方法引用的操做符是雙冒號::。
2.分類:
方法引用的標準形式是:類名::方法名
。(注意:只須要寫方法名,不須要寫括號)
有如下四種形式的方法引用:
3.示例:
使用示例以下:
public class Person { String name; LocalDate birthday; public Person(String name, LocalDate birthday) { this.name = name; this.birthday = birthday; } public LocalDate getBirthday() { return birthday; } public static int compareByAge(Person a, Person b) { return a.birthday.compareTo(b.birthday); } @Override public String toString() { return this.name; } }
public class MethodReferenceTest { @Test public static void main() { Person[] pArr = new Person[] { new Person("003", LocalDate.of(2016,9,1)), new Person("001", LocalDate.of(2016,2,1)), new Person("002", LocalDate.of(2016,3,1)), new Person("004", LocalDate.of(2016,12,1)) }; // 使用匿名類 Arrays.sort(pArr, new Comparator<Person>() { @Override public int compare(Person a, Person b) { return a.getBirthday().compareTo(b.getBirthday()); } }); //使用lambda表達式 Arrays.sort(pArr, (Person a, Person b) -> { return a.getBirthday().compareTo(b.getBirthday()); }); //使用方法引用,引用的是類的靜態方法 Arrays.sort(pArr, Person::compareByAge); } }
Java8添加的
Stream API(java.util.stream)
把真正的函數式編程風格引入到Java中。這是目前爲止對Java類庫最好的補充,由於Stream API
能夠極大提供Java程序員的生產力,讓程序員寫出高效率、乾淨、簡潔的代碼。使用 Steam 寫出來的代碼真的能讓人興奮,這裏鏈出以前的一篇文章:Java 8——函數式數據處理(流)
流能夠是無限的、有狀態的,能夠是順序的,也能夠是並行的。在使用流的時候,你首先須要從一些來源中獲取一個流,執行一個或者多箇中間操做,而後執行一個最終操做。中間操做包括filter
、map
、flatMap
、peel
、distinct
、sorted
、limit
和 substream
。終止操做包括 forEach
、toArray
、reduce
、collect
、min
、max
、count
、anyMatch
、allMatch
、noneMatch
、findFirst
和 findAny
。 java.util.stream.Collectors
是一個很是有用的實用類。該類實現了不少歸約操做,例如將流轉換成集合和聚合元素。
1.一些重要方法說明:
stream
: 返回數據流,集合做爲其源parallelStream
: 返回並行數據流, 集合做爲其源filter
: 方法用於過濾出知足條件的元素map
: 方法用於映射每一個元素對應的結果forEach
: 方法遍歷該流中的每一個元素limit
: 方法用於減小流的大小sorted
: 方法用來對流中的元素進行排序anyMatch
: 是否存在任意一個元素知足條件(返回布爾值)allMatch
: 是否全部元素都知足條件(返回布爾值)noneMatch
: 是否全部元素都不知足條件(返回布爾值)collect
: 方法是終端操做,這是一般出如今管道傳輸操做結束標記流的結束2.一些使用示例:
2.1 Filter 過濾:
stringCollection .stream() .filter((s) -> s.startsWith("a")) .forEach(System.out::println);
2.2 Sort 排序:
stringCollection .stream() .sorted() .filter((s) -> s.startsWith("a")) .forEach(System.out::println);
2.3 Map 映射:
stringCollection .stream() .map(String::toUpperCase) .sorted((a, b) -> b.compareTo(a)) .forEach(System.out::println);
2.4 Match 匹配:
boolean anyStartsWithA = stringCollection .stream() .anyMatch((s) -> s.startsWith("a")); System.out.println(anyStartsWithA); // true boolean allStartsWithA = stringCollection .stream() .allMatch((s) -> s.startsWith("a")); System.out.println(allStartsWithA); // false boolean noneStartsWithZ = stringCollection .stream() .noneMatch((s) -> s.startsWith("z")); System.out.println(noneStartsWithZ); // true
2.5 Count 計數:
long startsWithB = stringCollection .stream() .filter((s) -> s.startsWith("b")) .count(); System.out.println(startsWithB); // 3
2.6 Reduce 規約:
Optional<String> reduced = stringCollection
.stream()
.sorted()
.reduce((s1, s2) -> s1 + "#" + s2); reduced.ifPresent(System.out::println);
到目前爲止,臭名昭著的空指針異常是致使Java應用程序失敗的最多見緣由。之前,爲了解決空指針異常,Google公司著名的Guava
項目引入了Optional
類,Guava經過使用檢查空值的方式來防止代碼污染,它鼓勵程序員寫更乾淨的代碼。受到Google Guava的啓發,Optional
類已經成爲Java 8類庫的一部分。
Optional
其實是個容器:它能夠保存類型T的值,或者僅僅保存null。Optional
提供不少有用的方法,這樣咱們就不用顯式進行空值檢測。
咱們下面用兩個小例子來演示如何使用Optional類:一個容許爲空值,一個不容許爲空值。
Optional<String> fullName = Optional.ofNullable(null); System.out.println("Full Name is set? " + fullName.isPresent()); System.out.println("Full Name: " + fullName.orElseGet(() -> "[none]")); System.out.println(fullName.map(s -> "Hey " + s + "!").orElse("Hey Stranger!"));
若是Optional
類的實例爲非空值的話,isPresent()
返回true
,否從返回false
。爲了防止Optional爲空值,orElseGet()
方法經過回調函數來產生一個默認值。map()
函數對當前Optional
的值進行轉化,而後返回一個新的Optional
實例。orElse()
方法和orElseGet()
方法相似,可是orElse
接受一個默認值而不是一個回調函數。下面是這個程序的輸出:
Full Name is set? false Full Name: [none] Hey Stranger!
讓咱們來看看另外一個例子:
Optional<String> firstName = Optional.of("Tom"); System.out.println("First Name is set? " + firstName.isPresent()); System.out.println("First Name: " + firstName.orElseGet(() -> "[none]")); System.out.println(firstName.map(s -> "Hey " + s + "!").orElse("Hey Stranger!")); System.out.println();
下面是程序的輸出:
First Name is set? true First Name: Tom Hey Tom!
Java 8 在包java.time下包含了一組全新的時間日期API。新的日期API和開源的Joda-Time庫差很少,但又不徹底同樣,下面的例子展現了這組新API裏最重要的一些部分:
1.Clock 時鐘:
Clock
類提供了訪問當前日期和時間的方法,Clock是時區敏感的,能夠用來取代System.currentTimeMillis()
來獲取當前的微秒數。某一個特定的時間點也可使用Instant
類來表示,Instant
類也能夠用來建立老的java.util.Date
對象。代碼以下:
Clock clock = Clock.systemDefaultZone();
long millis = clock.millis(); Instant instant = clock.instant(); Date legacyDate = Date.from(instant); // legacy java.util.Date
2.Timezones 時區:
在新API中時區使用ZoneId
來表示。時區能夠很方便的使用靜態方法of
來獲取到。時區定義了到UTS時間的時間差,在Instant
時間點對象到本地日期對象之間轉換的時候是極其重要的。代碼以下:
System.out.println(ZoneId.getAvailableZoneIds());
// prints all available timezone ids ZoneId zone1 = ZoneId.of("Europe/Berlin"); ZoneId zone2 = ZoneId.of("Brazil/East"); System.out.println(zone1.getRules()); System.out.println(zone2.getRules()); // ZoneRules[currentStandardOffset=+01:00] // ZoneRules[currentStandardOffset=-03:00]
3.LocalTime 本地時間:
LocalTime
定義了一個沒有時區信息的時間,例如 晚上10點,或者 17:30:15。下面的例子使用前面代碼建立的時區建立了兩個本地時間。以後比較時間並以小時和分鐘爲單位計算兩個時間的時間差。代碼以下:
LocalTime now1 = LocalTime.now(zone1);
LocalTime now2 = LocalTime.now(zone2);
System.out.println(now1.isBefore(now2)); // false long hoursBetween = ChronoUnit.HOURS.between(now1, now2); long minutesBetween = ChronoUnit.MINUTES.between(now1, now2); System.out.println(hoursBetween); // -3 System.out.println(minutesBetween); // -239
LocalTime
提供了多種工廠方法來簡化對象的建立,包括解析時間字符串。代碼以下:
LocalTime late = LocalTime.of(23, 59, 59); System.out.println(late); // 23:59:59 DateTimeFormatter germanFormatter = DateTimeFormatter .ofLocalizedTime(FormatStyle.SHORT) .withLocale(Locale.GERMAN); LocalTime leetTime = LocalTime.parse("13:37", germanFormatter); System.out.println(leetTime); // 13:37
4.LocalDate 本地日期:
LocalDate表示了一個確切的日期,好比2014-03-11。該對象值是不可變的,用起來和LocalTime基本一致。下面的例子展現瞭如何給Date對象加減天/月/年。另外要注意的是這些對象是不可變的,操做返回的老是一個新實例。代碼以下:
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS); LocalDate yesterday = tomorrow.minusDays(2); LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4); DayOfWeek dayOfWeek = independenceDay.getDayOfWeek(); System.out.println(dayOfWeek); // FRIDAY
從字符串解析一個LocalDate類型和解析LocalTime同樣簡單。代碼以下:
DateTimeFormatter germanFormatter = DateTimeFormatter
.ofLocalizedDate(FormatStyle.MEDIUM)
.withLocale(Locale.GERMAN);
LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter); System.out.println(xmas); // 2014-12-24
5.LocalDateTime 本地日期時間:
LocalDateTime
同時表示了時間和日期,至關於前兩節內容合併到一個對象上了。LocalDateTime
和LocalTime
還有LocalDate
同樣,都是不可變的。LocalDateTime
提供了一些能訪問具體字段的方法。代碼以下:
LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59); DayOfWeek dayOfWeek = sylvester.getDayOfWeek(); System.out.println(dayOfWeek); // WEDNESDAY Month month = sylvester.getMonth(); System.out.println(month); // DECEMBER long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY); System.out.println(minuteOfDay); // 1439
只要附加上時區信息,就能夠將其轉換爲一個時間點Instant
對象,Instant
時間點對象能夠很容易的轉換爲老式的java.util.Date
。代碼以下:
Instant instant = sylvester
.atZone(ZoneId.systemDefault())
.toInstant();
Date legacyDate = Date.from(instant);
System.out.println(legacyDate); // Wed Dec 31 23:59:59 CET 2014
格式化LocalDateTime
和格式化時間和日期同樣的,除了使用預約義好的格式外,咱們也能夠本身定義格式。代碼以下:
DateTimeFormatter formatter =
DateTimeFormatter
.ofPattern("MMM dd, yyyy - HH:mm"); LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter); String string = formatter.format(parsed); System.out.println(string); // Nov 03, 2014 - 07:13
和java.text.NumberFormat
不同的是新版的DateTimeFormatter
是不可變的,因此它是線程安全的。
關於Java8中日期API更多的使用示例能夠參考Java 8中關於日期和時間API的20個使用示例。
自從Java 5引入了註解機制,這一特性就變得很是流行而且廣爲使用。然而,使用註解的一個限制是相同的註解在同一位置只能聲明一次,不能聲明屢次。Java 8打破了這條規則,引入了重複註解機制,這樣相同的註解能夠在同一地方聲明屢次。
重複註解機制自己必須用@Repeatable
註解。事實上,這並非語言層面上的改變,更多的是編譯器的技巧,底層的原理保持不變。讓咱們看一個快速入門的例子:
import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; public class RepeatingAnnotations { @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Filters { Filter[] value(); } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Repeatable(Filters.class) public @interface Filter { String value(); }; @Filter("filter1") @Filter("filter2") public interface Filterable { } public static void main(String[] args) { for(Filter filter: Filterable.class.getAnnotationsByType(Filter.class)) { System.out.println(filter.value()); } } }
正如咱們看到的,這裏有個使用@Repeatable(Filters.class)
註解的註解類Filter
,Filters
僅僅是Filter
註解的數組,但Java編譯器並不想讓程序員意識到Filters
的存在。這樣,接口Filterable
就擁有了兩次Filter
(並無提到Filter
)註解。
同時,反射相關的API提供了新的函數getAnnotationsByType()
來返回重複註解的類型(請注意Filterable.class.getAnnotation(Filters.class)
經編譯器處理後將會返回Filters的實例)。
Java 8擴展了註解的上下文。如今幾乎能夠爲任何東西添加註解:局部變量、泛型類、父類與接口的實現,就連方法的異常也能添加註解。下面演示幾個例子:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Collection; public class Annotations { @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) public @interface NonEmpty { } public static class Holder<@NonEmpty T> extends @NonEmpty Object { public void method() throws @NonEmpty Exception { } } @SuppressWarnings("unused") public static void main(String[] args) { final Holder<String> holder = new @NonEmpty Holder<String>(); @NonEmpty Collection<@NonEmpty String> strings = new ArrayList<>(); } }
在Java 8中,Base64編碼已經成爲Java類庫的標準。它的使用十分簡單,下面讓咱們看一個例子:
import java.nio.charset.StandardCharsets; import java.util.Base64; public class Base64s { public static void main(String[] args) { final String text = "Base64 finally in Java 8!"; final String encoded = Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8)); System.out.println(encoded); final String decoded = new String(Base64.getDecoder().decode(encoded), StandardCharsets.UTF_8); System.out.println(decoded); } }
程序在控制檯上輸出了編碼後的字符與解碼後的字符:
QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!
Base64類同時還提供了對URL、MIME友好的編碼器與解碼器(Base64.getUrlEncoder() / Base64.getUrlDecoder()
, Base64.getMimeEncoder() / Base64.getMimeDecoder()
)。
JavaFX
是一個強大的圖形和多媒體處理工具包集合,它容許開發者來設計、建立、測試、調試和部署富客戶端程序,而且和Java同樣跨平臺。從Java8開始,JavaFx已經內置到了JDK中。關於JavaFx更詳細的文檔可參考JavaFX中文文檔。
Java8中,HashMap內部實現又引入了紅黑樹(數組+鏈表+紅黑樹),使得HashMap的整體性能相較於Java7有比較明顯的提高。
區別:
引用自文章:Java 9 中的 9 個新特性、Java 9 新特性概述——IBM、【譯】使用示例帶你提早了解 Java 9 中的新特性
Oracle 公司(Java Library 開發者)新引進一個表明 Java Shell 的稱之爲 「jshell」 或者 REPL(Read Evaluate Print Loop)的新工具。該工具能夠被用來執行和測試任何 Java 中的結構,如 class,interface,enum,object,statements 等。使用很是簡單。
JDK 9 EA(Early Access)下載地址:https://jdk9.java.net/download/
G:\>jshell
| Welcome to JShell -- Version 9-ea
| For an introduction type: /help intro jshell> int a = 10 a ==> 10 jshell> System.out.println("a value = " + a ) a value = 10
一般,您但願在代碼中建立一個集合(例如,List 或 Set ),並直接用一些元素填充它。 實例化集合,幾個 「add」 調用,使得代碼重複。 Java 9,添加了幾種集合工廠方法:
Set<Integer> ints = Set.of(1, 2, 3); List<String> strings = List.of("first", "second");
除了更短和更好閱讀以外,這些方法也能夠避免您選擇特定的集合實現。 事實上,從工廠方法返回已放入數個元素的集合實現是高度優化的。這是可能的,由於它們是不可變的:在建立後,繼續添加元素到這些集合會致使 「UnsupportedOperationException」 。
在 Java 8 中,咱們能夠在接口中使用默認或者靜態方法提供一些實現方式,可是不能建立私有方法。
爲了不冗餘代碼和提升重用性,Oracle 公司準備在 Java SE 9 接口中引入私有方法。也就是說從 Java SE 9 開始,咱們也可以在接口類中使用 ‘private’ 關鍵字寫私有化方法和私有化靜態方法。
接口中的私有方法與 class 類中的私有方法在寫法上並沒有差別,如:
public interface Card{ private Long createCardID(){ // Method implementation goes here. } private static void displayCardDetails(){ // Method implementation goes here. } }
這裏只給出解決的問題,僅限瞭解....
Java 9 的定義功能是一套全新的模塊系統。當代碼庫愈來愈大,建立複雜,盤根錯節的「意大利麪條式代碼」的概率呈指數級的增加。這時候就得面對兩個基礎的問題: 很難真正地對代碼進行封裝, 而系統並無對不一樣部分(也就是 JAR 文件)之間的依賴關係有個明確的概念。每個公共類均可以被類路徑之下任何其它的公共類所訪問到, 這樣就會致使無心中使用了並不想被公開訪問的 API。此外,類路徑自己也存在問題: 你怎麼知曉全部須要的 JAR 都已經有了, 或者是否是會有重複的項呢? 模塊系統把這倆個問題都給解決了。
Java SE 9 迎來一些 Process API 的改進,經過添加一些新的類和方法來優化系統級進程的管控。
Process API 中的兩個新接口:
Process API 示例
ProcessHandle currentProcess = ProcessHandle.current();
System.out.println("Current Process Id: = " + currentProcess.getPid());
咱們知道,Java SE 7 引入了一個新的異常處理結構:Try-With-Resources
,來自動管理資源。這個新的聲明結構主要目的是實現「Automatic Better Resource Management」(「自動資源管理」)。
Java SE 9 將對這個聲明做出一些改進來避免一些冗長寫法,同時提升可讀性。
Java SE 7 示例
void testARM_Before_Java9() throws IOException { BufferedReader reader1 = new BufferedReader(new FileReader("journaldev.txt")); try (BufferedReader reader2 = reader1) { System.out.println(reader2.readLine()); } }
Java SE 9 示例
void testARM_Java9() throws IOException { BufferedReader reader1 = new BufferedReader(new FileReader("journaldev.txt")); try (reader1) { System.out.println(reader1.readLine()); } }
在 Java SE 9 中,Oracle 公司將改進 CompletableFuture API 來解決一些 Java SE 8 中出現的問題。這些被添加的 API 將用來支持一些延時和超時操做,實用方法和更好的子類化。
Executor exe = CompletableFuture.delayedExecutor(50L, TimeUnit.SECONDS);
這裏的 delayedExecutor() 是靜態實用方法,用來返回一個在指定延時時間提交任務到默認執行器的新 Executor 對象。
反應式編程的思想最近獲得了普遍的流行。 在 Java 平臺上有流行的反應式 庫 RxJava 和 R eactor。反應式流規範的出發點是提供一個帶非阻塞負壓( non-blocking backpressure ) 的異步流處理規範。反應式流規範的核心接口已經添加到了 Java9 中的 java.util.concurrent.Flow 類中。
Flow 中包含了 Flow.Publisher、Flow.Subscriber、Flow.Subscription 和 F low.Processor 等 4 個核心接口。Java 9 還提供了 SubmissionPublisher 做爲 Flow.Publisher 的一個實現。RxJava 2 和 Reactor 均可以很方便的 與 Flow 類的核心接口進行互操做。
長期以來,Stream API 都是 Java 標準庫最好的改進之一。經過這套 API 能夠在集合上創建用於轉換的申明管道。在 Java 9 中它會變得更好。Stream 接口中添加了 4 個新的方法:dropWhile, takeWhile, ofNullable。還有個 iterate 方法的新重載方法,可讓你提供一個 Predicate (判斷條件)來指定何時結束迭代:
IntStream.iterate(1, i -> i < 100, i -> i + 1).forEach(System.out::println);
第二個參數是一個 Lambda,它會在當前 IntStream 中的元素到達 100 的時候返回 true。所以這個簡單的示例是向控制檯打印 1 到 99。
除了對 Stream 自己的擴展,Optional 和 Stream 之間的結合也獲得了改進。如今能夠經過 Optional 的新方法 stram
將一個 Optional 對象轉換爲一個(多是空的) Stream 對象:
Stream<Integer> s = Optional.of(1).stream();
在組合複雜的 Stream 管道時,將 Optional 轉換爲 Stream 很是有用。
Java 9 中有新的方式來處理 HTTP 調用。這個遲到的特性用於代替老舊的 HttpURLConnection
API,並提供對 WebSocket 和 HTTP/2 的支持。注意:新的 HttpClient API 在 Java 9 中以所謂的孵化器模塊交付。也就是說,這套 API 不能保證 100% 完成。不過你能夠在 Java 9 中開始使用這套 API:
HttpClient client = HttpClient.newHttpClient();
HttpRequest req =
HttpRequest.newBuilder(URI.create("http://www.google.com")) .header("User-Agent","Java") .GET() .build(); HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandler.asString());
HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandler.asString());
除了這個簡單的請求/響應模型以外,HttpClient 還提供了新的 API 來處理 HTTP/2 的特性,好比流和服務端推送。
在 Java SE 9 中,Oracle 公司添加了一些新的實用方法到 java.util.Optional
類裏面。這裏我將使用一些簡單的示例來描述其中的一個:stream 方法。
若是一個值出如今給定 Optional 對象中,stream() 方法能夠返回包含該值的一個順序 Stream 對象。不然,將返回一個空 Stream。
stream()
方法已經被添加,並用來在 Optional 對象中使用,如:
Stream<Optional> emp = getEmployee(id) Stream empStream = emp.flatMap(Optional::stream)
這裏的 Optional.stream()
方法被用來轉化 Employee 可選流對象 到 Employee 流中,如此咱們即可以在後續代碼中使用這個結果。
咱們最後要來着重介紹的這個特性對於庫的維護者而言是個特別好的消息。當一個新版本的 Java 出現的時候,你的庫用戶要花費數年時間纔會切換到這個新的版本。這就意味着庫得去向後兼容你想要支持的最老的 Java 版本 (許多狀況下就是 Java 6 或者 7)。這實際上意味着將來的很長一段時間,你都不能在庫中運用 Java 9 所提供的新特性。幸運的是,多版本兼容 JAR 功能能讓你建立僅在特定版本的 Java 環境中運行庫程序時選擇使用的 class 版本:
multirelease.jar ├── META-INF │ └── versions │ └── 9 │ └── multirelease │ └── Helper.class ├── multirelease ├── Helper.class └── Main.class
在上述場景中, multirelease.jar 能夠在 Java 9 中使用, 不過 Helper 這個類使用的不是頂層的 multirelease.Helper 這個 class, 而是處在「META-INF/versions/9」下面的這個。這是特別爲 Java 9 準備的 class 版本,能夠運用 Java 9 所提供的特性和庫。同時,在早期的 Java 諸版本中使用這個 JAR 也是能運行的,由於較老版本的 Java 只會看到頂層的這個 Helper 類。
歡迎轉載,轉載請註明出處!轉載自@我沒有三顆心臟