答:增強 for 循環:foreach 語句,foreach 簡化了迭代器。
格式:// 增強for循環括號裡寫兩個參數,第一個是聲明一個變量,第二個就是需要迭代的容器
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;
System.out.println(entry.getKey() + "=" + entry.getValue());
}
總之,for-each 循環在簡潔性和預防 Bug 方面有著傳統 for 循環無法比擬的優勢,並且沒有性能損失。應該儘可能地使用 for-each 循環。遺憾的是,有三種常見的情況是無法使用 for-each 循環的:
過濾——如果需要遍歷集合,並刪除選定的元素,就需要使用顯式地迭代器,以便可以調用它的 remove 方法。
轉換——如果需要遍歷列表或者數組,並取代它部分或者全部的元素值(增刪、或對元素進行賦值),就需要列表迭代器或者數組索引,以便設定元素的值
平行迭代——如果需要並行地遍歷多個集合,就需要顯式地控制迭代器或者所因變量以便所有迭代器或者索引變量都可以得到同步前移
2)可變參數:解析:什麼意思呢?舉個例子:在 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 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的類來模擬枚舉功能。
1、私有的構造方法。
2、每個元素分別用一個公有的靜態成員變量表示。
可以有若干公有方法或抽象方法。採用抽象方法定義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());
}
}
使用枚舉類實現
public class EnumTest {
public static void main(String[] args) {
WeekDay day = WeekDay.FRI;
System.out.println(day);
System.out.println(day.name());
System.out.println(day.ordinal());
System.out.println(WeekDay. valueOf("SUN"));
System.out.println(WeekDay. values().length);
}
public enum WeekDay{
SUN,MON ,TUE,WED, THI,FRI ,SAT;
}
}
總結: 枚舉是一種特殊的類,其中的每個元素都是該類的一個實例對象,例如可以調用WeekDay.SUN.getClass().getName 和 WeekDay.class.getName()。
注意: 最後一個枚舉元素後面可以加分號,也可以不加分號。
實現帶有構造方法的枚舉
枚舉就相當於一個類,其中也可以定義構造方法、成員變量、普通方法和抽象方法。
枚舉元素必須位於枚舉體中的最開始部分,枚舉元素列表的最後要有分號與其他成員分隔。把枚舉中的成員方法或變量等放在枚舉元素的前面,編譯器會報告錯誤。
帶構造方法的枚舉:
構造方法必須定義成私有的
如果有多個構造方法,將根據枚舉元素創建時所帶的參數決定選擇哪個構造方法創建對象。
枚舉元素 MON 和 MON() 的效果一樣,都是調用默認的構造方法。
示例:
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" );
}
}
}
實現帶有抽象方法的枚舉
定義枚舉TrafficLamp,實現抽象的nextTrafficLamp方法:每個元素分別是由枚舉類的子類來生成的實例對象,這些子類採用類似內部類的方式進行定義。增加上表示時間的構造方法。
public class EnumTest {
public static void main(String[] args) {
TrafficLamp lamp = TrafficLamp.RED;
System.out.println(lamp.nextLamp());
}
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();
}
}
注意:
1、枚舉只有一個成員時,就可以作為一種單例的實現方式。
2、查看生成的class文件,可以看到內部類對應的class文件。
4)自動拆裝箱答:在 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);
Integer num3 = 129;
Integer num4 = 129;
System.out.println(num3 == num4);
Integer num5 = Integer.valueOf(12);
Integer num6 = Integer.valueOf(12);
System.out.println(num5 == num6);
示例:
public class AutoBox {
public static void main(String[] args) {
Integer iObj = 3;
System. out.println(iObj + 12);
Integer i1 = 13;
Integer i2 = 13;
System. out.println(i1 == i2);
i1 = 137;
i2 = 137;
System. out.println(i1 == i2);
}
}
注意:
如果有很多很小的對象,並且他們有相同的東西,那就可以把他們作為一個對象。
如果還有很多不同的東西,那就可以作為外部的東西,作為參數傳入。
這就是享元設計模式(flyweight)。
例如示例中的Integer對象,在-128~127範圍內的Integer對象,用的頻率比較高,就會作為同一個對象,因此結果為true。超出這個範圍的就不是同一個對象,因此結果為false。
5)泛型 Generics答:引用泛型之後,允許指定集合裡元素的類型,免去了強制類型轉換,並且能在編譯時刻進行類型檢查的好處。Parameterized Type作為參數和返回值,Generic是vararg、annotation、enumeration、collection的基石。
泛型可以帶來如下的好處總結如下:
類型安全:拋棄List、Map,使用List、Map給它們添加元素或者使用Iterator遍歷時,編譯期就可以給你檢查出類型錯誤
方法參數和返回值加上了Type: 拋棄List、Map,使用List、Map
不需要類型轉換:List list = new ArrayList();
類型通配符「?」: 假設一個列印List中元素的方法printList,我們希望任何類型T的List都可以被列印
6)靜態導入答:靜態導入:導入了類中的所有靜態成員,簡化靜態成員的書寫。
import語句可以導入一個類或某個包中的所有類
import static語句導入一個類中的某個靜態方法或所有靜態方法
import static java.util.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));
}
}
注意:
1、也可以通過import static java.lang.Math.*;導入Math類下所有的靜態方法。
2、如果將javac設置為了Java5以下,那麼靜態導入等jdk1.5的特性都會報告錯誤。
7)新的線程模型和並發庫Thread Framework(重要)答: 最主要的就是引入了 java.util.concurrent 包,這個都是需要重點掌握的。
HashMap 的替代者 ConcurrentHashMap 和 ArrayList 的替代者 CopyOnWriteArrayList 在大並發量讀取時採用 java.util.concurrent 包裡的一些類會讓大家滿意 BlockingQueue、Callable、Executor、Semaphore
8)內省(Introspector)答:是 Java 語言對 Bean 類屬性、事件的一種預設處理方法。例如類 A 中有屬性 name , 那我們通過 getName,setName 來得到其值或者設置新的值。通過 getName/setName 來訪問name屬性,這就是默認的規則。Java 中提供了一套 API 用來訪問某個屬性的 getter /setter 方法,通過這些 API 可以使你不需要了解這個規則(但你最好還是要搞清楚),這些 API 存放於包 java.beans 中。
一般的做法是通過類 Introspector 來獲取某個對象的 BeanInfo 信息,然後通過 BeanInfo 來獲取屬性的描述器 (PropertyDescriptor),通過這個屬性描述器就可以獲取某個屬性對應的 getter/setter 方法,然後我們就可以通過反射機制來 調用這些方法。
擴展閱讀:java Introspector(內省) 的介紹
9)註解(Annotations)答:
註解(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註解的理解和應用
10)新增 ProcessBuilder 類答:
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新特性及使用
11)新增Formatter格式化器(Formatter)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;
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");
formatter.format(Locale.FRANCE, "e = %+10.4f", Math.E);
formatter.format("%nAmount gained or lost since last statement: $ %(,.2f", 6217.58);
log.info("列印出格式化後的字符串:{}", formatter);
formatter.close();
}
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());
}
}
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);
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新特性及使用
12)新增 Scanner 類(Scanner)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() 方法即可。
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新特性及使用
13)StringBuilderStringBuilder 也是 Java5 中新增的類,主要用來代替 + 號和 StringBuffer 來更加高效的拼接字符串。StringBuffer 與 StringBuilder 都是繼承於 AbstractStringBuilder,主要的區別就是 StringBuffer 的函數上都有 synchronized 關鍵字,保證線程安全。
關於 StringBuilder 的使用這裡就不再詳細介紹了,網上文章也有很多。總之,對於動態字符串的拼接推薦使用 StringBuilder。靜態字符串的拼接直接使用 + 號或者字符串的 concat(String str) 方法,甚至也使用 StringBuilder 亦可。
參考文章:Java5新特性及使用
(二)Java 6 相關知識點關於 JDK 1.6 的新特性,了解一下就可以了…如果有興趣深入研究的童鞋,右轉這裡:Java6新特性及使用
1)Desktop 類和 SystemTray 類:答:
在JDK6中 ,AWT新增加了兩個類:Desktop 和 SystemTray 。
前者可以用來打開系統默認瀏覽器瀏覽指定的 URL,打開系統默認郵件客戶端給指定的郵箱發郵件,用默認應用程式打開或編輯文件(比如,用記事本打開以txt為後綴名的文件),用系統默認的印表機列印文檔;
後者可以用來在系統託盤區創建一個託盤程序.
2)使用 JAXB2 來實現對象與 XML 之間的映射答:
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等來實現同樣的功能。
3)理解StAX答:
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文檔映射到一顆內存樹,這樣就可以很容易地得到父節點和子結點以及兄弟節點的數據,但如果文檔很大,將會嚴重影響性能。
4)使用Compiler API答:
現在我們可以用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提供了更加豐富的方式去做動態編譯,而且是跨平臺的。
5)輕量級Http Server API答:
JDK6 提供了一個簡單的Http Server API,據此我們可以構建自己的嵌入式Http Server,它支持Http和Https協議,提供了HTTP1.1的部分實現,沒有被實現的那部分可以通過擴展已有的Http Server API來實現,程式設計師必須自己實現HttpHandler接口,HttpServer會調用HttpHandler實現類的回調方法來處理客戶端請求,在 這裡,我們把一個Http請求和它的響應稱為一個交換,包裝成HttpExchange類,HttpServer負責將HttpExchange傳給 HttpHandler實現類的回調方法.
6)插入式註解處理API(Pluggable Annotation Processing API)答:
插入式註解處理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來標識測試期間需要執行的測試方法。
7)用Console開發控制臺程序JDK6 中提供了java.io.Console 類專用來訪問基於字符的控制臺設備. 你的程序如果要與Windows下的cmd或者Linux下的Terminal交互,就可以用Console類代勞. 但我們不總是能得到可用的Console, 一個JVM是否有可用的Console依賴於底層平臺和JVM如何被調用. 如果JVM是在交互式命令行(比如Windows的cmd)中啟動的,並且輸入輸出沒有重定向到另外的地方,那麼就可以得到一個可用的Console實例.
8)對腳本語言的支持如: ruby, groovy, javascript.
9)Common annotationsCommon 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 各種技術的一致性。
10)Java DB(Derby)從 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)。
11)JDBC 4.0在 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 特性的文章了,這裡就直接黏了:Java 7新特性
1)Diamond Operator類型判斷是一個人特殊的煩惱,入下面的代碼:
Map<String,List<String>> anagrams = new HashMap<String,List<String>>();
通過類型推斷後變成:
Map<String,List<String>> anagrams = new HashMap<>();
註:這個<>被叫做diamond(鑽石)運算符,Java 7後這個運算符從引用的聲明中推斷類型。
2)在switch語句中使用字符串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() 與案例標籤來進行比較。
3)自動資源管理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) {
}
}
}
然而,在Java 7中引入了另一個很酷的特性,可以自動管理資源。它的操作也很簡單,我們所要做的就是在 try 塊中申明資源如下:
try(resources_to_be_cleant){
}
以上方法與舊的 try-finally 能最終寫成下面的代碼:
public voidnewTry() {
try(FileOutputStream fos = newFileOutputStream("movies.txt");
DataOutputStream dos = newDataOutputStream(fos)) {
dos.writeUTF("Java 7 Block Buster");
} catch(IOException e) {
}
}
上面的代碼也代表了這個特性的另一個方面:處理多個資源。FileOutputStream 和 DataOutputStream 在try語句中一個接一個地含在語句中,每一個都用分號(;)分隔符分隔開。我們不必手動取消或關閉流,因為當空間存在try塊時,它們將自動關閉。
在後臺,應該自動關閉的資源必須試驗 java.lang.AutoCloseable 接口。
任何實現 AutoCloseable 接口的資源都可以作為自動資源管理的候選。AutoCloseable 是 java.io.Closeable 接口的父類,JVM會在程序退出try塊後調用一個方法 close()。
4)帶下劃線的數字文本數字文字絕對是對眼睛的一種考驗。我相信,如果你給了一個數字,比如說,十個零,你就會像我一樣數零。如果不計算從右到左的位置,識別一個文字的話,就很容易出錯,而且很麻煩。Not anymore。Java在識別位置時引入了下劃線。例如,您可以聲明1000,如下所示:
int thousand = 1_000;
或1000000(一百萬)如下:
int million = 1_000_000
請注意,這個版本中也引入了二進位文字-例如「0b1」-因此開發人員不必再將它們轉換為十六進位。
5)改進的異常處理在異常處理區域有幾處改進。Java引入了多個catch功能,以使用單個抓到塊捕獲多個異常類型。
假設您有一個方法,它拋出三個異常。在當前狀態下,您將分別處理它們,如下所示:
public voidoldMultiCatch() {
try{
methodThatThrowsThreeExceptions();
} catch(ExceptionOne e) {
} catch(ExceptionTwo e) {
} catch(ExceptionThree e) {
}
}
在一個catch塊中逐個捕獲一個連續的異常,看起來很混亂。我還看到了捕獲十幾個異常的代碼。這是非常低效和容易出錯的。Java為解決這隻醜小鴨帶來了新的語言變化。請參閱下面的方法oldMultiCatch方法的改進版本:
public voidnewMultiCatch() {
try{
methodThatThrowsThreeExceptions();
} catch(ExceptionOne | ExceptionTwo | ExceptionThree e) {
}
}
多個異常通過使用 「|」 操作符在一個catch塊中捕獲。這樣,您不必編寫數十個異常捕獲。但是,如果您有許多屬於不同類型的異常,那麼您也可以使用「多個catch塊」塊。下面的代碼片段說明了這一點:
public voidnewMultiMultiCatch() {
try{
methodThatThrowsThreeExceptions();
} catch(ExceptionOne e) {
} catch(ExceptionTwo | ExceptionThree e) {
}
}
在上面的例子中,在和ExceptionThree屬於不同的層次結構,因此您希望以不同的方式處理它們,但使用一個抓到塊。
6)New file system API(NIO 2.0)那些使用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 允許您在對主題(目錄或文件)進行更改時接收通知事件。
具體的創建步驟就不給了,總之它的功能就跟它的名字一般,當文件發生更改的時候,能及時作出反饋。
7)Fork and Join(Fork/Join框架)在一個 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;
public class CountTask extends RecursiveTask<Integer> {
public static final int THRESHOLD = 2;
private int start;
private int end;
public CountTask(int start, int end) {
this.start = start;
this.end = end;
}
@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;
}
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());
}
}