Java反射机制
一、概述
Java反射机制定义
Java反射机制是在运行状态
中,对于任意一个类,都能够知道这个类中的所有属性
和方法
;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Java 反射机制的功能
1.在运行时判断任意一个对象所属的类。
2.在运行时构造任意一个类的对象。
3.在运行时判断任意一个类所具有的成员变量和方法。
4.在运行时调用任意一个对象的方法。
5.生成动态代理。
Java 反射机制的应用场景
1.逆向代码 ,例如反编译
2.与注解相结合的框架 例如Retrofit
3.单纯的反射机制应用框架 例如EventBus
4.动态生成类框架 例如Gson
学习反射
获得完整的包名和类名
有三种方式。不过一般都是使用第一种,通过包名+类名获取需要的类,扩展性好很多:
package com.demo;
public class ReflectionDemo {
public static void main(String[] args) {
try {
//第一种(推荐使用)
Class<?> cls1 = Class.forName("com.demo.Person");
System.out.println(cls1.getName());
// 第二种
Person per = new Person();
Class<?> cls2 = per.getClass();
System.out.println(cls2.getName());
// 第三种
Class<?> cls3 = Person.class;
System.out.println(cls3.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Person {}
打印结果
com.demo.Person
com.demo.Person
com.demo.Person
生成类的实例对象
使用Class对象的newInstance()
方法来创建该Class对象对应类的实例。这种方式要求该Class对象的对应类有默认构造器,而执行newInstance()方法时实际上是利用默认构造器来创建该类的实例。
反射进行类的实例化并获取构造方法
根据构造方法是否有参数,参数的类型有关。
package com.demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectionDemo {
public static void main(String[] args) {
try {
// 获取类
Class<?> cls1 = Class.forName("com.demo.Person");
// 直接获取实例(ps:需要获取的类中有无参构造方法,并且该构造方法不能是private的)
Person person = (Person) cls1.newInstance();
System.out.println("[ 直接获取实例 ]person.print() :" + person.print());
System.out.println("-----------------");
// 获取全部构造方法
Constructor<?>[] con = cls1.getConstructors();
for (int i = 0; i < con.length; i++) {
System.out.println("[ 获取全部构造方法 ]constructor[" + i + "] :" + con[i].toString());
}
System.out.println("-----------------");
// 通过带参数的构造方法获取实例(对应上面显示的构造方法顺序)
Person per1 = (Person) con[1].newInstance("maxchan");
System.out.println("[ 获取带参数的构造方法实例 ] : name:" + per1.getName());
Person per2 = (Person) con[2].newInstance("maxchan", "男");
System.out.println("[ 获取带参数的构造方法实例 ] : name:" + per2.getName() + " sex:" + per2.getSex());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
class Person {
private String name; // 姓名
private String sex; // 性别
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String name, String sex) {
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public String print() {
return "print";
}
}
运行结果:
[ 直接获取实例 ]person.print() :print
-----------------
[ 获取全部构造方法 ]constructor[0] :public com.demo.Person()
[ 获取全部构造方法 ]constructor[1] :public com.demo.Person(java.lang.String)
[ 获取全部构造方法 ]constructor[2] :public com.demo.Person(java.lang.String,java.lang.String)
-----------------
[ 获取带参数的构造方法实例 ] : name:maxchan
[ 获取带参数的构造方法实例 ] : name:maxchan sex:男
查看类的方法
查看类有什么方法,可以使用getMethods()
和getDeclaredMethods()
。
getMethods()只能获取该类以及父类使用public修饰的方法;getDeclaredMethods()获取的是此类的所有方法,没有获取父类的方法。
若要查看某个父类中的所有方法,可以通过类cls1.getSuperclass()先获取该父类,再调用getDeclaredMethods()方法。
当通过Method的invoke()
方法来调用对应的方法时,Java会要求程序必须有调用该方法的权限。如果程序确实需要调用某个对象的private方法,则可以先调用Method对象的如下方法。
setAccessible(boolean flag)
:将Method对象的acessible设置为指定的布尔值。值为true,指示该Method在使用时应该取消Java语言的访问权限检查;值为false,则知识该Method在使用时要实施Java语言的访问权限检查。
package com.demo;
import java.lang.reflect.Method;
public class ReflectionDemo {
public static void main(String[] args) {
try {
// 获取类
Class<?> cls1 = Class.forName("com.demo.Person");
// 获取方法(只有使用public修饰符的方法)
Method[] method = cls1.getMethods();
for (int i = 0; i < method.length; i++) {
System.out.println("method[" + i + "] :" + method[i]);
}
System.out.println("--------------------------");
//获取全部方法(使用缺省、pulbic、private、protected修饰的方法)
Method[] method_all = cls1.getDeclaredMethods();
for (int i = 0; i < method_all.length; i++) {
System.out.println("method_all[" + i + "] :" + method_all[i]);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Person {
String defaultString() {
return "defaultString";
}
private String privateString() {
return "privateString";
}
protected String protectedString() {
return "protectedString";
}
public String publicString() {
return "person";
}
}
打印结果
method[0] :public java.lang.String com.demo.Person.publicString()
method[1] :public final void java.lang.Object.wait() throws java.lang.InterruptedException
method[2] :public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
method[3] :public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
method[4] :public boolean java.lang.Object.equals(java.lang.Object)
method[5] :public java.lang.String java.lang.Object.toString()
method[6] :public native int java.lang.Object.hashCode()
method[7] :public final native java.lang.Class java.lang.Object.getClass()
method[8] :public final native void java.lang.Object.notify()
method[9] :public final native void java.lang.Object.notifyAll()
--------------------------
method_all[0] :java.lang.String com.demo.Person.defaultString()
method_all[1] :private java.lang.String com.demo.Person.privateString()
method_all[2] :protected java.lang.String com.demo.Person.protectedString()
method_all[3] :public java.lang.String com.demo.Person.publicString()
获取类的某个具体方法
和上面相同,若只需获取public方法,用getMethod([具体的方法名],[参数类型])
;若需要获取使用其它修饰符的方法,则需使用getDeclaredMethod([具体的方法名],[参数类型])
package com.demo;
import java.lang.reflect.Method;
public class ReflectionDemo {
public static void main(String[] args) {
try {
// 获取类
Class<?> cls1 = Class.forName("com.demo.Person");
// 获取类的某个具体的方法
Method method = cls1.getMethod("publicString");
System.out.println("method.getName() :" + method.getName());
System.out.println("--------------------------");
// 获取类的某个具体的方法
Method declared_method = cls1.getDeclaredMethod("privateString");
System.out.println("declared_method.getName() :" + declared_method.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
class Person {
String defaultString() {
return "defaultString";
}
private String privateString() {
return "privateString";
}
protected String protectedString() {
return "protectedString";
}
public String publicString() {
return "person";
}
}
打印结果:
method.getName() :publicString
--------------------------
declared_method.getName() :privateString
调用类的方法
package com.demo;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionDemo {
public static void main(String[] args) {
try {
// 获取类
Class<?> cls1 = Class.forName("com.demo.Person");
Person per = (Person) cls1.newInstance();
// 调用带参数的方法(第一个参数使用方法名,后面的参数是对应的参数类型,若有多个参数则显示多个参数类型,若无参数,则不显示)
//invoke方法的第一个参数是类的实例
Method param_method = cls1.getMethod("setContent", String.class);
param_method.invoke(per, "content");
// 调用不带参数的方法获取数据
Method method = cls1.getMethod("getContent");
String result = (String) method.invoke(per);
System.out.println("[ 调用不带参数的方法获取数据 ] :" + result);
// 调用不带参数的静态方法获取数据(只需将invoke方法的第一个参数改成null即可)
Method static_method = cls1.getMethod("getStaticContent");
String static_result = (String) static_method.invoke(null);
System.out.println("[ 调用不带参数的静态方法获取数据 ] :" + static_result);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
class Person {
public String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public static String getStaticContent() {
return "static content";
}
}
运行结果:
[ 调用不带参数的方法获取数据 ] :content
[ 调用不带参数的静态方法获取数据 ] :static content
变量的获取和变量值的设置
变量的获取使用Field,跟Method的用法很像。有getFields()
以及getDeclaredFields()
。
getDeclaredFields()可以获取全部变量
getFields()只能获取用public修饰的变量。
Field提供了两组方法来读取或设置成员变量的值:
getXXX(Object obj):获取obj对象的该成员变量的值。此处的XXX对应8种基本类型。如果该成员变量的类型是引用类型,则取消get后面的XXX。
setXXX(Object obj,XXX val):将obj对象的该成员变量设置成val值。
package com.demo;
import java.lang.reflect.Field;
public class ReflectionDemo {
public static void main(String[] args) {
try {
// 获取类
Class<?> cls1 = Class.forName("com.demo.Person");
// 获取public修饰的变量
Field[] field = cls1.getFields();
for (Field f : field) {
System.out.println("[ 获取public修饰的变量 ] (getFields):" + f.getName());
}
System.out.println("-----------------------------");
// 获取全部的变量
Field[] declared_field = cls1.getDeclaredFields();
for (Field f : declared_field) {
System.out.println("[ 获取全部的变量 ] (declared_field):" + f.getName());
}
System.out.println("-----------------------------");
// 通过变量名,设置或获取变量值
Person person = (Person) cls1.newInstance();
Field field1 = cls1.getDeclaredField("sex");
System.out.println("[ 获取变量名 ] :" + field1.get(person));
// 设置变量值
field1.set(person, "女");
System.out.println("[ 设置变量名 ] :" + field1.get(person));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
class Person {
private String name; // 姓名
public String sex = "男"; // 性别
}
运行结果:
[ 获取public修饰的变量 ] (getFields):sex
-----------------------------
[ 获取全部的变量 ] (declared_field):name
[ 获取全部的变量 ] (declared_field):sex
-----------------------------
[ 获取变量名 ] :男
[ 设置变量名 ] :女