当前位置: 首页 > news >正文

注解与反射

注解与反射

注解的作用或意义是什么?

注解本身没有任何意义,单独的注解就是一种注释。它需要结合如 反射、插桩等技术才有意义

Java注解(Annotation)又称Java标注,是元数据的一种形式,提供有关程序但不属于程序本身的数据注解对他们注解的代码没有直接影响。

注解初探

注解定义:
@Target/@Retention元注解:
元注解:注解上的注解 指示当前声明的注解可以作用那些位置@Target(ElementType.TYPE) 如果没有指定@Target的话 默认全部(TYPE->类 属性 METHOD->方法 构造方法 FIELD->属性 PARAMETER->参数)可以指示多个 ElementType.FIELD,ElementType.PARAMETER@Retention:保留级别 保留这个注解到什么时候   
@Retention(RetentionPolicy.SOURCE)(SOURCE->仅保留到源码阶段(javac编译成class之后就会被抹除),CLASS->保留在class文件中 但是在JVM加载class文件时会被忽略,RUNTIME->运行时 由JVM保留运行结果让我们获取这个注解 反射技术)
SOURCE<CLASS<RUNTIMEclass包含了SOURCE,RUNTIME包含了SOURCECLASS
public @interface Lance{//元素 可以指定基本数据类型 注解类型 引用类型String value() default "";
}

@Target

注解标记另一个注解,以限制可以应用注解的 Java 元素类型。目标注解指定以下元素类型之一作为其值:

  • ElementType.ANNOTATION_TYPE 可以应用于注解类型。
  • ElementType.CONSTRUCTOR 可以应用于构造函数。
  • ElementType.FIELD 可以应用于字段或属性。
  • ElementType.LOCAL_VARIABLE 可以应用于局部变量。
  • ElementType.METHOD 可以应用于方法级注解。
  • ElementType.PACKAGE 可以应用于包声明。
  • ElementType.PARAMETER 可以应用于方法的参数。
  • ElementType.TYPE 可以应用于类的任何元素。
注解使用:
@Lance("")
public class MainActivity extends AppcompatActivity{} 

APT技术

APT(Annotation Processor Tools) 注解处理器:

源码级别,在编译期RetentionPolicy.SOURCE能够获取注解与注解声明的包括类中的所有成员信息,一般用于生成额外的辅助类。

1.创建一个Java Module

JVM虚拟机不认识java文件,由javac编译成.class文件

.java->javac->.class

javac解析要编译的java类 就要采集到所有的注解信息 然后将所有注解信息包装成节点->Element->由javac调起注解处理器

由javac调用注解处理器 不用我们手动调用 只需要给javac指明要处理的注解处理程序

直接收特定注解
@SupportedAnnotationTypes("com.enjoy.annotation.Lance")
AbstractProcessor:JDK给我们提供的 LanceProcessor就是一个注解处理程序 javac会调用里面的process方法
public class LanceProcessor extends AbstractProcessor{@Overridepublic boolean process(Set<? extends TypeElement> set,RoundEnvironment roundEnvironment){//AbstractProcessor内部属性 processingEnvMessager messager = processingEnv.getMessager();messager.printMessage(Dialogstic.Kind.NOTE,"========");//会执行两次 可能与返回值有关 return false;}
} 
2.在resources里进行注册

名字是固定的 必须是下面

新建Direcory->resources/META-INF.services/javax.annotation.processing.Processor

注解处理程序的全类名
com.enjoy.compiler.LanceProcessor

字节码增强

在字节码中写代码

.class->格式 数据按照特定的方式记录与排列

.class->IO->byte[]->不能乱改

反编译后的class文件

public class MainActivity extends AppcompatActivity{public MainActivity(){}@InjectTimeprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);this.setContentView(2131296285);this.a();}@InjectTimevoid a(){try{Thread.sleep(2000L);}catch(InterruptedException var2){var2.prointStackTrace();}}
}

想要在@InjectTime注解的方法上第一行和最后一行都插入当前系统时间并算出两者相减的时间

注解的意义是 你是都要对这个方法进行增强

反射

运行时级别,在程序运行期间,通过反射技术动态获取注解与其元素,从而完成不同的逻辑判定。

反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和
方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。是Java被视为动态语言的关键。

Java反射机制主要提供了以下功能:

  • 在运行时构造任意一个类的对象
  • 在运行时获取或者修改任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法(属性);
Class

反射始于Class,Class是一个类,封装了当前对象所对应的类的信息。一个类中有属性,方法,构造器等,比如说
有一个Person类,一个Order类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是
Class,它应该有类名,属性,方法,构造器等。Class是用来描述类的类

获得Class对象

