1.注解
注解:Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 注解是元数据的一种形式,提供有关
于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。
元注解:在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为 meta-annotation(元注解)。一般的,我们在定义自定义注解时,需要指定的元注解有两个 :
@Target 指定注解的标记类型
ElementType.ANNOTATION_TYPE 可以应用于注解类型。
ElementType.CONSTRUCTOR 可以应用于构造函数。
ElementType.FIELD 可以应用于字段或属性。
ElementType.LOCAL_VARIABLE 可以应用于局部变量。
ElementType.METHOD 可以应用于方法级注解。
ElementType.PACKAGE 可以应用于包声明。
ElementType.PARAMETER 可以应用于方法的参数。
ElementType.TYPE 可以应用于类的任何元素。
@Retention 指定注解的作用域
RetentionPolicy.SOURCE - 标记的注解仅保留在源级别中,并被编译器忽略。
RetentionPolicy.CLASS - 标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略。
RetentionPolicy.RUNTIME - 标记的注解由 JVM 保留,因此运行时环境可以使用它。
另外还有@Documented 与 @Inherited 元注解,前者用于被javadoc工具提取成文档,后者表示允许子类
继承父类中定义的注解。
声明一个注解:
@Target(ElementType.TYPE)//注解可以标记的类型
@Retention(RetentionPolicy.SOURCE)//注解的作用域
public @interface Chen {
String value();
}
注解作用域应用场景:
- SOURCE: 常用于APT(Annotation Process Tools),作用于源码级别的注解,可提供给IDE语法检查、APT等场景使用。
- CLASS: 此种注解的应用场景为字节码操作。如:AspectJ、热修复Roubust中应用此场景。
- RUNTIME: 注解保留至运行期,意味着我们能够在运行期间结合反射技术获取注解中的所有信息。
构建一个注解处理器:
1.构建注解处理器类
//要处理的注解
@SupportedAnnotationTypes("com.example.annationtest.Lance")
public class DealProcessor extends AbstractProcessor {
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
//输出注解日志
Messager messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE,"=======================================这是一个注解处理器======================");
return false;
}
}
2.往文件中注册注解处理器:
3.使用,引入注解处理器:
dependencies {
//引入注解处理器
annotationProcessor project(':compiler')
}
作用于源码级代码作用域限定:
public class Test {
private static WeekEmumDay mCuurentDay;
private static int mCurrentIntDay;
enum WeekEmumDay{
SUNNDAY,MONDAY
}
private static final int SUNDAY =0;
private static final int MONDAY =1;
@IntDef({SUNDAY,MONDAY})
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.SOURCE)
@interface WeekDay {
}
//利用自带的注解检查
public static void setDrawable(@DrawableRes int id){
}
public static void main(String[] args) {
//设置int类型会有提示
//setDrawable(1);
//设置引用资源
setDrawable(R.drawable.ic_launcher_background);
setCurrentDay(10001);//此时传入类型不受限制
//限定输入类型一种是使用枚举 但是枚举开销较大
setCurrentDay1(WeekEmumDay.SUNNDAY);
//限定输入值范围
//setCurrentDay1(1);
setCurrentDay1(SUNDAY);
}
/**
* 使用逐渐限定输入的取值域
* @param day
*/
private static void setCurrentDay1(@WeekDay int day) {
}
/**
* 限定枚举类型
* @param day
*/
private static void setCurrentDay1( WeekEmumDay day) {
}
//未检查时
public static void setCurrentDay(int currentDay){
}
}
2.反射
反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们
使用 JDK 提供的反射 API 进行反射调用。反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和
方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。是Java被视为动态语言的关键。
Java反射机制主要提供了以下功能:
在运行时构造任意一个类的对象
在运行时获取或者修改任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的方法(属性)
获取Class:
获取Class对象的三种方式
1. 通过类名获取 类名.class
2. 通过对象获取 对象名.getClass()
3. 通过全类名获取 Class.forName(全类名) classLoader.loadClass(全类名)
判断是否为某个类的实例。
public native boolean isInstance(Object obj);
获取构造器信息:
Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的public构造函数(包括父类)
Constructor[] getConstructors() -- 获得类的所有公共构造函数
Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(包括私有)
Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)
获取类的成员变量(字段)信息:
Field getField(String name) -- 获得命名的公共字段
Field[] getFields() -- 获得类的所有公共字段
Field getDeclaredField(String name) -- 获得类声明的命名的字段
Field[] getDeclaredFields() -- 获得类声明的所有字段
调用方法:
Method getMethod(String name, Class[] params) -- 使用特定的参数类型,获得命名的公共方法
Method[]getMethods() -- 获得类的所有公共方法 包括父类的非private方法
Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型,获得类声明的命名的方法
Method[] getDeclaredMethods() -- 获得类声明的所有方法 类本市身
反射获取泛型真实类型:
当我们对一个泛型类进行反射时,需要的到泛型中的真实数据类型,来完成如json反序列化的操作。此时需要通
过 Type 体系来完成。 Type 接口包含了一个实现类(Class)和四个实现接口,他们分别是:
TypeVariable 泛型类型变量。可以泛型上下限等信息;
ParameterizedType具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)
GenericArrayType当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。
WildcardType通配符泛型,获得上下限信息;
实例:通过自定义注解与反射实现页面跳转的参数注入
public static void injectAutowired(Activity activity) {
Class extends Activity> cls = activity.getClass();
//获得数据
Intent intent = activity.getIntent();
Bundle extras = intent.getExtras();
if (extras == null) {
return;
}
//获得此类所有的成员
Field[] declaredFields = cls.getDeclaredFields();
for (Field field : declaredFields) {
if (field.isAnnotationPresent(Autowired.class)) {
Autowired autowired = field.getAnnotation(Autowired.class);
//获得key
String key = TextUtils.isEmpty(autowired.value()) ? field.getName() : autowired.value();
if (extras.containsKey(key)) {
Object obj = extras.get(key);
// todo Parcelable数组类型不能直接设置,其他的都可以.
//获得数组单个元素类型
Class> componentType = field.getType().getComponentType();
//当前属性是数组并且是 Parcelable(子类)数组
if (field.getType().isArray() &&
Parcelable.class.isAssignableFrom(componentType)) {
Object[] objs = (Object[]) obj;
//创建对应类型的数组并由objs拷贝
Object[] objects = Arrays.copyOf(objs, objs.length, (Class extends Object[]>) field.getType());
obj = objects;
}
field.setAccessible(true);
try {
field.set(activity, obj);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
3.代理(静态代理与动态代理)
静态代理:
public interface Message {
void message();
}
public class Lucy implements Message {
@Override
public void message() {
System.out.println("lucy message实际服务对象实现");
}
}
//静态代理Message的代理对象 中间隔离层
public class Agent implements Message {
private final Message message;
public Agent(Message message){
this.message = message;
}
public void before(){
System.out.println("前置处理");
}
@Override
public void message() {
before();
message.message();//实际提供服务者
after();
}
public void after(){
System.out.println("后置处理");
}
}
静态代理的缺点在于每增加一个服务功能 都需要创建对应的代理类 一个代理类代理一个接口
动态代理:
通过java提供的动态代理接口Proxy.newProxyInstance 获取代理对象 代理只能代理接口
方式如下:
final Avlin avlin = new Avlin();
final Lucy lucy = new Lucy();
Object o = Proxy.newProxyInstance(Test2.class.getClassLoader(),
new Class[]{Message.class, Wash.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态代理调用==============>"+method.getName());
return method.invoke(avlin,args);
}
});
Message message = (Message) o;
message.message();//调用代理类的方法 会回调到InvocationHandler(Object proxy(代理对象), Method method(调用的方法), Object[] args(方法的参数)) invoke函数
Wash wash = (Wash) o;
wash.wash();
}
注解 反射 动态代理实例 获取OnClicklistener事件的注入:
@OnClick({R.id.btn_1,R.id.btn_2})
private void click(View view){
switch (view.getId()){
case R.id.btn_1:
Toast.makeText(this,"触发点击事件Btn1",Toast.LENGTH_LONG).show();
break;
case R.id.btn_2:
Toast.makeText(this,"触发点击事件Btn2",Toast.LENGTH_LONG).show();
break;
}
}
//TODO:定义Onclick注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OnClick {
@IdRes int[] value();
}
//TODO:扫描注解与实例化对象:
final Method[] methods = clas.getDeclaredMethods();
for (final Method m : methods) {
System.out.println(m.getName());
if(m.isAnnotationPresent(OnClick.class)){
OnClick onClick = m.getAnnotation(OnClick.class);
int[] ids = onClick.value();
for (int id : ids) {
final View view = activity.findViewById(id);
System.out.println("id is:"+id);
//通过动态代理获取onclicklistener代理对象
Object o = Proxy.newProxyInstance(view.getClass().getClassLoader(),new Class[]{View.OnClickListener.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName());
m.setAccessible(true);
//通过反射对方法注入事件
return m.invoke(activity,args);
}
});
View.OnClickListener listener = (View.OnClickListener) o;
view.setOnClickListener(listener);
}
}
}
补充:Retrofit 的核心就是用到了动态代理+注解与反射 通过动态代理在使用者调用方法时在 invoke方法里进行处理最终通过okhttp来发起网络请求和响应,retrofit本质上是对okhttp的一个封装层框架