在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);
Class aClass = p1.getClass();
Person p = (Person) aClass.newInstance(); p.setName("mick"); p.setAge(12); System.out.println(p.toString());
|
这种方式构造对象只可以调用无参构造方法来构造。
通过获取构造器对象来实例化对象
Object p1 = new Person("jack",34);
Class aClass = p1.getClass();
Constructor declaredConstructor = aClass.getDeclaredConstructor();
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()
方法可以拿到所有的属性
未完待续…随时补充!