为什么Class.newInstance总是抛出异常?

时间:2015-01-04 09:04:49

标签: java exception reflection constructor runtime-error

我想定义一个自定义类加载器,以便在运行时加载我自己的类。

但是,即使我定义了零参数构造函数,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""
        }
    }
}

2 个答案:

答案 0 :(得分:7)

问题是您的Test类是使用默认(包)范围声明的,但在运行时,您的HelloTest类位于不同的包中。

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();