什么是反射涵紊?
反射是一種能夠在程序運(yùn)行時(shí)動(dòng)態(tài)訪問(wèn)掌逛、修改某個(gè)類中任意屬性(狀態(tài))和方法(行為)的機(jī)制(包括private實(shí)例和方法),java反射機(jī)制提供了以下幾個(gè)功能:
在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類禽篱;
在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象八千;
在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法;
在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法袖裕。
反射涉及到四個(gè)核心類:
java.lang.Class.java:類對(duì)象曹抬;
java.lang.reflect.Constructor.java:類的構(gòu)造器對(duì)象;
java.lang.reflect.Method.java:類的方法對(duì)象急鳄;
java.lang.reflect.Field.java:類的屬性對(duì)象谤民;
反射有什么用?
操作因訪問(wèn)權(quán)限限制的屬性和方法攒岛;
實(shí)現(xiàn)自定義注解赖临;
動(dòng)態(tài)加載第三方j(luò)ar包,解決android開(kāi)發(fā)中方法數(shù)不能超過(guò)65536個(gè)的問(wèn)題灾锯;
按需加載類兢榨,節(jié)省編譯和初始化APK的時(shí)間;
反射工作原理
當(dāng)我們編寫完一個(gè)Java項(xiàng)目之后顺饮,每個(gè)java文件都會(huì)被編譯成一個(gè).class文件吵聪,這些Class對(duì)象承載了這個(gè)類的所有信息,包括父類兼雄、接口吟逝、構(gòu)造函數(shù)、方法赦肋、屬性等块攒,這些class文件在程序運(yùn)行時(shí)會(huì)被ClassLoader加載到虛擬機(jī)中励稳。當(dāng)一個(gè)類被加載以后,Java虛擬機(jī)就會(huì)在內(nèi)存中自動(dòng)產(chǎn)生一個(gè)Class對(duì)象囱井。我們通過(guò)new的形式創(chuàng)建對(duì)象實(shí)際上就是通過(guò)這些Class來(lái)創(chuàng)建驹尼,只是這個(gè)過(guò)程對(duì)于我們是不透明的而已。
反射的工作原理就是借助Class.java庞呕、Constructor.java新翎、Method.java、Field.java這四個(gè)類在程序運(yùn)行時(shí)動(dòng)態(tài)訪問(wèn)和修改任何類的行為和狀態(tài)住练。
反射實(shí)例
分別演示三種獲取類信息的方式地啰、獲取當(dāng)前類的所有方法和獲取當(dāng)前類及其父類的所有方法、獲取當(dāng)前類的所有實(shí)例和獲取當(dāng)前類及其父類的所有實(shí)例讲逛、獲取父類信息亏吝、獲取接口信息、比較反射方法和實(shí)例的性能差異等幾個(gè)方面:
- 示例類:
父類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;
}
@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) {
LogE("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) {
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;
}
/**
* 獲取當(dāng)前類中的方法
*
* */
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());
// 獲取方法的參數(shù)類型列表
Class<?>[] paramClasses = getSalaryPerMonthMethod.getParameterTypes() ;
for (Class<?> class1 : paramClasses) {
LogE("getSalaryPerMonth 方法的參數(shù)類型 : " + class1.getName());
}
// 是否是 private 函數(shù),屬性是否是 private 也可以使用這種方式判斷
LogE(getSalaryPerMonthMethod.getName() + " is private " + Modifier.isPrivate(getSalaryPerMonthMethod.getModifiers()));
// 執(zhí)行方法
Object result = getSalaryPerMonthMethod.invoke(programMonkey);
LogE("getSalaryPerMonth 方法的返回結(jié)果: " + result);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 獲取當(dāng)前類和父類的所有方法
*
* */
private void getAllMethods() {
ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
// 獲取當(dāng)前類和父類的所有方法
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());
// 獲取方法的參數(shù)類型列表
Class<?>[] paramClasses = setmLanguageMethod.getParameterTypes() ;
for (Class<?> class1 : paramClasses) {
LogE("setmLanguage 方法的參數(shù)類型 : " + class1.getName());
}
// 是否是 private 函數(shù)括饶,屬性是否是 private 也可以使用這種方式判斷
LogE(setmLanguageMethod.getName() + " is private " + Modifier.isPrivate(setmLanguageMethod.getModifiers()));
// 執(zhí)行方法
Object result = setmLanguageMethod.invoke(programMonkey, "Java");
LogE("setmLanguage 方法的返回結(jié)果: " + 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;
}
/**
* 得到當(dāng)前類的所有實(shí)例
*
* */
private void getCurrentClassFields() {
ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
// 獲取當(dāng)前類的所有屬性
Field[] publicFields = programMonkey.getClass().getDeclaredFields();
for (Field field : publicFields) {
LogE("declared field name : " + field.getName());
}
try {
// 獲取當(dāng)前類的某個(gè)屬性
Field ageField = programMonkey.getClass().getDeclaredField("mAge");
// 獲取屬性值
LogE(" my age is : " + ageField.getInt(programMonkey));
// 設(shè)置屬性值
ageField.set(programMonkey, 10);
LogE(" my age is : " + ageField.getInt(programMonkey));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 得到當(dāng)前類及其父類的所有實(shí)例
*
* */
private void getAllFields() {
ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
// 獲取當(dāng)前類和父類的所有屬性
Field[] publicFields = programMonkey.getClass().getFields();
for (Field field : publicFields) {
LogE("field name : " + field.getName());
}
try {
// 獲取當(dāng)前類和父類的某個(gè)屬性
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);
}
}
- 演示結(jié)果:
三種獲取類信息的方式:
獲取當(dāng)前類的方法株茶、獲取當(dāng)前類和父類的所有方法:
獲取當(dāng)前類的所有實(shí)例图焰、獲取當(dāng)前類和父類的所有實(shí)例:
獲取父類信息:
獲取接口信息:
比較反射方法和實(shí)例的性能差異:
通過(guò)上面的示例可以發(fā)現(xiàn),通過(guò)反射能夠完成之前所描述的事情技羔,并且反射方法比反射實(shí)例要慢很多僵闯。
反射的特點(diǎn)
優(yōu)點(diǎn)
- 靈活、自由度高:不受類的訪問(wèn)權(quán)限限制藤滥,想對(duì)類做啥就做啥鳖粟;
缺點(diǎn)
性能問(wèn)題:通過(guò)反射訪問(wèn)、修改類的屬性和方法時(shí)會(huì)遠(yuǎn)慢于直接操作拙绊,但性能問(wèn)題的嚴(yán)重程度取決于在程序中是如何使用反射的向图。如果使用得很少,不是很頻繁标沪,性能將不會(huì)是什么問(wèn)題榄攀;
安全性問(wèn)題:反射可以隨意訪問(wèn)和修改類的所有狀態(tài)和行為,破壞了類的封裝性金句,如果不熟悉被反射類的實(shí)現(xiàn)原理檩赢,隨意修改可能導(dǎo)致潛在的邏輯問(wèn)題;
兼容性問(wèn)題:因?yàn)榉瓷鋾?huì)涉及到直接訪問(wèn)類的方法名和實(shí)例名违寞,不同版本的API如果有變動(dòng)贞瞒,反射時(shí)找不到對(duì)應(yīng)的屬性和方法時(shí)會(huì)報(bào)異常偶房;
說(shuō)明
通過(guò)反射訪問(wèn)方法比實(shí)例慢很多;
有用到反射的類不能被混淆憔狞;
反射存在性能問(wèn)題蝴悉,但使用不頻繁、按需使用時(shí)瘾敢,對(duì)程序性能影響并不大;
反射存在安全性問(wèn)題尿这,因?yàn)榭梢噪S意修改類的所有狀態(tài)和行為(包括private方法和實(shí)例)簇抵;
使用反射訪問(wèn)Android的API時(shí)需要注意因?yàn)椴煌珹PI版本導(dǎo)致的兼容性問(wèn)題;