Я создаю новый 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 и безопасности в последние несколько дней и продолжаю находить небольшие тонкости, подобные этому, поэтому трудно рассуждать.