Java 的内部类反射,使用 newInstance 总是抛 NoSuchMethodException 异常呢?

2019-07-22 17:59:57 +08:00
 amiwrong123
package com.prac;

public class outer {
    int i = 1;
    private static class staticInner{
        int j = 2;
        staticInner(){}
        void print(){
            System.out.println("in staticInner");
        }
    }
    private class normalInner{
        int k =3;
        void print(){
            System.out.println("in normalInner");
        }
    }
}

外部类,里面有一个静态内部类,非静态内部类,它们都是私有的。

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import com.prac.outer;
public class test2 {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        outer ooo = new outer();
        Class<?> clazz = null;
        try {
            clazz = Class.forName("com.prac.outer$normalInner");
            System.out.println(clazz.getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Constructor<?> con = clazz.getDeclaredConstructor();
        Object obj = con.newInstance();
    }
}

有一个测试类,我想试试用反射来创建私有的内部类实例,因为内部类是权限是私有的,所以获得 Class 引用只能通过 forName (不然就可以 import 这个内部类,然后通过类名.class来获得了)。

当 31 行,为clazz = Class.forName("com.prac.outer$normalInner")即获得非静态内部类 Class 的引用,然后 newInstance 报错:

com.prac.outer$normalInner
Exception in thread "main" java.lang.NoSuchMethodException: com.prac.outer$normalInner.<init>()
	at java.base/java.lang.Class.getConstructor0(Class.java:3354)
	at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2558)
	at test2.main(test2.java:38)

看起来好像是这篇博客讲的原因: https://www.jianshu.com/p/ecda088dcc5f 即非静态内部类构造时,构造函数会隐式地加一个 this 即外部类的引用。但这种情况还有解决办法吗?

当 31 行为clazz = Class.forName("com.prac.outer$staticInner")即获得静态内部类 Class 的引用,然后 newInstance 报错:

com.prac.outer$staticInner
Exception in thread "main" java.lang.IllegalAccessException: class test2 cannot access a member of class com.prac.outer$staticInner with modifiers ""
	at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:376)
	at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:639)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:490)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
	at test2.main(test2.java:39)

为什么这个就直接报错,有标识符的成员类就无法访问,因为是静态内部类吗?这种情况还有解决办法吗?

4621 次点击
所在节点    程序员
8 条回复
shily
2019-07-22 18:18:58 +08:00
测试一下就知道了啊,错误报在 getDeclaredConstructor(),这个是返回无参数的构造上。你已经通过博客知道,默认的非静态内部类会捕获外部类引用。

通过 clazz.getDeclaredConstructors() 可以知道它有一个构造,<init>(com.prac.outer),所以很容易修正啊。
通过 Constructor<?> con = clazz.getDeclaredConstructor(outer.class); 来获取构造方法
调用时是 Object obj = con.newInstance(ooo);
amiwrong123
2019-07-22 20:43:05 +08:00
@shily
好吧,原来是我 getDeclaredConstructor 用的不对,谢谢回答啦。
但是,现在就报错:
com.prac.outer$normalInner
Exception in thread "main" java.lang.IllegalAccessException: class test2 cannot access a member of class com.prac.outer$normalInner with modifiers "private"
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:376)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:639)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:490)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
at test2.main(test2.java:39)
所以不管是静态内部类还是非静态内部类,只要访问权限是私有,就不可以创建实例了吗
bringyou
2019-07-22 21:25:57 +08:00
contructor.setAccessible(true)
codeyung
2019-07-22 23:41:04 +08:00
setAccessible
amiwrong123
2019-07-23 09:54:25 +08:00
@bringyou
好吧,现在成功了。谢谢回答啦。
Constructor<?> con = clazz.getDeclaredConstructor(outer.class);
con.setAccessible(true);
Object obj = con.newInstance(ooo);
但这种情况下,我是不是只能用 Object 引用来指向这个实例,因为没法 import 私有内部类,所以也无法用私有内部类类型来声明变量了?
palmers
2019-07-23 10:20:53 +08:00
这种情况出现的原因,我认为有两方面:
1. 内部类实例化无参构造是有一个影藏参数就是外部类指针 this, 所以其实不是相比正常的无参构造它并不是无参构造; 非 static 需要先实例化外部类,所以需要这样:
```java
outer ot = new outer();
Class<?> clazz = Class.forName("com.prac.outer$normalInner");
Constructor<?> constructor = clazz.getDeclaredConstructor(outer.class);
Object o = constructor.newInstance(ot);
```
2. 对于私有类或方法外部访问都是拒绝的, 需要解决访问权限问题;

以上问题解决了, 我想基本不会有问题了,希望能帮到你
helloSpringBoot
2019-07-23 10:27:35 +08:00
@amiwrong123 私有的目的不就是这样吗。。。。
amiwrong123
2019-07-23 14:51:25 +08:00
@palmers
谢谢回答,现在都解决了。

@helloSpringBoot
虽然只能用 Object 引用来指向这个实例,但是有了这个实例我发现就可以做一些“坏事”了,比如获得私有成员。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/585169

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX