Java-我理解的反射

反射我还没具体用到过,只学习了理论知识,这里叙述下学习到的知识链:

什么是反射?为什么叫做反射?

通过某一种方式获取对象的Class实例对象(这里的对象指的是类的对象,如Person.class),通过这个Class对象(Class demo = Person.class; )反射出这个类的信息。

 

反射有什么用?

反射可以用来在运行时获取类的信息。

 

既然反射是用来获取类的信息的,那可以先看下他获取类的属性方法:

前置条件:获取对象的Class实例对象,我所知道的有三种方法,应用在不同的场景:

  • 第一种,通过类名获取:Class c1 = Person.class;
  • 第二种,通过类在项目中的位置获取: Class c2 = Class.forName("com.cai.reflection.Person");
  • 第三种,通过类的实例对象获取: Person person = new Person(); Class c3 = person.getClass();

三种方式获取的所需条件各不同,可以根据实际所有条件决定使用哪种方法,这里详细解释下Classname.class和Class.forName这两种的具体区别,主要是是否触发静态代码块的区别,demo如下:

我们在Person中加入一个静态游离代码块 

我们先使用forName方法获取Class对象试试:

可以在控制台看到静态代码块被触发了

 

接着使用Person.class这种方式试一下

控制台并没有输出什么,原因要追溯到Java类的装载顺序,这里引用jiese1990一张图片:

Alt ç±»å è½½ç5个é¶æ®µ

可以看到,静态代码块的执行是最后初始化步骤才执行的,初始化的触发条件可以看Oracle文档:https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html

其中的5.5初始化部分提及触发初始化的一些条件,其中有一条这么说到:

Invocation of certain reflective methods in the class library (§2.12), for example, in class Class or in package java.lang.reflect.

类加载的初始化阶段只有以下六种主动使用类的情况会触发:

 1、创建类的实例

 2、访问某个类或接口的静态变量,或者对该静态变量赋值

 3、调用某个类的静态方法

 4、反射(Class.forName("java.lang.String"))

 5、初始化一个类的子类

 6、Java虚拟机启动时被标明为启动类的类

Class.forName这种方法是属于其中第四点,而Person.class这种在load阶段已经加载完成了,不会触发初始化。

我们查看一下forName的源码可以发现:

    可以看到forName底层使用的也是 ClassLoader,ClassLoader中的loadClass只做Class加载,并不做初始化步骤,所以ClasssLoader不会触发静态代码块。

    但是forName内提供了一个参数供我们选择是否触发静态代码块的初始化,这也就是为什么forName同样用的是classLoader却能够触发类加载的初始化步骤。

 

接下来以获取一个父类属性为例子演示反射的使用过程:

我们再建一个Student类,继承自Person类,我们要通过这个Student类获取Person类的属性方法:

第一步自然是获取Student的Class实例

Class demo = Student.class;

接着就是反射的使用内容了,demo中有许多方法可以获取到Student的内容:

  • getName():获得类的完整名字。
  • getFields():获得类的public类型的属性。
  • getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
  • getMethods():获得类的public类型的方法。
  • getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
  • getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
  • getConstructors():获得类的public类型的构造方法。
  • getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
  • newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
  • .getSuperClass():获取父类Class实例。

我们要通过student获取其父类Person的属性,自然先要考虑 获取其父类的Class,这里写一个方法获取:

public static Field getDeclaredField(Class demo, String fieldName) {
    Field field = null;
    // 父级field的获取,demo为本层的Class实例,fieldName为要获取的父级属性名
    // field获取过程:主要通过getSuperclass方法,循环是为了判断这个类是否已经取到了超类Object的Class实例,这样确保可以取到所要的层级属性,而不仅仅局限于一两个层级。
    for(; demo != Object.class; demo = demo.getSuperclass()) {
        try {
            field = demo.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field;
        } catch (Exception e) {

        }
    }
    return null;
}

这里刚看的时候觉得setAccessible有点多余,以为getDeclaredField已经可以获取所以权限的属性字段了,为什么还要设置运行访问,原来这个运行访问是后面在通过该field获取内容的时候给予的权限,简而言之就是该设置是设置允许访问字段属性的内容,而不是获取字段属性的权限。

内容到这里差不多结束了,获取到了field之后,想要什么内容自己取即可,下面给上完整例子:

public class ReflectionTest {

    @Test
    public void testGetParentFields() throws Exception {
        // 获取子类的Class实例
        Class demo = Student.class;

        System.out.println(getFieldValue(demo, "name"));
    }

    @Test
    public void testStaticArea() {
        // 这样并不会初始化Class对象,Classname.class这种获取对象类实例的方法不属于反射方法。
        Class c1 = Person.class;
        try {
            // 这种会初始化Class对象,Class.forName()属于主动使用中的反射,它是一个要求装载后立刻初始化加载类的操作。
            // 但是他有提供第二个参数选择是否初始化静态代码块,
            Class c2 = Class.forName("com.cai.reflection.Person");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static Field getDeclaredField(Class demo, String fieldName) {
        Field field = null;
        // 父级field的获取,demo为本层的Class实例,fieldName为要获取的父级属性名
        // field获取过程:主要通过getSuperclass方法,循环是为了判断这个类是否已经取到了超类Object的Class实例,这样确保可以取到所要的层级属性
        for(; demo != Object.class; demo = demo.getSuperclass()) {
            try {
                field = demo.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field;
            } catch (Exception e) {

            }
        }
        return null;
    }

    public static Object getFieldValue(Class demo, String fieldName) throws Exception {
        Field field = getDeclaredField(demo, fieldName);

        return field.get(demo.newInstance());
    }
}

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值