classpath*: 和classpath:有什么区别_我们可以从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的資料與面試題,如果你在技術上面想提升自己的話,可以關注我,私信發送領取資料或者在評論區留下自己的聯系方式,有時間記得幫我點下轉發讓跟多的人看到哦。
總結
以上是生活随笔為你收集整理的classpath*: 和classpath:有什么区别_我们可以从Java“HelloWorld”中学到什么?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 现金价值以1000元为例怎么算,现金价值
- 下一篇: java美元兑换,(Java实现) 美元