程序编译与代码优化-早期(编译期)优化

1:概述

  1. 前端编译器:Sun的Javac、Eclipse JDT中的增量式编译器(EJC)
  2. JIT编译器:Hotspot VM的C1,C2编译器
  3. AOT编译器:GNC Compiler for the Java(GCJ)、Excelsior JET

    本章先介绍第一种前端编译器,也是大家都认可的编译器。

2:Javac编译器

    源码编译自OpenJdk7,从Sun Javac源码来看,编译大致可分为3个过程:

  1. 解析与填充符号表过程
  2. 插入式注解处理器的注解处理过程。
  3. 分析与字节码生成过程。

3:Java语法糖的味道

3.1:泛型与类型擦除

    Java的泛型是典型的语法糖。

public static void main(String[] args) {
        Map map = new HashMap();
        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 list) {
        System.out.println("invoke method(List list)");
    }

    public static void method(List list) {
        System.out.println("invoke method(List list)");
    }
}

    再看下面这段代码,这段代码增加了一个返回值就可以编译成功,前提是试用JDK1.6的Javac编译器。导致这种情况的出现使因为虚拟机的方法区的实现有关,在Class文件格式中,只要描述符不完全一致,就允许存在。

import java.util.ArrayList;
import java.util.List;

public class GenericTypes {

    public static String method(List list) {
        System.out.println("invoke method(List list)");
        return "";
    }

    public static int method(List list) {
        System.out.println("invoke method(List list)");
        return 1;
    }

    public static void main(String[] args) {
        method(new ArrayList());
        method(new ArrayList());
    }
}

    Java虚拟机引入了诸如Signature、Local VariableTypeTable等新的属性用于解决伴随泛型而来的的参数类型识别的问题。

3.2:自动装箱、拆箱与遍历循环

    下面的代码包含了泛型、自动装箱、自动拆箱、遍历循环与变长参数5种语法糖。

 public static void main(String[] args) {
        List list = Arrays.asList(1, 2, 3, 4);
        // 如果在JDK 1.7中,还有另外一颗语法糖 ,
        // 能让上面这句代码进一步简写成List 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除了上面说的语法糖外,还有内部类、枚举类、断言语句、对枚举和字符串的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 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;

/**
 * 程序名称规范的编译器插件:
* 如果程序命名不合规范,将会输出一个编译器的WARNING信息 */
public class NameChecker { private final Messager messager; NameCheckScanner nameCheckScanner = new NameCheckScanner(); NameChecker(ProcessingEnvironment processsingEnv) { this.messager = processsingEnv.getMessager(); } /** * 对Java程序命名进行检查,根据《Java语言规范》第三版第6.8节的要求,Java程序命名应当符合下列格式: * *
    *
  • 类或接口:符合驼式命名法,首字母大写。 *
  • 方法:符合驼式命名法,首字母小写。 *
  • 字段: *
      *
    • 类、实例变量: 符合驼式命名法,首字母小写。 *
    • 常量: 要求全部大写。 *
    *
*/
public void checkNames(Element element) { nameCheckScanner.scan(element); } /** * 名称检查器实现类,继承了JDK 1.6中新提供的ElementScanner6
* 将会以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等。

你可能感兴趣的:(JVM学习笔记)