Java 反射

前言

  • Java 反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。

  • 这也是 Java 被视为动态(或准动态)语言的一个关键性质。

  • 为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby 是动态语言,C++,Java,C# 不是动态语言。

  • Java 反射

  • 反射机制 快速入门

1、反射

  • 我们知道反射机制允许程序在运行时取得任何一个已知名称的 class 的内部信息,包括包括其 modifiers (修饰符),fields (属性),methods (方法) 等,并可于运行时改变 fields 内容或调用 methods。

    • 那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;
    • 还有动态代理的实现等等;
    • 但是需要注意的是反射使用不当会造成很高的资源消耗!
  • 反射可以用于解析注解中的信息。

  • 反射非常强大,但是学习了之后,会不知道该如何使用,反而觉得还不如直接调用方法来的直接和方便。

  • 通常来说,需要在学习了 Spring 的依赖注入,反转控制之后,才会对反射有更好的理解。

2、获取类对象

  • 所有的类,都存在一个类对象,这个类对象用于提供类本身的信息,描述这种类有几种构造方法,有多少属性,有哪些普通方法。

  • 获取类对象有 3 种方式

方式 说明
对象.getClass() 通常应用在,比如你传过来一个 Object 类型的对象,而我不知道你具体是什么类,用这种方法。
类名.class 该方法最为安全可靠,程序性能更高,这说明任何一个类都有一个隐含的静态成员变量 class
Class.forName() 通过 Class 对象的 forName() 静态方法来获取,用的最多,但可能抛出 ClassNotFoundException 异常。
  • 在一个 JVM 中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    try {
      Person p1 = new Person();
      Class c1 = p1.getClass(); // 获取类对象

      Class c2 = Person.class; // 获取类对象

    String className = "com.ys.reflex.Person"; // 包名.类名
      Class c3 = Class.forName("com.ys.reflex.Person"); // 获取类对象
      
    } catch (ClassNotFoundException e) {
    e.printStackTrace();
    }
  • 获取类对象的时候,会导致类属性被初始化。

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

2.1 类对象方法

  • 通过 Class 类获取成员变量、成员方法、接口、超类、构造方法等。
方法 介绍
getName() 获得类的完整名字。
getFields() 获得类的 public 类型的属性。
getDeclaredFields() 获得类的所有属性。包括 private 声明的和继承类。
getMethods() 获得类的 public 类型的方法。
getDeclaredMethods() 获得类的所有方法。包括 private 声明的和继承类。
getMethod(String name, Class[] parameterTypes) 获得类的特定方法,name 参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
getConstructors() 获得类的 public 类型的构造方法。
getConstructor(Class[] parameterTypes) 获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
newInstance() 通过类的不带参数的构造方法创建这个类的一个对象。

3、反射创建对象

  • 与传统的通过 new 来获取对象的方式不同。

  • 反射机制,会先拿到 “类对象”,然后通过类对象获取 “构造器对象”。

  • 再通过构造器对象创建一个对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import java.lang.reflect.Constructor;
    import charactor.Hero;

    try {
    String className = "charactor.Hero";
    Class pClass = Class.forName(className); // 获取类对象
    Constructor c = pClass.getConstructor(); // 获取构造器对象
    Hero h = (Hero) c.newInstance(); // 通过构造器 创建一个对象

    h.name = "gareen";
    System.out.println(h2);
    } catch (Exception e) {
    e.printStackTrace();
    }

4、访问属性

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

    1
    2
    3
    4
    5
    6
    Hero h = new Hero();

    Class pClass = h.getClass(); // 获取类对象

    Field field = pClass.getDeclaredField("name"); // 获取类 Hero 的名字叫做 name 的字段
    field.set(h, "teemo"); // 修改这个字段的值

5、调用方法

  • 通过反射机制,调用一个对象的方法

    1
    2
    3
    4
    5
    6
    Hero h = new Hero();

    Class pClass = h.getClass(); // 获取类对象

    Method method = pClass.getMethod("setName", String.class); // 获取这个名字叫做 setName,参数类型是 String 的方法
    method.invoke(h, "盖伦"); // 对 h 对象,调用这个方法

6、根据反射获取父类属性

  • 父类 Parent.java

    1
    2
    3
    4
    5
    6
    7
    public class Parent {

    public String publicField = "parent_publicField";
    protected String protectField = "parent_protectField";
    String defaultField = "parent_defaultField";
    private String privateField = "parent_privateField";
    }
  • 子类 Son.java

    1
    2
    3
    public class Son extends Parent {

    }
  • 测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class ReflectionTest {

    @Test
    public void testGetParentField() throws Exception {
    Class c1 = Class.forName("com.ys.model.Son");

    // 获取父类私有属性值
    System.out.println(getFieldValue(c1.newInstance(), "privateField"));
    }

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

    return field.get(object);
    }

    public static Field getDeclaredField(Object obj, String fieldName) {
    Field field = null;
    Class c = obj.getClass(); // 反射获得的子类 Class 对象
    for(; c != Object.class ; c = c.getSuperclass()){
    try {
    field = c.getDeclaredField(fieldName);
    field.setAccessible(true);
    return field;
    } catch (Exception e){
    // 这里什么都不要做!并且这里的异常必须这样写,不能抛出去。
    // 如果这里的异常打印或者往外抛,则就不会执行 c = c.getSuperclass(), 最后就不会进入到父类中了
    }
    }
    return null;
    }
    }
  • 通过执行上述代码,我们获得了父类的私有属性值。

  • 这里要注意的是直接通过反射获取子类的对象是不能得到父类的属性值的。

  • 必须根据反射获得的子类 Class 对象在调用 getSuperclass() 方法获取父类对象。

  • 然后在通过父类对象去获取父类的属性值。

文章目录
  1. 1. 前言
  2. 2. 1、反射
  3. 3. 2、获取类对象
    1. 3.1. 2.1 类对象方法
  4. 4. 3、反射创建对象
  5. 5. 4、访问属性
  6. 6. 5、调用方法
  7. 7. 6、根据反射获取父类属性
隐藏目录