第十章 早期(編譯期)優化html
一、Javac的源碼與調試前端
編譯期的分類:java
Javac的編譯過程:git
解析與填充符號表的過程:程序員
註解處理器:github
package com.ecut.javac; import java.util.EnumSet; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.util.ElementScanner7; import javax.tools.Diagnostic.Kind; //這個註解處理器對那些註解感興趣,使用*表示支持全部的Annotations @SupportedAnnotationTypes(value = "*") //這個註解處理器能夠處理那些Java版本的代碼,只支持Java1.8的代碼 @SupportedSourceVersion(value = SourceVersion.RELEASE_8) public class NameCheckProcessor extends AbstractProcessor { private NameCheck nameCheck; /** * 初始化檢查插件 * 繼承了AbstractProcessor的註解處理器能夠直接訪問繼承了processingEnv,它表明上下文環境,要穿件新的代碼、向編譯器輸出信息、獲取其餘工具類都須要用到這個實例 * * @param processingEnv ProcessingEnvironment */ @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); this.nameCheck = new NameCheck(processingEnv); } /** * 對語法樹的各個節點今夕名稱檢查 * java編譯器在執行註解處理器代碼時要調用的過程 * * @param annotations * @param roundEnv * @return */ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (!roundEnv.processingOver()) { for (Element element : roundEnv.getRootElements()) { nameCheck.check(element); } } return false; } /** * 程序名稱規範的編譯期插件 * 程序名稱規範的編譯器插件 若是程序命名不合規範,將會輸出一個編譯器的Warning信息 * * @author kevin */ public static class NameCheck { Messager messager = null; public NameCheckScanner nameCheckScanner; private NameCheck(ProcessingEnvironment processingEnv) { messager = processingEnv.getMessager(); nameCheckScanner = new NameCheckScanner(processingEnv); } /** * 對Java程序明明進行檢查,根據《Java語言規範(第3版)》6.8節的要求,Java程序命名應當符合下列格式: * <ul> * <li>類或接口:符合駝式命名法,首字母大寫。 * <li>方法:符合駝式命名法,首字母小寫。 * <li>字段: * <ul> * <li>類,實例變量:符合駝式命名法,首字母小寫。 * <li>常量:要求所有大寫 * </ul> * </ul> * * @param element */ public void check(Element element) { nameCheckScanner.scan(element); } /** * 名稱檢查器實現類,繼承了1.6中新提供的ElementScanner6<br> * 將會以Visitor模式訪問抽象語法數中得元素 * * @author kevin */ public static class NameCheckScanner extends ElementScanner7<Void, Void> { Messager messager = null; public NameCheckScanner(ProcessingEnvironment processingEnv) { this.messager = processingEnv.getMessager(); } /** * 此方法用於檢查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() == ElementKind.METHOD) { Name name = e.getSimpleName(); if (name.contentEquals(e.getEnclosingElement().getSimpleName())) { messager.printMessage(Kind.WARNING, "一個普通方法:" + name + " 不該當與類名重複,避免與構造函數產生混淆", e); checkCamelCase(e, false); } } super.visitExecutable(e, p); return null; } /** * 檢查變量是否合法 */ @Override public Void visitVariable(VariableElement e, Void p) { /* 若是這個Variable是枚舉或常量,則按大寫命名檢查,不然按照駝式命名法規則檢查 */ if (e.getKind() == ElementKind.ENUM_CONSTANT || e.getConstantValue() != null || heuristicallyConstant(e)) { checkAllCaps(e); } else { checkCamelCase(e, false); } super.visitVariable(e, p); return null; } /** * 判斷一個變量是不是常量 * * @param e * @return */ private boolean heuristicallyConstant(VariableElement e) { if (e.getEnclosingElement().getKind() == ElementKind.INTERFACE) { return true; } else if (e.getKind() == ElementKind.FIELD && e.getModifiers().containsAll(EnumSet.of(javax.lang.model.element.Modifier.FINAL, javax.lang.model.element.Modifier.STATIC, javax.lang.model.element.Modifier.PUBLIC))) { return true; } return false; } /** * 檢查傳入的Element是否符合駝式命名法,若是不符合,則輸出警告信息 * * @param e * @param initialCaps */ 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(Kind.WARNING, "名稱:" + name + " 應當已小寫字符開頭", e); return; } } else if (Character.isLowerCase(firstCodePoint)) { if (initialCaps) { messager.printMessage(Kind.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(Kind.WARNING, "名稱:" + name + "應當符合駝式命名法(Camel Case Names)", e); } } /** * 大寫命名檢查,要求第一個字符必須是大寫的英文字母,其他部分能夠下劃線或大寫字母 * * @param e */ private void checkAllCaps(VariableElement 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(Kind.WARNING, "常量:" + name + " 應該所有以大寫字母" + "或下劃線命名,而且以字符開否", e); } } } } }
測試類:後端
package com.ecut.javac; public class BADLY_NAME_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 NOTcameCASEmethodNAME() { return; } }
運行結果以下圖:數組
語義分析與字節碼生成:框架
二、Java語法糖的味道eclipse
泛型與類型檫除:
泛型擦除前:
package com.ecut.javac; import java.util.HashMap; import java.util.Map; public class GenericTest { public static void main(String[] args) { Map< String , String > map = new HashMap<>(); map.put("How are you ?","吃了嗎?"); map.put("Hi","您好!"); System.out.println(map.get("Hi")); } }
編譯後,泛型擦除後:
package com.ecut.javac; import java.io.PrintStream; import java.util.HashMap; import java.util.Map; public class GenericTest { public static void main(String[] args) { Map map = new HashMap(); map.put("How are you ?", "吃了嗎?"); map.put("Hi", "您好!"); System.out.println((String)map.get("Hi")); } }
泛型重載:
package com.ecut.javac; import java.util.List; public class GenericTypes { //報錯信息:「method(list<string>)」與「method(list<integer>)」衝突;兩種方法具備相同的擦除功能 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 "); } }
自動裝箱、拆箱與遍歷循環:
package com.ecut.javac; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class ForeachTest { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4); // JDK1.8 List<Integer> list2 = Stream.of(1, 2, 3, 4).collect(Collectors.toList()); // JDK1.9 //List<Integer> list3 = Lists.newArrayList(1, 2, 3, 4); int sum = 0; for (int i : list) { sum += i; } System.out.println(sum); } }
編譯後:
package com.ecut.javac; import java.io.PrintStream; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class ForeachTest { public static void main(String[] args) { List list = Arrays.asList(new Integer[] { Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3), Integer.valueOf(4) }); List list2 = (List)Stream.of(new Integer[] { Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3), Integer.valueOf(4) }).collect(Collectors.toList()); int sum = 0; for (Iterator localIterator = list.iterator(); localIterator.hasNext(); ) { int i = ((Integer)localIterator.next()).intValue(); sum += i; } System.out.println(sum); } }
泛型在編譯過程當中會進行擦除,將泛型參數去除;自動裝箱、拆箱在變以後被轉化成了對應的包裝盒還原方法,如Integer.valueOf()與Integer.intValue()方法;而遍歷循環則被還原成了迭代器的實現,這也是爲何遍歷器循環須要被遍歷的類實現Iterator接口的緣由。變長參數(asList()),它在調用的時候變成了一個數組類型的參數,在變長參數出來以前,程序員使用數組來完成相似功能。
package com.ecut.javac; /** * 基本數據類型和引用類型的區別主要在於基本數據類型是分配在棧上的,而引用類型是分配在堆上的 * 不管是基本數據類型仍是引用類型,他們都會先在棧中分配一塊內存,對於基本類型來講,這塊區域包含的是基本類型的內容; * 而對於引用類型來講,這塊區域包含的是指向真正內容的指針,真正的內容被手動的分配在堆上。 */ public class AutoBox { public static void main(String[] args) { Integer a = 1; Integer b = 2; Integer c = 3; Integer d = 3; Integer e = 321; Integer f = 321; Long g = 3L; Integer h = new Integer(3); Integer i = new Integer(3); /* 包裝類遇到「==」號的狀況下,若是不遇到算數運算符(+、-、*、……)是不會自動拆箱的.因此這裏「==」比較的是對象(地址) */ //true 對於Integer 類型,整型的包裝類系統會自動在常量池中初始化-128至127的值,若是c和d都指向同一個對象,即同一個地址。 System.out.println("c==d:" + (c == d)); //false 可是對於超出範圍外的值就是要經過new來建立包裝類型,因此內存地址也不相等 System.out.println("e==f:" + (e == f)); //true 由於遇到運算符自動拆箱變爲數值比較,因此相等。 System.out.println("c==(a+b):" + (c == (a + b))); //true 包裝類都重寫了equals()方法,他們進行比較時是比的拆箱後數值。可是並不會進行類型轉換 System.out.println("c.equals(a+b)" + (c.equals(a + b))); //true ==遇到算數運算符會自動拆箱(long) 3==(int)3 System.out.println("g==(a+b)" + (g == (a + b))); //false equals首先看比較的類型是否是同一個類型,若是是,則比較值是否相等,不然直接返回false System.out.println("g.equals(a+b):" + g.equals(a + b)); //true equals首先看比較的類型是否是同一個類型,若是是,則比較值是否相等,不然直接返回false System.out.println("h.equals(i):" + h.equals(i)); //false 經過new來建立包裝類型,因此內存地址也不相等 System.out.println("h == i:" + (h == i)); } }
條件編譯:
package com.ecut.javac; public class IfTest { public static void main(String[] args) { if(true){ System.out.println("true"); }else{ System.out.println("false"); } } }
編譯後:
package com.ecut.javac; import java.io.PrintStream; public class IfTest { public static void main(String[] args) { System.out.println("true"); } }
源碼地址:
https://github.com/SaberZheng/jvm-test/tree/master/src/com/ecut/javac
轉載請於明顯處標明出處: