Warum gibt Class.getPackage dasselbe Paket für Klassen aus verschiedenen Paketen zurück?

Ich mache ein neuesClassLoader und definiere ein neuesClass, was bedeutet, dass sich die neue Klasse in einem neuen Namespace befinden sollte, also AFAIK. Das seltsame ist, wenn ich @ anruClass.getPackage in der neuen Klasse gibt es genau dasselbe Objekt zurück, das durch Aufrufen von @ zurückgegeben wurdgetPackage in einer anderen Klasse in meinem Hauptnamensraum.

Laut demJVM spec:

Das Laufzeitpaket einer Klasse oder Schnittstelle wird durch den Paketnamen und das definierende Klassenladeprogramm der Klasse oder Schnittstelle bestimmt.

So mit anderen Worten, wenn Sie zwei Klassen in demselben Paket haben, aber von verschiedenen Klassenladeprogrammen geladen werden, werden sie als in verschiedenen Paketen befindlich betrachtet. (Dies kann auch durch Reflektion in meinem Testfall unten "bestätigt" werden.)

So wie komme ich, wenn ich das tue, bekomme ich das gleiche Ergebnis vongetPackage in beiden Klassen?

Hier ist mein Test:

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
        }
    }
}

It gibt aus:

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)

Wie erwartet, können Sie diese geladene Klasse normalerweise nicht über Reflection instanziieren, da package-private und sie sich in einem @ befindeander package (gleicher Name, anderer Namespace), das ist die korrekte AFAIK, weil es die Typensicherheit erzwingt.

Wundere mich nur, weil ich mich in den letzten Tagen mit der JVM und der Sicherheitsarchitektur befasst habe und immer wieder nach solchen Feinheiten Ausschau halte, sodass es schwierig ist, darüber nachzudenken.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage