前言
认识反射机制的作用,用途以及常用的类和方法。
一、什么是反射?
想要知道反射机制,得先了解java的两个重要概念:
- 编译期:是指把源码交给编译器编译成计算机可以执行的文件的过程。在Java中也就是把Java代码编成class文件的过程.编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。
- 运行期:是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来,在Java中把磁盘中的代码放到内存中就是类加载过程,类加载是运行期的开始部分。
而java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。简单的说就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
二、反射的作用
通过反射可以使程序代码访问装载到JVM中的类的内部信息。
-
获取已装载类的成员变量信息
-
获取已装载类的方法
-
获取已装载类的构造方法信息
三、常用类及其常用方法
1.Class类
要想知道一个类的属性和方法,必须先获得到该类的字节码文件对象。那么就得使用Class类中的方法。
如何获取一个类的Class实例?有三种方法:
方法一:直接通过一个类的静态变量class获取
Class cls = String.Class;
方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取
String s = “Hello”
Class cls = s.getClass();
方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取
Class cls = Class.forName(“java.lang.String”);
注:因为Class实例在JVM中是唯一的,所以上述方法获取的Class实例是同一个实例。
1.1.Class类的常用方法
类型 | 访问方法 | 返回值类型 | 说明 |
---|---|---|---|
包路径 | getPackage() | Packge对象 | 获取该类的存放路径 |
类名称 | getName() | String 对象 | 获取该类的名称 |
继承类 | getSuperclass() | Class 对象 | 获取该类继承的类 |
实现接口 | getlnterfaces() | Class 型数组 | 获取该类实现的所有接口 |
构造方法 | getConstructors() | Constructor 型数组 | 获取所有权限为 public 的构造方法 |
构造方法 | getDeclaredContruectors() | Constructor 对象 | 获取当前对象的所有构造方法 |
方法 | getMethods() | Methods 型数组 | 获取所有权限为 public 的方法 |
方法 | getDeclaredMethods() | Methods 对象 获取当前对象的所有方法 | |
成员变量 | getFields() | Field 型数组 | 获取所有权限为 public 的成员变量 |
成员变量 | getDeclareFileds() | Field 对象 | 获取当前对象的所有成员变量 |
1.2.Class类的常用方法演示
为了方便演示我创建了两个类:
package com.xync.hja;
public class Father {
public String fatherName;
private int fatherAge;
public void hello() {
System.out.println("I am father!");
}
public Father() {
super();
}
public Father(String fatherName, int fatherAge) {
super();
this.fatherName = fatherName;
this.fatherAge = fatherAge;
}
@Override
public String toString() {
return "Father [fatherName=" + fatherName + ", fatherAge=" + fatherAge + "]";
}
}
package com.xync.hja;
public class Son extends Father implements Serializable{
public String sonName;
public String sonAge;
@Override
public void hello() {
System.out.println("I am son!");
}
public Son() {
super();
}
public Son(String sonName, String sonAge) {
super();
this.sonName = sonName;
this.sonAge = sonAge;
}
private Son(String sonName) {
super();
this.sonName = sonName;
}
public void sonHello(String s) {
System.out.println(s);
}
private void privacyMethod() {
System.out.println("PrivateMethod!");
}
@Override
public String toString() {
return "Son [sonName=" + sonName + ", sonAge=" + sonAge + "]";
}
}
1.2.1.getPackage()
public class Test {
public static void main(String[] args) {
Class father = Father.class;
Package fatherPackage = father.getPackage();
System.out.println(fatherPackage);
}
}
结果如图:
1.2.2.getName()
public class Test {
public static void main(String[] args) {
Class father = Father.class;
String fatherName = father.getName();
System.out.println("类的完全限定名是:" + fatherName);
}
}
结果如图:
1.2.3.getSuperclass()
public class Test {
public static void main(String[] args) {
Class son = Son.class;
Class superclass = son.getSuperclass();
System.out.println("son的父类对象是"+superclass);
}
}
结果如图:
1.2.4.getInterfaces()
public class Test {
public static void main(String[] args) {
Class son = Son.class;
Class[] interfaces = son.getInterfaces();
for (Class cls : interfaces) {
System.out.println("son实现的接口是:" + cls);
}
}
}
结果如图:
1.2.5.getConstructors()和getDeclaredConstructors()
public class Test {
public static void main(String[] args) {
Class son = Son.class;
Constructor[] constructors = son.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("-----------------------------------------");
Constructor[] declaredConstructors = son.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
}
}
结果如图:
这里值得注意的是Son只带有一个参数的构造方法的访问修饰符为private,用getConstructors()是访问不到private修饰的构造方法的,而getDeclaredConstructors()可以
1.2.6.getMethods()和getDeclaredMethods()
public class Test {
public static void main(String[] args) {
Class son = Son.class;
Method[] sonMethods = son.getMethods();
Method[] sonDeclaredMethods = son.getDeclaredMethods();
for (Method sonMethod : sonMethods) {
System.out.println("sonMethod为:" + sonMethod.getName());
}
System.out.println("-----------------------------------------------");
for (Method sonDeclaredMethod : sonDeclaredMethods) {
System.out.println("sonMethod为:" + sonDeclaredMethod.getName());
}
}
}
结果如图:
这里值得注意的是,调用getMethods()方法时,不仅把自己类中public方法得到了,还将父类所有的public修饰的方法得到了,而使用getDeclaredMethods()只能得到自己类的所有方法,包括用private修饰符修饰的方法
1.2.7.getFields()和getDeclareFileds()
public class Test {
public static void main(String[] args) {
Class son = Son.class;
Field[] sonFields = son.getFields();
Field[] sonDclaredFields = son.getDeclaredFields();
for (Field sonField : sonFields) {
System.out.println("sonFields为:" + sonField);
}
System.out.println("------------------------------");
for (Field sonDclaredField : sonDclaredFields) {
System.out.println("sonFields为:" + sonDclaredField);
}
}
}
结果如图:
不难发现,本次getFields()方法中也将获得了父类的用public修饰的成员变量,而getDeclareFileds()方法只得到了自己的所有成员变量
2.Counstructor类
我们通常会使用new操作符创建新的实例:
Son son = new Son;
如果想通过反射来创建新的实例,可以调用Class提供的newInstance()方法
Son son = Son.Class.newInstance();
但使用Class.newInstance()是有局限的,它只能调用该类的public无参构造方法。
2.1.Counstructor类的常用方法
类型 | 访问方法 | 返回值类型 | 说明 |
---|---|---|---|
构造方法名称 | getName() | String 对象 | 获取该构造方法的名称 |
修饰符 | getModifiers() | int | 获取该构造方法的访问修饰符(int) |
参数 | getParamenterTypes() | Class 型数组 | 获取构造方法的参数类型 |
对象 | newInstance(Object…args) | 实例对象 | 使用当前的构造方法来创建一个对象 |
2.2.Counstructor类的常用方法演示
2.2.1.newInstance(Object…args)()
刚才我们说到使用Class.newInstance()是无法调用非public修饰的构造方法及有参的构造方法的,我们可以用Counstructor类的**newInstance(Object…args)**来调用
public class Test {
public static void main(String[] args) throws Exception {
Constructor<Father> constructor = Father.class.getConstructor(String.class);
Father father = constructor.newInstance("father");
System.out.println(father);
}
}
结果如图:
注意,如果Father.class.getConstructor(参数类型)中传入的参数类型与 constructor.newInstance(参数类型)传入的参数类型不一致的话,会抛 java.lang.IllegalArgumentException异常
2.2.2.getName()
public class Test {
public static void main(String[] args) {
Class son = Son.class;
Constructor[] constructors = son.getConstructors();
for (Constructor constructor : constructors) {
System.out.println("构造方法名为:" + constructor.getName());
}
}
}
结果如图:
2.2.3.getModifiers()
public class Test {
public static void main(String[] args) {
Class son = Son.class;
Constructor[] sonDeclaredconstructors = son.getDeclaredConstructors();
for (Constructor sonDeclaredconstructor : sonDeclaredconstructors) {
System.out.println("构造方法名为:" + sonDeclaredconstructor.getModifiers());
}
}
}
结果如图:
我们可以发现getModifiers()返回值的类型是int,我们并不好理解1或者2的意义,所以我们可以用Modifier工具类的toString()方法
public class Test {
public static void main(String[] args) {
Class son = Son.class;
Constructor[] sonDeclaredconstructors = son.getDeclaredConstructors();
for (Constructor sonDeclaredconstructor : sonDeclaredconstructors) {
System.out.println("构造方法名为:" + Modifier.toString(sonDeclaredconstructor.getModifiers()));
}
}
}
结果如图:
2.2.4.getParamenterTypes()
public class Test {
public static void main(String[] args) throws Exception {
Constructor<Father> constructor = Father.class.getConstructor(String.class);
Class<?>[] parameterTypes = constructor.getParameterTypes();
for (Class parameterType : parameterTypes) {
System.out.println(parameterType.getName());
}
}
}
结果如图:
3.Field类
对于任意的一个Object实例,只要我们获取了它的Class,就可以获取它的一切信息,比如通过一个Class实例获取字段信息(成员变量)。
3.1.Field类的常用方法
类型 | 访问方法 | 返回值类型 | 说明 |
---|---|---|---|
字段名称 | getName() | String 对象 | 返回该Field对象所表示的字段的名称 |
字段类型 | getType() | Class | 表示获取当前Field对象所表示字段的类型 |
修饰符 | getModifiers() | int类型 | 表示获取当前Field对象所表示字段的修饰符 |
对象 | get(Object obj) | Object类型 | 表示获取指定对象obj中当前Field对象所表示的字段的值 |
对象 | set(Object obj, Object value) | Object类型 | 返回void类型,表示将指定对象obj中当前Field对象所表示的字段的值设置为value |
3.2.Field类的常用方法演示
3.2.1.getName()
public class Test {
public static void main(String[] args) {
Class cls = Father.class;
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
}
}
结果如图:
3.2.2.getType()
public class Test {
public static void main(String[] args) {
Class cls = Father.class;
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getType());
}
}
}
结果如图:
3.2.3.getType()
public class Test {
public static void main(String[] args) {
Class cls = Father.class;
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println(Modifier.toString(field.getModifiers()));
}
}
}
结果如图:
3.2.4.get(Object obj)
public class Test {
public static void main(String[] args) throws Exception {
Class cls = Father.class;
Constructor constructor = cls.getConstructor(String.class);
Father father = (Father) constructor.newInstance("Bob");
Field field = cls.getField("fatherName");
System.out.println(field.get(father));
}
}
结果如图:
注意:get方法中的参数是要传入你想访问的字段所属类的对象
3.2.5set.(Object obj,Object value)
public class Test {
public static void main(String[] args) Exception {
Class cls = Father.class;
Constructor constructor = cls.getConstructor();
Father father = (Father) constructor.newInstance();
Field field = cls.getField("fatherName");
field.set(father, "Bob(我是通过set方法传入的值~)");
System.out.println("fatherName为:" + field.get(father));
}
}
结果如图:
4.Method类
Method类是Java语言中的一个重要类,它用于表示类或接口中的方法。
4.1.Method类的常用方法
类型 | 访问方法 | 返回值类型 | 说明 |
---|---|---|---|
方法名称 | getName() | String 对象 | 获取该方法对象的名称 |
返回值类型 | getReturnType() | Class<?>[] | 表示获取方法返回类型 |
参数类型 | getParameterTypes() | Class<?>[] | 表示获取方法参数类型数组 |
对象 | invoke(Object obj, Object… args) | Object类型 | 在指定对象上调用此方法 |
4.2.Method类的常用方法演示
4.2.1.getName()
public class Test {
public static void main(String[] args) throws Exception {
Class cls = Son.class;
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
}
}
结果如图:
4.2.2.getReturnName()
public class Test {
public static void main(String[] args) throws Exception {
Class cls = Son.class;
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName()+ "返回值类型是:" + method.getReturnType());
}
}
}
结果如图:
4.2.3.getParameterTypes()
public class Test {
public static void main(String[] args) Exception {
Class cls = Son.class;
Method method = cls.getMethod("sonHello", String.class);
Class[] parameterTypes = method.getParameterTypes();
for (Class parameterType : parameterTypes) {
System.out.println(parameterType.getName());
}
}
}
结果如图:
4.2.4.invoke(Object obj,Object… args)
4.2.4.1.调用方法
public class Test {
public static void main(String[] args) throws Exception{
Class cls = Son.class;
Son father = (Son) cls.newInstance();
Method method = cls.getMethod("sonHello", String.class);
method.invoke(father, "通过invoke调用Son的方法");
}
}
结果如图:
4.2.4.2.调用静态方法
public class Test {
public static void main(String[] args) throws Exception {
Method m = Integer.class.getMethod("parseInt", String.class);
Integer n = (Integer) m.invoke(null, "12345");
System.out.println(n);
}
}
结果如图:
我们可以发现调用静态方法时,invoke(Object obj,Object… args)方法的第一个参数填null就可以调用静态方法了
4.2.4.3.调用非public方法
如果想要使用非public方法,我们需要通过在Method.setAccessible(Boolean boolean)方法传入参数true才可以使用,否则会抛异常
public class Test {
public static void main(String[] args) throws Exception {
Son son = new Son();
Method method = son.getClass().getDeclaredMethod("privacyMethod");
method.setAccessible(true);
method.invoke(son);
}
}
结果如图:
4.2.4.4.多态
还有一个问题,在Father类和Son类中都一个hello方法,如果我通过一个父类的引用指向子类创建对象,那么invoke调用的方法执行的是父类的还是子类的呢?
public class Test {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
Father cls = new Son();
Method method = cls.getClass().getMethod("hello");
method.invoke(cls);
}
}
结果如图:
我们可以发现最终执行的是子类的方法,因此,在使用反射调方法时,依然遵循这多态原则