首页 > 代码库 > 深入浅出反射
深入浅出反射
什么是反射?
反射是一种能够在程序运行时动态访问、修改某个类中任意属性(状态)和方法(行为)的机制(包括private实例和方法),java反射机制提供了以下几个功能:
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法。
反射涉及到四个核心类:
java.lang.Class.java:类对象;
java.lang.reflect.Constructor.java:类的构造器对象;
java.lang.reflect.Method.java:类的方法对象;
java.lang.reflect.Field.java:类的属性对象;
反射有什么用?
操作因访问权限限制的属性和方法;
实现自定义注解;
动态加载第三方jar包,解决android开发中方法数不能超过65536个的问题;
按需加载类,节省编译和初始化APK的时间;
反射工作原理
当我们编写完一个Java项目之后,每个java文件都会被编译成一个.class文件,这些Class对象承载了这个类的所有信息,包括父类、接口、构造函数、方法、属性等,这些class文件在程序运行时会被ClassLoader加载到虚拟机中。当一个类被加载以后,Java虚拟机就会在内存中自动产生一个Class对象。我们通过new的形式创建对象实际上就是通过这些Class来创建,只是这个过程对于我们是不透明的而已。
反射的工作原理就是借助Class.java、Constructor.java、Method.java、Field.java这四个类在程序运行时动态访问和修改任何类的行为和状态。
反射实例
分别演示三种获取类信息的方式、获取当前类的所有方法和获取当前类及其父类的所有方法、获取当前类的所有实例和获取当前类及其父类的所有实例、获取父类信息、获取接口信息、比较反射方法和实例的性能差异等几个方面:- 示例类:
父类Personon.java:
<pre name="code" class="java">package com.eebbk.reflectdemo; public class Person { String mName; String mSex; public int mAge; public Person(String aName, String aSex, int aAge) { mName = aName; mSex = aSex; mAge = aAge; } public int getmAge() { return mAge; } public void setmAge(int mAge) { this.mAge = mAge; } public String getmName() { return mName; } public void setmName(String mName) { this.mName = mName; } public String getmSex() { return mSex; } public void setmSex(String mSex) { this.mSex = mSex; } private String getDescription() { return "黄种人"; } }
接口ICompany.java:
package com.eebbk.reflectdemo; public interface ICompany{ String getCompany(); }
子类ProgramMonkey.java:
package com.eebbk.reflectdemo; public class ProgramMonkey extends Person implements ICompany { String mLanguage = "C#"; String mCompany = "BBK"; public ProgramMonkey(String aName, String aSex, int aAge) { super(aName, aSex, aAge); } public ProgramMonkey(String language, String company, String aName, String aSex, int aAge) { super(aName, aSex, aAge); mLanguage = language; mCompany = company; } public String getmLanguage() { return mLanguage; } public void setmLanguage(String mLanguage) { this.mLanguage = mLanguage; } private int getSalaryPerMonth() { return 12306; } @Override public String getCompany() { return mCompany; } }
示例类ReflectActivity.java:
public class ReflectActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_reflect_layout); } public void onClick(View v) { switch (v.getId()) { case R.id.getClassObjectBtnId: { getClassObject(); } break; case R.id.getMethodInfoBtnId: { getMethodInfo(); } break; case R.id.getFieldInfoBtnId: { getFieldInfo(); } break; case R.id.getSuperClassInfoBtnId: { getSuperClass(); } break; case R.id.getInterfaceInfoBtnId: { getInterfaces(); } break; case R.id.compareMethodAndFieldBtnId: { compareCallMethodAndField(); } break; default: { } break; } } private void getClassObject() { Class<?> classObject = null; classObject = getClassObject_1(); LogE("classObject_1 name : " + classObject.getName()); classObject = getClassObject_2(); LogE("classObject_2 name : " + classObject.getName()); classObject = getClassObject_3(); LogE("classObject_3 name : " + classObject.getName()); } private void getMethodInfo() { getAllMethods(); getCurrentClassMethods(); } private void getFieldInfo() { getAllFields(); getCurrentClassFields(); } private void getSuperClass() { ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12); Class<?> superClass = programMonkey.getClass().getSuperclass(); while (superClass != null) { Log.e("programMonkey's super class is : " + superClass.getName()); // 再获取父类的上一层父类,直到最后的 Object 类,Object 的父类为 null superClass = superClass.getSuperclass(); } } private void getInterfaces() { ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12); Class<?>[] interfaceses = programMonkey.getClass().getInterfaces(); for (Class<?> class1 : interfaceses) { Log.e("programMonkey's interface is : " + class1.getName()); } } private void compareCallMethodAndField() { long callMethodCostTime = getCallMethodCostTime(10000); Log.e("callMethodCostTime == " + callMethodCostTime); long callFieldCostTime = getCallFieldCostTime(10000); Log.e("callFieldCostTime == " + callFieldCostTime); } private long getCallMethodCostTime(int count) { long startTime = System.currentTimeMillis(); for (int index = 0; index < count; index++) { ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12); try { Method setmLanguageMethod = programMonkey.getClass().getMethod( "setmLanguage", String.class); setmLanguageMethod.setAccessible(true); setmLanguageMethod.invoke(programMonkey, "Java"); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } return System.currentTimeMillis() - startTime; } private long getCallFieldCostTime(int count) { long startTime = System.currentTimeMillis(); for (int index = 0; index < count; index++) { ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12); try { Field ageField = programMonkey.getClass().getDeclaredField( "mLanguage"); ageField.set(programMonkey, "Java"); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } return System.currentTimeMillis() - startTime; } /** * 获取当前类中的所有方法 * * */ private void getCurrentClassMethods() { ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12); Method[] methods = programMonkey.getClass().getDeclaredMethods(); for (Method method : methods) { LogE("declared method name : " + method.getName()); } try { Method getSalaryPerMonthMethod = programMonkey.getClass().getDeclaredMethod("getSalaryPerMonth"); getSalaryPerMonthMethod.setAccessible(true); // 获取返回类型 Class<?> returnType = getSalaryPerMonthMethod.getReturnType(); Log.e("getSalaryPerMonth 方法的返回类型 : " + returnType.getName()); // 获取方法的参数类型列表 Class<?>[] paramClasses = getSalaryPerMonthMethod.getParameterTypes(); for (Class<?> class1 : paramClasses) { Log.e("getSalaryPerMonth 方法的参数类型 : " + class1.getName()); } // 是否是 private 函数,属性是否是 private 也可以使用这种方式判断 LogE(getSalaryPerMonthMethod.getName()+ " is private "+ Modifier.isPrivate(getSalaryPerMonthMethod.getModifiers())); // 执行方法 Object result = getSalaryPerMonthMethod.invoke(programMonkey); LogE("getSalaryPerMonth 方法的返回结果: " + result); } catch (Exception e) { e.printStackTrace(); } } /** * 获取当前类和父类的所有公有方法 * * */ private void getAllMethods() { ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12); // 获取当前类和父类的所有公有方法 Method[] methods = programMonkey.getClass().getMethods(); for (Method method : methods) { Log.e("method name : " + method.getName()); } try { Method setmLanguageMethod = programMonkey.getClass().getMethod("setmLanguage", String.class); setmLanguageMethod.setAccessible(true); // 获取返回类型 Class<?> returnType = setmLanguageMethod.getReturnType(); Log.e("setmLanguage 方法的返回类型 : " + returnType.getName()); // 获取方法的参数类型列表 Class<?>[] paramClasses = setmLanguageMethod.getParameterTypes(); for (Class<?> class1 : paramClasses) { Log.e("setmLanguage 方法的参数类型 : " + class1.getName()); } // 是否是 private 函数,属性是否是 private 也可以使用这种方式判断 Log.e(setmLanguageMethod.getName() + " is private "+ Modifier.isPrivate(setmLanguageMethod.getModifiers())); // 执行方法 Object result = setmLanguageMethod.invoke(programMonkey, "Java"); Log.e("setmLanguage 方法的返回结果: " + result); } catch (Exception e) { e.printStackTrace(); } } private Class<?> getClassObject_1() { return ProgramMonkey.class; } private Class<?> getClassObject_2() { ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12); return programMonkey.getClass(); } private Class<?> getClassObject_3() { try { return Class.forName("com.eebbk.reflectdemo.ProgramMonkey"); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } /** * 得到当前类的所有实例 * * */ private void getCurrentClassFields() { ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12); // 获取当前类的所有属性 Field[] publicFields = programMonkey.getClass().getDeclaredFields(); for (Field field : publicFields) { Log.e("declared field name : " + field.getName()); } try { // 获取当前类的某个属性 Field ageField = programMonkey.getClass().getDeclaredField("mAge"); // 获取属性值 Log.e(" my age is : " + ageField.getInt(programMonkey)); // 设置属性值 ageField.set(programMonkey, 10); LogE(" my age is : " + ageField.getInt(programMonkey)); } catch (Exception e) { e.printStackTrace(); } } } /** * 得到当前类和父类的所有公有属性 * * */ private void getAllFields() { ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12); // 得到当前类和父类的所有公有属性 Field[] publicFields = programMonkey.getClass().getFields(); for (Field field : publicFields) {<pre name="code" class="java"> try {Log.e("field name : " + field.getName()); // 获取当前类和父类的某个公有属性Field ageField = programMonkey.getClass().getField("mAge"); LogE(" age is : " + ageField.getInt(programMonkey)); ageField.set(programMonkey, 8); LogE(" my age is : " + ageField.getInt(programMonkey));
} catch (Exception e) { e.printStackTrace(); } } }
- 演示结果:
三种获取类信息的方式:
获取当前类的方法、获取当前类和父类的所有公有方法:
获取当前类的所有实例、获取当前类和父类的所有公有实例:
获取父类信息:
获取接口信息:
比较反射方法和实例的性能差异:
通过上面的示例可以发现,通过反射能够完成之前所描述的事情,并且反射方法比反射实例要慢很多。
反射的特点
优点
- 灵活、自由度高:不受类的访问权限限制,想对类做啥就做啥;
缺点
性能问题:通过反射访问、修改类的属性和方法时会远慢于直接操作,但性能问题的严重程度取决于在程序中是如何使用反射的。如果使用得很少,不是很频繁,性能将不会是什么问题;
安全性问题:反射可以随意访问和修改类的所有状态和行为,破坏了类的封装性,如果不熟悉被反射类的实现原理,随意修改可能导致潜在的逻辑问题;
兼容性问题:因为反射会涉及到直接访问类的方法名和实例名,不同版本的API如果有变动,反射时找不到对应的属性和方法时会报异常;
说明
通过反射访问方法比实例慢很多;
有用到反射的类不能被混淆;
反射存在性能问题,但使用不频繁、按需使用时,对程序性能影响并不大;
反射存在安全性问题,因为可以随意修改类的所有状态和行为(包括private方法和实例);
使用反射访问Android的API时需要注意因为不同API版本导致的兼容性问题;
深入浅出反射