无所不能的反射

反射

类加载的区别

我们来看以下这张图,
图中的RTTI方式,我们编写完java代码就进行编译,即在编译器我们就生成了class文件,这个过程就是我们常规的对一个对象的创建过程,在运行期我们再将其加载到内存中,进行运作:Animal animal = new Dog();即我们在编译器的时候就已经确定了子类对象到底具体是什么了

图中我们的反射在编译器没有确定什么东西,在运行期我们就可以动态的确定一个Animal对象是一个Dog还是Cat
在这里插入图片描述

JVM类加载流程和内存结构

我们编写的java文件在类编译器中会编译为class文件,我们下图中cafe babe这份文件(包含类的所有信息)。然后我们的ClassLoader将文件load到内存中,经过加载、验证、准备、解析、初始化这些阶段后,加载进内存之后我们就会对这个文件进行一个内存的管理
在这里插入图片描述

Class文件包含的内容

在这里插入图片描述

生成对象的步骤

横线以上其实就是我们常用到的生成对象(编译期),横线以下就是我们用到的反射方式进行创建对象(运行期),二者的共同点都是从构造函数进行创建对象。
在这里插入图片描述

获取Class类实例的三种方式

同一个对象的class对象只有一个,所以无论通过哪种方式创建,他们的对象都是同一个

@Test
public void test() throws Throwable{
    // 方式一  类.class
    Class personClazz = Person.class;

    // 方式二  实例.getClass()
    Person person = new Person();
    Class personClazz1 = person.getClass();

    // 方式三  Class.forName("类的全路径")
    Class personClazz2 = Class.forName("com.reflect.Person");

    System.out.println(personClazz == personClazz1);
    System.out.println(personClazz == personClazz2);
}

通过反射创建对象

/**
* // 无参数
* <bean id="person" class="com.muse.reflect.Person" />
*
* // 有参数
* <bean id="person" class="com.muse.reflect.Person" >
*    <constructor-arg index="0" type="java.lang.String" value="muse"/>
* </bean>
*
*/
@Test
public void test2() throws Throwable{
   /** 首先:获得Person的字节码 */
   Class personClazz = Class.forName("com.reflect.Person");

   /** 其次:通过Class对象,创建构造方法对象 */
   Constructor constructor1 = personClazz.getConstructor(String.class, Integer.class, Byte.class,Boolean.class); // 初始化有参构造方法对象
   Constructor constructor = personClazz.getConstructor(); // 初始化无参构造方法

   /** 最后:通过构造方法创建对象 */
   // 调用无参数构造方法创建Person对象
   Person person = (Person) constructor.newInstance();
   person.setName("ironMan");
   System.out.println("person=" + person);

   // 调用有参数构造方法创建Person对象
   Person person1 = (Person) constructor1.newInstance("ironMan", 10, (byte) 1, true);
   System.out.println("person1=" + person1);
}

反射的使用

获得属性

/**
 * public属性的
 */
@Test
public void test3() throws Throwable{
    // 第一步:获得Class
    Class personClazz = Person.class;

    // 第二步:获得构造方法
    Constructor<Person> constructor = personClazz.getConstructor();
    Person person = constructor.newInstance();

    // 第三步:通过Class对象,获得Field对象
    Field nameField = personClazz.getField("name");

    // 第四步:操作Field,获得属性值
    String name = String.valueOf(nameField.get(person));

    System.out.println(name);
}

获得私有属性

没有sexField.setAccessible(true);这句代码的话,会提示无权访问private属性

/**
 * private
 */
@Test
public void test4() throws Throwable{
    // 第一步:获得Class
    Class personClazz = Person.class;

    // 第二步:获得构造方法
    Constructor<Person> constructor = personClazz.getConstructor();
    Person person = constructor.newInstance();

    // 第三步:通过Class对象,获得Field对象
    Field sexField = personClazz.getDeclaredField("sex");
    sexField.setAccessible(true);

    // 第四步:操作Field,获得属性值
    System.out.println(sexField.get(person));
}

获得public属性值

通过反射获得类的public属性值

