java反射与内部类

本文讨论了在使用Java反射尝试实例化内部私有类时遇到的问题。当内部类没有显示创建无参构造函数时,反射操作会失败。通过源码分析,发现反射在查找构造函数时遵循特定规则,尤其是对于内部类,其构造函数会包含对外部类的引用,导致反射无法找到无参构造。静态内部类则不同,它有独立的无参构造函数。总结了反射实例化内部类的规则及内部类编译后的特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

时间记录:2019-11-19

问题描述: 今天在使用 quartz 开始定时任务的时候,我当时考虑到实现 job 不会给外部使用的情况下,使用了内部的私有类,然后发现其不能够进行实例化操作,然后看了下源码,发现其实通过反射的方式将类进行实例化的,然后我当时写的任务类的问题,导致了其通过反射的方式不能够实例化。
下面探索下反射的方式进行的实例化操作,为什么不行

第一部分: 私有类且没用进行无参构造函数显示创建

package com.huo;

/**
 * 反射的方式初始化测试
 * @author huoruilin
 */
public class TestReflection
{
    public static void main(String[] args)
    {
        try
        {
            Item item = (Item)Item.class.newInstance();
        }catch (IllegalAccessException | InstantiationException exception)
        {
            exception.printStackTrace();
        }
    }
    private class Item
    {

    }
}

然后很明显的是就会报找不到其无参的构造函数,如下报错内容

java.lang.InstantiationException: com.huo.TestReflection$Item
	at java.lang.Class.newInstance(Class.java:418)
	at com.huo.TestReflection.main(TestReflection.java:13)
Caused by: java.lang.NoSuchMethodException: com.huo.TestReflection$Item.<init>()
	at java.lang.Class.getConstructor0(Class.java:3069)
	at java.lang.Class.newInstance(Class.java:403)
	... 1 more

然后我们把其的无参构造函数显示的表示

 private class Item
    {
        public Item()
        {
            
        }
    }

然后依然是会报错,如下的报错

java.lang.InstantiationException: com.huo.TestReflection$Item
	at java.lang.Class.newInstance(Class.java:418)
	at com.huo.TestReflection.main(TestReflection.java:13)
Caused by: java.lang.NoSuchMethodException: com.huo.TestReflection$Item.<init>()
	at java.lang.Class.getConstructor0(Class.java:3069)
	at java.lang.Class.newInstance(Class.java:403)
	... 1 more

这个时候我想为什么会没有找到其的构造函数,难道内部类的构造函数有什么特殊的地方,还是说反射的方式查找的无参构造函数有什么特殊的地方,我们调试发现,其寻找的构造函数,确实有点特殊的地方。以及内部类中的构造函数的特殊地方。我们不免思考,这样的设计方式的目的在什么地方。

public T newInstance()
        throws InstantiationException, IllegalAccessException
    {
        if (System.getSecurityManager() != null) {
            checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
        }

        // NOTE: the following code may not be strictly correct under
        // the current Java memory model.

        // Constructor lookup
        if (cachedConstructor == null) {
            if (this == Class.class) {
                throw new IllegalAccessException(
                    "Can not call newInstance() on the Class for java.lang.Class"
                );
            }
            try {
                Class<?>[] empty = {};
                //这个地方就是查找当前的类的构造函数的地方
                final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
                // Disable accessibility checks on the constructor
                // since we have to do the security check here anyway
                // (the stack depth is wrong for the Constructor's
                // security check to work)
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<Void>() {
                        public Void run() {
                                c.setAccessible(true);
                                return null;
                            }
                        });
                cachedConstructor = c;
            } catch (NoSuchMethodException e) {
                throw (InstantiationException)
                    new InstantiationException(getName()).initCause(e);
            }
        }
        Constructor<T> tmpConstructor = cachedConstructor;
        // Security check (same as in java.lang.reflect.Constructor)
        int modifiers = tmpConstructor.getModifiers();
        if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            if (newInstanceCallerCache != caller) {
                Reflection.ensureMemberAccess(caller, this, null, modifiers);
                newInstanceCallerCache = caller;
            }
        }
        // Run constructor
        try {
            return tmpConstructor.newInstance((Object[])null);
        } catch (InvocationTargetException e) {
            Unsafe.getUnsafe().throwException(e.getTargetException());
            // Not reached
            return null;
        }
    }

getConstructor0 中我们看到了 Member.DECLARED 那么这个表示什么意思呢?
我们从源码中可以看到

Member is an interface that reflects identifying information about
a single member (a field or a method) or a constructor.
是一个反映有关单个成员(字段或方法)或构造函数的标识信息的接口。

DECLARED
标识类或接口的已声明成员的集合,不包括继承的成员。

我们来看具体的方法信息

 private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
                                        int which) throws NoSuchMethodException
    {
        //这一步有找到其的构造函数,且数量为1,然后再下面的判断中被舍弃,然后报找不到方法
        Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
        for (Constructor<T> constructor : constructors) {
            if (arrayContentsEq(parameterTypes,
                                constructor.getParameterTypes())) {
                return getReflectionFactory().copyConstructor(constructor);
            }
        }
        throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
    }

我们来看下 arrayContentsEq 的意思,比较的意思是查看当前的构造函数是不是无参构造函数

 private static boolean arrayContentsEq(Object[] a1, Object[] a2) {
        if (a1 == null) {
            return a2 == null || a2.length == 0;
        }

        if (a2 == null) {
            return a1.length == 0;
        }
        //这里直接返回了
        if (a1.length != a2.length) {
            return false;
        }

        for (int i = 0; i < a1.length; i++) {
            if (a1[i] != a2[i]) {
                return false;
            }
        }

        return true;
    }

这里导致的其,没有无参构造函数,也就是指返回的构造函数不是无参构造函数,那么这个构造函数代表的是什么含义,我们来自己来获取下这个构造函数,看看为什么不是无参构造函数

package com.huo;

import java.lang.reflect.Constructor;

/**
 * 反射的方式初始化测试
 * @author huoruilin
 */
public class TestReflection
{
    public static void main(String[] args)
    {
        Constructor[] constructors = Item.class.getDeclaredConstructors();
        for(Constructor c:constructors){
            System.out.println(c);
        }
    }
    
    private class Item
    {
        public Item()
        {
        
        }
    }
}

结果如下,我们发现了其会带上 TestReflection 这个外部类

public com.huo.TestReflection$Item(com.huo.TestReflection)

我们来看下真正执行的class文件中的内容一探究竟,我们看下TestReflection$Item.class这个内部类的实际执行内容

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.huo;

class TestReflection$Item {
    public TestReflection$Item(TestReflection var1) {
        this.this$0 = var1;
    }
}

很明显其没有显示的无参构造函数,而且java文件中的无参构造直接被忽略了,从而导致了反射的方式实例化的方式没有成功。从这里我们可以和上面的查找其构造函数对应上了,其找到一个构造函数的原因。然后内部类是和外部的类进行关联的,想要创建内部的类,就需要先创建外部的类,然后才能创建内部的类,所以这里的构造函数会传一个外部的类。如果是一个静态的内部类我们会发现其是有无参构造函数的,然后这个内部类是独立的无需与外部类进行关联。

总结:
1 反射的方式进行实例化的操作是要查找其无参构造函数的,先从缓存中查找,缓存中没有查找到,再去虚拟机中查找
2 内部类的编译会生成一个带其外部类的一个构造函数,而且会直接忽视了其java中的无参构造,不参与 了编译【还是需要了解下编译的规则】

时间记录:2019-11-19

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值