1:概述 前端編譯器:Sun的Javac、Eclipse JDT中的增量式編譯器(EJC) JIT編譯器:Hotspot VM的C1,C2編譯器 AOT編譯器:GNC Compiler for the Java(GCJ)、Excelsior JET
本章先介紹第一種前端編譯器,也是大家都認可的編譯器。 2:Javac編譯器
源碼編譯自OpenJdk7,從Sun Javac源碼來看,編譯大致可分為3個過程: 解析與填充符號表過程 插入式註解處理器的註解處理過程。 分析與位元組碼產生過程。 3:Java文法糖的味道 3.1:泛型與類型擦除
Java的泛型是典型的文法糖。
public static void main(String[] args) { Map<String, String> map = new HashMap<String, String>(); map.put("hello", "你好"); map.put("how are you?", "吃了沒。"); System.out.println(map.get("hello")); System.out.println(map.get("how are you?"));}
上述代碼編譯後的結果是
public static void main(String[] args) { Map map = new HashMap(); map.put("hello", "你好"); map.put("how are you?", "吃了沒。"); System.out.println((String) map.get("hello")); System.out.println((String) map.get("how are you?"));}
Java泛型在某些情境是存在缺陷的,例如下面代碼是編譯不通過的
import java.util.List;public class GenericTypes { public static void method(List<String> list) { System.out.println("invoke method(List<String> list)"); } public static void method(List<Integer> list) { System.out.println("invoke method(List<Integer> list)"); }}
再看下面這段代碼,這段代碼增加了一個傳回值就可以編譯成功,前提是試用JDK1.6的Javac編譯器。導致這種情況的出現使因為虛擬機器的方法區的實現有關,在Class檔案格式中,只要描述符不完全一致,就允許存在。
import java.util.ArrayList;import java.util.List;public class GenericTypes { public static String method(List<String> list) { System.out.println("invoke method(List<String> list)"); return ""; } public static int method(List<Integer> list) { System.out.println("invoke method(List<Integer> list)"); return 1; } public static void main(String[] args) { method(new ArrayList<String>()); method(new ArrayList<Integer>()); }}
Java虛擬機器引入了諸如Signature、Local VariableTypeTable等新的屬性用於解決伴隨泛型而來的的參數類型識別的問題。 3.2:自動裝箱、拆箱與遍曆迴圈
下面的程式碼封裝含了泛型、自動裝箱、自動拆箱、遍曆迴圈與變長參數5種文法糖。
public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4); // 如果在JDK 1.7中,還有另外一顆文法糖 , // 能讓上面這句代碼進一步簡寫成List<Integer> list = [1, 2, 3, 4]; int sum = 0; for (int i : list) { sum += i; } System.out.println(sum); }
下面這段代碼是上面這段代碼的編譯之後得到
public static void main(String[] args) { List list = Arrays.asList( new Integer[] { Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3), Integer.valueOf(4) }); int sum = 0; for (Iterator localIterator = list.iterator(); localIterator.hasNext(); ) { int i = ((Integer)localIterator.next()).intValue(); sum += i; } System.out.println(sum); }
3.3:條件編譯
public static void main(String[] args) { if (true) { System.out.println("block 1"); } else { System.out.println("block 2"); }}
上面的代碼反編譯後會變成
public static void main(String[] args) { System.out.println("block 1");}
下面的代碼編譯器拒絕編譯
public static void main(String[] args) { // 編譯器將會提示“Unreachable code” while (false) { System.out.println(""); }}
Java除了上面說的文法糖外,還有內部類、枚舉類、Assert 陳述式、對枚舉和字串的switch支援(JDK1.7支援)、try語句中定義和關閉資源(JDK1.7支援)等。 3.4:實戰:插入式註解處理器 3.4.1:實戰目標
實現一個類似checkstyle、FindBug類似的代碼檢查工具。為了實現這個效果,我們要定一些書寫規範: 類(或介面):符合駝式命名法,首字母大寫 方法:符合駝式命名法,首字母小寫 欄位:類或執行個體變數(符合駝式命名法,首字母小寫);常量(要求全部由大寫字母和底線組成,並且第一個字母不能是底線) 3.4.2:代碼實現
import javax.annotation.processing.*;import javax.lang.model.SourceVersion;import javax.lang.model.element.Element;import javax.lang.model.element.TypeElement;import java.util.Set;// 可以用"*"表示支援所有Annotations@SupportedAnnotationTypes("*")// 只支援JDK 1.6的Java代碼@SupportedSourceVersion(SourceVersion.RELEASE_6)public class NameCheckProcessor extends AbstractProcessor { private NameChecker nameChecker; /** * 初始化名稱檢查外掛程式 */ @Override public void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); nameChecker = new NameChecker(processingEnv); } /** * 對輸入的文法樹的各個節點進行進行名稱檢查 */ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (!roundEnv.processingOver()) { for (Element element : roundEnv.getRootElements()) nameChecker.checkNames(element); } return false; }}
import javax.annotation.processing.Messager;import javax.annotation.processing.ProcessingEnvironment;import javax.lang.model.element.*;import javax.lang.model.util.ElementScanner6;import java.util.EnumSet;import static javax.lang.model.element.ElementKind.*;import static javax.lang.model.element.Modifier.FINAL;import static javax.lang.model.element.Modifier.PUBLIC;import static javax.lang.model.element.Modifier.STATIC;import static javax.tools.Diagnostic.Kind.WARNING;/** * 程式名稱規範的編譯器外掛程式:<br> * 如果程式命名不合規範,將會輸出一個編譯器的WARNING資訊 */public class NameChecker { private final Messager messager; NameCheckScanner nameCheckScanner = new NameCheckScanner(); NameChecker(ProcessingEnvironment processsingEnv) { this.messager = processsingEnv.getMessager(); } /** * 對Java程式命名進行檢查,根據《Java語言規範》第三版第6.8節的要求,Java程式命名應當符合下列格式: * * <ul> * <li>類或介面:符合駝式命名法,首字母大寫。 * <li>方法:符合駝式命名法,首字母小寫。 * <li>欄位: * <ul> * <li>類、執行個體變數: 符合駝式命名法,首字母小寫。 * <li>常量: 要求全部大寫。 * </ul> * </ul> */ public void checkNames(Element element) { nameCheckScanner.scan(element); } /** * 名稱檢查器實作類別,繼承了JDK 1.6中新提供的ElementScanner6<br> * 將會以Visitor模式訪問抽象文法樹中的元素 */ private class NameCheckScanner extends ElementScanner6<Void, Void> { /** * 此方法用於檢查Java類 */ @Override public Void visitType(TypeElement e, Void p) { scan(e.getTypeParameters(), p); checkCamelCase(e, true); super.visitType(e, p); return null; } /** * 檢查方法命名是否合法 */ @Override public Void visitExecutable(ExecutableElement e, Void p) { if (e.getKind() == METHOD) { Name name = e.getSimpleName(); if (name.contentEquals(e.getEnclosingElement().getSimpleName())) messager.printMessage(WARNING, "一個普通方法 “" + name + "”不應當與類名重複,避免與建構函式產生混淆", e); checkCamelCase(e, false); } super.visitExecutable(e, p); return null; } /** * 檢查變數命名是否合法 */ @Override public Void visitVariable(VariableElement e, Void p) { // 如果這個Variable是枚舉或常量,則按大寫命名檢查,否則按照駝式命名法規則檢查 if (e.getKind() == ENUM_CONSTANT || e.getConstantValue() != null || heuristicallyConstant(e)) checkAllCaps(e); else checkCamelCase(e, false); return null; } /** * 判斷一個變數是否是常量 */ private boolean heuristicallyConstant(VariableElement e) { if (e.getEnclosingElement().getKind() == INTERFACE) return true; else if (e.getKind() == FIELD && e.getModifiers().containsAll(EnumSet.of(PUBLIC, STATIC, FINAL))) return true; else { return false; } } /** * 檢查傳入的Element是否符合駝式命名法,如果不符合,則輸出警告資訊 */ private void checkCamelCase(Element e, boolean initialCaps) { String name = e.getSimpleName().toString(); boolean previousUpper = false; boolean conventional = true; int firstCodePoint = name.codePointAt(0); if (Character.isUpperCase(firstCodePoint)) { previousUpper = true; if (!initialCaps) { messager.printMessage(WARNING, "名稱“" + name + "”應當以小寫字母開頭", e); return; } } else if (Character.isLowerCase(firstCodePoint)) { if (initialCaps) { messager.printMessage(WARNING, "名稱“" + name + "”應當以大寫字母開頭", e); return; } } else conventional = false; if (conventional) { int cp = firstCodePoint; for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) { cp = name.codePointAt(i); if (Character.isUpperCase(cp)) { if (previousUpper) { conventional = false; break; } previousUpper = true; } else previousUpper = false; } } if (!conventional) messager.printMessage(WARNING, "名稱“" + name + "”應當符合駝式命名法(Camel Case Names)", e); } /** * 大寫命名檢查,要求第一個字母必須是大寫的英文字母,其餘部分可以是底線或大寫字母 */ private void checkAllCaps(Element e) { String name = e.getSimpleName().toString(); boolean conventional = true; int firstCodePoint = name.codePointAt(0); if (!Character.isUpperCase(firstCodePoint)) conventional = false; else { boolean previousUnderscore = false; int cp = firstCodePoint; for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) { cp = name.codePointAt(i); if (cp == (int) '_') { if (previousUnderscore) { conventional = false; break; } previousUnderscore = true; } else { previousUnderscore = false; if (!Character.isUpperCase(cp) && !Character.isDigit(cp)) { conventional = false; break; } } } } if (!conventional) messager.printMessage(WARNING, "常量“" + name + "”應當全部以大寫字母或底線命名,並且以字母開頭", e); } }}
最後在提供一個較差的代碼編碼規範代碼:
public class BADLY_NAMED_CODE { enum colors { red, blue, green; } static final int _FORTY_TWO = 42; public static int NOT_A_CONSTANT = _FORTY_TWO; protected void BADLY_NAMED_CODE() { return; } public void NOTcamelCASEmethodNAME() { return; }}
3.4.3:運行和測試
先用javac編譯NameChecker.java和NameCheckProcessor.java;然後javac -processor NameCheckProcessor BADLY_NAMED_CODE.java 命令編譯這段範例程式碼。 3.4.3:其它應用案例
Hibernate Validator Annotation Processor、Project Lombok等。