/**
* getField 只能获取public的,包括从父类继承来的字段。
* getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(true))
*/
@Test
public void getPublicField() throws Throwable {
   /** 首先:获得Person的字节码 */
   Class personClazz = Person.class;
   
   /** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
   Person person = (Person) personClazz.getConstructor().newInstance();

   /** 第三:通过Class对象,获得Field对象 */
   Field nameField = personClazz.getField("name");
   // Field nameField = personClazz.getDeclaredField("name");  使用getDeclaredField也是ok的。

   /**
    * Field.toString();
    * --------------------------------------------------------------------
    * public String toString() {
	*     int mod = getModifiers();
	*     return (((mod == 0) ? "" : (Modifier.toString(mod) + " "))
	*       + getType().getTypeName() + " "
	*       + getDeclaringClass().getTypeName() + "."
	*       + getName());
    * }
    *
    * 【Field的重要方法】
    * System.out.println("获取字段的类型:" + nameField.getType());
    * System.out.println("获取字段的名字:" + nameField.getName());
    * System.out.println("获取字段的访问修饰符:" + Modifier.toString(nameField.getModifiers()));
    * System.out.println("获取字段所在类的全路径:" + nameField.getDeclaringClass().getName());
    */
   System.out.println(nameField); // protected java.lang.Integer com.Person.name

   /** 最后:获取字段的类型 */
   String name = String.valueOf(nameField.get(person));
   System.out.println(name);
}

获得private属性

/**
* 获得private属性
* @throws Throwable
*/
@Test
public void getPrivateField() throws Throwable {
   /** 首先:获得Person的字节码 */
   Class personClazz = Person.class;

   /** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
   Person person = (Person) personClazz.getConstructor().newInstance();

   /** 第三:通过Class对象,获得Field对象 */
   // Field sexField = personClazz.getField("sex");  不能使用getField,否则报错:java.lang.NoSuchFieldException: sex
   Field sexField = personClazz.getDeclaredField("sex");
   sexField.setAccessible(true); // 必须设置为true

   /** 最后:获取字段的类型 */
   Byte sex = (Byte) sexField.get(person);
   System.out.println(sex);
}

获得protected属性

/**
 * 获得protected属性
 *
 * @throws Throwable
 */
@Test
public void getProtectedField() throws Throwable {
    /** 首先:获得Person的字节码 */
    Class personClazz = Person.class;

    /** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
    Person person = (Person) personClazz.getConstructor().newInstance();

    /** 第三:通过Class对象,获得Field对象 */
    Field ageField = personClazz.getDeclaredField("age");
    ageField.setAccessible(true); // 必须设置为true,如果不设置,则报错:java.lang.IllegalAccessException: Class ReflectionTest can not access a member of class com.muse.Person with modifiers "protected"
    
    /** 最后:获取字段的类型 */
    Integer age = (Integer) ageField.get(person);
    System.out.println(age);
}

获得default属性

/**
 * 获得default属性
 *
 */
@Test
public void getDefaultField() throws Throwable {
    /** 首先:获得Person的字节码 */
    Class personClazz = Person.class;

    /** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
    Person person = (Person) personClazz.getConstructor().newInstance();

    /** 第三:通过Class对象,获得Field对象 */
    Field isMarriageField = personClazz.getDeclaredField("isMarriage");
    isMarriageField.setAccessible(true); // 必须设置为true,如果不设置,则报错:java.lang.IllegalAccessException: Class ReflectionTest can not access a member of class com.muse.Person with modifiers ""

    /** 最后:获取字段的类型 */
    Boolean isMarriage = (Boolean) isMarriageField.get(person);
    System.out.println(isMarriage);
}

反射的应用

采用反射机制来实现一个工具BeanUtils,可以将一个对象属性相同的值赋值给另一个对象

public static void convertor(Object originObj, Object targetObj) throws Throwable{
   // 第一步,获得class对象
   Class orginClazz = originObj.getClass();
   Class targetClazz = targetObj.getClass();

   // 第二步,获得Field
   Field[] orginFields =  orginClazz.getDeclaredFields();
   Field[] targetFields =  targetClazz.getDeclaredFields();

   // 第三步:赋值
   for (Field originField : orginFields) {
       for (Field targetField : targetFields) {
           if (originField.getName().equals(targetField.getName())) {
               originField.setAccessible(true);
               targetField.setAccessible(true);
               targetField.set(targetObj, originField.get(originObj));
           }
       }
   }
}
  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值