反射使用场景、注解(Annotation)

125 阅读6分钟

注解 -- Annotation

复习

1、反射 --- 它是Java的底层 --- 它也是框架设计的顶层。

运行时探究和使用编译时未知的类

2、这种颠覆式的操作是把本来归JVM玩的类模版对象,在运行时拿来给我们玩儿。

在Java中给每个类都提供了一个Class的对象。这个Class对象本质是JVM加载了一个类以后,要记录这个类的信息供它自己查阅使用。而这个Class类被先人提供了以后,我们发现除了JVM可以调用,我们程序员也能在程序运行过程中主动调用到它。

3、我们需要体会“动态性”的效果。这里的动态是指“编译时未知,运行时确定”。

4、所有反射操作一定是3步:

4-1、获取Class对象;

a、类型名.class
b、实例对象.getClass()
cClass.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的,然后我们才可以在运行起来以后,通过反射获取到它。