注解 -- Annotation
复习
1、反射 --- 它是Java的底层 --- 它也是框架设计的顶层。
运行时探究和使用编译时未知的类
2、这种颠覆式的操作是把本来归JVM玩的类模版对象,在运行时拿来给我们玩儿。
在Java中给每个类都提供了一个Class的对象。这个Class对象本质是JVM加载了一个类以后,要记录这个类的信息供它自己查阅使用。而这个Class类被先人提供了以后,我们发现除了JVM可以调用,我们程序员也能在程序运行过程中主动调用到它。
3、我们需要体会“动态性”的效果。这里的动态是指“编译时未知,运行时确定”。
4、所有反射操作一定是3步:
4-1、获取Class对象;
a、类型名.class
b、实例对象.getClass()
c、Class.forName("类的字符串形式限定名");
a方式最全面,可以操作所有的类型,获取它们的Class对象;
b方式只针对Object的子类的对象才能使用,因为getClass()来自于Object。也就是说只有类类型(包括枚举和记录),以及数组可以。
c方式针对所有的引用数据类型;这种方式是最灵活的一种方式,因为它的类型信息是以String的形式提供的,所以它的值可以在代码中不确定而是通过程序外部输入进来的。另外这里我们还涉及了一个“冷”知识点---数组的字符串限定名是什么样子的? Student[] --> "[Lcom.lovo.bean.Student"
Serializable[] --> "[Ljava.io.Serializable"
byte[] "[B" short[] "[S" int[] "[I" long[] "[J" -- 特殊 float[] "[F" double[] "[D" char[] "[C" boolean[] "[Z" -- 特殊
4-2、探究构造、方法、属性
构造Constructor
方法Method
属性Field
Constructor[] allPublic = getConstructors() -- 获取所有的公共构造
Constructor[] all = getDeclaredConstructors()--获取所有声明的构造
getConstructor(形参列表的class对象) -- 获取指定的公共构造
getDeclaredConstructor(形参列表的class对象)--获取指定声明的构造
方法和属性请自己推演
4-3、使用探究到的构造产生实例对象;
-- newInstance()
使用探究到的方法完成方法调用;
-- invoke()
使用探究到的属性完成赋值/取值。
-- set()/get()
反射使用的场景
1、工具类的底层实现 -- 比如:FastJson
我们来利用反射的API模仿一下。
2、关联关系的注入
比如:在A类中需要用到B类,但是在不同的场景下B类的实现各不相同。那么就会有很多B的子类,这个时候A关联B不要去关联B的子类。那么A在操作的时候,可以通过反射去实例化B的子类对象,然后交给自己去用。
注解的概念
Annotation 是一种能够被书写到Java源代码中的元数据(metadata:描述数据的数据)形式。类、方法、变量、参数和包都可以使用它来进行描述。Annotation不会对它描述的代码操作有任何的直接影响。
JDK中自带的常用注解
@Override -- 要求编译器在编译时检查被修饰的方法是否是一个正确的重写;
@Deprecated -- 被它修饰的方法会被标记为过时(过时不是错误!!),然后编译器就可以提示我们。通常都是用一根斜线把这个方法名划掉。
@SuppressedWarnings -- 用来把代码中的警告信息给消除掉,让它不再报黄色警告。
通过这三种注解的使用,我们了解到了注解的使用表象:
1、@注解名(参数......)
如果一个注解没有参数,那么()可以省略
2、注解可以写在类的声明、方法的声明、变量的声明(属性、参数、局部变量)、和包的声明
3、注解不仅仅是写给程序员看的,它还可以影响到编译器,甚至是运行期。
注解的本质
Annotation在本质上就是一个Java类型,是JDK1.5提出的。
1、我们可以把Annotation理解为一种特殊的接口,因为所有的Annotation都默认继承了java.lang.annotation.Annotation的接口。但是在实现的语法中,我们不能做直接的接口继承,而是要使用@interface关键字声明它。
public @interface 注解名{
}
2、在注解的{}内,我们要填写它的内容。而它的内容既不是方法也不是属性,或者说语法上既像方法又像属性。
public @interface 注解名{
访问修饰符 数据类型 标识名();
......
}
注意
访问修饰符都是public的,可以省略;
数据类型只有5种(基本数据类型、String、Class、枚举、注解类型),以及这5种的一维数组;
标识名应该是名词,因为它代表了一个配置项或者叫做属性名
()不可少,不是参数列表,仅仅是一种特有语法。
如果某个类型元素有默认值,那么应该是用default接在后面;
3、一个注解定义的时候除了上面两点,还可以控制它的使用位置和获取时机
用@Target注解 定义当前注解的书写位置
用@Retention注解 定义当前注解的获取时机
@Target要求填入的有类声明处、方法声明处、构造声明处、变量声明处、参数声明处等等,而且可以同时配置多个
@Retention只有3个选择:源代码、class文件和运行时。通常都是运行时。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface 注解名{
访问修饰符 数据类型 标识名();
......
}
另外还有两个修饰注解的注解:@Documented和@Inherited
使用注解
1、首先根据该注解的@Target写在正确的位置;
2、书写的语法:
@注解名(类型元素=值1,类型元素=值2....)
注意
1、如果一个注解没有类型元素,那么可以省略它后面的()
@Overrid @Override()
2、如果一个注解只有一个类型元素,且名字叫做value,那么可以省略"value="
@MyAnnotation({"hello","world"})
@MyAnnotation(value={"hello","world"})
3、数组类型的类型元素,如果只给一个值,那么可以省略掉{}
@MyAnnotation("hello")
@MyAnnotation({"hello"})
@MyAnnotation(value="hello")
注解到底在干什么?
其实,定义注解就是在定义一个具有更强针对性的配置。
注解如何影响运行期
首先一个注解要想在运行期获取到,那么它的@Retention必须是RUNTIME的,然后我们才可以在运行起来以后,通过反射获取到它。