下面是小编为大家整理的我们能从javaHelloWorld学到什么(完整文档),供大家参考。
我们能从 java 的 HelloWorld 学到什么?
•
#文章 •
java •
GB 原创及翻译小组 评论(0)阅读(511) 申请达人, 去除赞助商链接 这是每个 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.
9. public static void main(java.lang.String[]); 10.
Code: 11.
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 12.
3: ldc #3; //String Hello World
13.
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 14.
8: return 15. }
上面的代码中包含两个方法:
一个是默认构造函数, 这是由编译器推断出, 另一个是 main方法。
每个方法下面, 都有一系列指令, 比如 aload_0,invokespecial #1,等 下面的每个方法, 也有说明, 如 aload_0, invokespecial# 1, 等指令可以在 java 指令清单里查到。
例如, aload_0 指令是加载一个从栈中引用的本地变量 0, getstatic 指令获取一个类的静态字段值。
注意“#2” 指令在 getstatic 指令后指向运行常量池。
www.zglengcc.com常量池是一个 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. { 37. public HelloWorld(); 38.
Code: 39.
Stack=1, Locals=1, Args_size=1
40.
0: aload_0 41.
1: invokespecial #1; //Method java/lang/Object."<init>":()V 42.
4: return 43.
LineNumberTable:
44.
line 2: 0 45.
46.
47. public static void main(java.lang.String[]); 48.
Code: 49.
Stack=2, Locals=1, Args_size=1 50.
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 51.
3: ldc #3; //String Hello World 52.
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 53.
8: return 54.
LineNumberTable:
55.
line 9: 0 56.
line 10: 8 57. }
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()
方法完成后, 它会从堆栈中弹出执行完成。
推荐访问:我们能从javaHelloWorld学到什么 学到 完整 文档