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

时间:2010-08-11 04:22:22

标签: java jvm classloader

我创建了一个新的ClassLoader并使其定义了一个新的Class,这意味着新的类应该在一个新的命名空间中,它就是AFAIK。奇怪的是,当我在新类上调用Class.getPackage时,它会返回与我在主命名空间中的任何其他类上调用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: 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)

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

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

1 个答案:

答案 0 :(得分:5)

getPackage方法未指定。以下是bug 4256589对此的说法:

  

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

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

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

相关问题