初识 Java 注解

正在学习的小白。不当之处,请多多指教java

目录程序员

  • 前言
  • 自定义注解的使用
  • 元注解
  • 注解的解析反射
  • 解析注解的做用(引入IOC概念)

前言

java中有四大类型,其中三个是:枚举,接口,类。编程

今天简单认识一下第四个类型:注解。数组

什么是注解bash

Annotation 这里是"注解"的意思。
除此以外,这个单词患有一个“注释”的意思。咱们都知道,注释是给程序员看的。那么注解呢?
注解是给程序看的,因此Annotation既有注解也有注释的意思
复制代码

咱们很早就见过一些注解:jdk中的@Override,@FunctionalInterface,Junit框架的@Test等。框架

注解的基本语法

枚举,接口,类,这个三个类型咱们都写过(他们的源码 ),基本语法,想必你们都知道。jvm

那么注解有源码吗?答案是确定的,jvm没有那么厉害,不可能仅凭一个@+一个单词就知道程序想表达什么。ide

咱们以@FunctionalInterface(关于这个注解不清楚的能够参考函数式编程的内容)为例:函数式编程

@FunctionalInterface
interface UsB{
    void show();
}
复制代码

按住Ctrl点击注解进入,就能够看到@FunctionalInterface的源码:函数

package java.lang;
import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
复制代码

先不着急认识这些是什么意思,看懂其构成就能够,而后咱们照猫画虎,本身写一个:

/** * 自定义注解 */
@interface MyAnnotation{ }
复制代码
  1. 因而,咱们完成了一个自定义注解,由此,能够基本知道注解的基本构成:

@interface 注解名字{ }

那么,注解有像其余普通类同样的属性吗?

  1. 有,可是,注解的属性不太同样,他是这样写的,

String name(); 注解中 这是属性,不是接口中的方法。

  1. 既然是属性,那就能够赋值:

注解属性赋值语法:String name() default "属性";

(ps:能够赋值,但通常再也不内部赋值,给外部使用者赋值)

  1. 那么属性这样写,注解有方法吗?有的话的方法该怎么写?

很差意思,注解是没有方法的!

  1. 普通类中的属性,能够是任意类型,那么注解也同样吗??

注解对属性类型是有要求的:

8个基本数据类型 / 字符串类型 / Class类型 / 注解类型 / 枚举类型 及其一维数组

  1. 属性补充:

注解中只有一个属性, 那么请将该属性定义为 value 名称. 好处: 使用该注解时能够省略 value=

以上6点就是注解的基本语法。

自定义注解的使用

以前咱们使用注解,都是固定的,好比@Override只能在(重写)方法使用,@FunctionalInterface只能在(有且仅有一个要实现的方法的)接口使用。乳沟随便使用,就会马上编译报错。 那么,使用咱们刚刚自定义的注解,使用上有限制吗?

@MyAnnotation("省略了value")//能够用在类上
public class AnnotationDemo {
    @MyAnnotation("zhangsan") String name;//能够用在属性上
    @MyAnnotation("show") //能够用在方法上
    public void show(){}
}

//自定义注解
@interface MyAnnotation{
    //属性
   String value();
}
复制代码

元注解

这样看来,咱们目前自定义的注解是没有任何使用位置的限制的,再回头看看,前面@FunctionalInterface注解的源码,或者@Override的源码,发现咱们自定义的注解少了几样东西。没错,少了一些注解。准确说,少了一些 “元注解”

package java.lang;
import java.lang.annotation.*;
//三个元注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
复制代码

什么是元注解,“元”意为:最开始,初始的意思,那么元注解就是其实的注解,或者叫,注解的注解。是用来修饰说明注解的。

元注解都来自于:java.lang.annotation.* 下

先来认识一下元注解:

@Target

意为目标,也就是说明注解的使用范围

来看一下@Target的源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}
复制代码

能够看到,他也有本身的元注解,和一个一维数组属性ElementType[],进入ElementType[],咱们能够看到,ElementType是一个枚举类(控制文本长度,去除了全部源码的注释):

public enum ElementType {
    TYPE,
    FIELD,
    METHOD,
    PARAMETER,
    CONSTRUCTOR,
    LOCAL_VARIABLE,
    ANNOTATION_TYPE,
    PACKAGE,
    TYPE_PARAMETER,
    TYPE_USE
}
复制代码

这些枚举类型很容易看出,Target注解的value值,能够是这些枚举元素,例如:FIELD表示使用在属性上,METHOD可使用在方法上,等等,不在一一说明。

使用: 、

@Target({ElementType.FIELD,ElementType.METHOD}) 注意,多个值用中括号,属性名为value,可省略

能够加在咱们前面写的自定义注解上看看效果。

@Retention

意为,保留策略,又称之为生命周期。

咱们继续进入@Retention的源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}
复制代码

也只有一个属性,进入RetentionPolicy查看:

public enum RetentionPolicy {
    SOURCE,
    CLASS,
    RUNTIME
}
复制代码

RetentionPolicy也是一个枚举类,只有三个值。

这三个值很好理解,他与java程序的声明周期是一一对应的:

源码阶段(SOURCE),编译阶段(CLASS),运行阶段(RUNTIME)。