获取class对象的三种方式

  1. 通过类名获取 类名.class
  2. 通过对象获取 对象名.getClass()
  3. 通过全类名获取 Class.forName(全类名) / classLoader.loadClass(全类名)
  • 使用Class类的 forName 静态方法
public static Class<?> forName(String className)
  • 直接获取某一个对象的class
Class<?> kclass = int.class;
Class<?> classInt = Integet.TYPE
  • 调用某个对象的getClass方法
StringBuilder str = new StringBuilder("123");
Class<?> kclass = str.getClass();
判断是 否为某个类的实例

一般的 我们用instanceof关键字来判断是否为某个类的实例,同时我们也可以借助反射中Class对象的 isInstance()方法来判断是否为某个类的实例 它是一个native方法:

public native boolean isInstance(Object obj);

判断是否为某个类的类型

public boolean isAssignableForm(Class<?> cls)
创建实例

通过反射来生成对象主要有两种方式

  • 使用Class对象的newInstance()方法来创建Class对象对应类的实例
Class<?> c = String.class;
Object str = c.newInstance();
  • 先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()来创建实例。这种方法可以用指定的构造器构造类的实例。
//获取String所对应的Class对象
Class<?> c= String.class;
//获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);
//根据构造器创建实例
Object obj = constructor.newInstance("2333");
System.out.println(obj);
获取构造器信息

得到构造器的方法

Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的public构造函数(包括父类)
Constructor[] getConstructors() -- 获得类的所有公共构造函数
Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(包括私有)
Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)                                                 

获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的
一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例:

public T newInstance(Object ... initargs)
获取类的成员变量(字段)信息

获取字段信息的方法

Field getField(String name)--获取命名的公共字段+父类成员 (不包括private只能是public)
Field[] getFields()--获取类的公共字段
Field getDeclaredField(String name)--获取类声明的命名的字段
Field[] getDeclaredFields()--获取类声明的所有字段  不包括父类 所有作用域 
调用方法

获取方法信息的方法

Method getMethod(String name,Class[] params)--使用特定的参数类型 获得命名的公共方法
Method[] getMethods() --获得类的所有公共方法
Method getDeclaredMethod(String name,Class[] params)--使用特定的参数类声明的命名的方法 Method[] getDeclaredMethods()--获得类声明的所有方法  

当我们从类中获取了一个方法后,我们可以用invoke方法来调用这个 invoke方法原型为

public Object invoke(Obejct obj,Object ... args)
利用反射创建数组

数组在java里是比较特殊的一种类型。它可以赋值给一个Object Reference其中的Array类为java.lang.reflect.Array类 我们通过Array.newInstance()创建数组对象 它的原型是

public static Object newInstance(Class<?> componentType,int length);
反射获取泛型真实类型

当我们对一个泛型类型进行反射时,需要得到泛型中的真实数据类型,来完成如json反序列化的操作,此时需要通过 type体系来完成,type接口包含了一个实现类(class对象)和四个实现接口,

public final class Class implements Type{

}

他们分别是:

  • TypeVariable 泛型类型变量,可以泛型上下限等信息
  • ParamterizedType 具体泛型类型 可以获得元数据中泛型签名(泛型真实类型)
  • GenericArrayType 当需要描述的类型是 泛型类的数组 如List[],map[] 此接口会作为type实现
  • WildcardType 通配符泛型,获得上下限信息

Json反序列化操作

