Почему Class.getPackage возвращает тот же пакет для классов из разных пакетов?

Я создаю новый ClassLoader и определим новый Class, что означает, что новый класс должен находиться в новом пространстве имен, которое есть, AFAIK. Странно, когда я вызываю Class.getPackage в новый класс, он возвращает тот же самый объект, который возвращается, вызывая getPackage на любом другой класс в моем основном пространстве имен.

В соответствии с спецификацией JVM:

Пакет времени выполнения класса или интерфейс определяется пакетом имя и определение загрузчика классов класса или интерфейса.

Таким образом, если у вас есть два класса в одном пакете, но они загружаются разными загрузчиками классов, они считаются в разных пакетах. (Это также можно "подтвердить" через отражение в моем тестовом примере ниже.)

Итак, howcome, когда я это делаю, получаю тот же результат от 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 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 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

Метод getPackage не указан. Вот что говорит ошибка 4256589:

ClassLoader.getPackage( "foo" ) возвращает объект пакета, определенный для пакета foo в этом конкретном загрузчике классов, или если этот загрузчик классов не определил package foo, метод возвращает то, что определитель родительского класса определил для foo, если таковые имеются.

Для меня это говорит о том, что объект Package, возвращаемый getPackage, зависит от того, был ли classloader "определен" пакетом классов сам, или если он нашел этот пакет в своем родительском загрузчике классов. И поведение, видимое, похоже, согласуется с этим.

Это довольно непоследовательно. Но действительно ли имеет значение, есть ли один объект пакета или несколько объектов пакета? Разумеется, не нужно иметь значения, чтобы вводить безопасность или безопасность... если вы не внедрили какую-то специальную схему безопасности на основе пакетов в пользовательском загрузчике классов или диспетчере безопасности.