當編寫一個java源代碼文件時,此文件通常被稱為編譯單元(有時也被稱為轉譯單元)。每個編譯單元都必須有一個后綴名.java,而在編譯單元內則可以有一個public類,該類的名稱必須與文件的名稱相同(包括大小寫,但不包括文件的后綴名.java)。每個編譯單元只能有一個public類,否則編譯器就不會接受(即只能有0-1個public類)。如果在該編譯單元之中還有額外的類的話(即其他非public類),那么在包之外的世界是無法看見這些類的,這是因為它們不是public類,而且它們主要用來為主public類提供支持。
首先說一下為什么public的類名要與.java文件名一致的問題~
B.java文件
1 package com.culiu.ccj.servant.tagstatistics.test; 2 3 /** 4 * 描述: 5 * 創建人: BruceCloud 6 * 創建時間: 2016/5/18 10:10. 7 */ 8 public class B { 9 } 10 11 class C{ 12 }
Test.java文件
1 package com.culiu.ccj.servant.tagstatistics.test.aa; 2 3 import com.culiu.ccj.servant.tagstatistics.test.B; 4 5 /** 6 * 描述: 7 * 創建人: BruceCloud 8 * 創建時間: 2016/5/18 10:10. 9 */ 10 public class Test { 11 public static void main(String[] args) { 12 try { 13 B a = new B(); 14 // C c = new C(); 15 Class c = Class.forName("com.culiu.ccj.servant.tagstatistics.test.C"); 16 System.out.println(c.getName()); 17 } catch (ClassNotFoundException e) { 18 e.printStackTrace(); 19 } 20 } 21 }
上面代碼B.java中, 有2個類(public B和非public的 C), 在B.java這個編譯單元中, public B是該編譯單元對外的接口類, C則是包內使用的類, 根據java的編程規則, 在Test.java中第14行引入C時會編譯出錯, 提示你找不到C這個類, 因為C沒有被public修飾,
所以C只能在com.culiu.ccj.servant.tagstatistics.test包下面使用, 而第13行B卻可以使用, 因為在使用B的時候在上面使用了import com.culiu.ccj.servant.tagstatistics.test.B;對B進行了導入(可以導入的前提是com.culiu.ccj.servant.tagstatistics.test.B
這個編譯單元提供了對外公開的接口, 即public修飾的類), 而你使用import com.culiu.ccj.servant.tagstatistics.test.C;的時候會報錯, 提示你找不到C, 因為不存在com.culiu.ccj.servant.tagstatistics.test.C這個編譯單元(即使存在也不存在public入口)~~
接下來在程序運行到Test.java第13行時, jvm會去加載B這個類, 而加載的方式就是通過import后面的編譯單元名稱進行加載的(即通過com.culiu.ccj.servant.tagstatistics.test.B進行加載的, 加載的內容就是該編譯單元的入口類, 就是public修飾的類), 所以
public修飾的的類名必須要與.java文件名一致, 就是為了方便在加載編譯單元入口類的時候不用在進行名字轉換了, 這樣省了很多麻煩, 直接去加載com.culiu.ccj.servant.tagstatistics.test.B.class就行了, 而無需在進行名字轉換~~~
試想一下, 如果B.java中的public類的名字是C, 那么在加載編譯單元B的時候還需要去查找編譯單元B的入口類名字, 假想步驟如下:
1.取得編譯單元B的位置import com.culiu.ccj.servant.tagstatistics.test.B;
2.再在1的位置中找到要加載的入口類(此時因為public的類名跟B.java的文件名不一致, 所以無法直接通過1中的com.culiu.ccj.servant.tagstatistics.test.B.class來加載了, 需要進行
入口類的名稱查找或者轉換, 最終加載的也就是com.culiu.ccj.servant.tagstatistics.test.C.class)
這樣會很麻煩~~莫不如直接規定public的類與.java文件名相同~~~
PS: 上面Test.java中有一點需要注意, 雖然B.java中的C類無法在其他包下使用, 但是卻可以在其他包下通過反射來進行加載~~如第15行~~
到此, 為什么public類名必須和.java文件名相同的問題講完了~~
接下來講為什么.java文件中只能有一個public類~~~
因為在文件系統中, 一個文件只能有一個名字, 這里的情況就是一個編譯單元(即.java文件)只能有一個名, 而這個名字的作用恰好還是jvm用來加載該編譯單元入口類的, 而上面講了jvm在
加載編譯單元入口類的時候是通過編譯單元名字來進行加載的, , 所以如果你的編譯單元中有多個public類(即有多個入口), 那么jvm就無法分辨到底要去加載哪個入口類了~~
到此, 為什么.java文件中只能有一個public類的問題講完了~~
寫在最后:
以上內容都是本人自己的理解, 不敢保證java設計者們就是這么想的, 我寫出的目的也方便日后自己忘了的時候還能翻出來此文再來回想一下曾經的想法~~
歡迎各路Java大神來討論一下並留下自己的理解~~一切的一切都是以最終能搞明白問題的本質為目的~~所以還請口下留情~~