为什么Class.getPackage为来自不同包的类返回相同的Package? [英] Why does Class.getPackage return the same Package for classes from different packages?

查看:106
本文介绍了为什么Class.getPackage为来自不同包的类返回相同的Package?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I make a new ClassLoader and make it define a new Class, which means that new class should be in a new namespace, which it is, AFAIK. The strange thing is, when I call Class.getPackage on the new class, it returns the exact same object as returned by calling getPackage on any other class in my main namespace.

根据 JVM规范:

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

The runtime package of a class or interface is determined by the package name and defining class loader of the class or interface.

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

So in other words, if you have two classes in the same package, but are loaded by different classloaders, they are considered to be in different packages. (This can also be "confirmed" via reflection in my test case below.)

那么当我这样做时,我在两个类上都从getPackage得到了相同的结果吗?

So howcome when I do this I get the same result from getPackage on both classes?

这是我的考试:

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,因为它是强制类型安全.

As expected, you can't normally instantiate this loaded class via reflection, because package-private and it's in a different package (same name, different namespace), which is correct AFAIK, because it's enforcing type safety.

奇怪的是,由于最近几天我一直在研究JVM和安全性体系结构,并且不断发现这样的微妙之处,因此很难对此进行推理.

Just wondering because I've been studying the JVM and security architecture the last few days and keep finding little subtleties like this so it's hard to reason about.

推荐答案

getPackage方法未指定.这是 bug 4256589 所说的:

The getPackage method is underspecified. Here's what bug 4256589 says about it:

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

ClassLoader.getPackage("foo") returns the package object defined for package foo in this particular class loader, or if this class loader didn't define package foo, the method returns what the parent class loader has defined for foo, if any.

对我来说,这表示getPackage返回的Package对象取决于类加载器是否定义"了类包本身,还是在其父类加载器中找到了该包.行为似乎与此相符.

To me, this says that the Package object returned by getPackage depends on whether the classloader "defined" a classes package itself, or if it found that package in its parent classloader. And the behavior are seeing seems to be consistent with this.

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

It is rather inconsistent. But does it really make any difference whether there is one package object or multiple package objects? Certainly, it shouldn't make any difference to type safety or to security ... unless you implemented some special package-based security scheme in a custom classloader or security manager.

这篇关于为什么Class.getPackage为来自不同包的类返回相同的Package?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