IllegalAccessException

异常信息

1
2
3
4
5
6
7
Exception in thread "main" java.lang.IllegalAccessException: Class fluffy.mo.MyClassloader can not access a member of class fluffy.mo.Car with modifiers "public"
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
	at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:413)
	at fluffy.mo.MyClassloader.test4(MyClassloader.java:91)
	at fluffy.mo.MyClassloader.main(MyClassloader.java:68)

在使用反射创建对象时,被提示没权限

代码示例

1
2
3
package fluffy.mo;
public class Person{
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
package fluffy.mo;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;

public class MyClassloader extends ClassLoader {

    private byte[] getData(String name) throws Exception {
        String path = getClass().getResource("/").getPath();
        path = path + name.replace(".", "/") + ".class";
        InputStream in = new FileInputStream(new File(path));
        byte[] data = new byte[in.available()];
        in.read(data);
        in.close();
        return data;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] data = null;
        try {
            data = this.getData(name);

        } catch (Exception e) {
            e.printStackTrace();
            throw new ClassNotFoundException();

        }
        Class<?> aClass = defineClass(name, data, 0, data.length);
        return aClass;
    }

    static void test1() throws Exception {
        MyClassloader c1 = new MyClassloader();
        String name = "fluffy.mo.Person";

        Class pCls = c1.findClass(name);
        Person p1 = new Person();
        Object p2 = pCls.getConstructor().newInstance();
        // Person p3  = (Person)p2;
        // 强转报错,虽然二者都是Person对象,但二者的类加载器不同
        System.out.println("test1-done");
    }

    public static void main(String[] args) throws Exception {
        // 加载类的时候没有调用 loadClass 方法, 而是 findClass
        // 否则会有父类委托,导致每次都由AppClassLoader加载
        test1();

        Class carCls = null;
        Car car1 = new Car();
        String carName = "fluffy.mo.Car";

        // 方式一
        carCls = Class.forName(carName);
        carCls.newInstance();
        // carCls.getConstructor().newInstance();

        // 方式二
        carCls = Class.forName(carName, true, new MyClassloader());
        carCls.getConstructor().newInstance();

        // 方式三
        MyClassloader classloader1 = new MyClassloader();
        carCls = classloader1.findClass(carName);
        // test4(carCls); // error

        // 方式四: 为了解决方式三的报错
        Class classUtil = classloader1.findClass("fluffy.mo.MyClassloader");
        ClassLoader classloaderObj = (ClassLoader) classUtil.newInstance();
        // classloader1与
        // classloader1的类文件由AppClassloader加载,而classloaderObj的类文件由classloader1加载
        Method[] methods = classUtil.getMethods();
        for (Method method : methods) {
            if ("test4".equals(method.getName())) {
                method.invoke(classloaderObj, carCls);
            }
        }

        System.out.println("test-66-done");
    }

    public static void test4(Class carCls) throws Exception {
        Object p2 = carCls.getConstructor().newInstance();
        System.out.println(p2.getClass().getClassLoader());
        System.out.println(p2);
    }

}

// 在没有显示声明空构造函数的情况下,通过反射创建实例也会有不同的结果
class Car {
    static {
        System.out.println("init car");
    }

    public Car() {
    }
}

原因

由于Car没有声明public导致在反射创建对象时,存在权限问题。
为何方式四的carCls.getConstructor().newInstance()不报错,方式三报错呢
因为方式四在创建Car对象时,test方法和Car类二者在同一个包名下,有权限访问
方式三,test4方法的类由AppClassloader加载, carCls由classloader1加载,虽然二者包名相同,但隔着类加载器
方式三的问题也可以给Car加上public 声明解决

方式二是为啥成功了,因为方式二其实走的loadClass方法,会进行父类委托,返回的class其实是由AppClassloader加载的