• 我们能从java的HelloWorld学到什么? 分类: Java 2014-01-12 18:27 509人阅读 评论(0) 收藏


    这是每个Java程序员都知道的。虽然简单,但是从一个简单的问题可以引入更深的思考。在这篇文章中,我们将讨论这个简单的程序。如果能更多的帮到你,请留下宝贵的意见。

    HelloWorld.java

    1. public class HelloWorld {
    2. /**
    3. * @param args
    4. */
    5. public static void main(String[] args) {
    6. // TODO Auto-generated method stub
    7. System.out.println("Hello World");
    8. }
    9. }

     1、为什么一切都开始于一个类?

    Java程序是由类组成,一个类包含方法和属性。这是由于它的面向对象的特征:一切皆对象,每个对象都是一个类的实例。面向对象编程有很多优势,比如更好的模块化,扩展性强等

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

    “main”方法是程序的入口,它是静态的。 “static”是指该方法是类的一部分,而不是对象的一部分。

    这是为什么?我们为什么不把一个非静态方法作为程序的入口?

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

    参数“String[] args”表示一个字符串数组可以被传入到该程序,用来初始化程序。

    3、HelloWorld的字节码

    执行这个程序,Java文件首先编译为java字节码储存在.class文件里。

    字节码是什么样子的呢?

    首先,字节码本身是无法读取。如果我们用一个十六进制编辑器打开,它看起来像下面这样:

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

    “javap -C”打印出每个方法的反汇编代码。反汇编代码的意思是包括Java字节码的说明。

    1. javap -classpath . -c HelloWorld

     

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

     上面的代码中包含两个方法:一个是默认构造函数,这是由编译器推断出,另一个是main方法。

    每个方法下面,都有一系列指令,比如 aload_0,invokespecial #1,等

    下面的每个方法,也有说明,如aload_0,invokespecial#1,等指令可以在java指令清单里查到。例如,aload_0指令是加载一个从栈中引用的本地变量0,getstatic 指令获取一个类的静态字段值。注意“#2” 指令在getstatic指令后指向运行常量池。常量池是一个JVM运行时数据区,查看 。我们可以用“javap -verbose”命令来查看常量池。

    此外,每个指令开始于一个数字,如0,1,4等。在.class文件中,每个方法都有一个对应的字节码数组。这些数字对应的每一个操作码和它的参数都存储在数组中的索引中。每个操作码为1个字节,指令可以有0个或多个参数。这就是为什么数字是不连续的。

    现在,我们可以用“javap -verbose” 查看.class文件进一步研究。

    1. javap -classpath . -verbose HelloWorld

     

    1. Compiled from "HelloWorld.java"
    2. public class HelloWorld extends java.lang.Object
    3. SourceFile: "HelloWorld.java"
    4. minor version: 0
    5. major version: 50
    6. Constant pool:
    7. const #1 = Method #6.#15; // java/lang/Object."<init>":()V
    8. const #2 = Field #16.#17; // java/lang/System.out:Ljava/io/PrintStream;
    9. const #3 = String #18; // Hello World
    10. const #4 = Method #19.#20; // java/io/PrintStream.println:(Ljava/lang/String;)V
    11. const #5 = class #21; // HelloWorld
    12. const #6 = class #22; // java/lang/Object
    13. const #7 = Asciz <init>;
    14. const #8 = Asciz ()V;
    15. const #9 = Asciz Code;
    16. const #10 = Asciz LineNumberTable;
    17. const #11 = Asciz main;
    18. const #12 = Asciz ([Ljava/lang/String;)V;
    19. const #13 = Asciz SourceFile;
    20. const #14 = Asciz HelloWorld.java;
    21. const #15 = NameAndType #7:#8;// "<init>":()V
    22. const #16 = class #23; // java/lang/System
    23. const #17 = NameAndType #24:#25;// out:Ljava/io/PrintStream;
    24. const #18 = Asciz Hello World;
    25. const #19 = class #26; // java/io/PrintStream
    26. const #20 = NameAndType #27:#28;// println:(Ljava/lang/String;)V
    27. const #21 = Asciz HelloWorld;
    28. const #22 = Asciz java/lang/Object;
    29. const #23 = Asciz java/lang/System;
    30. const #24 = Asciz out;
    31. const #25 = Asciz Ljava/io/PrintStream;;
    32. const #26 = Asciz java/io/PrintStream;
    33. const #27 = Asciz println;
    34. const #28 = Asciz (Ljava/lang/String;)V;
    35. {
    36. public HelloWorld();
    37. Code:
    38. Stack=1, Locals=1, Args_size=1
    39. 0: aload_0
    40. 1: invokespecial #1; //Method java/lang/Object."<init>":()V
    41. 4: return
    42. LineNumberTable:
    43. line 2: 0
    44. public static void main(java.lang.String[]);
    45. Code:
    46. Stack=2, Locals=1, Args_size=1
    47. 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
    48. 3: ldc #3; //String Hello World
    49. 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
    50. 8: return
    51. LineNumberTable:
    52. line 9: 0
    53. line 10: 8
    54. }

     JVM定义:运行常量池提供一个类似于传统的编程语言的符号表函数,尽管它包含了比典型的符号表范围更广的数据

    “invokespecial #1″指令指向#1常量在常量池中.常量是”Method #6.#15;“从数字上看,我们就可以按递归方式来得到最终的常量。

    LineNumberTable提供用来调试java源代码对应字节码的行数信息例如,在main方法里Java源代码第9行对应字节码0,第10行对应字节码8。

    如果你想知道更多关于字节码,您可以创建和编译一个更复杂的类来看一看。HelloWorld确实是个很简单的例子。

    4、它是如何在JVM中执行?

    现在的问题是如何JVM加载类并调用main方法?

    在main方法执行之前,JVM需要分三步走加载、连接以及初始化该类。1)加载二进制的类和接口到jvm中。 2)连接合并二进制数据到正在运行状态的jvm。连接有三步构成,验证、准备、解析。验证确保了类/接口在结构上正确的;准备工作包括所需要的类/接口分配内存;解析符号引用。最后3)初始化变量并初始化值

     

    这个装载工作是由Java类加载器完成的。当JVM启动时,3个类加载器被使用:

    1.引导类加载器:加载位于/ jre / lib目录的核心Java库。这是jvm核心的一部分,并且是原生的代码。

    2.扩展类加载器:加载代码的扩展目录(例如,/jar/ lib / ext目录)。

    3.系统类加载器:在CLASSPATH中找到负载代码。

    所以HelloWorld类是由系统类加载器加载。当执行的主要方法,它会触发加载,链接和其他相关的类的初始化(查看) ,如果它们存在。

    最后,main()的帧被加载到jvm堆栈,程序计数器(PC)被相应地设置。程序计数器然后指示println()帧的加载到JVM堆栈。当main()方法完成后,它会从堆栈中弹出执行完成。

    参考文献:

    1、负载

    2、类加载机制

    3、类加载器

    via:http://www.programcreek.com/2013/04/what-can-you-learn-from-a-java-helloworld-program/

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    aop
    javascript学习笔记
    Hibernate一对多关联映射的配置及其级联删除问题
    Hibernate一对多单向关联和双向关联映射方法及其优缺点 (待续)
    剑指offer---08---动态规划:跳台阶
    剑指offer---07---动态规划:斐波那契数列
    剑指offer---06---数组,二分法---旋转数组的最小数字
    剑指offer---05---用栈实现队列
    剑指offer---03---从尾到头打印链表---链表
    剑指offer---04-树--重建二叉树(前序和中序)
  • 原文地址:https://www.cnblogs.com/pjdssswe/p/4696106.html
Copyright © 2020-2023  润新知