文章目录
注解
java注解(Annotation)又称为java标注,注解是JDK5.0引入的一种能在代码中加入元数据的机制,不直接影响程序的运行,通过工具或框架,在编译时、类加载时或运行时处理这些元数据,来改变程序的行为或提供额外的功能。
注解声明
声明一个注解类型
下面就是@MyAnnotation
注解声明
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
String value();
}
元注解
什么是元注解,元注解就是注解的注解,如上面@MyAnnotation
注解,其中ElementType.METHOD
表示只能用于在方法上的注解,而@Target(ElementType.METHOD)
用于在注解上的注解,也就是所谓的元注解。
-
@Target
注解标注另一个注解,用于限制应用注解的java元素类型。常见的值为:
ElementType.Type
类、接口(注解类型)或枚举声明ElementType.FIELD
字段或属性声明ElementType.METHOD
方法声明ElementType.PARAMETER
方法的参数声明ElementType.CONSTRUCTOR
构造函数声明ElementType.LOCAL_VARIABLE
局部变量声明ElementType.ANNOTATION_TYPE
注解类型声明ElementType.PACKAGE
包声明
-
@Retention
定义注解的保留策略,常见的值为:
RetentionPolicy.SOURCE
注解只保留在源码中,也就是在编译时,编译后丢弃。(常用技术APT)RetentionPolicy.CLASS
注解在编译时由编译器保留,但JVM运行时不可见。(常用技术字节码增强)RetentionPolicy.RUNTIME
注解运行时也存在,由JVM保留。(常用技术反射)
三者之间关系 .java文件 -> .class文件 -> 内存中的字节码,SOURCE包含于CLASS,CLASS包含于RUNTIME
-
@Documented 用于被javadoc工具提取成文档
-
@Inherited 表示允许子类继承父类中定义的注解
反射
一般想调用一个对象内部的属性或者方法,我们会去通过new 实例化获取此类的引用,再使用这个类的引用进行操作。
如果一开始不知道要使用什么对象,就不会去实例化创建对象,这时可以通过JDK提供的的反射API进行反射调用。反射就是在运行时,对于任意一个类都能知道此类的所有属性、所有方法;对于任意一个对象都能调用它的所有属性、所有方法,并能修改,我称为全知全能(hh,小说看多了)。
什么是Class
知道任意一个类,对象的属性,方法,其中类、对象不就是class吗,想要反射,那就得从class类开始,一个class类,用来封装了当前对象所对应的类的信息,通过这个class类,我们就能获取他有哪些属性,方法,构造,实现了什么接口等等
获取Class对象
想开始操作了,那如何获取Class类了
-
Class.forName(com.example.application.Apple)
第一种通过调用Class.forName(String className)
传入全类名获取Class对象。 -
Class<Integer> intClass = int.class;
Class<Integer> integerClass = Integer.TYPE;
第二种,通过类名.class获取。
-
Class<? extends Food> foodClass= new Food().getClass();
第三种,通过实例对象获取。
判断是否为某个类的实例
public native boolean isInstance(Object obj)
判断是否为某个类的类型
public boolean isAssignableFrom(Class<?> cls)
创建Class类描述类的实例
//该native方法用于创建实例
public native T newInstance()//Class类
//方式一
Class<?> intClass = int.class;
Object num = intClass.newInstance();
//方式二,用于有构造器,并想指定构造器,实例构造类的
Class<?> stringClass = String.class;
Constructor<?> constructor = stringClass.getConstructor(String.class);
Object str = constructor.newInstance("hello");
获取构造器信息
Constructor<T> getConstructor(Class<?>... parameterTypes) -- 使用特殊参数,获取所有自己与父类的public修饰的构造函数
Constructor<?>[] getConstructors() -- 获取所有自己与父类的public修饰的构造函数
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) -- 使用特殊参数,获取自己的所有构造函数(不论修饰符)
Constructor<?>[] getDeclaredConstructors() -- 获取自己的所有构造函数(不论修饰符)
Class源码中方法的规律:
无Declared,获取所有自己与父类的public修饰的**
有Declared,获取所有自己的**(不论修饰符)
无后缀s,可通过特殊参数,指定所有的**
有后缀s,获取所有的**
获取成员变量(字段)信息
Field getField(String name)//Class类
获取成员方法,并调用成员方法
Method getMethod(String name, Class<?>... parameterTypes)//Class类
Object invoke(Object obj, Object... args)//通过获取的方法调用这个方法 Method类
通过反射创建数组
public static Object newInstance(Class<?>... parameterTypes,int length);//Array类
反射获取泛型真实类型
当我们对一个泛型类进行反射时,想要获取泛型中的真实数据类型,来完成json反序列化操作,就需要通过Type
体系来完成,Type
接口中包含了一个实现类(Class)和四个实现接口,分别是:
- TypeVariable
- 泛型类型变量,可以获取泛型上下限等信息。
- ParameterizedType
- 具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)。
- GenericArrayType
- 当需要描述的类型是泛型类的数组时,比如List[]、Map[],此接口会作为Type的实现。
- WildcardType
- 通配符泛型,获得上下限信息。
使用案例:
-
fastjson+okhttp请求
public class HttpUtil { public static <T> void response(Back<T> back){ //模拟一串序列化数据 String data = "{\"code\":200,\"data\":{\"name\":\"apple\"}}"; //反序列化 T o = JSON.parseObject(data, back.getType()); back.onSuccess(o); } // public static <T> void response(Back<T> back, TypeReference<T> typeRef){ // //模拟一串序列化数据 // String data = "{\"code\":200,\"data\":{\"name\":\"apple\"}}"; // //反序列化 // T o = JSON.parseObject(data, typeRef); // back.onSuccess( o); // } public abstract static class Back<T>{ Type type; public Back() { //反射获取类的泛型信息 Type superclass = getClass().getGenericSuperclass(); //获取到泛型的具体信息,也就是泛型真是类型 ParameterizedType parameterizedType = (ParameterizedType) superclass; //泛型可能不只一个所以取[0]位,<T,E,K> type = parameterizedType.getActualTypeArguments()[0]; } public Type getType() { return type; } abstract void onSuccess(T data); } } public class test { public static void main(String[] args) { // HttpUtil.response((Res<Fruit> data) -> { // System.out.println(data.getClass()); // System.out.println(data.code); // System.out.println(data.data.name); // }, new TypeReference<>(){}); //匿名内部类实现Back抽象类,onSuccess作为回调函数 HttpUtil.response(new HttpUtil.Back<Res<Fruit>>() { @Override void onSuccess(Res<Fruit> data) { System.out.println(data.getClass()); System.out.println(data.code); System.out.println(data.data.name); } }); } }
其中注释的代码中创建
new TypeReference<Res<Fruit>>(){}
为什么需要花括号。因为没有花括号创建的泛型对象,它在运行期,会被泛型擦除成Object,Object类型就无法让fastjson知道具体要反序列是那个对象。
而加入花括号(抽象类或接口)就会动态创建子对象,在子对象中就会具现化泛型信息,该signature信息就会记录到Class元数据中。
再上述代码中Back就为一个抽象类,构造函数中获取泛型的信息,但构造的时候Class元数据中还未有signature泛型信息,所以需要实现抽象类,将signature泛型信息记录到Class元数据中。
-
Android Intent 传值(注解+反射)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectExtra {
String value() default "";
}
public class InjectUtil {
public static void initExtra(Activity activity){
Intent intent = activity.getIntent();
Bundle extras = intent.getExtras();
if(extras==null) return;
Class<? extends Activity> aClass = activity.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields) {
//判断该属性的注解是否为@InjectExtra
if(field.isAnnotationPresent(InjectExtra.class)){
InjectExtra extra = field.getAnnotation(InjectExtra.class);
//获取注解中的值
String key = extra.value();
if(key==null || key.isEmpty()) key = field.getName();
if(extras.containsKey(key)){
Object obj = extras.get(key);
Class<?> componentType = field.getType().getComponentType();
//特殊处理Parcelable的实现类
if(field.getType().isArray() &&
Parcelable.class.isAssignableFrom(componentType)){
Objects[] objs = (Objects[]) obj;
Objects[] objects = Arrays.copyOf(objs,objs.length,(Class<? extends Objects[]>) field.getType());
obj = objects;
}
//设置true,可修改私有属性
field.setAccessible(true);
try {
//参数一,那一对象的属性赋值,参数而:值
field.set(activity,obj);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this,MainActivity2.class);
intent.putExtra("name","apple");
intent.putExtra("boy",false);
intent.putExtra("food","orange");
intent.putExtra("id",555);
startActivity(intent);
}
}
public class MainActivity2 extends AppCompatActivity {
@InjectExtra
String name;
@InjectExtra("boy")
Boolean isBoy;
@InjectExtra("food")
String fruit;
@InjectExtra
int id;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main2);
InjectUtil.initExtra(this);
Toast.makeText(this, "name: "+name+" \nisBoy: "+isBoy+" \nfruit: "+fruit+" \nid: "+id, Toast.LENGTH_LONG).show();
}
}
上述就是一个简单的注解加反射,实现快速注入Intent传入的参数