这是每个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/

我们能从java的HelloWorld学到什么?的更多相关文章

  1. Java学习-005-初学常用的几个经典循环控制源代码

    最近一段时间公司 App 改版,一直处在需求评审.代码评审.测试计划.测试用例.用例评审.用例执行.缺陷管理.测试总结的循环中,因而博客也好久没有更新了.虽然工作确实忙了点,但是也是自己懒惰了,从今天 ...

  2. JAVA的helloworld

    java环境设置------------- 在环境变量中设置以下三个变量:JAVA_HOME=C:\j2sdk1.4.1 //可以改为相应的目录CLASSPATH=%JAVA_HOME%\lib\to ...

  3. Java的HelloWorld程序

    Java的HelloWorld程序 1.新建文本文档,编写HelloWorld程序,最后保存时记得保存成.java格式 2.在D盘新建一个HelloJava文件夹用于保存java程序 3.使用WIN+ ...

  4. JAVA_SE基础——5.第一个Java程序HelloWorld&注释的应用

    配置完JDK&环境变量后,我们就可以开始写程序了,那么程序怎么写呢,用什么工具呢,我建议 为了方便学习,我们最好在一个磁盘下建立一个专门的文件来写java程序,比如就在D盘下建立一个名为&qu ...

  5. Java web每天学之Servlet工作原理详情解析

    上篇文章中我们介绍了Servlet的实现方式以及Servlet的生命周期,我们这篇文章就来介绍一下常用对象. 点击回顾:<Java Web每天学之Servlet的工作原理解析>:<J ...

  6. Java Web每天学之Servlet的原理解析

    Java Web每天学之Servlet的工作原理解析,上海尚学堂Java技术文章Java Web系列之二上一篇文章Java Web每天学之Servlet的工作原理解析是之一,欢迎点击阅读. Servl ...

  7. [转] Java程序员学C#基本语法两个小时搞定(对比学习)

    Java程序员学C#基本语法两个小时搞定(对比学习)   对于学习一门新的语言,关键是学习新语言和以前掌握的语言的区别,但是也不要让以前语言的东西,固定了自己的思维模式,多看一下新的语言的编程思想. ...

  8. Java RMI HelloWorld

    Java RMI HelloWorld   RMI 远程方法调用. 顾名思义就是可以像调用本地程序方法一样调用远程(其他JVM)的程序方法.   分为3个部分: Stub:中介,代理. 封装了远程对象 ...

  9. 学java得这样学,学习确实也得这样

    引言 软件开发之路是充满荆棘与挑战之路,也是充满希望之路.Java学习也是如此,没有捷径可走.梦想像<天龙八部>中虚竹一样被无崖子醍醐灌顶而轻松获得一甲子功力,是很不现实的.每天仰天大叫& ...

随机推荐

  1. Django之ModelForm操作

    一.ModelForm的使用 顾名思义,ModelForm就是将Model与Form进行绑定,Form有自动生成表单的作用,但是每一个forms字段需要自己手动填写,而Model就是数据库表包含了所有 ...

  2. Python查看文件属性

    import os print(os.stat('my_module.py')) 输出: os.stat_result(st_mode=33188, st_ino=7348222, st_dev=16 ...

  3. APICloud框架——融云+UIChatTools实现即时通讯聊天

    今天完成了公司app的聊天界面的收发消息功能,结合融云2和UIChatTools模块实现,只是实现了基本功能,好多细节还没有实现,废话不多说,上代码 输入框页面(win) 先引入所需模块 // 融云模 ...

  4. renren-fast-vue-动态路由

    在renren-fast-vue项目中,左侧边栏的系统管理这一模块的路由采用的是动态路由的写法, 模块中的路由内容由后台动态生成,在前端开发阶段,采用的是mock模拟数据生成 先是在左侧边栏(view ...

  5. spark的任务调度模式

    spark任务调度和资源分配 1.Spark调度模式 FIFO和FAIR Spark中的调度模式主要有两种:FIFO和FAIR. 默认情况下Spark的调度模式是FIFO(先进先出),谁先提交谁先执行 ...

  6. python培训拾遗

    20140421 1. 三大利器: dir----列出所有内部方法 a=1 dir(a) 可以列出所有内部方法,就是带两个下划线的:带一个下划线的是普通方法 help---查看帮助 help(a.bi ...

  7. docker学习路线图

    https://yq.aliyun.com/articles/40494?spm=a2c4e.11153959.teamhomeleft.23.6ea918b1KErlfs

  8. ios移动输入框被软键盘遮挡

    页面输入框会出现被软键盘挡住的问题: 解决方法:获取input点击事件设置body高度 $('input').bind('click',function(e){ var $this = $(this) ...

  9. 转-C++之虚函数不能定义成内联函数的原因

    转自:https://blog.csdn.net/flydreamforever/article/details/61429140 在C++中,inline关键字和virtual关键字分别用来定义c+ ...

  10. 框架-.NET:Spring.Net

    ylbtech-框架-Spring.Net:Spring.Net Spring.NET为建立企业级应用提供了一套轻量级的解决方案.通过Spring.NET,我们可以用统一且透明的方式来配置应用程序.S ...