Java反射机制

在Java中,所有对象都有两种类型,即编译时类型和运行时类型。

编译时类型是在程序代码编译解决确定的类型,而运行时类型是在程序运行时根据实际的对象类型确定的。

并且由于多态机制,很多时候一个对象的编译时类型和运行时类型并不是一致的。

譬如,对于如下代码:

Object i = new String("qwe");
i.getClass();

对象i的编译时类型为Object,但是当我们使用getClass()获取它的所属类的Class类对象时,得到的结果却是java.lang.String

如果想要调用对象运行时类型的方法,那么就需要反射机制,因为在编译的时候,并不知道对象的运行时信息。

反射概述

反射机制允许我们在运行时借助Reflection API获取到任何类的内部信息,并可以直接操作任何对象的属性和方法。

当类被JVM加载之后,会在方法区产生一个Class类型的对象,这个类包含了完整的类的结构信息。

反射机制就是基于每个类的唯一的Class对象实现的。

反射第一步——获得Class对象

在反射操作的第一步,首先是要获得一个Class类对象,之后的反射操作都是基于这个Class实例来完成的,可以说,Class对象是反射的基本。

Class

Object类中,有一个方法,public final Class getClass(),Java所有的对象都继承了这个方法,通过这个方法可以返回一个Class对象。

一个Class对象具有如下特点:

Class对象只能由系统建立 一个被加载的类在JVM中只会有一个Class实例 Class类是Reflection的根源,任何想要动态加载、运行的类,唯有先获得对应的Class对象 通过Class对象可以完整地得到一个类中所有被加载的结构

获取Class实例的方法

一共有三种获得Class实例的常用方法:

获得编译期间已知类型的Class实例
直接通过类的class属性获得,安全可靠且性能高,例如:
Class clazz = String.class;
已知实例,获取其运行时类型 通常通过实例的getClass()方法来获得 通过类的全类名来获得 使用Class类提供的静态方法forName()获取,例如:
Class clazz = Class.forName("java.lang.String");

注意:所有的Java类型都有Class对象,对于数组来说,只要类型和维度相同,就是同一个Class对象。

应用场景

在以下的案例中,都对Person类来进行反射及操作:

public class Person {
    private String name;
    private Integer age;
    public String email;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", email='" + email + '\'' +
                '}';
    }

    public Person(String name, Integer age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

}

创建运行时类的对象

有两种方式构造一个运行时类的对象:

使用Class对象的newInstance()方法

不过,这种方式构造对象有两个条件:

类必须有无参构造器 构造器的访问权限满足
Object p1 = new Person("jack",34);
// 获得 p1 对象的运行时类型
Class aClass = p1.getClass();
// 调用 Class 对象的 newInstance() 方法实例化对象
Person p = (Person) aClass.newInstance();
p.setName("mick");
p.setAge(12);
System.out.println(p.toString());

这种方式构造对象只可以调用无参构造方法来构造。

通过获取构造器对象来实例化对象

Object p1 = new Person("jack",34);

// 获得 p1 对象的运行时类型
Class aClass = p1.getClass();

// 通过 Class 对象的 getDeclaredConstructor() 方法
// 获得对应类的构造器对象,根据传入的类型参数确定返回哪个构造器对象
Constructor declaredConstructor = aClass.getDeclaredConstructor();

// 通过构造器对象的 newInstance() 方法实例化对象
// 这里获得的是一个无参构造器,则参数为空
Person o = (Person)declaredConstructor.newInstance(new Object[]{});
o.setName("mick");
o.setAge(78);
System.out.println(o.toString());

// 获得有参构造器对象
Constructor declaredConstructor1 = aClass.getDeclaredConstructor(String.class,Integer.class);
Person o1 = (Person)declaredConstructor1.newInstance(new Object[]{"jack", Integer.valueOf(89)});
System.out.println(o1.toString());

获得运行时类的完整结构

常用方法:

public Class<?>[] getInterfaces()获得实现的全部接口 public Class<? Super T> getSuperclass()获得所继承的父类
获得方法Method: public Method[] getDeclaredMethods()返回此Class对应的类或接口的全部方法 public Method[] getMethods()返回此Class对应的类或接口的public方法
Method类中: public Class<?> getReturnType()获得方法全部的返回值 public Class<?>[] getParameterTypes()获得方法全部的参数方法 public int getModifiers()获得方法的修饰符 public Class<?>[] getExceptionTypes()获得方法的异常信息
获得属性Field: public Field[] getFields()获得Class对应的类的属性 public Field[] getDeclaredFields()获得Class对应的类的全部属性 在Filed对象中: public int getModifiers()获得属性的修饰符 public Class<?> getType()获得属性类型 public String getName()获得属性的名字
注解相关: getAnnotations()获取运行时类的注解

Method相关

Object o = new Person();
Class<?> aClass = o.getClass();
Method[] methods = aClass.getMethods();
for (Method m : methods) {
    System.out.println("Method");
    System.out.println(m);
    Class<?>[] parameterTypes = m.getParameterTypes();
    System.out.println("Parame");
    for (Class c : parameterTypes) {
        System.out.println(c);
    }
    System.out.println("Modifier");
    int modifiers = m.getModifiers();
    System.out.println(modifiers);
    Class<?> returnType = m.getReturnType();
    System.out.println("Return");
    System.out.println(returnType);
}

Field相关

Object o = new Person();
Class<?> aClass = o.getClass();
Field[] fields = aClass.getFields();
for (Field f : fields) {
    System.out.println("Field");
    int modifiers = f.getModifiers();
    System.out.println("Modifier");
    System.out.println(modifiers);
    Class<?> type = f.getType();
    System.out.println("Type");
    System.out.println(type);
    String name = f.getName();
    System.out.println("Name");
    System.out.println(name);
}
System.out.println("--------------getDeclaredFields---------------------");
Field[] declaredFields = aClass.getDeclaredFields();
for (Field f : declaredFields) {
    System.out.println("Field");
    int modifiers = f.getModifiers();
    System.out.println("Modifier");
    System.out.println(modifiers);
    Class<?> type = f.getType();
    System.out.println("Type");
    System.out.println(type);
    String name = f.getName();
    System.out.println("Name");
    System.out.println(name);
}

getFields()方法只能拿到public修饰的属性,而getDeclaredFields()方法可以拿到所有的属性

未完待续…随时补充!

文章来源:

Author:月梦
link:https://ymiir.netlify.app//java/Java反射机制.html