为什么Class.getPackage为来自不同包的类返回相同的包?

我创建一个新的ClassLoader并使它定义一个新的Class ,这意味着新的类应该在一个新的命名空间中,它就是AFAIK。 奇怪的是,当我在新类上调用Class.getPackage时,它返回与在我的主命名空间中的任何其他类上调用getPackage返回的完全相同的对象。

根据JVM规范 :

类或接口的运行时包由包名称和类或接口的类加载器定义。

换句话说,如果在同一个包中有两个类,但是由不同的类加载器加载,则它们被认为是在不同的包中。 (这也可以通过下面的测试用例中的reflection来“确认”。)

那么当我这样做时,我会从两个类的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: pkg.MyClassLoader@7987aeca lc ClassLoader parent: sun.misc.Launcher$AppClassLoader@1f7182c1 my ClassLoader: sun.misc.Launcher$AppClassLoader@1f7182c1 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) 

正如预期的那样,你通常不能通过reflection来实例化这个加载的类,因为package-private和它在不同的包(同名,不同的命名空间)中,这是正确的AFAIK,因为它强制执行类型安全。

只是想知道,因为我最近几天一直在研究JVM和安全架构,并且不断发现这样的微妙之处,因此很难推理。

getPackage方法未指定。 以下是关于它的错误4256589 :

ClassLoader.getPackage(“foo”)返回在此特定类加载器中为包foo定义的包对象,或者如果此类加载器未定义包foo,则该方法返回父类加载器为foo定义的内容(如果有)。

对我来说,这说明getPackage返回的Package对象取决于类加载器是否“定义”了一个类包本身,或者它是否在其父类加载器中找到了该包。 行为看起来似乎与此一致。

这是相当不一致的。 但是,是否有一个包对象或多个包对象真的有什么区别吗? 当然,除非您在自定义类加载器或安全管理器中实现了一些特殊的基于包的安全方案,否则它不应对类型安全或安全性产生任何影响。