了解反射
Java反射是指在程序运行期获取对象所有信息。
简单来说,我们可以在对一个实例一无所知的情况下通过反射来获取、调用对象的字段(Field)、方法(Method)、通过构造方法获取实例以及了解类的继承关系。
你一定记得,在学习JDBC(Java DataBase Connectivity,Java程序访问数据库的标准接口)的时候,获取一个数据库连接,首先要加载驱动:
Class.forName("com.mysql.jdbc.Driver");
如果项目中添加了MySQL驱动的jar包,那么上面的语句就可以将Driver类成功注册到java.sql.DriverManager当中,然后再使用Connection con = DriverManager.getConnection(url , username , password ) ;
就可以获取数据库连接。
Class.forName("XXX");
语法就是利用反射获取Class的一种,除此之外还有其他两种,后续会说到。
Class对象
JVM每加载一个class到内存,就会为其创建一个Class对象实例与之关联。
例如:JVM加载String类时,首先加载String.class文件,经过加载->链接->初始化将类加载到内存,然后会为String类
创建一个Class实例:
Class cls = new Class(String);
这个Class实例是由JVM内部来创建的,通过JDK源码我们可以看出:
public class Class{
private Class(){}
}
构造方法是私有的,所以我们自己写的Java程序是无法创建的。(这里可以留一个问号,因为文章摘要写的是私有构造并非一定访问不了,这里怎么就不能自己创建了呢?)
Class包含了它所指向的class的所有信息:包括类名、包名、父类、实现的接口、所有方法、字段等。
获取Class有三种方式:
- 类名.class
Class cls = String.class;
- 对象名.getClass()
String str = "hello";
Class cls = str.getClass();
- Class.forName(“java.lang.String”)
Class cls = Class.forName("java.lang.String");
上述三种方法获取的Class实例都是同一个实例,因为Class实例在JVM中是唯一的,我们可以通过==
来比较两个Class实例是否相同:
// 判断两个Class是否是同一个实例,使用 == 和 instanceof 的区别是
// instanceof 不仅判断是否是同一个Class,还会判断前者是否是后者的子类
Integer n = new Integer(123);
boolean b1 = n instanceof Integer; // true,因为n是Integer类型
boolean b2 = n instanceof Number; // true,因为n是Number类型的子类
boolean b3 = n.getClass() == Integer.class; // true,因为n.getClass()返回Integer.class
boolean b4 = n.getClass() == Number.class; // false,因为Integer.class!=Number.class
注:除了类、接口、数组会有对应的Class实例外,JVM也为每一种基本类型如int创建Class,通过int.class访问。
如果我们获取了Class,我们就可以用它来创建对象实例以及实现各种 非 常规*的操作。
访问字段 Field
通过Class实例,可以使用以下方法来获取对象的字段信息:
根据字段名获取某个字段
Field getField(name)
:获取某个public Field
(包括从父类继承过来的字段)Field getDeclaredField(name)
:获取当前类的某个 Field
(不包括父类)
获取字段列表
Field[] getFields()
:获取所有public Field
(包括父类)Field[] getDeclaredFields()
:获取当前类的 所有 Field
(不包括父类)
注意到包含 Declared关键字的方法可以访问类的所有字段,包括private字段,然后通过setAccessible(true)
方法可以获取字段的值。
Field对象包含了一个字段的所有信息:
getName()
: 返回字段名称- getType(): 返回字段类型,也是Class实例,例如String.class
getModifiers
: 获取字段的修饰符,返回值是int类型,不同bit表示不同含义
以String类的value字段为例:
public final class String {
// value 字段定义
private final byte[] value;
}
利用反射获取value字段的信息
// 获取String类所有字段信息,包括private字段
Field field = String.class.getDeclaredField("value");
field.getName(); // value
field.getType(); // class [B 表示byte[]类型
int mofify = field.getModifiers();
Modifier.isFinal(modify); // true
Modifier.isPublic(modify); // false
Modifier.isProtected(modify); // false
Modifier.isPrivate(modify); // true
Modifier.isStatic(modify); // false
通过Field获取属性的值
通过Field设置属性的值
调用方法 Method
通过Class对象可以访问字段及获取字段的值,同样的,也可以通过它来获取一个类的方法信息:
根据字段名获取某个方法,通过参数列表来区分重载方法
Method getMethod(name, Class...)
:获取某个public Method
(包括从父类继承过来的方法)Method getDeclaredMethod(name, Class...)
:获取当前类的某个 Method(不包括父类)
获取方法列表
Method[] getMethods()
:获取所有public Method
(包括从父类继承过来的方法)Method[] getDeclaredMethods()
:获取当前类的 所有 Method
(不包括父类)
一个Method包含一个方法的所有信息:
getName()
: 返回方法名称getReturnType()
返回方法返回值类型,Class类型getParameterTypes()
: 返回方法参数类型,Class数组getModifiers()
: 返回方法的修饰符,int类型,不同的bit表示不同的含义
调用方法
调用静态方法
调用非public方法
多态