java_反射

什么是反射

概念

  • 放射:将类的各个部分封装为其他对象,这就是反射机制。

java代码在内存中经历的三个阶段

1、 Source 源代码阶段

  • 执行javac编译命令从.java文件到.class文件的过程都是在源代码阶段,.class字节码文件会将类分为多个部分,其中分为成员变量部分,成员方法部分,构造方法部分等。

2、 Class 类对象阶段

  • 通过ClassLoader(类加载器)将字节码文件加载到内存中。
  • 通过Class类对象来描述进入内存中的字节码文件的特征和行为。将成员变量、成员方法、构造方法等封装成单独的对象放入Class类对象中。
  • 最后我们可以通过Class对象的一些行为创建具体的某个对象。

3、 runtime 运行时阶段

  • new 类();

反射的好处

  • 在程序的运行期间操作这些对象。
  • 降低程序的耦合性,提高程序的可扩展性。


获取Class类对象的方法

  • 获取class类对象的方式有三种,分别对应的java代码经历的三个阶段

1、 Class.forName(“全类名”);

  • 将字节码文件加载进内存,返回class对象
  • 多用于配置文件,将类名定义在配置文件中

2、 类名.class;

  • 通过类名的属性class获取
  • 多用于参数传递

3、 对象.getClass();

  • getClass()方法是定义在Object类中的
  • 多用于对象获取字节码

  • 同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次。

package top.uaoie.day03;
import top.uaoie.domain.Person;
public class ReflectDome01 {
    public static void main(String[] args) throws Exception {
        //1. Class.forName("全类名");
        Class clazz1 = Class.forName("top.uaoie.domain.Person");
        //2. 类名.class;
        Class clazz2 = Person.class;
        //3. 对象.getClass();
        Person p = new Person();
        Class clazz3 = p.getClass();

        System.out.println(clazz1);
        System.out.println(clazz2);
        System.out.println(clazz3);
        //比较三个对象
        System.out.println(clazz1 == clazz2);
        System.out.println(clazz1 == clazz3);
    }
}


反射的应用

1、 获取所有的成员变量

  • Field[] getFields()
  • Field getFied(String name)
  • Field[] getDeclaredFields()
  • Field getDeclaredFied(String name)

2、 获取所有的构造方法

  • Constructor<?>[] getConstructors()
  • Constructor getConstructors(类<?>… parameterTypes)
  • Constructor<?>[] getDeclaredConstructors()
  • Constructor getDeclaredConstructors(类<?>… parameterTypes)

3、 获取所有的成员方法

  • Method[] getMethods()
  • Menthod getMethod(String name,类<?>…parameterTypes)
  • Method[] getDeclaredMethods()
  • Menthod getDeclaredMethod(String name,类<?>…parameterTypes)

4、 获取类名

  • String getName()

以下为演示通过反射获取所有的成员变量

  • 创建一个名为Person的类,里面的代码如下:
package top.uaoie.domain;
public class Person {
    public String a;
    protected String b;
    String c;
    private String d;

    @Override
    public String toString() {
        return "Person{" +
                "a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }
}
  • 再新建一个名为ReflectDome02的类,里面的代码如下:
package top.uaoie.day03;
import top.uaoie.domain.Person;
import java.lang.reflect.Field;
public class ReflectDome02 {
    public static void main(String[] args) throws Exception {
        //获取Person的Class对象,此处是在阶段二部分获取的
        //也可在第一阶段获取,如:Class.forName("top.uaoie.domain.Person");
        Class p = Person.class;
        //获取成员变量
        Field[] fields = p.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("---------------------");
        Field a = p.getField("a");
        System.out.println(a);
        //创建一个对象
        Person person = new Person();
        //获取成员变量a的值
        Object value = a.get(person);
        System.out.println(value);
        //设置成员变量a的值
        a.set(person, "这是a的值");
        System.out.println(person);
        System.out.println("=================");
        Field[] declaredFields = p.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        Field d = p.getDeclaredField("d");
        //忽略访问权限修饰符
        d.setAccessible(true);//暴力反射
        Object value2 = d.get(person);
        System.out.println(value2);

    }
}

小结

  • 获取类的class对象有多种方法,其中使用Class.forName可以通过读取配置文件获取全类名
  • Class.forName和getField等类是代码会报错是因为传入的是字符串,所有可能不存在这个类获或者方法,所以需要处理异常
  • 通过反射可以直接访问到任何成员变量名,不受访问修饰符的限制
  • 可以setAccessible设置为true来暴力反射获取或者更改成员变量的值

版权声明:本文为Juaoie原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/Juaoie/p/12355337.html