您当前的位置:中国亲子品牌网资讯正文

咱们我们能够从JavaHelloWorld中学到什么

放大字体  缩小字体 时间:2020-01-14 22:19:45  阅读:5716+ 来源:自媒体 作者:Java教学

原标题:我们大家可以从Java“HelloWorld”中学到什么?

这是每个Java程序员都知道的程序。它很简单,但是简单的开始可以导致对更复杂概念的深入理解。在这篇文章中,我将探讨从这个简单的程序中学到什么。

公共 HelloWorld {

/ **

* @参数参数

* /

public static void main (String [ ] args ) {

// TODO自动生成的方法存根

System。出来。println (“ Hello World” );

} }

1.为什么一切都从一堂课开始?

Java程序是从类构建的,每个方法和字段都必须在一个类中。这是由于它具有面向对象的功能:一切都是一个对象,它是一个类的实例。相对于功能性编程语言,面向对象的编程语言具有很多优势,例如更好的模块化,可扩展性等。

2.为什么总是有“主要”方法?

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

这是怎么回事?我们为啥不将非静态方法作为程序入口?

如果方法不是静态的,则需要先创建一个对象才能使用该方法。因为必须在对象上调用该方法。为了进入目的,这是不现实的。没有鸡肉,我们就无法获得鸡蛋。因此,程序进入方法是静态的。

参数“ String [] args”指示可以将字符串数组发送到程序以帮助程序初始化。

3. HelloWorld的字节码

为了执行该程序,首先将Java文件编译为存储在.class文件中的Java字节码。字节码是怎样的?字节码本身不可读。如果我们使用十六进制编辑器,则如下所示:

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

“ javap -c”打印出该类中每个方法的反汇编代码。反汇编代码表示组成Java字节码的指令。

javap -classpath。-c HelloWorld

从“ HelloWorld.java”编译而成的公共 HelloWorld 扩展了 Java。郎。对象{ public HelloWorld ();

代码:

0 : aload_0

1 : 调用特殊#1 ; //方法java / lang / Object。“ <init>” :()V

4 : 返回

公共 静态 无效主( java中。郎。字符串[ ] );

代码:

0 : 静态#2 ; //字段java / lang / System.out:Ljava / io / PrintStream;

3 : ldc#3 ; // String Hello World

5 : invokevirtual#4 ; //方法java / io / PrintStream.println:(Ljava / lang / String;)V

8 : return }

上面的代码包含两种方法:一种是默认的构造函数,由编译器推断出来;另一种是默认的构造函数。另一种是主要方法。

在每种方法之下,都有一系列指令,例如aload_0,invokespecial#1等。可以在Java字节码指令列表中查找每个指令的作用。例如,aload_0将局部变量0的引用加载到堆栈上,getstatic获取类的静态字段值。请注意,在getstatic指令指向运行时常量池之后,将显示“#2”。常量池是JVM运行时数据区域之一。这使我们看一下常量池,能够正常的使用“ javap -verbose”命令来完成。

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

现在,我们能够正常的使用“ javap -verbose”进一步看一看该类。

javap -classpath。详细的HelloWorld

从“ HelloWorld.java”编译而成的公共 HelloWorld 扩展了 Java。郎。对象

SourceFile : “ HelloWorld.java”

次要版本: 0

主要版本: 50

常量池:const#1 = Method #6。#15 ; // java / lang / Object。“ <init>” :()V const#2 = 字段 #16。#17 ; // java / lang / System.out:Ljava / io / PrintStream;const#3 = 字符串 #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 = ASCII码; const#10 = Asciz LineNumberTable ; const#11 = Asciz主函数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你好世界; const#19 = 类别 #26 ; // java / io / PrintStreamconst#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 = ASCII输出; const#25 = Asciz Ljava / io/ PrintStream ;; const#26 = Asciz java / io / PrintStream ; const#27 = Asciz println ; const#28 = Asciz ( Ljava / lang / String ; ) V ;

{ 公共 HelloWorld ();

代码:

堆栈= 1,Locals = 1,Args_size = 1

0 : aload_0

1 : invokespecial#1 ; //方法java / lang / Object。“ <init>” :()V

4 : 返回

LineNumberTable :

第2行: 0

公共 静态 无效主( java中。郎。字符串[ ] );

代码:

堆栈= 2,Locals = 1,Args_size = 1

0 : getstatic#2 ; //字段java / lang / System.out:Ljava / io / PrintStream;

3 : ldc#3 ; // String Hello World

5 : invokevirtual#4 ; //方法java / io / PrintStream.println:(Ljava / lang / String;)V

8 : 返回

LineNumberTable :

行9 : 0

行10 : 8 }

从JVM规范开始:运行时常量池的功能类似于常规编程语言的符号表,尽管它包含的数据范围比典型的符号表还大。

“ invokespecial#1”指令中的“#1”指向常量池中的#1常量。该常量为“方法#6.#15;”。从数字中,我们大家可以递归获得最终常数。

LineNumberTable向调试器提供信息,以指示Java源代码的哪一行对应于哪个字节代码指令。例如,Java源代码中的第9行对应于main方法中的字节代码0,而行10对应于字节代码8。

如果您想了解更多有关字节码的信息,可以创建并编译一个更复杂的类以进行查看。HelloWorld确实是这样做的起点。

4.如何在JVM中执行?

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

在执行main方法之前,JVM需要1)加载,2)链接和3)初始化类。1)加载将类/接口的二进制形式带入JVM。2)链接将二进制类型的数据合并到JVM的运行时状态中。链接包括3个步骤:验证,准备和可选的解决方案。验证可确保类/接口在结构上正确;准备工作涉及分配类/接口所需的内存;分辨率解析符号引用。最后3)初始化为类变量分配了适当的初始值。

此加载作业由Java类加载器完成。启动JVM时,将使用三个类加载器:

1. Bootstrap类加载器:加载位于以下位置的核心Java库: / jre / lib目录。它是核心JVM的一部分,并用本机代码编写。

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

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

因此,HelloWorld类由系统类加载器加载。当main方法执行时,它将触发其他依赖类的加载,链接和初始化(如果存在)。

最后,将main()框架压入JVM堆栈,并相应地设置程序计数器(PC)。PC然后指示将println()帧推送到JVM堆栈。当main()方法完成时,它将从堆栈中弹出并执行完毕。

最后,开发这么多年我也总结了一套学习Java的资料与面试题,如果你在技术上面想提升自己的话,可以关注我,加我的助教静静老师的扣扣:2725514576。有时间记得帮我点下转发让跟多的人看到哦。

责任编辑: