2010-08-11 45 views
6

我制作了一个新的ClassLoader并定义了一个新的Class,这意味着新的类应该位于新的名称空间中,它是AFAIK。奇怪的是,当我在新类上调用Class.getPackage时,它会返回与我的主名称空间中任何其他类上调用getPackage所返回的完全相同的对象。为什么Class.getPackage为不同包中的类返回相同的包?

按照JVM spec

一个类的运行时包或 接口是由包 名称和 类或接口的定义类装载程序来确定。

换句话说,如果你在同一个包中有两个类,但是由不同的类加载器加载,它们被认为是在不同的包中。 (这也可以通过我在下面的测试案例中的反思来“证实”。)

那么怎样才能做到这一点,我从两个类的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: [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'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和安全体系结构,并一直在寻找这样的微妙之处,所以很难推理。

回答

5

getPackage方法未详细说明。下面是bug 4256589说一下:

ClassLoader.getPackage(“富”)返回在这个特殊的类加载器包 FOO定义的包对象,如果这个类加载器没有定义 包foo,那么方法返回父类加载器为foo(如果有)定义的 。

对我来说,这是说由getPackage返回的Package对象取决于“定义”类加载器是否班包本身,或者如果它发现包在父类加载器。看到的行为似乎与此一致。

这是相当不一致的。但是,它是否真的有区别是否有一个包对象或多个包对象?当然,除非您在自定义类加载器或安全管理器中实施了一些特殊的基于软件包的安全方案,否则它应该不会对安全类型或安全性产生任何影响。

+0

是的,父ClassLoader被调用。 “但是否真的有区别是否有一个包对象或多个包对象?“我认为这是一种罕见的用例,但我浏览的openjdk实现当然包含了反射检查,结果它比较了ClassLoader和完全限定的名称,而不是调用任何'getPackage'。 – 2010-08-11 05:08:09

+0

I猜测这是为什么OSGi人讨厌'拆分包'的原因之一(分布在jar文件,捆绑包,类加载器中的包) – Thilo 2010-08-11 05:26:11

+0

@Longpoke - 我的*“它真的有什么区别......”*要点是,一旦你理解你可以编程的异常,就像你找到的代码一样。 – 2010-08-11 08:05:47

相关问题