在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对象具有如下特点:

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

获取Class实例的方法

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

  1. 获得编译期间已知类型的Class实例
    直接通过类的class属性获得,安全可靠且性能高,例如:
    Class clazz = String.class;
  2. 已知实例,获取其运行时类型
    通常通过实例的getClass()方法来获得
  3. 通过类的全类名来获得
    使用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()方法

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

  1. 类必须有无参构造器
  2. 构造器的访问权限满足
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());

获得运行时类的完整结构

常用方法:

  1. public Class<?>[] getInterfaces()获得实现的全部接口
  2. public Class<? Super T> getSuperclass()获得所继承的父类
    获得方法Method:
  3. public Method[] getDeclaredMethods()返回此Class对应的类或接口的全部方法
  4. public Method[] getMethods()返回此Class对应的类或接口的public方法
    Method类中:
  5. public Class<?> getReturnType()获得方法全部的返回值
  6. public Class<?>[] getParameterTypes()获得方法全部的参数方法
  7. public int getModifiers()获得方法的修饰符
  8. public Class<?>[] getExceptionTypes()获得方法的异常信息
    获得属性Field:
  9. public Field[] getFields()获得Class对应的类的属性
  10. public Field[] getDeclaredFields()获得Class对应的类的全部属性
    Filed对象中:
  11. public int getModifiers()获得属性的修饰符
  12. public Class<?> getType()获得属性类型
  13. public String getName()获得属性的名字
    注解相关:
  14. 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()方法可以拿到所有的属性

未完待续…随时补充!