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