語(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ǔ)法對(duì)語(yǔ)言的功能并沒(méi)有影響。
Java中的泛型,變長(zhǎng)參數(shù),自動(dòng)拆箱/裝箱,條件編譯等都是,下面做簡(jiǎn)單的介紹和分析。
泛型
與C#中的泛型相比,Java的泛型可以算是“偽泛型”了。在C#中,不論是在程序源碼中、在編譯后的中間語(yǔ)言,還是在運(yùn)行期泛型都是真實(shí)存在的。Java則不同,Java的泛型只在源代碼存在,只供編輯器檢查使用,編譯后的字節(jié)碼文件已擦除了泛型類(lèi)型,同時(shí)在必要的地方插入了強(qiáng)制轉(zhuǎn)型的代碼。
泛型代碼:
- public static void main(String[] args) {
- List<String> stringList = new ArrayList<String>();
- stringList.add('oliver');
- System.out.println(stringList.get(0));
- }
將上面的代碼的字節(jié)碼反編譯后:
- public static void main(String args[])
- {
- List stringList = new ArrayList();
- stringList.add('oliver');
- System.out.println((String)stringList.get(0));
- }
自動(dòng)拆箱/裝箱 自動(dòng)拆箱/裝箱是在編譯期,依據(jù)代碼的語(yǔ)法,決定是否進(jìn)行拆箱和裝箱動(dòng)作。
裝箱過(guò)程:把基本類(lèi)型用它們對(duì)應(yīng)的包裝類(lèi)型進(jìn)行包裝,使基本類(lèi)型具有對(duì)象特征。
拆箱過(guò)程:與裝箱過(guò)程相反,把包裝類(lèi)型轉(zhuǎn)換成基本類(lèi)型。
需要注意的是:包裝類(lèi)型的“==”運(yùn)算在沒(méi)有遇到算數(shù)運(yùn)算符的情況下不會(huì)自動(dòng)拆箱,而其包裝類(lèi)型的equals()方法不會(huì)處理數(shù)據(jù)類(lèi)型轉(zhuǎn)換,所以:
- Integer a = 1;
- Integer b = 1;
- Long c = 1L;
- System.out.println(a == b);
- System.out.println(c.equals(a));
這樣的代碼應(yīng)該盡量避免自動(dòng)拆箱與裝箱。
循環(huán)歷遍(foreach) 語(yǔ)法:
- List<Integer> list = new ArrayList<Integer>();
- for(Integer num : list){
- System.out.println(num);
- }
Foreach要求被歷遍的對(duì)象要實(shí)現(xiàn)Iterable接口,由此可想而知,foreach迭代也是調(diào)用底層的迭代器實(shí)現(xiàn)的。反編譯上面源碼的字節(jié)碼:
- List list = new ArrayList();
- Integer num;
- Integer num;
- for (Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(num)){
- num = (Integer) iterator.next();
- }
條件編輯 很多編程語(yǔ)言都提供了條件編譯的途徑,C,C 中使用#ifdef。Java語(yǔ)言并沒(méi)有提供這種預(yù)編譯功能,但是Java也能實(shí)現(xiàn)預(yù)編譯。
- if(true){
- System.out.println('oliver');
- }else{
- System.out.println('lee');
- }
這段代碼的字節(jié)碼反編譯后只有一條語(yǔ)句:
- System.out.println('oliver');
在編譯器中,將會(huì)把分支不成立的代碼消除,這一動(dòng)作發(fā)生在編譯器解除語(yǔ)法糖階段。
所以說(shuō),可以利用條件語(yǔ)句來(lái)實(shí)現(xiàn)預(yù)編譯。
枚舉 枚舉類(lèi)型其實(shí)并不復(fù)雜,在JVM字節(jié)碼文件結(jié)構(gòu)中,并沒(méi)有“枚舉”這個(gè)類(lèi)型。
其實(shí)源程序的枚舉類(lèi)型,會(huì)在編譯期被編譯成一個(gè)普通了類(lèi)。利用繼承和反射,這是完全可以做到的。
看下面一個(gè)枚舉類(lèi):
- public enum EnumTest {
- OLIVER,LEE;
- }
反編譯字節(jié)碼后:
- public final class EnumTest extends Enum {
-
- private EnumTest(String s, int i) {
- super(s, i);
- }
-
- public static EnumTest[] values() {
- EnumTest aenumtest[];
- int i;
- EnumTest aenumtest1[];
- System.arraycopy(aenumtest = ENUM$VALUES, 0,
- aenumtest1 = new EnumTest[i = aenumtest.length], 0, i);
- return aenumtest1;
- }
-
- public static EnumTest valueOf(String s) {
- return (EnumTest) Enum.valueOf(EnumTest, s);
- }
-
- public static final EnumTest OLIVER;
- public static final EnumTest LEE;
- private static final EnumTest ENUM$VALUES[];
-
- static {
- OLIVER = new EnumTest('OLIVER', 0);
- LEE = new EnumTest('LEE', 1);
- ENUM$VALUES = (new EnumTest[] { OLIVER, LEE });
- }
- }
至于更多細(xì)節(jié),可以參考父類(lèi)Enum。
變長(zhǎng)參數(shù) 變長(zhǎng)參數(shù)允許我們傳入到方法的參數(shù)是不固定個(gè)數(shù)。
對(duì)于這個(gè)方法:
- public void foo(String str,Object...args){
-
- }
我們可以這樣調(diào)用:
- foo('oliver');
- foo('oliver',new Object());
- foo('oliver',new Integer(1),'sss');
- foo('oliver',new ArrayList(),new Object(),true,1);
參數(shù)args可以是任意多個(gè)。
其實(shí),在編譯階段,args是會(huì)被編譯成Object [] args。
- public transient void foo(String s, Object aobj[])
- {
- }
這樣,變長(zhǎng)參數(shù)就可以實(shí)現(xiàn)了。
但是要注意的是,變長(zhǎng)參數(shù)必須是方法參數(shù)的最后一項(xiàng)。