一文透析Java反射

反射的概念和作用

Java 程序可以加载一个运行时才知道名称的类,得到它的完整构造,并可以生成实例对象、访问变量、或者调用其方法。
Java的反射机制主要提供了以下功能:

  • 在运行时判断任意一个对象所属的类。
  • 在运行时判断任意一个对象所具有的成员变量和方法。
  • 在运行时构造任意一个类的对象。
  • 在运行时调用任意一个对象的方法和变量。
  • 生成动态代理(Dynamic Proxy)

反射机制大大增强了程序的灵活性,运行期进行代码转配的特性使我们得以构建灵活的代码。这点,在各种框架中得到了很好的展现。

Class 类型理解

开始理解 Class 类之前,我们先来看一下类在 JVM(Java Virtual Machine)层面怎么表示这个类。
对象模型
每一个Java类(字节码文件),在被JVM加载的时候,JVM会给这个类创建一个instanceKlass,保存在方法区,用来在JVM层表示该Java类。当我们在Java代码中,使用new创建一个对象的时候,JVM会创建一个instanceOopDesc对象,这个对象中包含了对象头以及实例数据。

我们获取的 Class 对象即表示这个类类型 instanceKlass 对象。且 Class 没有公共构造方法。Class 对象是在加载类时由Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

Class 对象的三种获取方式

Java 提供了一套反射API来描述类,即 java.lang.reflect 包。
反射类关系图
Construcor:描述类的构造方法。
Field:描述类的成员变量。
Method:描述类的方法。
Modifer:类内个元素的修饰符。
Array:用来对数组进行操作。

构造方法的获取

// 获得类的所有公共构造方法列表(public 修饰的构造方法)
Construcotr<?>[] getConstructors();
// 获得类的所有构造方法列表
Constructor<?>[] getDeclaredConstructors();

// 获得特定参数类型的公共构造函数(参数为可变长参数,不传参为无参构造)
Constructor<T> getConstructor(Class<?>... parameterTypes);
// 获得特定参数的构造函数
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes);

成员变量的获取

// 类的指定公共成员字段
Field getField(String name);
// 类的指定成员字段
Field getDeclaredField(String name);

// 获取所有公共成员字段
Field[] getFields();
// 获取所有成员字段
Field[] getDeclaredFields();

Field 类为变量提供了相应了 getter/setter 方法,以获取和修改变量的值。

成员方法的获取

// 获取指定公共成员方法
Method getMethod(String name, class<?>... parameterTypes);
// 获取指定成员方法
Method getDeclaredMethod(String name, class<?>... parameterTypes);

// 获取所有公共成员方法
Method[] getMethods();
// 获取所有成员方法
Method[] getDeclaredMethods();

暴力反射

如果类的成员方法、变量等被 private 修饰符修饰,默认情况下,外部无权访问。但是使用暴力反射,即使为类私有的属性或方法等,也可以进行操作。如下例所示:
实体类:

public class ReflectionTest {
    public Integer id;
    public String name;
    private String password;
}

不使用暴力反射进行测试:

public static void main(String[] args) throws Exception {
        Class klass = ReflectionTest.class;
        ReflectionTest reflectionTest = (ReflectionTest) Class.forName(klass.getName()).newInstance();
        Field password = klass.getDeclaredField("password");
        System.out.println(password.get(reflectionTest));
    }

测试结果:
非法访问:IllegalAccessException
在这里插入图片描述
使用暴力反射:

public static void main(String[] args) throws Exception {
        Class klass = ReflectionTest.class;
        ReflectionTest reflectionTest = (ReflectionTest) Class.forName(klass.getName()).newInstance();
        Field password2 = klass.getDeclaredField("password");
        password2.setAccessible(true);
        System.out.println(password2.get(reflectionTest));
    }

测试结果:
没有非法访问的异常。
在这里插入图片描述

反射的应用

动态代理

动态代理是反射的典型应用之一,如下段源码(java.lang.reflect.Proxy 中的 newProxyInstance 方法):

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        Class<?> cl = getProxyClass0(loader, intfs);

        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                    	// 暴力破解
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值