*該博客轉自 http://blog.csdn.net/danchu/article/details/54986442
語法糖(Syntactic Sugar),也稱糖衣語法,指在計算機語言中添加的某種語法,這種語法對語言本身功能來說沒有什么影響,只是為了方便程序員的開發,提高開發效率。說白了,語法糖就是對現有語法的一個封裝。
Java作為一種與平台無關的高級語言,當然也含有語法糖,這些語法糖並不被虛擬機所支持,在編譯成字節碼階段就自動轉換成簡單常用語法。一般來說Java中的語法糖主要有以下幾種:
1. 泛型與類型擦除
2. 自動裝箱與拆箱,變長參數、
3. 增強for循環
4. 內部類與枚舉類
泛型與類型擦除
Java語言並不是一開始就支持泛型的。在早期的JDK中,只能通過Object類是所有類型的父類和強制類型轉換來實現泛型的功能。強制類型轉換的缺點就是把編譯期間的問題延遲到運行時,JVM並不能為我們提供編譯期間的檢查。
在JDK1.5中,Java語言引入了泛型機制。但是這種泛型機制是通過類型擦除來實現的,即Java中的泛型只在程序源代碼中有效(源代碼階段提供類型檢查),在編譯后的字節碼中自動用強制類型轉換進行替代。也就是說,Java語言中的泛型機制其實就是一顆語法糖,相較與C++、C#相比,其泛型實現實在是不那么優雅。
/** * 在源代碼中存在泛型 */ public static void main(String[] args) { Map<String,String> map = new HashMap<String,String>(); map.put("hello","你好"); String hello = map.get("hello"); System.out.println(hello); }
當上述源代碼被編譯為class文件后,泛型被擦除且引入強制類型轉換
public static void main(String[] args) { HashMap map = new HashMap(); //類型擦除 map.put("hello", "你好"); String hello = (String)map.get("hello");//強制轉換 System.out.println(hello); }
自動裝箱與拆箱
Java中的自動裝箱與拆箱指的是基本數據類型與他們的包裝類型之間的相互轉換。
我們知道Java是一門面向對象的語言,在Java世界中有一句話是這么說的:“萬物皆對象”。但是Java中的基本數據類型卻不是對象,他們不需要進行new操作,也不能調用任何方法,這在使用的時候有諸多不便。因此Java為這些基本類型提供了包裝類,並且為了使用方便,提供了自動裝箱與拆箱功能。自動裝箱與拆箱在使用的過程中,其實是一個語法糖,內部還是調用了相應的函數進行轉換。
下面代碼演示了自動裝箱和拆箱功能
public static void main(String[] args) { Integer a = 1; int b = 2; int c = a + b; System.out.println(c); }
經過編譯后,代碼如下
public static void main(String[] args) { Integer a = Integer.valueOf(1); // 自動裝箱 byte b = 2; int c = a.intValue() + b;//自動拆箱 System.out.println(c); }
變長參數
所謂變長參數,就是方法可以接受長度不定確定的參數
變長參數特性是在JDK1.5中引入的,使用變長參數有兩個條件,一是變長的那一部分參數具有相同的類型,二是變長參數必須位於方法參數列表的最后面。變長參數同樣是Java中的語法糖,其內部實現是Java數組。
public class Varargs { public static void print(String... args) { for(String str : args){ System.out.println(str); } } public static void main(String[] args) { print("hello", "world"); } }
編譯為class文件后如下,從中可以很明顯的看出變長參數內部是通過數組實現的
public class Varargs { public Varargs() { } public static void print(String... args) { String[] var1 = args; int var2 = args.length; //增強for循環的數組實現方式 for(int var3 = 0; var3 < var2; ++var3) { String str = var1[var3]; System.out.println(str); } } public static void main(String[] args) { //變長參數轉換為數組 print(new String[]{"hello", "world"}); } }
增強for循環
增強for循環與普通for循環相比,功能更強並且代碼更簡潔
增強for循環的對象要么是一個數組,要么實現了Iterable接口。這個語法糖主要用來對數組或者集合進行遍歷,其在循環過程中不能改變集合的大小。
public static void main(String[] args) { String[] params = new String[]{"hello","world"}; //增強for循環對象為數組 for(String str : params){ System.out.println(str); } List<String> lists = Arrays.asList("hello","world"); //增強for循環對象實現Iterable接口 for(String str : lists){ System.out.println(str); } }
編譯后的class文件為
public static void main(String[] args) { String[] params = new String[]{"hello", "world"}; String[] lists = params; int var3 = params.length; //數組形式的增強for退化為普通for for(int str = 0; str < var3; ++str) { String str1 = lists[str]; System.out.println(str1); } List var6 = Arrays.asList(new String[]{"hello", "world"}); Iterator var7 = var6.iterator(); //實現Iterable接口的增強for使用iterator接口進行遍歷 while(var7.hasNext()) { String var8 = (String)var7.next(); System.out.println(var8); } }
內部類
內部類就是定義在一個類內部的類
Java語言中之所以引入內部類,是因為有些時候一個類只在另一個類中有用,我們不想讓其在另外一個地方被使用。內部類之所以是語法糖,是因為其只是一個編譯時的概念,一旦編譯完成,編譯器就會為內部類生成一個單獨的class文件,名為outer$innter.class。
public class Outer { class Inner{ } }
使用javac編譯后,生成兩個class文件Outer.class和Outer$Inner.class,其中Outer$Inner.class的內容如下:
class Outer$Inner { Outer$Inner(Outer var1) { this.this$0 = var1; } }
內部類分為四種:成員內部類、局部內部類、匿名內部類、靜態內部類,每一種都有其用法,這里就不介紹了
枚舉類型
枚舉類型就是一些具有相同特性的類常量
java中類的定義使用class,枚舉類的定義使用enum。在Java的字節碼結構中,其實並沒有枚舉類型,枚舉類型只是一個語法糖,在編譯完成后被編譯成一個普通的類。這個類繼承java.lang.Enum,並被final關鍵字修飾。
public enum Fruit { APPLE,ORINGE }
使用jad對編譯后的class文件進行反編譯后得到:
//繼承java.lang.Enum並聲明為final public final class Fruit extends Enum { public static Fruit[] values() { return (Fruit[])$VALUES.clone(); } public static Fruit valueOf(String s) { return (Fruit)Enum.valueOf(Fruit, s); } private Fruit(String s, int i) { super(s, i); } //枚舉類型常量 public static final Fruit APPLE; public static final Fruit ORANGE; private static final Fruit $VALUES[];//使用數組進行維護 static { APPLE = new Fruit("APPLE", 0); ORANGE = new Fruit("ORANGE", 1); $VALUES = (new Fruit[] { APPLE, ORANGE }); } }