@Retention的属性不是数组,因此只能选择一个值

如:@Retention(RetentionPolicy.CLASS)

ps : RetentionPolicy.RUNTIME 最经常使用,由于一般和反射结合使用,而反射是在运行时操做类。

注解的反射解析

(不了解反射能够参考个人另外一篇笔记:Java 反射机制那些事

咱们经过代码的方式,简单说明下,如何利用反射解析注解

先来准备一个 Student类:

public class Student {
    //一个属性
    public String name;
    public Student() { }
    //一个构造
    public Student(String name) { this.name = name; }
    //一个方法
    public void show(String msg){
        System.out.println("show方法 = " + msg);
    }
    //重写toString
    @Override
    public String toString() {
        return "Student{name= "+name+"}';
    }
}
复制代码

再来写一个自定义注解:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.CONSTRUCTOR})
public @interface MyAnnotation{
   String value();
}
复制代码

而后,根据自定义注解的做用范围,在Student类上加上咱们自定义的注解,而后赋上一些值:

@MyAnnotation("小明")
    public Student(String name) {
        this.name = name;
    }
    @MyAnnotation("小红来了")
    public void show(String msg){
        System.out.println("show方法 = " + msg);
    }
复制代码

在新建一个类,实现咱们的反射部分代码,这里个人类就叫作:AnnotationDemo

补充:

注解 某种意义上讲能够做为 一种配置文件(常见的配置文件 .properties 或者 .xml)

​ 既然写了一个文件,就要去对他进行一些 读写 操做,若是不去读取并使用他的内容,呢这个(配置)文件存在在程序中有什么意义??文件是保存信息数据的,因此文件不读出来,是没有意义的。

​ 而读出配置文件的内容 称之为 解析!

​ 注解有个解析技术,叫作: 反射!

​ 写了注解,就至关于,写了配置文件不读取!是没有意义的。

反射是在运行时,操做Class对象,注解写在Student类中,因此,反射能够操做Student的Class对象。

咱们就先AnnotationDemo类中在利用反射获取一个Student里面的自定义注解:

public class AnnotationDemo {
	public static void main(String[] args) throws NoSuchMethodException {
        //1.获取Student的Class对象
        Class<?> clazz = Student.class;
        //2.先从构造器开刀,找到构造器
        Constructor<?> constructor = clazz.getConstructor(String.class);
        /** * 3.使用构造起的方法 isAnnotationPresent(), * 方法意为:有没有(参数)注解存在?注意:(一个方法。类等能够有多个注解) * 参数:注解类型的class对象 * 返回值:存在(true), */
        boolean annotation = constructor.isAnnotationPresent(MyAnnotation.class);
        //若是存在,来获取这个注解
        if (annotation) {
            /* getAnnotation(注解.class)获取注解 此时获取到 Student满参构造上的注解 */
            MyAnnotation myAnnotation = constructor.getAnnotation(MyAnnotation.class);
            //注解有个属性叫value
            String value = myAnnotation.value();
            System.out.println("value = " + value);        //value = 小明
        }
    }
}
复制代码

以上代码,就将 Student满参构造上的注解的属性值读取出来了。

解析注解做用(引入IOC概念)

仍是和反射同样的问题?这样解析注解有什么用?

这要结合具体场景,有些项目中可能要本身定义注解使用,而最多使用的地方就是框架。

题外话——引入IOC概念

上面的示例代码咱们能够看到,AnnotationDemo类中的一系列代码,就获取到了Student的满参构造的注解的value值。那么获取到注解的属性值,咱们就能够将值反转到(传入)这个构造里面去。

这个就叫作控制反转(IOC)

怎么传值呢?继续看代码!

//部分代码和上面同样,注释省略
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> clazz = Student.class;
        Constructor<?> constructor = clazz.getConstructor(String.class);
        boolean annotation = constructor.isAnnotationPresent(MyAnnotation.class);
        if (annotation) {
            MyAnnotation myAnnotation = constructor.getAnnotation(MyAnnotation.class);
            String value = myAnnotation.value();
            System.out.println("value = " + value);        //value = 小明
            
            /* 利用 newInstance 方法,就能够得到Student实例 */
            Object obj = constructor.newInstance(value);
            System.out.println("obj = " + obj);

        }
    }
复制代码

​ 咱们能够想一下,假如AnnotationDemo类和自定义注解,不是咱们所写,是一种别人写的框架,本身历来不知道这样一些代码,而只是用一个注解,传了个值,就构造出了一个实例对象。这就是框架技术的一部分底层原理。

​ 接下来解析Student的show方法的注解:

public class AnnotationDemo {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取Class对象
        Class<?> clazz = Student.class;
        //获取方法
        Method show = clazz.getMethod("show", String.class);
        boolean b = show.isAnnotationPresent(MyAnnotation.class);
        if (b) {
            //每一个反射对象都有这样一个方法,获取注解
            MyAnnotation annotation = show.getAnnotation(MyAnnotation.class);
            String value = annotation.value();
           show.invoke(clazz.newInstance(), value);
           //运行,查看结果
                //show方法 = 小红来了
        }
    }
}
复制代码

相关文章
相关标签/搜索