Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 深入淺出反射

深入淺出反射

編輯:關於Android編程

\

什麼是反射?

反射是一種能夠在程序運行時動態訪問、修改某個類中任意屬性(狀態)和方法(行為)的機制(包括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;
	}

	@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) {
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版本導致的兼容性問題;

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved