在Java中,所有对象都有两种类型,即编译时类型和运行时类型。
编译时类型是在程序代码编译解决确定的类型,而运行时类型是在程序运行时根据实际的对象类型确定的。
并且由于多态机制,很多时候一个对象的编译时类型和运行时类型并不是一致的。
譬如,对于如下代码:
代码语言:javascript复制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());
这种方式构造对象只可以调用无参构造方法来构造。
通过获取构造器对象来实例化对象
代码语言:javascript复制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相关
代码语言:javascript复制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相关
代码语言:javascript复制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()
方法可以拿到所有的属性
未完待续…随时补充!
版权声明: 如无特别声明,本文版权归 月梦の技术博客 所有,转载请注明本文链接。
(采用 CC BY-NC-SA 4.0 许可协议进行授权)
本文标题:《 Java反射机制 》
本文链接:https://ymiir.netlify.app//java/Java反射机制.html
本文最后一次更新为 天前,文章中的某些内容可能已过时!