什么是反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的以及动态调用对象的方法的功能称为Java的反射机制。
java反射机制提供的功能:
在运行时判定任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判定任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的方法
反射应用场景
操作因访问权限限制的属性和方法
如private属性和方法,又如在android开发中,阅读sdk源码会发现有什么多方法加了hide注解,是google为了安全起见加的隐藏,应用层是无法直接访问的,这时可以通过反射在运行时调用(android 9.0 加了白名单机制,在黑名单的接口即使通过反射都无法调用)。
实现自定义注解
android有很多火爆的开源框架都应用了自定义注解和反射,如EventBus,Retrofit;
动态加载插件
很多大型App都用了动态加载和热修复的插件化技术,也利用了反射的方式,实现宿主(Host)对插件(LibPlugin)的调用,详细可以了解插件化开源框架,如VirtualApp,DroidPlugin,RePlugin
Java反射机制
反射涉及到四个核心类
java.lang.Class.java:类对象;
java.lang.reflect.Constructor.java:类的构造器对象;
java.lang.reflect.Method.java:类的方法对象;
java.lang.reflect.Field.java:类的属性对象;
反射工作原理
要正确理解Java反射机制就得了解Class这个类,反射正是对Class类进行操作。当我们编写好一个Java程序(Java文件),会先将其编译(生成class文件),而运行程序,JVM会加载class文件到内存,并产生一个Class对象,通过这个Class对象我们就能获得加载到虚拟机当中这个Class对象对应的父类、接口、方法、成员以及构造方法的声明和定义等信息,我们通过new的形式创建对象实际上就是通过这些Class来创建。
反射的工作原理就是借助Class.java、Constructor.java、Method.java、Field.java这四个类在程序运行时动态访问和修改任何类的行为和状态。
反射常用的API
1、获取反射中的Class对象
在 Java API 中,获取 Class 类对象有三种方法:
第一种,使用 Class.forName 静态方法。
当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。
Class clz = Class.forName("java.lang.String");
第二种,使用 .class 方法。
这种方法只适合在编译前就知道操作的 Class。
Class clz = String.class;
第三种,使用类对象的 getClass() 方法。
String str = new String("Hello");
Class clz = str.getClass();
2、通过反射创建类对象
通过反射创建类对象主要有两种方式:通过 Class 对象的 newInstance() 方法、通过 Constructor 对象的 newInstance() 方法。
第一种:通过 Class 对象的 newInstance() 方法。
Class clz = Stock.class;
Stock stock = (Stock)clz.newInstance();
第二种:通过 Constructor 对象的 newInstance() 方法
Class clz = Stock.class;
Constructor constructor = clz.getConstructor();
Stock stock = (Stock)constructor.newInstance();
通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。
Class clz = Stock.class;
Constructor constructor = clz.getConstructor(String.class, String.class,String.class);
Stock stock= (Stock)constructor.newInstance("000001", "平安银行","sz");
3、通过反射获取类属性、方法、构造器
我们通过 Class 对象的 getFields() 、getDeclaredFields()方法获取属性,通过getMethod()、getDeclaredMethod获取方法。
带的方法只能获取类本身包括私有在内的属性或方法,而不带Declared的方法不能获取私有属性或方法,但包括父类及自身的所有公有属性或方法。
Class clz = Stock.class;
Field[] fields = clz.getFields();//包括父类的所有公有属性(不包括私有的)
Field[] declaredFields = clz.getDeclaredFields();//自身的公有、私有属性
Method[] methods = clz.getMethods();//包括父类的所有公有方法(不包括私有的)
Method[] declaredMethods = clz.getDeclaredMethods();//自身的公有、私有方法
4、通过反射获取属性值及执行方法
Class stockClass= Stock.class;
Object stockObject = stockClass.newInstance();
Field marketField = stockClass.getDeclaredField("market");
marketField.setAccessible(true);//私有属性或方法,必须设置accessible=true,否则抛出IllegalAccessException异常
Log.i(TAG, "reflectPrivateField:" + marketField.get(stockObject));
Method method = stockClass.getDeclaredMethod("getTrend", String.class);
method.setAccessible(true);
Object trend = method.invoke(stockObject, "5");
Log.i(TAG, "reflectPrivateMethod:" + trend);
完整代码示例
Prodct.java
public class Product {
private String mCode;
private String mName;
public String type;
public void setCode(String code) {
mCode = code;
}
public void setName(String name) {
mName = name;
}
public String getCode() {
return mCode;
}
public String getName() {
return mName;
}
public String toString() {
return "Product{code=" + mCode + ",name=" + mName + ",type=" + type + "}";
}
}
Stock.java
public class Stock extends Product {
private String market = "sz";
private int getTrend(String price) {
if (TextUtils.isEmpty(price)) {
return 0;
} else if (price.contains("-")) {
return -1;
} else {
return 1;
}
}
@Call("do_sth_call")
public void doSth() {
Log.i("Stock", "do sth call");
}
public String toString() {
return "Stock{code=" + getCode() + ",name=" + getName() + ",type=" + type + ",market=" + market + "}";
}
}
Call.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Call {
String value();
}
ReflectTest.java
public class ReflectTest {
public static final String TAG = "reflect";
public static void testReflect() {
reflectNewInstance();
reflectPrivateField();
reflectPrivateMethod();
reflectAnnotation();
reboot();
}
/**
* 创建对象
*/
public static void reflectNewInstance() {
try {
Class> stockClass = Class.forName("com.demo.reflect.Stock");
Object stockObject = stockClass.newInstance();
Product product = (Product) stockObject;
product.setCode("000001");
product.setName("平安银行");
product.type = "1";
Log.i(TAG, "reflectNewInstance stock=" + product.toString());
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
/**
* 反射访问私有属性
*/
public static void reflectPrivateField() {
try {
Class> stockClass = Class.forName("com.demo.reflect.Stock");
Object stockObject = stockClass.newInstance();
Field marketField = stockClass.getDeclaredField("market");
marketField.setAccessible(true);
Log.i(TAG, "reflectPrivateField:" + marketField.get(stockObject));
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
/**
* 反射访问私有方法
*/
public static void reflectPrivateMethod() {
try {
Class> stockClass = Class.forName("com.demo.reflect.Stock");
Object stockObject = stockClass.newInstance();
Method method = stockClass.getDeclaredMethod("getTrend", String.class);
method.setAccessible(true);
Object trend = method.invoke(stockObject, "5");
Log.i(TAG, "reflectPrivateMethod:" + trend);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
/**
* 反射调用注解方法
*/
public static void reflectAnnotation() {
try {
Class> stockClass = Class.forName("com.demo.reflect.Stock");
Object stockObject = stockClass.newInstance();
Method[] methods = stockClass.getMethods();
for (Method method : methods) {
Call call = method.getAnnotation(Call.class);
if (call != null && "do_sth_call".equals(call.value())) {
method.invoke(stockObject);
}
}
} catch (Exception e) {
}
}
// 重启手机
public static void reboot() {
try {
Class> cServiceManager = Class.forName("android.os.ServiceManager");
Method mGetService = cServiceManager.getMethod("getService", String.class);
Object oPowerManagerService = mGetService.invoke(null, Context.POWER_SERVICE);
Class> cIPowerManagerStub = Class.forName("android.os.IPowerManager$Stub");
Method mReboot = cIPowerManagerStub.getMethod("reboot", boolean.class, String.class, boolean.class);
Method mAsInterface = cIPowerManagerStub.getMethod("asInterface", IBinder.class);
Object oIPowerManager = mAsInterface.invoke(null, oPowerManagerService);
mReboot.invoke(oIPowerManager, true, null, true);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
反射执行reboot方法,会抛出异常,java.lang.reflect.InvocationTargetException,异常原因是java.lang.SecurityException: Neither user 10547 nor current process has android.permission.REBOOT。原因是没有reboot的操作权限。
Caused by: java.lang.SecurityException: Neither user 10547 nor current process has android.permission.REBOOT.
W/System.err: at android.os.IPowerManager$Stub$Proxy.reboot(IPowerManager.java:1596)
W/System.err: at com.android.server.power.PowerManagerService$BinderService.reboot(PowerManagerService.java:5662)
总结
本文总结了反射的作用及应用场景,列举了常用的API,并给出代码示例。反射既有优点也有缺点。
优点是提供了灵活的机制,想对类做啥就做啥,一些黑科技就是用了反射实现了一般开发无法做到的功能,另外很多框架应用了反射使得开发更加便利高效。
缺点是运行时反射操作方法会比直接调用性能慢,取决于如何反射,一般可以忽略,另外一个问题就是安全性,随意修改私有属性和访问私有方法,破坏了类的封装性,可能潜在逻辑隐患,再一个是在安卓上应用,可能出现适配问题。因此应用反射前要充分了解这些缺点带来的影响。
参考
深入浅出反射
https://zhuanlan.zhihu.com/p/21423208
大白话说Java反射:入门、使用、原理
https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html
java.lang.Class
java.lang.reflect.Method