Way To JAVA-1.编译

编译

编译有三种:

  • 前端编译器("编译器的前端")
    作用:将.java文件转变成.class文件
    代表:Sun的Javac、Eclipse JDT中的增量式编译器(ECJ)

  • 虚拟机的后端运行期编译器(JIT编译器,Just In Time Compiler)
    作用:把字节码转变成机器码的过程
    代表:HotSpot VM的C1、C2编译器

  • 静态提前编译器(AOT编译器,Ahead Of Time Compiler)
    作用:直接把*.java文件编译成本地机器代码
    代表:GNU Compiler for Java(GCJ)、Excelsior JET

Javac编译器的源码获取

(Javac是由java语言编写的程序)
书本上推荐->看源码。
#但是我不推荐。
#等实力达到了一定的水平,一定要看。
#但是像我这样比较菜的人,看的话,真的是压力山大。

下载:
OpenJDK6源码: http://download.java.net/openjdk/jdk6/

Paste_Image.png

下载下来。

1、建立一个java工程(本人使用的是IDEA)
2、
解压下载的文件,将:
openjdk-6-src-b27-26_oct_2012\langtools\src\share\classes
中的com文件夹,整个复制到工程的源码中

Paste_Image.png

然后会玩的可以畅游源码了。

理论

看不来源码的,来聊聊理论。

* 过程
0.准备过程:初始化插入式注解处理器
1.词法分析和语法分析
2.输入到符号表
3.执行注解处理
4.分析及字节码生成
  标注
  数据流分析
  解语法糖
  生成字节码
* 过程
0.准备过程:初始化插入式注解处理器
1.词法分析和语法分析
  词法分析:将源代码的字符流转标为标记(Token)集合。
                  例如:
                  ”int a = b+2“ 代表6个标记{int,a,=,b,+,2}
  语法分析:根据Token序列来构建抽象语法树。

2.输入到符号表(并不懂...)
  符号表:一组由符号地址和符号信息构成的表格。
              在语义分析中,符号表所等级的内容将用于语义检查和产生中间代码。
              在目标代码生成阶段,当对符号名进行地址分配时,符号表是地址分配的依据。
3.执行注解处理
  提供了一组插入式注解处理器的标准API在编译器见对注解进行处理,
  我们可以将它看作是一组编译器插件,它们可以读取、修改、添加抽象语法书中的任意元素。
  如果这些插件在处理注解期间对语法树进行了修改,那么编译器将回到解析及填充符号表的过程重新处理,
直到所有的插入式注解处理器都没有再对语法书进行修改为止,每一个循环称为一个Round。
即下图的循环。就是从阶段1重新开始。

  
  ---->解析与填充符号表---->注解处理---->语法分析与字节码生成---->
        ^                          |
        |                          |
        ----------------------------
            

4.语义分析及字节码生成

      (语法分析之后,编译器获得了程序代码的抽象语法树表示,
语法树能表示一个结构正确的源程序抽象,但无法保证源程序是符合逻辑的。
而#语义分析的主要任务是对结构上正确的源程序进行上下文有关性质的审查,如类型检查。
)
  4.1 语义分析
    4.1.1标注检查
            检查诸如:变量使用前是否已被声明、变量与赋值之间数据类型是否匹配等等。
    4.1.2数据流分析
            数据以及控制流分析是对程序上下文逻辑的进一步验证。
            检查诸如:程序局部变量在使用前是否有赋值、方法的每条路径是否都有返回值、是否所有的受查异常都被正确处理了等问题。
            (
             #编译时期的数据及控制流分析与类加载时的数据及控制流分析的目的基本一致,
             #但校验范围有所区别,有一些校验项只有在编译期或运行期才能进行。
             /*例子:
             public void f(final int arg){
                 final int var=0;
             }
             public void f(int arg){
                 int var=0;
             }
             这两个函数生成的class文件不会有差别,
             因为,局部变量的不变性(加了个final),实在编译器保证的
             */)
             
  4.3解语法糖
             语法糖:指的是在计算机语言中添加的某种语法,这种语法对语言的功能没有影响,
                    但是更方便程序员使用。通常情况下,语法糖呢个增加可读性,减少出错。
             java常用的语法糖:范型,变长参数,自动装箱拆箱,等等
             解语法糖:虚拟机事实上并不支持这些语法,在编译阶段被还原回简单的基础语法结构
  4.4生成字节码
             字节码生成:不仅将前面各个步骤生成的信息转化为字节码写入磁盘,
                        编译器还进行了少量的代码添加和转换工作。
                        /*
                        实例构造器
                      (包括默认构造器,如果用户代码不提供任何构造函数,
                         编译器会默认添加一个没有参数,访问性与当前类一直的默认构造函数,
                         在填充符号表阶段完成) 
                     与 类型构造器。*/
                        在这个阶段会将实例构造器<init>方法以及类构造器<clinit>方法添加到语法树种。
                        以及其他依稀代码替换作用于优化程序的实现逻辑,如把字符串的加载操作替换为 
                        StringBuffer或是StringBuilder的apend操作。