public class Deserialize{static class Response<T>{T data;int code;String message;public Response(T data,int code,String message){this.data = data;this.code = code;this.message = message;}}static class Data{String result;public Data(String result){this.result = result;}}
}//test
public static void main(String[] args){Response<Data> dataResponse = new Response(new Data("数据"),1,"成功");Gson gson = new Gson();String json = gson.toJson(dataResponse);//反序列化Type type = new TypeToken<Response<Data>>(){}.getType();Response<Data> response = gson.fromJson(json,type);System.out.println(response.data.getClass());//自定义一个TypeToken 为什么人家写protecte 不同的包下protected必须加{}  //有花括号: 代表匿名内部类 没有花括号就是对象 对象没有记录Response<Data>东西 可在字节码中查看 //加{}是 class 匿名内部类 编译后会多一个 类名$1.class 匿名内部类保存了泛型的具体类型Type type = TypeRefrence<Response<Data>>(){}.getType();}static class TypeRefrence<T>{Type type;public TypeRefrence(){//获得泛型类型 Type genericSuperclass = getClass().getGenericSuperclass();ParameterizedType type = (ParamterizedType) genericSuperclass;//可以有多个泛型 <T,A,C,B,A>Type[] actualTypeArguments = type.getActualTypeArguments();type = actualTypeArguments[0];}public Type getType(){return type;}
}
>>>>>>>>>>TypeToken源码解析
public class TypeToken<T>{final Class<? super T> rawType;final Type type;final int hashCode;protected TypeToken(){this.type = getSuperclassTypeParameter(this.getClass());this.rawType = Types.getRawType(this.type);this.hashCode = this.type.hashCode();}static Type getSuperclassTypeParameter(Class<?> subclass){//获取当前对象的泛型类型Type superclass = subclass.getGenericSuperclass();if(superclass instanceof Class){throw new RuntimeException("Missing type parameter");}else{//具体泛型类型ParameterizedType parameterized = (ParameterizedType) superclass;return Types.canonicalize(parameterized.getActualTypeArguments()[0]);}}}   
demo
public static void injectView(Activity activity){//当前activity对应的class对象Class<? extends Activity> cls = activity.getClass();cls.getSuperclass();//父类的class对象 //获得此类所有的成员Field[] declaredFields = cls.getDeclaredFields();for(Field declaredField : declaredFields){if(declaredField.isAnnotationPresent(InjectView.class)){//是否被该注解声明 InjectView injectView = declaredField.getAnnotation(InjectView.class);//获得注解中设置的idint id = injectView.value();View view = activity.findViewById(id);//拿到当前view //反射设置 属性的值declaredField.setAccessible(true);//允许访问 如果为private 设置访问权限 try{//反射赋值 declaredField->TextView 吧view的值赋给给MainActivity中的TvdeclaredField.set(activity,view);//设置activity中的TextView吧值设为view }catch(IllegalAccessException e){e.printStackTrace();}}}
}

打标签

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.Field)
public @interface InjectView{@IdRes int value();
}

使用

@InjectView(R.id.textView)
TextView tv;
课后作业

getIntent()拿到上一个页面传递过来的数据

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired{String value() default "";
}
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);//获得keyString key = TextUtils.isEmpty(autowired.value())?field.getName():autowired.value();if(extras.containsKey(key)){Object obj = extras.get(key);//Parcelable数组类型不能直接设置 其他都可以//获得单个元素类型Class<?> componentType = field.getType().getComponentType();//当前属性是数组并且是Parcelable(子类)数组if(field.getType().isArray() && Paracelable.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(IllegaAccessException e){e.printStackTrace();}}}}
}

相关文章:

  • SystemVerilog学习 (5)——接口
  • 11.10 知识总结(数据的增删改查、如何创建表关系、Django框架的请求生命周期流程图)
  • Django模板层知识
  • virtualbox基本配置
  • ubuntu安装mysql8.0.35过程和报错处理
  • Python采集某网站数据实现简单可视化效果, webpack Js逆向解密
  • XML Web 服务 Eclipse实现中的sun-jaxws.xml文件
  • Mac 本地部署thinkphp8【部署环境以及下载thinkphp】
  • txt2xml
  • DNS服务器典型配置
  • 算法模板题记录
  • Python万圣节礼物
  • LeetCode 2656. K 个元素的最大和:一次遍历(附Python一行版代码)
  • 【Pytorch和深度学习】栏目导读
  • Oneid方案
  • [译] React v16.8: 含有Hooks的版本
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • Apache Zeppelin在Apache Trafodion上的可视化
  • go语言学习初探(一)
  • iOS动画编程-View动画[ 1 ] 基础View动画
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • python学习笔记-类对象的信息
  • redis学习笔记(三):列表、集合、有序集合
  • Spring Boot MyBatis配置多种数据库
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • Windows Containers 大冒险: 容器网络
  • 二维平面内的碰撞检测【一】
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 原生js练习题---第五课
  • 怎么将电脑中的声音录制成WAV格式
  • #微信小程序:微信小程序常见的配置传值
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (四) Graphivz 颜色选择
  • (转)scrum常见工具列表
  • .Net 4.0并行库实用性演练
  • .net core 6 集成 elasticsearch 并 使用分词器
  • .net core Swagger 过滤部分Api
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .net core 客户端缓存、服务器端响应缓存、服务器内存缓存
  • .NET delegate 委托 、 Event 事件
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .net和jar包windows服务部署
  • .NET应用架构设计:原则、模式与实践 目录预览
  • .Net中ListT 泛型转成DataTable、DataSet
  • @GlobalLock注解作用与原理解析
  • @vue/cli脚手架
  • [2024] 十大免费电脑数据恢复软件——轻松恢复电脑上已删除文件
  • [BUG] Authentication Error
  • [C++数据结构](22)哈希表与unordered_set,unordered_map实现
  • [ComfyUI进阶教程] animatediff视频提示词书写要点
  • [docker] Docker的私有仓库部署——Harbor
  • [Foreman]解决Unable to find internal system admin account