先來看一看今天的效果:
代碼效果:
效果不重要,重要的是代碼:
注解
官方解釋:
從JDK5開始,Java增加對元資料的支持,也就是注解,注解與注釋是有一定區別的,可以把注解理解為代碼里的特殊標記,這些標記可以在編譯,類加載,運行時被讀取,并執行相應的處理,通過注解開發人員可以在不改變原有代碼和邏輯的情況下在源代碼中嵌入補充資訊,
百度百科
定義一個注解:
public @interface SZJ{
}
元注解:
元注解是用來標示當前注解是干什么用的,什么時候用,常用的有2個
@Target
- ElementType.ANNOTATION_TYPE 應用于注解型別,
- ElementType.CONSTRUCTOR 應用于建構式,
- ElementType.FIELD 應用于欄位或屬性,
- ElementType.LOCAL_VARIABLE 應用于區域變數,
- ElementType.METHOD 應用于方法級注解,
- ElementType.PACKAGE 可以應用于包宣告,
- ElementType.PARAMETER 可以應用于方法的引數,
- ElementType.TYPE 可以應用于類的任何元素,
@Retention
- RetentionPolicy.SOURCE 僅用于原始碼級別,會被編譯器忽略[編譯成class后,會被class丟棄該注解]
- RetentionPolicy.CLASS 在編譯器由編譯器保留,JVM會忽略
- RetentionPolicy.RUNTIME 由JVM保留,運行時可以使用
CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS,
SOURCE<CLASS<RUNTIME
舉例:

@Retention注解解釋:
RetentionPolicy.SOURCE 僅用于原始碼級別,會被編譯器忽略[編譯成class后,會被class丟棄該注解]

這個注解太偏原始碼,用到的地方比較少
RetentionPolicy.CLASS 在編譯器由編譯器保留,JVM會忽略

這個注解一般是APT技術用到的比較多,像一些ARouter,EventBus,Butterknifer等等框架都是用的這個注解.
反射
定義:
反射就是在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和
方法;對于任意一個物件,都能夠呼叫它的任意方法和屬性;并且能改變它的屬性,是Java被視為動態語言的關鍵,
百度百科
使用場景:
不知道某個類是什么類,用來干什么的,就可以通過反射機制直接呼叫到他內部的屬性,以及方法
反射與Class:
反射始于Class,Class是一個類,封裝了當前物件所對應的類的資訊,一個類中有屬性,方法,構造器等,比如說有一個Person類,一個Book類,這些都是不同的類,現在需要一個類,用來描述類,這就是Class,它應該有類名,屬性,方法,構造器等,Class是用來描述類的類,
獲得Class物件:
- 類名獲取: 類名.class
- 物件名獲取: 物件名.getClass()
- 全類名獲取: Class.forName(“全類名”)
代碼展示:
public void test() {
System.out.println("我是方法哦");
//類名獲取
Class<?> intCls = int.class;
Class<?> strCls = String.class;
//物件名獲取
UserBean userBean = new UserBean();
Class<? extends UserBean> userBeanCls = userBean.getClass();
//全類名獲取
try {
Class<?> userBeanCls2 = Class.forName("com.example.annotation.bean.UserBean");
} catch (Exception e) {
e.printStackTrace();
}
}
反射的🌰:
public void test() {
System.out.println("我是方法哦");
//TODO 物件名獲取 class
UserBean userBean = new UserBean();
Log.i("szjUserBean",userBean.toString());
try {
Class<? extends UserBean> userBeanCls = userBean.getClass();
Field nameField = userBeanCls.getDeclaredField("name");
//允許訪問 private
nameField.setAccessible(true);
//修改狀態
nameField.set(userBean,"SZJ");
Log.i("szjUserBean",userBean.toString());
//TODO 全類名獲取 class
Class<?> userBeanCls2 = Class.forName("com.example.annotation.bean.UserBean");
Field nameField2 = userBeanCls2.getDeclaredField("name");
nameField2.setAccessible(true);
//修改狀態
nameField2.set(userBean,"我是SZJ");
Log.i("szjUserBean",userBean.toString());
//TODO 反射呼叫方法
Method test = userBeanCls.getMethod("test",String.class,int.class);
String userTestRt = (String) test.invoke(userBean, "張三1", 22);
Log.i("szjUserBean",userTestRt);
Method test2 = userBeanCls.getDeclaredMethod("test2",String.class,int.class);
//允許訪問private
test2.setAccessible(true);
String userTestRt2 = (String) test2.invoke(userBean, "張三2", 33);
Log.i("szjUserBean2",userTestRt2);
} catch (Exception e) {
e.printStackTrace();
Log.i("szjError", e.toString());
}
}
效果圖:

- getDeclaredField(String) 獲得自己的成員屬性[不包括父類,包括private]
- getFields(String) 獲得自己+父類的成員屬性[不包括private]
- Field[] = getDeclaredFields() 獲取到所有的屬性
- getMethod(引數名,引數.class) 獲得方法[不包括父類,包括private]
- getDeclaredMethod(引數名,引數.class) 獲得自己+父類的成員方法[不包括private]
- Method[] = getDeclaredMethods() 獲取所有的方法
- Field.setAccessible(boolean) 是否允許訪問private屬性
反射獲取到注解:
- Field.isAnnotationPresent(Class) 屬性上是否有該注解
- Field.getAnnotation(Class) 獲取到屬性上注解
- Annotation[] = Method.getAnnotations() 獲取到方法上的所有注解
- Annotation.annotationType() 獲取到方法上注解的注解型別
注解配合反射實作效果
定義注解:
//運行時存在
@Retention(RetentionPolicy.RUNTIME)
//作用來屬性上
@Target(ElementType.FIELD)
public @interface szjFind {
//IdRes: 用來區分傳入 int 型別 是否是id
@IdRes int value();
}
通過反射和注解給控制元件賦值:
//TODO findViewById
public void initFind(Activity activity) {
//獲取 Class
Class<? extends Activity> cls = activity.getClass();
//getFields 獲得自己+父類的成員 不包括 private
//getDeclaredFields 獲得自己的成員(不包括父類,包括 private)
//獲得此類所有的屬性
Field[] fields = cls.getDeclaredFields();
//遍歷這個類中的所有屬性
for (Field field : fields) {
//判斷屬性是否被 szjFind 注解
if (field.isAnnotationPresent(szjFind.class)) {
//獲得注解的實體
szjFind szjFind = field.getAnnotation(szjFind.class);
assert szjFind != null;
//獲取 id
int id = szjFind.value();
//找到 Id
View view = activity.findViewById(id);
//允許操作 private 屬性
field.setAccessible(true);
try {
//反射設定屬性的值
field.set(activity, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
在Activity中注冊:
szjAnnotationUtil.getInstance().initFind(this);
代碼總覽圖:

因為高度重復,其余代碼請下載demo觀看
注意:在實際開發專案中不建議采用這種方式,因為反射非常耗時,本篇只是以[獲取控制元件ID/onClick/Intent]他來舉例!
完整代碼
原創不易,您的點贊就是對我最大的支持!
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/294741.html
標籤:其他