4.4的补充   
#关于类型构造器和实例构造器
public class Solution {
    private int m;
    private static int n;
    public Solution()    {
        m=1;//相当于this.m=1;
        n=2;//猛的一看实例字段和静态字段的访问没有什么区别
    }
}

public class Solution {
    private static int n=1;
    static{
        n=2;        //这里的n的最终值是1还是2呢?顺序是先初始化为1,然后被类型构造函数赋值为2
    }
}


public class Solution {
    public static int m;
    public Solution(){
        System.out.println("实例构造器");    }
    static{
        System.out.println("类型构造器");    }

    public static  void main(String[] args)    {
        Solution.m=1;        //这个时候,Test类第一次被访问,所以会调用类型构造器,输出结果:"类型构造器"
        Solution test=new Solution();        //这个时候,Test类不是第一次访问,但是创建一个类的实例,所以输出结果:"实例构造器"
        //如果把上面的代码  Solution.m=1; 去掉的话,输出结果还是一样
        //如果去掉Solution test=new Solution();  那么就只有“类型构造器”了
    }
}

输出:/*
类型构造器
实例构造器*/

java语法糖

  • 语法糖-泛型
    Java的泛型旨在程序源码中存在,在比那以后的字节码文件中,就被替换为原来的原生类型,并在相应的地方插入强制转换代码吗,因此对于运行期的java语言来说,ArrayList<String> 与ArrayList<Integer> 就是同一个类。(关键字:类型擦除,伪泛型)

情况1:

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"));
}

先将上面的编译成Class文件,再反编译:

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"));
}

情况2:

public class A{
    public static void method(List<String> list){
         System.out.println("String");
    }
    public static void method(List<Integer> list){
        System.out.println("Integer");
    }
}
上面的代码是无法编译通过的。因为擦除导致两个方法的参数类型一致。

public class A{
    public static String method(List<String> list){
         System.out.println("String");
        return "";    
    }
    public static int method(List<Integer> list){
        System.out.println("Integer");
        return 1;
    }
}
书本上说可以编译执行,但是Idea中不行,可能我是java1.7吧?,不过书上说在Class文件格式中,
只要描述符不是完全一致的两个方法就可以共存。(更细节要看class文件中的格式再说)

所以编译和class文件中的检查操作合起来才是最终判断结果。
  • 自动装箱、拆箱与遍历循环
public static void  main(String[] args){
    List<Integer> list = Arrays.asList(1,2,3,4);
    int sum = 0;
    for (int i:list){
        sum=i;
    }
}

下面是编译以后的样子:

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;
    }
}

关于自动拆箱,与装箱
http://www.cnblogs.com/danne823/archive/2011/04/22/2025332.html

  • 条件编译
    C#中会有:#ifdef 来做条件编译
    而 java更直接:
public static void main(String[] args){
    if(true){
        System.out.println("1");
    }else{
        System.out.println("2");
    }
} 

编译后再反编译的结果

public static void main(String[] args){
        System.out.println("1");
} 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,637评论 18 139
  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,740评论 0 33
  • Java 语言支持的类型分为两类:基本类型和引用类型。整型(byte 1, short 2, int 4, lon...
    xiaogmail阅读 1,346评论 0 10
  • 徒步路线:通渭县城——义岗镇 徒步里程:40km 来通渭两天,都是住在通渭长征精神研究院甘肃分院办公室,两天都是...
    郭步鉴阅读 1,539评论 1 4
  • 本文选自《吸引力法则》大宝库 今天早上又有两位朋友不约而同的问我一样的问题。 其中一位问:“有一个也是朋友的已婚男...