2010-08-11 47 views
6

我製作了一個新的ClassLoader並定義了一個新的Class,這意味着新的類應該位於新的名稱空間中,它是AFAIK。奇怪的是,當我在新類上調用Class.getPackage時,它會返回與我的主名稱空間中任何其他類上調用getPackage所返回的完全相同的對象。爲什麼Class.getPackage爲不同包中的類返回相同的包?

按照JVM spec

一個類的運行時包或 接口是由包 名稱和 類或接口的定義類裝載程序來確定。

換句話說,如果你在同一個包中有兩個類,但是由不同的類加載器加載,它們被認爲是在不同的包中。 (這也可以通過我在下面的測試案例中的反思來「證實」。)

那麼怎樣才能做到這一點,我從兩個類的getPackage得到了相同的結果?

這裏是我的測試:

package pkg; 
import java.io.*; 

// Yes, you can try commenting this class, you'll get the same result. 
class LoadedClass { 
    LoadedClass() { 
     System.out.println("LoadedClass init"); 
    } 
} 

class MyClassLoader extends ClassLoader { 
    Class<?> defineClass(String name, byte[] b) { 
     return defineClass(name, b, 0, b.length); 
    } 
} 

class Main { 
    public static void main(String[] args) throws Exception { 
     MyClassLoader mcl = new MyClassLoader(); 

     // load compiled class from file 
     FileInputStream fileinputstream = new FileInputStream(
      "/home/me/test/pkg/LoadedClass.class" /* <- point to whever these classes 
                * are being compiled to. */ 
     ); 
     int numberBytes = fileinputstream.available(); 
     byte classBytes[] = new byte[numberBytes]; 
     fileinputstream.read(classBytes); 
     fileinputstream.close(); 

     Class<?> lc = mcl.defineClass("pkg.LoadedClass", classBytes); 
     Package myPackage = Main.class.getPackage(); 
     Package lcPackage = lc.getPackage(); 
     System.out.println("lc package: " + lcPackage); 
     System.out.println("my package: " + myPackage); 
     System.out.println("lc ClassLoader: " + lc.getClassLoader()); 
     System.out.println("lc ClassLoader parent: " + 
          lc.getClassLoader().getParent()); 
     System.out.println("my ClassLoader: " + Main.class.getClassLoader()); 
     System.out.println("are they equal? " + (lcPackage == myPackage)); 
     if (lcPackage == myPackage) { 
      System.out.println("okay... we should be able to instantiate " + 
           "the package if that's true, lets try"); 
      lc.newInstance(); // boom as expected 
     } 
    } 
} 

它輸出:

lc package: package pkg 
my package: package pkg 
lc ClassLoader: [email protected] 
lc ClassLoader parent: [email protected] 
my ClassLoader: [email protected] 
are they equal? true 
okay... we should be able to instantiate the package if that's true, lets try 
Exception in thread "main" java.lang.IllegalAccessException: Class pkg.Main can not access a member of class pkg.LoadedClass with modifiers "" 
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65) 
    at java.lang.Class.newInstance0(Class.java:349) 
    at java.lang.Class.newInstance(Class.java:308) 
    at pkg.Main.main(Main.java:42) 

正如預期的那樣,無法正常實例通過反射這種加載類,因爲包專用,它是在一個不同的包(相同的名稱,不同的名稱空間),這是正確的AFAIK,因爲它強制執行類型安全。

只是想知道,因爲過去幾天我一直在研究JVM和安全體系結構,並一直在尋找這樣的微妙之處,所以很難推理。

回答

5

getPackage方法未詳細說明。下面是bug 4256589說一下:

ClassLoader.getPackage(「富」)返回在這個特殊的類加載器包 FOO定義的包對象,如果這個類加載器沒有定義 包foo,那麼方法返回父類加載器爲foo(如果有)定義的 。

對我來說,這是說由getPackage返回的Package對象取決於「定義」類加載器是否班包本身,或者如果它發現包在父類加載器。看到的行爲似乎與此一致。

這是相當不一致的。但是,它是否真的有區別是否有一個包對象或多個包對象?當然,除非您在自定義類加載器或安全管理器中實施了一些特殊的基於軟件包的安全方案,否則它應該不會對安全類型或安全性產生任何影響。

+0

是的,父ClassLoader被調用。 「但是否真的有區別是否有一個包對象或多個包對象?「我認爲這是一種罕見的用例,但我瀏覽的openjdk實現當然包含了反射檢查,結果它比較了ClassLoader和完全限定的名稱,而不是調用任何'getPackage'。 – 2010-08-11 05:08:09

+0

I猜測這是爲什麼OSGi人討厭'拆分包'的原因之一(分佈在jar文件,捆綁包,類加載器中的包) – Thilo 2010-08-11 05:26:11

+0

@Longpoke - 我的*「它真的有什麼區別......」*要點是,一旦你理解你可以編程的異常,就像你找到的代碼一樣。 – 2010-08-11 08:05:47

相關問題