反射

类对象

类的区别在于有不同的方法,不同的属性
类对象,就是用于描述这种类,都有什么属性,什么方法的

获取类对象

获取类对象有3种方法

  • Class.forName
  • Test.class
  • new Test().getClass()

在一个JVM中,一种类,只会有一个类对象存在。所以上三种方法取出来的类对象,都是一样的

注: 准确的讲是一个ClassLoader下,一种类,只会有一个类对象存在。通常一个JVM下,只会有一个ClassLoader。

无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。(除了直接使用Class c = Hero.class这种方式,这种方式不会导致静态属性被初始化)

创建对象

与传统通过new来获取对象的方式不同,反射机制会先拿到类的类对象,然后通过类对象获取“构造器对象”,再通过构造器对象创建一个对象

    Class tmpClass = Class.forName("com.xxx.What");
    Constructor c = tmpClass.getConstructor();
    What res = (What) c.newInstance();

访问属性

通过反射机制访问对象的属性

为了访问属性,把属性改为public

对于private修饰的成员,需要使用setAccessible(true)才能访问和修改

修改属性

通过反射机制修改对象的属性

getField和getDeclaredField的区别: 这两个方法都是用于获取字段

  • getField 只能获取public的,包括从父类继承来的字段。
  • getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(true))
package com.xxx.test;

import com.xxx.What;

import java.lang.reflect.Field;

public class Refletion {
    public static void main(String[] args) {
        What good = new What();

        good.name = "santa";
        good.saySomething();
        try {
            Field f1 = good.getClass().getDeclaredField("name");
            f1.set(good, "bad bad");

            System.out.println(good.name);
            good.saySomething();

        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

调用方法

通过反射机制调用Hero的setName

package com.xxx.test;

import com.xxx.What;

import java.lang.reflect.Method;

public class Refletion {
    public static void main(String[] args) {
        What good = new What();

        good.name = "santa";
        good.saySomething();
        try {
            Method m = good.getClass().getMethod("setName", String.class);

            m.invoke(good, "good good");

            good.saySomething();

        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

反射的方便性

通常来说,需要在学习了Spring 的依赖注入,反转控制之后,才会对反射有更好的理解,在这里举两个例子,来演示一下反射的一种实际运用。

class

package com.xxx;

public class Way1 {
    public static void saySomething(){
        System.out.println("way1 run");
    }
}

//`````````````````````````````````````

package com.xxx;

public class Way2 {
    public static void saySomething(){
        System.out.println("way2 run");
    }
}

main

package com.xxx.test;

import com.xxx.Way1;
import com.xxx.Way2;

public class Plactise {

    public static void main(String[] args) {
        //Way1.saySomething();
        Way2.saySomething();
    }
}

假若项目要从执行way1 换到way2,需要重新修改代码并且编译才能实现更改。

使用反射方式,可以准备一个配置文件, 里面存放的是类的名称,和要调用的方法名。 在测试类Test中,首先取出类名称和方法名,然后通过反射去调用这个方法。

当需要从调用way1,切换到调用way2,需要修改代码,也不需要重新编译,只需要修改配置文件,再运行即可。

这也是Spring框架的最基本的原理,只是它做的更丰富,安全,健壮。