首先,咱們來看一看當咱們用命令執行javac和java的時候,系統作了些什麼工做,假如咱們如今有一個文件夾以下圖1所示,有兩個java文件。java
圖1:文件目錄示意圖c++
其中,Main.java的內容很是簡單:jvm
public class Main { public static void main(String[] args) { System.out.println("hello world"); } }
咱們先在cmd下把目錄切換到,上圖1所示的目錄G:\oj\workdir\kown\test下:函數
圖2:切換目錄測試
發現一個好玩的事情,在用戶(home)目錄下直接切換,是不行的,必須有改變盤符的命令才能切換,如圖2,能夠交換第一句和第二句的順序。固然更加快捷的方式是在圖1所示的文件夾下按F4,輸入cmd而後回車。編碼
咱們來編譯一下Main.java 文件輸入javac Main.javaspa
圖3:javac Maincode
CMD是怎樣執行javac Main.java的呢?首先是解析命令字符串,有空格分隔。把第一個參數做爲可執行程序,後面的參數做爲可執行程序的參數。就像咱們寫c,c++等程序的main函數int main(int argc, char *argv[]),argc是參數個數,argv[]是參數。Javac是用c語言編寫的,確定也有main函數,咱們的外部程序就能夠經過argv[]這個參數把外部參數傳遞給javac,一樣CMD也是一個可執行程序,他也有這樣的輸入參數。內存
因此整個過程,就是咱們在CMD下輸入javac Main.java 就是把javac Main.java做爲參數經過argc這樣的參數傳遞給CMD,CMD解析參數字符串,知道要執行javac程序,CMD首先會查找當前目錄下有沒有javac這樣的可執行程序,你能夠嘗試在當前文件夾下加入一個javac.com, javac.bat或者javac.exe這樣的可執行文件,從新運行javac Main.java試一試,就知道CMD首先查找的是當前目錄了。utf-8
若是找到則執行,若是沒有找到就開始查找系統環境變量path指定的目錄。這就是咱們要把相似於C:\Program Files\Java\jdk7\bin這樣的目錄加入到系統環境變量path中的緣由了。
如今咱們來執行java命令,先看一看咱們的目錄狀況,只是多了一個編譯Main.java產生的字節碼文件Main.class:
圖4:當前目錄
執行java Main
圖5:java Main
那麼執行java和javac有什麼相同和不一樣的地方呢?對於CMD過程都是同樣,只是執行javac和java這兩個程序自己不同,javac只是編譯java的源代碼成爲class的字節碼。Java就是執行字節碼。下面咱們來看一看java的執行過程。
首先,經過path查找到java.exe,這是Windows下的可執行文件,在C:\Program Files\Java\jdk7\bin下能夠找到。
java.exe會鏈接一個jvm.dll,dll文件是Windows的動態連接庫,裏面就是一些模塊,函數,只不過他能夠動態加載而已。而jvm.dll的做用有初始化一個JVM(java虛擬機),
而後、JVM初始化一個BootstrapLoader(啓動類加載器)實例,Bootstrap Loader自動加載ExtendedLoader(標準擴展類加載器),並將其父Loader設爲Bootstrap Loader。Bootstrap Loader自動加載AppClass Loader(系統類加載器),並將其父Loader設爲Extended Loader。
有了類加載器,就要加載類了吧。首先是Bootstrap Loader加載,Bootstrap Loader加載類的路徑寫在有JRE環境變量中,JVM運行也被加在了系統環境變量中,能夠經過System.getProperty("sun.boot.class.path")這樣的方式獲得。同理Extended Loader加載類的目錄能夠經過System.getProperty("java.ext.dirs")獲得。AppClassLoader加載類的目錄能夠經過System.getProperty("java.class.path")獲得。
看到AppClassLoader的java.class.path是否是有一點眼熟,這個就是java系統環境變量中的classpath。
運行一個class時,老是由AppClass Loader(系統類加載器)開始加載指定的類。可是每一個類加載器會先將加載任務委託給其父加載器,若是其父找不到,再由本身去加載。
因此咱們運行java Main的時候AppClassLoader會在BootstrapLoader指定的加載路徑查找Main.class,而後在ExtendedLoader指定的路徑查找Main.class,最後在AppClassLoader指定的classpath目錄下查找。
值得注意的是當咱們在當前目錄下運行java Main可以加載到Main.class有兩種可能的緣由:
一是:AppClassLoader搜索了當前路徑。
二是:JVM動態的把當前路徑加入到了classpath中。
經過把當前文件夾下的OpenMain.class文件添加到classpath中,只是讓他們的輸出不一樣,測試後發現先執行的是classpath下的同名文件,咱們知道應該是JVM動態的把當前路徑添加到了classpath最後。
咱們注意到Main.java是沒有包名的,那麼有包名的怎麼辦內?咱們來看一看OpenMain.java
OpenMain.java的類容以下:
package kown.test; public class OpenMain { public static void main(String[] args) { System.out.println("hi world"); } }
編譯OpenMain.java:
圖6:javac -encoding
咱們發現是編碼不可映射錯誤,這是編碼的問題,javac默認使用的系統環境變量默認的字符集(中文通常是GBK),由於上面的OpenMain.java文件是用utf-8編碼。因此沒有問題咱們能夠用:Javac –encoding utf-8 OpenMain.java來編譯,也能夠先把OpenMain.java另存爲ANSI格式,再來編譯,上面是採用改變了文件OpenMain.java的編碼來處理。
運行:java OpenMain
圖7:wrong name
發現wrong name:/kown/test/OpenMain,這是怎麼回事呢?咱們先來看一看上面圖7和下面圖8的區別。
圖8:沒法加載主類
圖8這樣的錯誤的緣由是BootstrapLoader,ExtendedLoader和AppClassLoader加載的目錄下根本就找不到OpenMain.class這個文件。而圖7的錯誤緣由是找到了OpenMain.class文件,可是沒有找到OpenMain這個類。咱們來分析一下圖7中的錯誤,Exception in Thread main java.lang.NoClassDefFoundError:OpenMain······經過這個咱們知道JVM已經啓動了,而且已經加載了OpenMain.class字節碼。可是給了咱們一個wrong name :kown/test/OpenMain
咱們知道kown/test是咱們的包名,而且加了包名才產生了錯誤。
從新思考一下,java中爲何要引入包的概念,其實和命名空間同樣是爲了區分不一樣模塊同名的問題,在想想咱們爲何在不一樣的包中就可使用相同的類名呢?那是由於咱們在類的名字前面加上包名來做爲類的名稱,由於報名不一樣,因此咱們的類的名字實際上是不一樣的。這就是爲何在java中咱們常常要使用全限名例如咱們用Class.forName(java.lang.String)咱們要使用全限類名java.lang.String的緣由。
咱們在看下面圖9和圖10這兩張圖再繼續分析:
圖9:OpenMain.class所在的目錄下運行
圖10:包名的父目錄下運行
狀況一:圖9:OpenMain.class所在的目錄G:\oj\workdir\kown\test下分別運行了java OpenMain和java kown/test/OpenMain,一個是帶上包名的,一個是沒有帶包名的。當咱們不加包名的時候就告訴咱們類名錯誤,當我加上包名的時候就告訴咱們找不到類。
狀況二:圖10:包名的父目錄G:\oj\workdir下分別運行了java OpenMain和java kown/test/OpenMain,也是一個是帶上包名的,一個是沒有帶包名的,當咱們不加包名的時候告訴咱們找不到類,加了包名終於運行成功了。
爲何這麼神奇,讓咱們來分析總結一下:
結合狀況一和狀況二,咱們能夠大膽的猜想java首先是經過他的參數來找class文件的,在狀況一運行java OpenMain,由於當前目錄有OpenMain.class文件,因此加載class文件階段沒有問題,而後加載類,java把參數OpenMain做爲類名,用相似於Class.forName("OpenMain");這樣的方式來加載OpenMain類,因此獲得了java.lang.NoClassDefFoundError:OpenMain還有各個類加載器的Unknown Source錯誤,由於根本沒有OpenMain這個類,而是kown.test.OpenMain這個類。
對於狀況二,直接java OpenMain獲得的錯誤其實應該是找不到主類,由於G:\oj\workdir根本沒有這個類,之因此會出現像上面的狀況,是由於我爲了測試AppClassLoader搜索了當前路徑,仍是JVM動態的把當前路徑加入到了classpath中。把OpenMain打了jar包加入了C:\Program Files\Java\jre7\lib\ext文件夾下。必定是jar包,而不是class文件纔有效。
因此java會按參數指定的路徑下來搜索class文件,而後加載class文件到內存,而後把路徑加上文件名做爲全限類名來加載類。