第十章 早期(编译期)优化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
转载请于明显处标明出处: