深入浅出谈反射

架构之美

共 10911字,需浏览 22分钟

 ·

2021-10-24 17:45


-     什么是反射    -


反射是一种能够在程序运行时动态访问、修改某个类中任意属性(状态)和方法(行为)的机制(包括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:

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; }
@Overridepublic String getCompany(){return mCompany; }}

示例类ReflectActivity.java:

public class ReflectActivity extends Activity{@Overrideprotected 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) { LogE("programMonkey's super class is : " + superClass.getName());
superClass = superClass.getSuperclass(); } }
private void getInterfaces() { ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12); Class[] interfaceses = programMonkey.getClass().getInterfaces();for (Class class1 : interfaceses) { LogE("programMonkey's interface is : " + class1.getName()); } }
private void compareCallMethodAndField(){long callMethodCostTime = getCallMethodCostTime(10000); LogE("callMethodCostTime == " + callMethodCostTime);long callFieldCostTime = getCallFieldCostTime(10000); LogE("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(); LogE("getSalaryPerMonth 方法的返回类型 : " + returnType.getName());

Class[] paramClasses = getSalaryPerMonthMethod.getParameterTypes() ;for (Class class1 : paramClasses) { LogE("getSalaryPerMonth 方法的参数类型 : " + class1.getName()); }

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) { LogE("method name : " + method.getName()); }
try { Method setmLanguageMethod = programMonkey.getClass().getMethod("setmLanguage", String.class); setmLanguageMethod.setAccessible(true);

Class returnType = setmLanguageMethod.getReturnType(); LogE("setmLanguage 方法的返回类型 : " + returnType.getName());

Class[] paramClasses = setmLanguageMethod.getParameterTypes() ;for (Class class1 : paramClasses) { LogE("setmLanguage 方法的参数类型 : " + class1.getName()); }

LogE(setmLanguageMethod.getName() + " is private " + Modifier.isPrivate(setmLanguageMethod.getModifiers()));

Object result = setmLanguageMethod.invoke(programMonkey, "Java"); LogE("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) { LogE("declared field name : " + field.getName()); }
try {
Field ageField = programMonkey.getClass().getDeclaredField("mAge");
LogE(" 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) { LogE("field name : " + field.getName()); }
try {
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(); } }
private void LogE(String msg){ Log.e("Reflection", "============== " + msg); }}

  • 演示结果:

三种获取类信息的方式:

获取当前类的方法、获取当前类和父类的所有方法:

获取当前类的所有实例、获取当前类和父类的所有实例:

获取父类信息:

获取接口信息:

比较反射方法和实例的性能差异:

通过上面的示例可以发现,通过反射能够完成之前所描述的事情,并且反射方法比反射实例要慢很多。

反射的特点

优点

  • 灵活、自由度高:不受类的访问权限限制,想对类做啥就做啥;

缺点

  • 性能问题:通过反射访问、修改类的属性和方法时会远慢于直接操作,但性能问题的严重程度取决于在程序中是如何使用反射的。如果使用得很少,不是很频繁,性能将不会是什么问题;

  • 安全性问题:反射可以随意访问和修改类的所有状态和行为,破坏了类的封装性,如果不熟悉被反射类的实现原理,随意修改可能导致潜在的逻辑问题;

  • 兼容性问题:因为反射会涉及到直接访问类的方法名和实例名,不同版本的API如果有变动,反射时找不到对应的属性和方法时会报异常;


-     说明    -

  • 通过反射访问方法比实例慢很多;

  • 有用到反射的类不能被混淆;

  • 反射存在性能问题,但使用不频繁、按需使用时,对程序性能影响并不大;

  • 反射存在安全性问题,因为可以随意修改类的所有状态和行为(包括private方法和实例);

  • 使用反射访问Android的API时需要注意因为不同API版本导致的兼容性问题;


-     参考资料    -


  • The Reflection API
    (https://docs.oracle.com/javase/tutorial/reflect/ )
  • 公共技术点之 Java 反射 Reflection
    (http://a.codekk.com/detail/Android/Mr.Simple/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8B%20Java%20%E5%8F%8D%E5%B0%84%20Reflection )
  • 从Java反射机制到Android注解框架
    (http://blog.csdn.net/yyh352091626/article/details/51657908 )
  • Java 反射基础(上)
    (http://blog.csdn.net/My_TrueLove/article/details/51298218)
  • Java 反射基础(下) 
    http://blog.csdn.net/My_TrueLove/article/details/51306921)


者:张明云

来源:

https://www.jianshu.com/p/5db744d8fcd0

浏览 44
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报