一、注解
注解在开发中的作用不言而喻,目前比较流行的开源框架基本都用到了注解,本文结合Retrofit框架的注解来学习和总结,手写注解绑定框架。
annotation注解
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 注解是元数据的一种形式,提供有关于程序但不属于程序本身的数据,不会影响代码的实际逻辑,仅仅起到辅助性的作用。
内置注解
比较常见的内置注解:
@Override 此注解只适用于修饰方法,主要用于在子类中覆盖父类中的方法
@Deprecated 此注解可以修饰方法、属性、类,用来标志被弃用的代码
@SuppressWarnings 用于忽略编译器的警告信息
元注解
在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为 metaannotation(元注解)。
一般的,我们在定义自定义注解时,需要用到@Retention、@Target、@Inherited、 @Documented这几个元注解。
@Retention
注解指定标记注解的存储方式,有三个取值
RetentionPolicy.SOURCE - 标记的注解仅保留在源级别中,并被编译器忽略,编译时被丢弃,可以用作语法检查。
RetentionPolicy.CLASS - 标记的注解在编译时由编译器保留,但 JVM会忽略,可以配合APT用来生成代码,比如ARouter、Butterknifer等框架。
RetentionPolicy.RUNTIME - 标记的注解由 JVM 保留,因此运行时环境可以使用它,可以结合反射获取注解中的所有信息 ,比如Retrofit框架。
@Target
注解标记另一个注解,定义注解可以用于什么对象。取值范围是ElementType类型,定义如下
ElementType.ANNOTATION_TYPE - 可以应用于注解类型,注解的注解
ElementType.CONSTRUCTOR - 可以应用于构造函数
ElementType.FIELD - 可以应用于字段或属性
ElementType.LOCAL_VARIABLE - 可以应用于局部变量
ElementType.METHOD - 可以应用于方法级注解
ElementType.PACKAGE - 可以应用于包声明
ElementType.PARAMETER - 可以应用于方法的参数
ElementType.TYPE - 可以应用于类的任何元素
@Inherited
表示允许子类继承父类中定义的注解。 如果一个子类想获取到父类上的注解信息,那么必须在父类使用的注解上面加上@Inherited
@Documented
表明这个注解应该被 javadoc工具记录,默认情况下javadoc是不包括注解的,但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理,所以注解类型信息也会被包括在生成的文档中,是一个标记注解,没有成员。
@Documented //表明这个注解应该被javadoc工具记录
@Target(METHOD) //允许在方法上使用该注解
@Retention(RUNTIME) //注解保留在运行时
public @interface GET {
//default声明参数默认值,一般参数名为value,在使用的时候可以把value省略掉
String value() default "";
}
二、什么是反射?
假如你写了一段代码:Object o=new Object();
这个时候,我们明确知道Object是什么类,并且能够获得此类的引用,于是通过new直接对这个类进行实例化,之后使用这个类对象进行操作。
反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用JDK 提供的反射API进行反射调用。
反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,并且能改变它的属性。
Java反射机制主要在运行时提供了以下功能:
- 实例化任意一个类的对象
- 调用任意一个对象的方法(属性)
- 获取任意对象的属性,并且能改变对象的属性
Java 的动态就体现在这,通过反射可以实现动态装配、动态代理等,但是反射的过度使用会严重消耗系统资源。
Class
反射始于Class,Class是一个类,封装了当前对象所对应的类的信息。一个类中有属性,方法,构造器等,比如说 有一个Person类,一个Order类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是 Class,它应该有类名,属性,方法,构造器等。Class是用来描述类的类。
获取反射中的Class对象
在 Java API 中,获取 Class 类对象有三种方法:
第一种:使用 Class.forName 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象
Class clz = Class.forName("java.lang.String");
Class clz = String.class;
第三种:调用对象的getClass方法
String str = new String("Hello World");
Class clz = str.getClass();
通过反射创建类对象
通过反射来创建对象主要有两种方式。
第一种:通过 Class 对象的 newInstance() 方法,这种方法只能使用默认的无参构造方法
Class<?> c = String.class;
Object str = c.newInstance();
第二种:通过 Constructor 对象的 newInstance() 方法,这种方法可以选择特定构造方法。
先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。
//获取Apple所对应的Class对象
Class clz = Apple.class;
//获取Apple类带两个参数的构造器
Constructor constructor = clz.getConstructor(String.class, int.class);
//根据构造器创建实例
Apple apple = (Apple)constructor.newInstance("红富士", 10);
通过反射获取类属性、方法、构造器
通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性,而getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性
Class clz = Apple.class;
//获得命名的公共字段
Field[] fields = clz.getField(String name);
//获得Apple类的所有公共字段
Field[] fields = clz.getFields();
//获得Apple类声明的命名的字段
Field[] fields = clz.getDeclaredField(String name);
//获得Apple类声明的所有字段
Field[] fields = clz.getDeclaredFields();
获取方法信息的方法
//使用特定的参数类型,获得命名的公共方法
Method getMethod(String name, Class[] params)
//获得类的所有公共方法
Method[] getMethods()
//使用特写的参数类型,获得类声明的命名的方法
Method getDeclaredMethod(String name, Class[] params)
//获得类声明的所有方法
Method[] getDeclaredMethods()
获取构造器的方法
//获得使用特殊的参数类型的public构造函数(包括父类)
Constructor getConstructor(Class[] params)
//获得类的所有公共构造函数
Constructor[] getConstructors()
//获得使用特定参数类型的构造函数(包括私有)
Constructor getDeclaredConstructor(Class[] params)
//获得类的所有构造函数(与接入级别无关)
Constructor[] getDeclaredConstructors()
获取类属性类方法、类构造器时,如果要获取私有方法或私有构造器,则必须使用有 declared 关键字的方法。
反射获取泛型真实类型
当我们对一个泛型类进行反射时,需要得到泛型中的真实数据类型,来完成如json反序列化的操作,此时需要通过 Type 体系来完成,Type 接口包含了一个实现类(Class)和四个实现接口,他们分别是:
GenericArrayType:该接口表示一种数组类型,比如List[]、Map[],此接口会作为Type的实现;
public class TestType<T> {
List<String>[] lists;
public static void main(String[] args) throws Exception{
Field f = TestType .class.getDeclaredField("lists");
GenericArrayType genericType =(GenericArrayType)f.getGenericType();
System.out.println(genericType.getGenericComponentType());
}
}
ParameterizedType:该接口表示参数化类型,具体的泛型类型,如 Collection<String>;
public class TestType {
Map<String, String> map;
public static void main(String[] args) throws Exception {
Field f = TestType.class.getDeclaredField("map");
System.out.println(f.getGenericType());//java.util.Map<java.lang.String, java.lang.String>
ParameterizedType pType = (ParameterizedType) f.getGenericType();
System.out.println(pType.getRawType()); // interface java.util.Map
for (Type type : pType.getActualTypeArguments()) {
System.out.println(type); // 打印两遍: class java.lang.String
}
}
}
TypeVariable<D>:该接口是各种类型变量的公共高级接口,可以表示泛型声明的参数类型(不存在的类型),如 class ClassName<T>和Collection<T>,这里的T就是一个类型变量;
public class TestType<K extends Comparable & Serializable, V> {
K key;
V value;
public static void main(String[] args) throws Exception {
// 获取字段的类型
Field fk = TestType.class.getDeclaredField("key");
Field fv = TestType.class.getDeclaredField("value");
TypeVariable keyType = (TypeVariable)fk.getGenericType();
TypeVariable valueType = (TypeVariable)fv.getGenericType();
// getName 方法
System.out.println(keyType.getName()); // K
System.out.println(valueType.getName()); // V
//getGenericDeclaration 方法
System.out.println(keyType.getGenericDeclaration()); // class com.test.TestType
System.out.println(valueType.getGenericDeclaration()); // class com.test.TestType
// getBounds 方法
System.out.println("K 的上界:"); // 有两个
for (Type type : keyType.getBounds()) { //interface java.lang.Comparable
System.out.println(type); //interface java.io.Serializable
}
System.out.println("V 的上界:"); //没明确声明上界的, 默认上界是 Object
for (Type type : valueType.getBounds()) { // class java.lang.Object
System.out.println(type);
}
}
}
WildcardType:表示一个通配符类型表达式,如 ?、? extends Number 或 ? super Integer。
public class TestType {
private List<? extends Number> a; // 上限
private List<? super String> b; //下限
public static void main(String[] args) throws Exception {
Field fieldA = TestType.class.getDeclaredField("a");
Field fieldB = TestType.class.getDeclaredField("b"); // 先拿到范型类型
ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();
// 再从范型里拿到通配符类型
WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0];
WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0];
// 方法测试
System.out.println(wTypeA.getUpperBounds()[0]); // class java.lang.Number
System.out.println(wTypeB.getLowerBounds()[0]); // class java.lang.String
// 看看通配符类型到底是什么, 打印结果为: ? extends java.lang.Number
}
}
反射的原理
验证:格式(class文件规范) 语义(final类是否有子类) 操作
准备:静态变量赋初值和内存空间,final修饰的内存空间直接赋原值,此处不是用户指定的初值。
解析:符号引用转化为直接引用,分配地址
初始化:有父类先初始化父类,然后初始化自己;将static修饰代码执行一遍,如果是静态变量,则用户指定值覆盖原有初值;如果是代码块,则执行一遍操作。
Java的反射就是利用上面第二步加载到jvm中的.class文件来进行操作的,Java在编译之后会生成一个 class文件,反射通过 字节码文件找到其类中的方法和属性等。