我想定义一个自定义类加载器,以便在运行时加载我自己的类。
但是,即使我定义了零参数构造函数,Class.newInstance
总是会失败。
异常消息是:
java.lang.IllegalAccessException:类Hello无法使用修饰符" public"
访问类Test的成员为什么?
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
class CustomClassLoader extends ClassLoader {
private Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
public String toString() {
return CustomClassLoader.class.getName();
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (classes.containsKey(name)) {
return classes.get(name);
}
byte[] classData;
try {
classData = loadClassData(name);
} catch (IOException e) {
throw new ClassNotFoundException("Class [" + name
+ "] could not be found", e);
}
Class<?> c = defineClass(name, classData, 0, classData.length);
resolveClass(c);
classes.put(name, c);
return c;
}
private byte[] loadClassData(String name) throws IOException {
BufferedInputStream in = new BufferedInputStream(
ClassLoader.getSystemResourceAsStream(name.replace(".", "/")
+ ".class"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
int i;
while ((i = in.read()) != -1) {
out.write(i);
}
in.close();
byte[] classData = out.toByteArray();
out.close();
return classData;
}
}
class Test
{
public Test()
{}
public void Hello()
{}
}
public class Hello {
public static void main(String[] args)
{
try {
CustomClassLoader loader = new CustomClassLoader();
Class<?> c = loader.findClass("Test"); // OK!
Object o = c.newInstance(); // ALWAYS FAIL!
}
catch (Exception e)
{
String s = e.getMessage();
// s is "java.lang.IllegalAccessException: Class Hello can not access"
// " a member of class Test with modifiers "public""
}
}
}
答案 0 :(得分:7)
问题是您的Test
类是使用默认(包)范围声明的,但在运行时,您的Hello
和Test
类位于不同的包中。
Java中类的名称(唯一标识符)是完全限定类名和加载它的类加载器的组合:
在运行时,类或接口不是由其名称单独确定,而是由一对确定:它的二进制名称(§4.2.1)及其定义的类加载器。每个这样的类或接口都属于一个运行时包。类或接口的运行时包由包名称和类或接口的类加载器定义。 (JVMS 5.3)
在这种情况下,Test
类正由下游类加载器加载,因此它的(默认)包不被认为是Hello
的相同(默认)包(由系统类加载器)。因此,您无法访问构造函数,因为类本身不是公共的。
如果您将Test
设为单独的公共顶级类或使用反射使其可用,则此示例将有效。
答案 1 :(得分:1)
您需要将该课程公开:
public class Test { ... }
或者,获取对构造函数的引用并使其可访问:
Constructor<?> constructor = c.getConstructor();
constructor.setAccessible(true);
Object o = constructor.newInstance();