(1)一个人只要自己不放弃自己,整个世界也不会放弃你.
(2)天生我才必有大用
(3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟.
(4)做难事必有所得
(5)精神乃真正的刀锋
(6)战胜对手有两次,第一次在内心中.
(7)好好活就是做有意义的事情.
(8)亡羊补牢,为时未晚
(9)科技领域,没有捷径与投机取巧。
(10)有实力,一年365天都是应聘的旺季,没实力,天天都是应聘的淡季。
(11)基础不牢,地动天摇
(12)写博客初心:成长自己,辅助他人。当某一天离开人世,希望博客中的思想还能帮人指引方向.
(13)编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~
保留在运行时级别注解的应用场景
1.注解的应用场景
(1)根据注解的保留级别不同,对注解的使用自然存在不同场景。
(2)由注解的三个不同保留级别可知,注解作用于:源码、字节码与运行时时你能举一些案例吗?
级别 | 技术 | 说明 |
---|---|---|
源码 | APT | 在编译期能够获取注解声明的类,包括类中的所有成员信息,一般用于生成额外的辅助类。 |
字节码 | 字节码增强 | 在编译出class后,通过修改Class数据以实现修改代码逻辑目的。对于是否需要修改的区分或者修改为不同逻辑的判断可以使用注解 |
运行时 | 反射 | 在程序运行期间,通过反射技术动态获取注解与其元素,从而完成不同的逻辑判定。 |
2.保留在RUNTIME级别的注解结合反射技术的应用
(1)在程序运行期间,通过反射技术动态获取注解及其元素,从而完成不同的逻辑判定。
2.1反射
(1)一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的,并且能够获得此类的引用。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
(2)反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用 JDK 提供的反射 API 进行反射调用。
(3)反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。是Java被视为动态语言的关键。
2.2Java反射机制主要提供了以下功能
- 在运行时构造任意一个类的对象
- 在运行时获取或者修改任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法(属性)
2.3注解+反射自动完成findViewById
2.3.1Field与DecleredField的区别
(1)Field
获得自己+父类的成员(不包括private,只能是public)
(2)DecleredField
只能获得自己的成员(不能获得父类,所有作用域)
2.3.1定义注解
package com.gdc.javabase.annotation;
import android.annotation.SuppressLint;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import androidx.annotation.IdRes;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
@IdRes int value();
}
2.3.2 使用注解与反射实现findViewById
public class InjectUtils {
//s1.反射是基于CLASS
public static void injectView(Activity activity) {
Class<? extends Activity> cls = activity.getClass();
//s1.获得此类所有的成员变量
Field[] fields = cls.getDeclaredFields();
//s2.遍历
for(Field field : fields){
//s3.判断属性是否被InjetView注解声明
boolean ret = field.isAnnotationPresent(InjectView.class);
if(ret){
//s4.判断这个属性上有注解了,此时就还需要获取这个注解
InjectView injectView = field.getAnnotation(InjectView.class);
//s5.获取注解中设置的id值
int id = injectView.value();
View view = activity.findViewById(id);
//s6.反射设置属性的值
//s7.设置访问权限,允许操作私有成员private属性,
field.setAccessible(true);
//s8.设置(反射赋值)
try {
//s9.通过反射将注解中的值设置给view
field.set(activity,view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
2.3.3使用
package com.gdc.javabase.activity;
import android.os.Bundle;
import android.widget.TextView;
import com.gdc.javabase.R;
import com.gdc.javabase.annotation.InjectView;
import com.gdc.javabase.annotation.Lance;
import com.gdc.javabase.inject.InjectUtils;
import androidx.appcompat.app.AppCompatActivity;
@Lance("注解作用于类上")
public class MainActivity extends AppCompatActivity {
private int i;
private int j;
//s1.利用反射与注解完成findViewById的功能
@InjectView(R.id.tv)
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InjectUtils.injectView(this);
tv.setText("控件初始化成功!");
}
}
3.反射获取泛型的真实类型
3.1Type
(1)当我们对一个泛型类进行反射时,需要得到泛型中的真实数据类型,来完成如json反序列化的操作。此时需要通过Type 体系来完成。Type 接口包含了一个实现类(Class)和四个实现接口,他们分别是:
-
TypeVariable
泛型类型变量。可以泛型上下限定信息; -
ParameterizedType
具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型) -
GenericArrayType
当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。 -
WildcardType
通配符泛型,获得上下限信息;
(2)Type体系是什么?
- 是java.lang.refect包下面是一个接口,就一个方法getTypeName;有接口就有实现类。
- Type的实现类就是Class,平时用的Class就是Type接口的实现类。
- 它还有四个对应的子接口,如上,这些子接口可以让我们获得泛型真实的类型。
4.9.1TypeVariable
(1)泛型类型变量。可以获取泛型上下限定信息;
package com.annotations.class1.test;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
public class TestType <K extends Comparable & Serializable,V> {
K key;
V value;
public static void main(String[] args) throws NoSuchFieldException {
//s1.获取字段类型
Field fk = TestType.class.getDeclaredField("key");
Field fv = TestType.class.getDeclaredField("value");
//s2.获取泛型类型
TypeVariable keyType = (TypeVariable) fk.getGenericType();
TypeVariable valueType = (TypeVariable) fv.getGenericType();
//s3.getName打印变量名
System.out.println(keyType.getName());
System.out.println(valueType.getName());
//s4.getGenericDeclaration获取泛型声明
System.out.println(keyType.getGenericDeclaration());
System.out.println(valueType.getGenericDeclaration());
//s5.getBounds()方法
System.out.println("K的上界:");
for(Type type : keyType.getBounds()){
System.out.println(type);
}
System.out.println("V的上界:");
for(Type type : valueType.getBounds()){
System.out.println(type);
}
}
}
运行结果:
K
V
class com.annotations.class1.test.TestType
class com.annotations.class1.test.TestType
K的上界:
interface java.lang.Comparable
interface java.io.Serializable
V的上界:
class java.lang.Object
4.9.2ParameterizedType
(1)具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)
(2)案例
package com.annotations.class1.test;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
public class TestType1 {
Map<String,String> map;
public static void main(String[] args) throws Exception {
//s1.获取指定字段
Field field = TestType1.class.getDeclaredField("map");
//s2.输出字段的泛型类型
System.out.println(field.getGenericType());
//s3.获取字段的泛型类型
ParameterizedType pType = (ParameterizedType) field.getGenericType();
//s4.获取泛型类型的实际类型
System.out.println(pType.getRawType());
//s5.获取字段的泛型类型的参数类型
for(Type type : pType.getActualTypeArguments()){
System.out.println(type);
}
}
}
(2)运行结果
java.util.Map<java.lang.String, java.lang.String>
interface java.util.Map
class java.lang.String
class java.lang.String
4.9.3GenericArrayType
(1)当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。
(2)案例
package com.annotations.class1.test;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.util.List;
public class TestType2<T> {
List<String>[] lists;
public static void main(String[] args) throws NoSuchFieldException {
//s1.获取字段
Field f = TestType2.class.getDeclaredField("lists");
//s2.获取泛型数组类型
GenericArrayType genericArrayType = (GenericArrayType) f.getGenericType();
//s3.获取泛型数组元素类型
System.out.println(genericArrayType.getGenericComponentType());
}
}
(2)运行结果
java.util.List<java.lang.String>
4.9.4WildcardType
(1)通配符泛型,获得上下限信息;
(2)案例代码
package com.annotations.class1.test;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.WildcardType;
import java.util.List;
public class TestType3 {
//s1.泛型上界限定
private List<? extends Number> a;
//s2.泛型下界限定
private List<? super Number> b;
public static void main(String[] args) throws NoSuchFieldException {
//s1.得到字段
Field fieldA = TestType3.class.getDeclaredField("a");
Field fieldB = TestType3.class.getDeclaredField("b");
//s2.获取泛型类型
ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();
//s3.从泛型中拿到通配符类型
WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0];
WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0];
//s4.测试方法
System.out.println(wTypeA.getUpperBounds()[0]);
System.out.println(wTypeB.getLowerBounds()[0]);
//s5.查看通配符类型到底是什么
System.out.println(wTypeA);
}
}
(2)运行结果
class java.lang.Number
class java.lang.Number
? extends java.lang.Number
4.9.5泛型与注解的配合使用
4.9.5.1案例场景描述
服务器给我们响应数据,返回一个json数据,我们对这个json数据进行一个JavaBean的封装,它有可能这样一个数据结构,这个数组一会儿可能是A类型,或者其他接口又变成B类型的,再换一个接口又变成C类型的,所以我们在定义一个实体的时候,会用泛型来定义。
4.9.5.2用Gosn进行序列化服务器响应的数据
package com.gdc.javabase.refflect.type.gson;
import com.google.gson.Gson;
public class Deserialize1 {
/**
* 定义泛型
* @param <T>
*/
static class Response<T>{
//s1.数据一会儿可能是A类型的,一会儿也可能是B类型的,所以来定义这种类型实体的时候采用了泛型定义。
T data;
//s2.成功码
int code;
//s3.描述
String message;
@Override
public String toString() {
return "Response{" + "data=" + data + ", code=" + code + ", message='" + 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;
}
@Override
public String toString() {
return "Data{" + "result='" + result + '\'' + '}';
}
}
public static void main(String[] args) {
//s1.使用泛型
Response<Data> dataResponse =
new Response(new Data("数据"),1,"成功");
//s2.用Gosn进行序列化服务器响应的数据
Gson gson = new Gson();
String json = gson.toJson(dataResponse);
System.out.println(json);
}
}
4.9.5.3用Gosn反序列化服务器响应的数据
public class Deserialize2 {
/**
* 定义泛型
* @param <T>
*/
static class Response<T>{
//s1.数据一会儿可能是A类型的,一会儿也可能是B类型的,所以来定义这种类型实体的时候采用了泛型定义。
T data;
//s2.成功码
int code;
//s3.描述
String message;
@Override
public String toString() {
return "Response{" + "data=" + data + ", code=" + code + ", message='" + 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;
}
@Override
public String toString() {
return "Data{" + "result='" + result + '\'' + '}';
}
}
public static void main(String[] args) {
//s1.使用泛型
Response<Data> dataResponse =
new Response(new Data("数据"),1,"成功");
//s2.用Gosn进行序列化服务器响应的数据
Gson gson = new Gson();
String json = gson.toJson(dataResponse);
System.out.println(json);
//s3.反序列化json数据
/*
s3.1为什么不能按照如下代码反序列化?
(1)如果不是一个泛型类型的话,我们就直接传Response.class就可以了。
(2)但是此时的Response是一个泛型类型。
(3)为什么会报ClassCastException,因为我们指定Response<Data>泛型的实际类型参数为Data,
而gson.fromJson(json,Response.class)返回的是一个LinkedTreeMap类型。
(4)在反序列化的时候,Gson不知道有Data的存在,只告诉了它给我反序列化出来一个Response,所以这个时候它会把
Data认为是LinkedTreeMap,所以如何使用强制的将LinkedTreeMap转换为Data这个实际类型参数的话,就会报类型
转换异常。
*/
/*Response<Data> response = gson.fromJson(json, Response.class);
System.out.println(response.data.getClass());*/
/*
s3.2应该使用Type类型解决这个问题
(1)Gson中提供了一个TypeToken泛型类型
(2)TypeToken<Response<Data>>泛型中有我们需要的Response<Data>泛型信息,所以能帮我们反序列化成功
(3)为什么泛型擦除之后,仍然可以得到泛型的真实类型?
因为TypeToken<Response<Data>>(){}其实是一个匿名内部类,它在CLASS中会记录Response<Data>泛型信息,
这部分信息不会被擦除。
*/
Type type = new TypeToken<Response<Data>>(){}.getType();
System.out.println(type);
Response<Data> response = gson.fromJson(json, type);
System.out.println(response.data.getClass());
}
}
4.9.5.4自定义泛型类型实现反序列化
public class Deserialize {
static class Response<T>{
//s1.数据一会儿可能是A类型的,一会儿也可能是B类型的,所以来定义这种类型实体的时候采用了泛型定义。
T data;
//s2.成功码
int code;
//s3.描述
String message;
@Override
public String toString() {
return "Response{" + "data=" + data + ", code=" + code + ", message='" + 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;
}
@Override
public String toString() {
return "Data{" + "result='" + result + '\'' + '}';
}
}
/**
* 不使用gson中的TypeToken,自定义一个TypeReference<T>泛型
* @param <T>
*/
static class TypeReference<T>{
Type type;
public TypeReference(){
//s1.在运行的时候来获得这个泛型T类型
Type genericSuperclass = getClass().getGenericSuperclass();
//s2.Type genericSuperclass得到的是一个ParameterizedType类型,可以强转
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
//s3.因为该类可以指定多个泛型,不仅仅是T. TypeReference<T,A,B,C>所以是个数组
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
//s4.拿到T
type = actualTypeArguments[0];
}
public Type getType() {
return type;
}
}
public static void main(String[] args) {
//s1.使用泛型
Response<Data> dataResponse = new Response(new Data("数据"),1,"成功");
//s2.用Gosn进行序列化服务器响应的数据
Gson gson = new Gson();
String json = gson.toJson(dataResponse);
System.out.println(json);
//s3.反序列化json数据
/*
(1)注意加{}与不加的区别,不加的时候会报强制转换异常,有{}代表的是匿名内部类,没有{}就是一个对象
(2)对象的类型是TypeReference,然后这个TypeReference的class文件中不会记录Response<Data>,
这是因为泛型擦除引起的。
(3)如果是匿名内部类,它就会在TypeReference的class字节码文件中记录Response<Data>,所以不会导致反序列化失败。
(4)Type type = new TypeReference<Response<Data>>().getType();
*/
Type type = new TypeReference<Response<Data>>(){}.getType();
System.out.println(type);
//s5.反序列化响应数据
/*
(1)因为new TypeReference<Response<Data>>(){}.getType()得到的Type有泛型Response<Data>信息
所以可以反序列化成功。
*/
Response<Data> response = gson.fromJson(json,type);
System.out.println(response.data.getClass());
}
}
4.9.5.4.1注意
new TypeReference<Response<Data>>().getType();
new TypeReference<Response<Data>>(){}.getType();
(1)加{}与不加的含义不一样,一个是匿名内部类,一个是创建的一个对象。
(2)匿名内部类的信息在类的Class信息中会被保留下来,即TypeReference<Response<Data>>泛型的泛型信息可以被保留下来,可以通过ASMBytecodeViewer插件查看字节码信息,明确了这一点,也就明确了为什么需要加上{}来确保反序列化成功。
(3)没{}是创建一个对象,由于泛型擦除的原因,将不清楚TypeReference<Response<Data>>这个泛型里面究竟放的是什么数据类型,所以反序列化会失败。
5.总结
(1)重点在于注解的应用场景
(2)把注解定义在CLASS级别,可以解析字节码文件,解析的时候来获取注解中的各种信息,可以利用注解对某些方法进行修改。
(3)如何来区分某些方法呢?这其实就是一个AOP编程思想,而向切面编程。
(4)要对CLASS文件中的某些方法实现注入,要对方法区分就需要给要注入的方法打上注解,用不同的注解加以区分,每一个注解所标示的方法就相当于是一个切面。
(5)把注解定义在RUNTIME运行时级别,可以在运行时用反射去获取到注解的一些信息。
6.打赏鼓励
感谢您的细心阅读,您的鼓励是我写作的不竭动力!!!