一.反射呼叫方法,獲取成員變數值
假如我們有這樣一個類:
public class Hello {
public String name = "張三";
private int age = 43;
public String getSex( String aaa) {
return aaa;
}
private String getClassName() {
return "還在九年義務教育";
}
}
複製程式碼
要求不去建立物件去呼叫裡邊的方法和獲取裡邊的值,我們可以通過反射去獲取成員變數裡邊的值和呼叫裡邊的方法:
1.1 呼叫public修飾的方法:
try {
//獲取類的位元組碼
Class<Hello> clz = Hello.class;
//獲取公共方法物件
Method mt = clz.getMethod("getSex", String.class);
//繫結呼叫方法,獲取返回值
String invoke = (String) mt.invoke(clz.newInstance(), "eeeeee");
//列印返回值
Log.e("invoke", invoke);
} catch (Exception e) {
}複製程式碼
1.2 呼叫private修飾的方法:
try {
//獲取類的位元組碼
Class<Hello> clz = Hello.class;
//獲取私有方法物件
Method getClassName = clz.getDeclaredMethod("getClassName");
//開啟修飾符限制
getClassName.setAccessible(true);
//呼叫方法
String invoke = (String) getClassName.invoke(clz.newInstance());
//列印返回值
Log.e("invoke", invoke);
} catch (Exception e) {
e.printStackTrace();
}複製程式碼
1.3 獲取public 修飾的成員變數:
try {
//獲取類的位元組碼
Class<Hello> clz = Hello.class;
//獲取公共屬性物件
Field name = clz.getField("name");
//獲取公共屬性的名字
String name1 = name.getName();
//獲取公共屬性的值
Object name2 = name.get(clz.newInstance());
Log.e("rrrrrrrrre",name1+":"+name2);
} catch (Exception e) {
e.printStackTrace();
} 複製程式碼
1.4 獲取private修飾的成員變數:
try {
//獲取類的位元組碼
Class<Hello> clz = Hello.class;
//獲取私有屬性物件
Field age = clz.getDeclaredField("age");
//獲取私有屬性的名字
String age1 = age.getName();
//開啟許可權修飾符開關
age.setAccessible(true);
//獲取私有屬性的值
int age2 = age.getInt(clz.newInstance());
//列印獲取到的值
Log.e("rrrrrrrrre",age1+":"+age2);
} catch (Exception e) {
e.printStackTrace();
} 複製程式碼
二. 註解的知識點
2.1.註解
就是 :使用 @interface 標識一下,它也是一種型別 ,同類和介面列舉一樣如:
public @interface TestAnnotation {
}複製程式碼
2.2.元註解
要想讓自定義註解管用,需要用的元註解 。元註解就是可以註解到自定義註解上的註解 ,元註解有一下:
- @Retention
- @Documented
- @Target
- @Inherited
- @Repeatable
2.3 @Retention 解釋說明了自定義註解的保留期,取值有:
- RetentionPolicy.SOURCE 註解只在原始碼階段保留,在編譯器進行編譯時它將被丟棄忽視。
- RetentionPolicy.CLASS 註解只被保留到編譯進行的時候,它並不會被載入到 JVM 中。
- RetentionPolicy.RUNTIME 註解可以保留到程式執行的時候,它會被載入進入到 JVM 中,所以在程式執行時可以獲取到它們。
2.4 @Documented 文件,我也不知道幹什麼用的
2.5 @Target 解釋說明了自定義註解的作用範圍,有下面的取值:
- ElementType.ANNOTATION_TYPE 可以給一個註解進行註解
- ElementType.CONSTRUCTOR 可以給構造方法進行註解
- ElementType.FIELD 可以給屬性進行註解
- ElementType.LOCAL_VARIABLE 可以給區域性變數進行註解
- ElementType.METHOD 可以給方法進行註解
- ElementType.PACKAGE 可以給一個包進行註解
- ElementType.PARAMETER 可以給一個方法內的引數進行註解
- ElementType.TYPE 可以給一個型別進行註解,比如類、介面、列舉
2.6@Inherited Inherited
是繼承的意思,但是它並不是說註解本身可以繼承而是說如果一個超類被 @Inherited 註解過的註解進行註解的話,那麼如果它的子類沒有被任何註解應用的話,那麼這個子類就繼承了超類的註解。
2.7@Repeatable 重複的意思
2.8 註解只有屬性,沒有方法,寫法: int id() default -1; 名字是:id,型別是int ,預設值是-1 ,如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
public int id() default -1;
public String msg() default "Hi";
}複製程式碼
2.9 注意:
- 如果一個註解內僅僅只有一個名字為 value 的屬性時,應用這個註解時可以直接接屬性值填寫到括號內
- 註解沒有任何屬性,應用這個註解的時候,括號都可以省略
三 註解的獲取
假如註解要求執行時也可以獲取,那麼我們怎麼獲取呢? 通過反射,介紹Class裡邊的一些方法:
如有類Test 和自定義註解TestAnnotation
//是否有被TestAnnotation註解
boolean annotation = Test.class.isAnnotationPresent(TestAnnotation.class);
if (annotation) {
//獲取註解物件
TestAnnotation annotation1 = Test.class.getAnnotation(TestAnnotation.class);
//獲取被註解的值
Log.e("rrrrrr", "" + annotation1.id() + "=========" + annotation1.msg());
}複製程式碼
四.反射和註解有什麼用
仿照ButterKnife寫一個類似的功能,但是原理不一樣,主要是為了理解反射我註解,當然反射和註解還有很多很大的用處,我能力有限,理解不了那麼深,開始貼程式碼:
佈局:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:id="@+id/tv"
android:text="aaaa"
android:layout_height="wrap_content" />
</android.support.constraint.ConstraintLayout>複製程式碼
程式:
@BindRes(R.layout.activity_test)
public class TestAnnotation extends AppCompatActivity {
@Bind(R.id.tv)
TextView mTextView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Inject.inJect(this);
mTextView.setText("XiFanYin");
}
@BindClick(R.id.tv)
public void onClick2(View view) {
if (view.getId() == R.id.tv) {
Toast.makeText(this, "點選", Toast.LENGTH_SHORT).show();
}
}
}複製程式碼
自定義註解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bind {
int value();
}複製程式碼
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindClick {
int[] value();
}複製程式碼
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindRes {
int value();
}
複製程式碼
然後就是關鍵的一個類,通過反射去找控制元件賦值,設定點選事件:
public class Inject {
public static void inJect(AppCompatActivity activity) {
try {
bindRes(activity);//繫結佈局
bindView(activity);//查詢控制元件
bindClick(activity);//查詢控制元件
} catch (Exception e) {
e.printStackTrace();
}
}
private static void bindClick(AppCompatActivity activity) {
Class<? extends AppCompatActivity> clz = activity.getClass();
Method[] methods = clz.getMethods();//得到所有方法
for (Method method : methods) {
if (method.isAnnotationPresent(BindClick.class)) {//方法下是否有BindClick註解
BindClick annotation = method.getAnnotation(BindClick.class);
int[] value = annotation.value();
for (int id : value) {
View view = activity.findViewById(id);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
method.setAccessible(true);
try {
method.invoke(activity, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
});
}
}
}
}
private static void bindRes(AppCompatActivity activity) {
Class<? extends AppCompatActivity> clz = activity.getClass();
BindRes annotation = clz.getAnnotation(BindRes.class);
if (annotation != null) {
int value = annotation.value();
activity.setContentView(value);
}
}
public static void bindView(AppCompatActivity activity) throws IllegalAccessException {
Class<? extends AppCompatActivity> clz = activity.getClass();
Field[] fields = clz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
if (fields[i].isAnnotationPresent(Bind.class)) {
Bind bind = fields[i].getAnnotation(Bind.class);
int value = bind.value();
View view = activity.findViewById(value);
fields[i].setAccessible(true);
fields[i].set(activity, view);
} else {
System.out.println("沒有");
}
// System.out.println(fields[i]);
}
}
}
複製程式碼
就實現了類似ButterKnife的功能。
四.動態代理
作用:在不修改被代理物件的原始碼上,進行功能的增強。
定義一個可以賣東西營業執照介面
public interface Sell {
void maidongxi();
}複製程式碼
有一個人想買紅旗渠煙,就要實現這個營業執照
public class hongqiqu implements Sell {
@Override
public void maidongxi() {
Log.e("rrrrrrrrrr", "我能賣紅旗渠");
}
}複製程式碼
過了一段時間,他想掙的更多,就像去賣酒。然後我們需要修改hongqiqu類的程式碼,如果對於專案而言,要麼去繼承,要麼去使用包裝者模式去擴充套件,很少修改本來的類,今天我們使用動態代理去實現:
public class GuitaiA implements InvocationHandler {
private Object pingpai;
public GuitaiA(Object pingpai) {
this.pingpai = pingpai;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Log.e("rrrrrrr","我能賣酒了");
method.invoke(pingpai, objects);
return null;
}
}複製程式碼
看著這個模式有點像包裝者模式,只不過加了反射呼叫,簡單的修改都能讓他不再去關係去呼叫什麼方法名字是什麼。
然後載主介面中,
//紅旗渠物件
hongqiqu hongqiqu = new hongqiqu();
//傳遞到裡邊
InvocationHandler jingxiao = new GuitaiA(hongqiqu);
//獲取對臺代理介面
Sell dynamicProxy= (Sell) Proxy.newProxyInstance(hongqiqu.class.getClassLoader()
, hongqiqu.class.getInterfaces()
, jingxiao);
dynamicProxy.maidongxi();複製程式碼
發現沒有修改hongqiqu類中的程式碼,他就可以即賣紅旗渠,又可以賣酒
這就是動態代理,把豐富的功能抽離出來,傳入誰,我就去豐富擁有我這個功能!完美解耦~
五,依賴注入和控制反轉
假如你要出遠門,你可以選擇火車或者汽車,按照常規編碼:
如果選擇火車,程式碼應該這樣寫
public class Person {
private HotCar car;
public Person() {
car = new HotCar();
}
public void chumen() {
car.drive();
}
}複製程式碼
如果選擇汽車,程式碼修改成這樣:
public class Person {
// private HotCar hotCar;
private Car car;
public Person() {
car = new Car();
// hotCar = new HotCar();
}
public void chumen() {
car.drive();
// hotCar.drive();
}
}複製程式碼
每次選擇不同的交通工具,都要修改,能不能不修改?可以這樣寫:
5.1 抽象介面:
public interface Driveable {
//駕駛
void drive();
}
複製程式碼
5.2.人傳入介面
public class Person {
private Driveable driveable;
public Person(Driveable driveable) {
this.driveable = driveable;
}
public void chumen() {
driveable.drive();
}
}
複製程式碼
5.3 呼叫
Car car = new Car();
HotCar hotCar = new HotCar();
Person p = new Person(hotCar);
p.chumen();複製程式碼
這裡傳入什麼物件去使用什麼方法,通過介面去解耦,而不用每次都去修改person類,讓我們更專注與原則什麼樣的交通工具,這個傳遞的物件就叫依賴注入,控制權同時也進行了轉換,不再讓person去控制,而是我傳入什麼,你只能用什麼,這就是依賴注入和控制反轉。