目錄:
Java語(yǔ)法糖系列一:可變長(zhǎng)度參數(shù)和foreach循環(huán)
http://www.reibang.com/p/628568f94ef8
Java語(yǔ)法糖系列二:自動(dòng)裝箱/拆箱和條件編譯
http://www.reibang.com/p/946b3c4a5db6
Java語(yǔ)法糖系列三:泛型與類(lèi)型擦除
http://www.reibang.com/p/4de08deb6ba4
Java語(yǔ)法糖系列四:枚舉類(lèi)型
http://www.reibang.com/p/ae09363fe734
Java語(yǔ)法糖系列五:內(nèi)部類(lèi)和閉包
http://www.reibang.com/p/f55b11a4cec2
好久沒(méi)做總結(jié),打算挖個(gè)坑整理一下Java語(yǔ)法糖相關(guān)的知識(shí)扰她。文字邀层、說(shuō)明等知識(shí)均來(lái)源于互聯(lián)網(wǎng)和自己的見(jiàn)解,例子都是自己寫(xiě)的摄狱,侵刪,歡迎討論~
語(yǔ)法糖
語(yǔ)法糖(Syntactic Sugar),也叫糖衣語(yǔ)法,是英國(guó)計(jì)算機(jī)科學(xué)家彼得·約翰·蘭達(dá)(Peter J. Landin)發(fā)明的一個(gè)術(shù)語(yǔ)低淡。指的是,在計(jì)算機(jī)語(yǔ)言中添加某種語(yǔ)法瞬项,這種語(yǔ)法能使程序員更方便的使用語(yǔ)言開(kāi)發(fā)程序蔗蹋,同時(shí)增強(qiáng)程序代碼的可讀性,避免出錯(cuò)的機(jī)會(huì)囱淋。
幾乎每種語(yǔ)言都提供語(yǔ)法糖猪杭,它只是編譯器實(shí)現(xiàn)的一些小把戲罷了,編譯期間以特定的字節(jié)碼或者特定的方式對(duì)這些語(yǔ)法做一些處理妥衣,開(kāi)發(fā)者就可以直接方便地使用了皂吮。這些語(yǔ)法糖雖然不會(huì)提供實(shí)質(zhì)性的功能改進(jìn),但是它們或能提高性能称鳞、或能提升語(yǔ)法的嚴(yán)謹(jǐn)性涮较、或能減少編碼出錯(cuò)的機(jī)會(huì)稠鼻。Java提供給了用戶(hù)大量的語(yǔ)法糖冈止,比如泛型、自動(dòng)裝箱候齿、自動(dòng)拆箱熙暴、foreach循環(huán)闺属、變長(zhǎng)參數(shù)、內(nèi)部類(lèi)周霉、枚舉類(lèi)掂器、斷言(assert)等。
學(xué)習(xí)語(yǔ)法糖原理最好的辦法就是反編譯看源碼~
可變長(zhǎng)度參數(shù)
看以下代碼
public static void main(String[] args){
String [] params=new String[]{
"111","222","333","444"
};
print("AAA","BBB","CCC","DDD");
print(params);
}
public static void print(String... params)
{
System.out.println();
for (int i = 0; i < params.length; i++)
{
System.out.print(params[i]+"~");
}
}
print方法的參數(shù)的意思是表示傳入的params個(gè)數(shù)是不定的俱箱,代碼的運(yùn)行結(jié)果:
AAA~BBB~CCC~DDD~
111~222~333~444~
我用數(shù)組遍歷的方式把參數(shù)遍歷出來(lái)了国瓮,同時(shí)print方法也接受數(shù)組參數(shù),這說(shuō)明了可變參數(shù)是利用數(shù)組實(shí)現(xiàn)的狞谱。查看編譯出來(lái)的源碼:
public static void main(String[] paramArrayOfString)
{
String[] arrayOfString = { "111", "222", "333", "444" };
print(new String[] { "AAA", "BBB", "CCC", "DDD" });
print(arrayOfString);
}
public static void print(String[] paramArrayOfString)
{
System.out.println();
for (int i = 0; i < paramArrayOfString.length; ++i)
{
System.out.print(paramArrayOfString[i] + "~");
}
}
發(fā)現(xiàn)print方法的參數(shù)部分由String... params 變成了 String[] paramArrayOfString
數(shù)組參數(shù)乃摹,說(shuō)明可變長(zhǎng)度參數(shù)是用數(shù)組實(shí)現(xiàn)的。
最后跟衅,注意一點(diǎn)孵睬,可變長(zhǎng)度參數(shù)必須作為方法參數(shù)列表中的的最后一個(gè)參數(shù)且方法參數(shù)列表中只能有一個(gè)可變長(zhǎng)度參數(shù)。
foreach循環(huán)原理
public static void print(String... params)
{
System.out.println();
for (String s:params)
{
System.out.print(s+"~");
}
}
把上面的print函數(shù)換成foreach循環(huán)伶跷,查看編譯出來(lái)的源碼
public static void print(String[] paramArrayOfString)
{
System.out.println();
String[] arrayOfString = paramArrayOfString;
int i = arrayOfString.length;
for (int j = 0; j < i; ++j) {
String str = arrayOfString[j];
System.out.print(str + "~");
}
}
發(fā)現(xiàn)foreach部分被替換成了普通的for循環(huán)掰读,說(shuō)明對(duì)于數(shù)組,foreach是用普通for循環(huán)實(shí)現(xiàn)的叭莫。
如果遍歷的對(duì)象不是數(shù)組蹈集,而是List、Map等有實(shí)現(xiàn)迭代器Iterable接口的容器又是怎么實(shí)現(xiàn)的呢雇初?再看一個(gè)例子
public static void main(String[] args){
// TODO Auto-generated method stub
List<String> list = new ArrayList<String>();
list.add("AAA");
list.add("BBB");
for(String l : list)
{
System.out.print(l+"~");
}
Set<String> set=new HashSet<String>();
set.add("CCC");
set.add("DDD");
for(String s : set)
{
System.out.print(s+"~");
}
}
}
查看編譯出來(lái)的源碼
public static void main(String[] paramArrayOfString)
{
ArrayList localArrayList = new ArrayList();
localArrayList.add("AAA");
localArrayList.add("BBB");
localArrayList.add("CCC");
localArrayList.add("DDD");
for (Object localObject1 = localArrayList.iterator();
((Iterator)localObject1).hasNext(); )
{
localObject2 = (String)((Iterator)localObject1).next();
System.out.print(((String)localObject2) + "~");
}
localObject1 = new HashSet();
((Set)localObject1).add("AAA");
((Set)localObject1).add("BBB");
((Set)localObject1).add("CCC");
((Set)localObject1).add("DDD");
for (Object localObject2 = ((Set)localObject1).iterator();
((Iterator)localObject2).hasNext(); )
{
String str = (String)((Iterator)localObject2).next();
System.out.print(str + "~");
}
}
List和Set的foreach都被編譯成用迭代器遍歷的形式了雾狈,說(shuō)明在對(duì)有實(shí)現(xiàn)Iterable接口的對(duì)象采用foreach語(yǔ)法糖的話(huà),編譯器會(huì)將這個(gè)for關(guān)鍵字轉(zhuǎn)化為對(duì)目標(biāo)的迭代器使用抵皱。
所以如果想要自己自定義的類(lèi)可以采用foreach語(yǔ)法糖就要實(shí)現(xiàn)Iterable接口了善榛。
一點(diǎn)拓展
ArrayList除了支持線(xiàn)性訪(fǎng)問(wèn)(sequential access)外還支持隨機(jī)訪(fǎng)問(wèn)外(random access)
這是因?yàn)閍rrayList還實(shí)現(xiàn)了RandomAccess接口,而Map呻畸、Set等沒(méi)有移盆。
查看JDK關(guān)于RandomAccess接口的說(shuō)明如下,版本是JDK1.8
It is recognized that the distinction between random and sequential
- access is often fuzzy. For example, some <tt>List</tt> implementations
- provide asymptotically linear access times if they get huge, but constant
- access times in practice. Such a <tt>List</tt> implementation
- should generally implement this interface. As a rule of thumb, a
- <tt>List</tt> implementation should implement this interface if,
- for typical instances of the class, this loop:
for (int i=0, n=list.size(); i < n; i++)
list.get(i);
- runs faster than this loop:
for (Iterator i=list.iterator(); i.hasNext(); )
i.next();
- @since 1.4
我們直接看最重要的部分伤为,從JDK1.4開(kāi)始咒循,根據(jù)經(jīng)驗(yàn),對(duì)于實(shí)現(xiàn)了RandomAccess接口的List绞愚,如ArrayList叙甸、CopyOnWriteArrayList, RoleList, RoleUnresolvedList, Stack, Vector,直接使用for循環(huán)遍歷runs faster than 迭代器遍歷位衩。
其實(shí)如果看過(guò)ArrayList源碼的同學(xué)也可以注意到:ArrayList底層是采用數(shù)組實(shí)現(xiàn)的裆蒸,如果采用Iterator遍歷,那么還要?jiǎng)?chuàng)建許多指針去執(zhí)行這些值(比如next();hasNext())等糖驴,這樣必然會(huì)增加內(nèi)存開(kāi)銷(xiāo)以及執(zhí)行效率僚祷。
簡(jiǎn)單測(cè)試了一下佛致,空遍歷10萬(wàn)條數(shù)據(jù)不做其他任何操作,對(duì)于A(yíng)rrayList用foreach遍歷和for遍歷耗時(shí)分別是5ms和1ms辙谜。例子都好簡(jiǎn)單就不貼出來(lái)了俺榆。