一.反射调用方法,获取成员变量值
假如我们有这样一个类:
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去控制,而是我传入什么,你只能用什么,这就是依赖注入和控制反转。