漫話:為什么Java中的main方法必須是public static void的?








在Java中,想必所有人都不會(huì)對(duì)main方法感到陌生,main方法是Java應(yīng)用程序的入口方法。程序運(yùn)行時(shí),要執(zhí)行的第一個(gè)方法就是main方法。
在使用Java寫(xiě)下第一個(gè)hello world的時(shí)候,我們需要?jiǎng)?chuàng)建一個(gè)main方法,當(dāng)我們使用Spring Boot啟動(dòng)一個(gè)web應(yīng)用的時(shí)候,我們也同樣需要一個(gè)main方法。
當(dāng)我們?cè)贗ntellij IDEA中想要?jiǎng)?chuàng)建main 方法的時(shí)候,只需要輸入psvm就會(huì)自動(dòng)幫忙創(chuàng)建一個(gè)main方法:

我們得到一個(gè)main方法后,不知道你有沒(méi)有發(fā)現(xiàn),任何時(shí)候,我們要?jiǎng)?chuàng)建的main方法的形式都是一樣的:
public?static?void?main(String[]?args)?{
}
首先都是public的、都是static的,返回值都是void,方法名都是main,入?yún)⒍际且粋€(gè)字符串?dāng)?shù)組。
以上的方法聲明中,唯一可以改變的的部分就是方法的參數(shù)名,你可以把a(bǔ)rgs改成任意你想要使用的名字。
當(dāng)然,main方法還可以寫(xiě)成以下形式,不過(guò)其實(shí)沒(méi)啥區(qū)別:
public?static?void?main(String... args)?{
}
那么,不知道大家有沒(méi)有想過(guò),為什么main方法必須得是public static void類型的,他的參數(shù)又必須得是一個(gè)字符串?dāng)?shù)組呢?




在《Java語(yǔ)言規(guī)范》中,對(duì)于Java虛擬機(jī)的啟動(dòng)給出了明確的定義:Java虛擬機(jī)是通過(guò)加載指定的類,然后調(diào)用該類中的main方法而啟動(dòng)的。
也就是說(shuō),通過(guò)調(diào)用某個(gè)指定類的main方法,傳遞給他單個(gè)的字符串?dāng)?shù)組參數(shù),就可以啟動(dòng)Java虛擬機(jī)。
一個(gè)main方法想要被執(zhí)行,需要經(jīng)過(guò)幾個(gè)步驟,首先對(duì)應(yīng)的類需要被虛擬機(jī)加載,然后需要進(jìn)行鏈接和初始化、之后才是調(diào)用main方法。
那么一個(gè)方法想要被調(diào)用,根據(jù)他的訪問(wèn)限定符以及方法類型不同,被調(diào)用的條件也是不同的。


Java中,可以使用訪問(wèn)控制符來(lái)保護(hù)對(duì)類、變量、方法和構(gòu)造方法的訪問(wèn)。Java 支持 4 種不同的訪問(wèn)權(quán)限。
default (即默認(rèn),什么也不寫(xiě)): 在同一包內(nèi)可見(jiàn),不使用任何修飾符。使用對(duì)象:類、接口、變量、方法。
private : 在同一類內(nèi)可見(jiàn)。使用對(duì)象:變量、方法。注意:不能修飾類(外部類)
public : 對(duì)所有類可見(jiàn)。使用對(duì)象:類、接口、變量、方法
protected : 對(duì)同一包內(nèi)的類和所有子類可見(jiàn)。使用對(duì)象:變量、方法。注意:不能修飾類(外部類)。
以上四種控制符都可以用來(lái)修飾方法,但是被修飾的方法的訪問(wèn)權(quán)限就不同了。
而對(duì)于main方法來(lái)說(shuō),我們需要通過(guò)JVM直接調(diào)用他,那么就需要他的限定符必須是public的,否則是無(wú)法訪問(wèn)的。




static是靜態(tài)修飾符,被他修飾的方法我們稱之為靜態(tài)方法,靜態(tài)方法有一個(gè)特點(diǎn),那就是靜態(tài)方法獨(dú)立于該類的任何對(duì)象,它不依賴類特定的實(shí)例,被類的所有實(shí)例共享。只要這個(gè)類被加載,Java虛擬機(jī)就能根據(jù)類名在運(yùn)行時(shí)數(shù)據(jù)區(qū)的方法區(qū)內(nèi)定找到他們。
而對(duì)于main方法來(lái)說(shuō),他的調(diào)用過(guò)程是經(jīng)歷了類加載、鏈接和初始化的。但是并沒(méi)有被實(shí)例化過(guò),這時(shí)候如果想要調(diào)用一個(gè)類中的方法。那么這個(gè)方法必須是靜態(tài)方法,否則是無(wú)法調(diào)用的。


如果大家對(duì)于C語(yǔ)言和C++語(yǔ)言有一定的了解的話,就會(huì)知道,像 C、C++ 這種以 int 為 main 函數(shù)返回值的編程語(yǔ)言。
這個(gè)返回值在是程序退出時(shí)的 exit code,一般被命令解釋器或其他外部程序調(diào)用已確定流程是否完成。一本正常情況下用 0 返回,非 0 為異常退出。
而在Java中,這個(gè)退出過(guò)程是由JVM進(jìn)行控制的,在發(fā)生以下兩種情況時(shí),程序會(huì)終止其所有行為并退出:
1、所有不是后臺(tái)守護(hù)線程的線程全部終止。
2、某個(gè)線程調(diào)用了Runtime類或者System類的exit方法,并且安全管理器并不禁止exit操作。
上面的兩種情況中,第二種情況一旦發(fā)生,JVM是不會(huì)管main方法有沒(méi)有執(zhí)行完的,他都會(huì)終止所有行為并退出,這時(shí)候main方法的返回值是沒(méi)有任何意義的。
所以,main方法的返回值就被固定要求為void。





Java應(yīng)用程序是可以通過(guò)命令行接受參數(shù)傳入的,從命令行傳遞的參數(shù)可以在java程序中接收,并且可以用作輸入。
因?yàn)槊钚袇?shù)最終都是以字符串的形式傳遞的,并且有的時(shí)候命令行參數(shù)不止一個(gè),所以就可能傳遞多個(gè)參數(shù)。
這時(shí)候,作為Java應(yīng)用程序執(zhí)行的入口,main方法就需要能夠接受這多個(gè)字符串參數(shù),那么就使用字符串?dāng)?shù)組了。
main方法是JVM執(zhí)行的入口,為了方便JVM調(diào)用,所以需要將他的訪問(wèn)權(quán)限設(shè)置為public,并且靜態(tài)方法可以方便JVM直接調(diào)用,無(wú)需實(shí)例化對(duì)象。
因?yàn)镴VM的退出其實(shí)是不完全依賴main方法的,所以JVM并不會(huì)接收main方法的返回值,所以給main方法定義一個(gè)返回值沒(méi)有任何意義。所以main方法的返回值為void。
為了方便main函數(shù)可以接受多個(gè)字符串參數(shù)作為入?yún)ⅲ运男螀㈩愋捅欢x為String[]。




關(guān)于作者:漫話編程,是一個(gè)通過(guò)漫畫(huà)+音頻的形式講解枯燥的編程知識(shí)的公眾號(hào)。致力于讓編程變得更有樂(lè)趣。
