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

L̲̳*_*̲̳̳ 6 java jvm classloader

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

根据JVM规范:

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

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

那么当我这样做时,我得到了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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它输出:

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)
Run Code Online (Sandbox Code Playgroud)

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

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

Ste*_*n C 5

getPackage方法未明确.以下是关于它的错误4256589:

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

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

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