Я создаю новый ClassLoader
и заставляю его определять новый Class
, что означает, что новый класс должен находиться в новом пространстве имен, которым он является, AFAIK. Странно то, что когда я вызываю Class.getPackage
для нового класса, он возвращает точно такой же объект, который был возвращен путем вызова getPackage
для любого другого класса в моем основном пространстве имен.
Согласно спецификации JVM :
Пакет времени выполнения класса или
интерфейс определяется пакетом
имя и определение загрузчика класса
класс или интерфейс.
Другими словами, если у вас есть два класса в одном пакете, но загружены разными загрузчиками классов, они считаются в разных пакетах. (Это также можно «подтвердить» с помощью отражения в моем тестовом примере ниже.)
Так почему же, когда я делаю это, я получаю одинаковый результат от 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 и архитектуру безопасности в последние несколько дней и постоянно нахожу маленькие тонкости, подобные этой, так что об этом трудно рассуждать.