(Java基础篇)七、Java关于Class类的理解

反射机制

  编译阶段不知道创建哪个类的时候可以使用反射机制,在运行过程中创建。例如SpringBean工厂,在运行时创建各个bean就利用了反射机制。先看一段代码。

class Student {
    private String name = "默认值";

    public Student() {
        System.out.println("调用无参构造器");
    }

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}

public class TestReflect {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
        Class<?> c = Class.forName("com.wyb.csdnlearning.base.Student");
        // 实例化
        Object obj = c.newInstance();
        // 获取并填充属性
        Field name = c.getDeclaredField("name");
        // 打破封装(可以访问私有)
        name.setAccessible(true);
        name.set(obj, "路人甲");
        // 获取和调用方法
        Method setName = c.getDeclaredMethod("setName", String.class);
        setName.invoke(obj, "路人乙");
        Method toString = c.getDeclaredMethod("toString");
        System.out.println(toString.getDeclaredAnnotations().toString());
        System.out.println(toString.invoke(obj));
    }
}

  这里面有三大关键类,第一个是Class类,第二个是Field类,第三个是Method类,着三种类分别对应:类名、属性、方法。为了更好的了解,我们从源码分析。

Class类结构图
  上图是Class类的类结构图,可以看到主要实现了Type、GenericDeclaration和AnnotatedElement三个接口。其中Type比较简单,只有一个默认方法,获取类型名(全程),但是Class也重写了它,作用是如果是数组类型,根据维度再添加一些细节(如果是二维,那就后面加两个"[][]")。GenericDeclaration也只有一个方法——getTypeParameters,该方法用于获取类、方法、构造方法的泛型。AnnotatedElement接口定义获取注解相关信息的方法。现在重点关注一下Class类的newInstance()方法。

// Class类源码
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) {
    	// 不允许使用Class来创建Class类
        if (this == Class.class) {
            throw new IllegalAccessException(
                "Can not call newInstance() on the Class for java.lang.Class"
            );
        }
        try {
            Class<?>[] empty = {};
            // 获取构造器,这里用的是无参构造器(empty = {})
            // 后面的Member.DECLARED忽略继承的
            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;
                        }
                    });
            // 把构造器保存起来,以后再new的时候不用再加载了
            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 {
    	// 这里开始new,但实际上这个过程还是很复杂
    	// 使用Constructor的方法
	    // 调用DelegatingConstructorAccessorImpl实体类的方法
	    // 然后到NativeConstructorAccessorImpl
	    // 最终会到无参构造器中
        return tmpConstructor.newInstance((Object[])null);
    } catch (InvocationTargetException e) {
        Unsafe.getUnsafe().throwException(e.getTargetException());
        // Not reached
        return null;
    }
}
// 获取属性方法
public Field getDeclaredField(String name)
    throws NoSuchFieldException, SecurityException {
    checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
    // 这个做法是获取所有的Fields列表,然后一个个对比名字能不能对的上
    Field field = searchFields(privateGetDeclaredFields(false), name);
    if (field == null) {
        throw new NoSuchFieldException(name);
    }
    return field;
}

  在通过Class获取到Field之后,Field提供了一系列的getter和setter方法。同样,Method类是目标类的方法。有一点需要注意的是,无论是用Field的get和set方法还是Method的invoke方法,都需要传入具体实例对象,因为在获取Field或Method的时候,获取的是Class的而非Instance的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值