深入理解Java HelloWorld

一段代码,只写了hello world,底层发生了什么?控制台输出的时候,底层怎么做的?

总结

main方法是程序的入口,并且是静态方法;
Java文件首先被编译成Java字节码存储到.class文件中;
在main方法执行之前, JVM需要加载、链接以及初始化这个类。
后续在整理。。。

转载:深入理解Java HelloWorld

详细说明

HelloWorld是每个Java程序员都知道的程序。它很简单,但是简单的开始可以引导你去深入了解更复杂的东西。这篇文章将探究从这个HelloWorld这个简单程序中可以学到的东西。如果你对HelloWorld有独到的理解,欢迎留下你的评论。

HelloWorld.java

public class HelloWorld {
 
    /**
     * 
     * @param args
     */
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("Hello World");
    }

1、为什么一切都是从类开始?

Java程序是从类开始构建的, 每个方法和字段都必须在类里面。这是由于Java面向对象的特性: 一切都是对象,它是类的一个实例。面向对象编程语言相比函数式编程语言有许多的优势,比如更好的模块化、可扩展性等等。

2、为什么总有一个“main方法”?

main方法是程序的入口,并且是静态方法。static关键字意味着这个方法是类的一部分,而不是实例对象的一部分。为什么会这样呢? 为什么我们不用一个非静态的方法作为程序的入口呢?

如果一个方法不是静态的,那么对象需要先被创建好以后才能使用这个方法。因为这个方法必须要在一个对象上调用。对于一个入口来说,这是不现实的。因此,程序的入口方法是静态的。

参数 “String[] args”表明可以将一个字符串数组传递给程序来帮助程序初始化。

3、HelloWorld程序的字节码

为了执行这个程序,Java文件首先被编译成Java字节码存储到.class文件中。那么字节码看起来是什么样的呢?字节码本身是不可读的,如果我们使用一个二进制编辑器打开,它看起来就像下面那样:

java-bytecode-example

在上面的字节码中,我们可以看到很多的操作码(比如CA、4C等等),它们中的每一个都有一个对应的助记码(比如下面例子中的aload_0)。操作码是不可读的,但是可以使用javap来查看.class文件的助记形式。

对于类中的每个方法执行“javap -c”可以输出反汇编代码。反汇编代码即组成Java字节码的指令。

javap -classpath .  -c HelloWorld

Compiled from "HelloWorld.java"
public class HelloWorld extends java.lang.Object{
public HelloWorld();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return
  
public static void main(java.lang.String[]);
  Code:
   0:   getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc #3; //String Hello World
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return
}

上面的代码包含两个方法: 一个是编译器推断出来的默认的构造器;另外一个是main方法。

接下来,每个方法都有一系列的指令。比如aload_0、invokespecial #1等等。可以在Java字节码指令集中查到每个指令的功能,例如aload_0用来从局部变量0中加载一个引用到堆栈,getstatic用来获取类的一个静态字段值。可以注意到,getstatic指令之后的“#2″指向的是运行期常量池。常量池是JVM运行时数据区之一。我们可以通过“javap -verbose”命令来查看常量池。

另外, 每个指令从一个数字开始,比如0、1、4等等。在.class文件中,每个方法都有一个对应的字节码数组。这些数字对应于存储每个操作码及其参数的数组的下标。每个操作码都是1个字节长度,并且指令可以有0个或多个参数。这就是为什么这些数字不是连续的原因。

现在,我们使用“javap -verbose”这个命令来进一步观察这个类。

javap -classpath . -verbose HelloWorld

Compiled from "HelloWorld.java"
public class HelloWorld extends java.lang.Object
  SourceFile: "HelloWorld.java"
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method   #6.#15; //  java/lang/Object."<init>":()V
const #2 = Field    #16.#17;    //  java/lang/System.out:Ljava/io/PrintStream;
const #3 = String   #18;    //  Hello World
const #4 = Method   #19.#20;    //  java/io/PrintStream.println:(Ljava/lang/String;)V
const #5 = class    #21;    //  HelloWorld
const #6 = class    #22;    //  java/lang/Object
const #7 = Asciz    <init>;
const #8 = Asciz    ()V;
const #9 = Asciz    Code;
const #10 = Asciz   LineNumberTable;
const #11 = Asciz   main;
const #12 = Asciz   ([Ljava/lang/String;)V;
const #13 = Asciz   SourceFile;
const #14 = Asciz   HelloWorld.java;
const #15 = NameAndType #7:#8;//  "<init>":()V
const #16 = class   #23;    //  java/lang/System
const #17 = NameAndType #24:#25;//  out:Ljava/io/PrintStream;
const #18 = Asciz   Hello World;
const #19 = class   #26;    //  java/io/PrintStream
const #20 = NameAndType #27:#28;//  println:(Ljava/lang/String;)V
const #21 = Asciz   HelloWorld;
const #22 = Asciz   java/lang/Object;
const #23 = Asciz   java/lang/System;
const #24 = Asciz   out;
const #25 = Asciz   Ljava/io/PrintStream;;
const #26 = Asciz   java/io/PrintStream;
const #27 = Asciz   println;
const #28 = Asciz   (Ljava/lang/String;)V;
  
{
public HelloWorld();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return
  LineNumberTable: 
   line 2: 0
  
  
public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=1, Args_size=1
   0:   getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc #3; //String Hello World
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return
  LineNumberTable: 
   line 9: 0
   line 10: 8
}

引用JVM规范中的描述:运行期常量池提供的功能类似传统编程语言的符号表所起作用, 尽管它比传统的符号表包含的内容更广范。

“invokespecial #1″指令中的“#1″指向常量池中的#1常量。这个常量是 “Method #6.#15;”。通过这个数字,我们可以递归地得到最终的常量。

LineNumberTable为调试器提供了用来指示Java源代码与字节码指令之间的对应信息。例如,Java源代码中的第9行对应于main方法中的字节码0,并且第10行对应于字节码8。

如果想要了解更多关于字节码的内容,可以创建一个更加复杂的类进行编译和查看,HelloWorld真的只是一个开始。

4、HelloWorld在JVM中是如何执行的?

现在的问题是JVM是怎样加载这个类并调用main方法?

在main方法执行之前, JVM需要加载链接以及初始化这个类。
1.加载将类/接口的二进制形式装入JVM中。

2.链接将二进制类型的数据融入到JVM的运行时。链接由3个步骤组成:验证、准备、以及解析(可选)。验证确保类、接口在结构上是正确的;准备涉及到为类、接口分配所需要的内存;解析是解析符号引用。

3.最后,初始化为类变量分配正确的初始值。

[站外图片上传中...(image-e94670-1525403472093)]

加载工作是由Java类加载器来完成的。当JVM启动时,会使用下面三个类加载器:

  1. Bootstrap类加载器:加载位于/jre/lib目录下的核心Java类库。它是JVM核心的一部分,并且使用本地代码编写。
  2. 扩展类加载器:加载扩展目录中的代码(比如/jar/lib/ext)。
  3. 系统类加载器:加载在CLASSPATH上的代码。

所以,HelloWorld类是由系统加载器加载的。当main方法执行时,它会触发加载其它依赖的类,进行链接和初始化。前提是它们已经存在。

最后,main()帧被压入JVM堆栈,并且程序计数器(PC)也进行了相应的设置。然后,PC指示将println()帧压入JVM堆栈栈顶。当main()方法执行完毕会被弹出堆栈,至此执行过程就结束了。

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