我製作了一個新的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和安全體系結構,並一直在尋找這樣的微妙之處,所以很難推理。
是的,父ClassLoader被調用。 「但是否真的有區別是否有一個包對象或多個包對象?「我認爲這是一種罕見的用例,但我瀏覽的openjdk實現當然包含了反射檢查,結果它比較了ClassLoader和完全限定的名稱,而不是調用任何'getPackage'。 – 2010-08-11 05:08:09
I猜測這是爲什麼OSGi人討厭'拆分包'的原因之一(分佈在jar文件,捆綁包,類加載器中的包) – Thilo 2010-08-11 05:26:11
@Longpoke - 我的*「它真的有什麼區別......」*要點是,一旦你理解你可以編程的異常,就像你找到的代碼一樣。 – 2010-08-11 08:05:47