一、注解

1.1、概述

  • 从JDK 5.0 开始, Java 增加了对元数据(MetaData) 的支持, 也就是Annotation(注解)
  • Annotation 其实就是代码里的特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理。通过使用Annotation, 程序员可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。
  • Annotation 可以像修饰符一样被使用, 可用于修饰包,类, 构造器, 方法, 成员变量, 参数, 局部变量的声明, 这些信息被保存在Annotation 的“name=value” 对中。
  • 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。
  • 未来的开发模式都是基于注解的。

1.2、Java中的常见注解

1.2.1、图示

0153.png

@Override:一般在实现了接口的方法上标识,作用是告诉编译器该方法是接口的方法,表示重写。

@Deprecated:一般在接口中的方法上进行标识,作用是该方法过时了。

@Suppvisewarnings:忽视警告,@SuppressWarnings(“deprecation”)

1.2.2、代码示例

  • 创建Person类

    1. public interface Person
    2. {
    3. public String name();
    4. public int age();
    5. @Deprecated
    6. public void sing();
    7. }
  • 创建Child.java类,并实现Person.java类

    1. public class Child implements Person {
    2. @Override
    3. public String name() {
    4. return null;
    5. }
    6. @Override
    7. public int age() {
    8. return 0;
    9. }
    10. @Override
    11. public void sing() {
    12. }
    13. }
  • 创建测试类Test.java
    1. public class Test {
    2. public void sing(){
    3. Person p = new Child();
    4. p.sing();
    5. }
    6. }
  • 此时调用的sing()会有一条横切线标识,标识当前方法即将过期或失效。
    0154.png

1.2.3、问题解决

  1. **此时为了解决这个问题,可以在方法明上添加一个@SuppressWarnings('deprecation')注解,用于标识忽略这个警告。**
  2. **注意**:sing()是过期的方法,一般都是旧版本jdk中使用的,在新的jdk版本中虽然还能用,但是,类似这样的方法大部分存在是为了保证其它旧的项目能够正常运行,而新开发的项目已经不再使用或使用可替代的方法。

1.3、Java第三方注解

1.3.1、图示

0155.png

1.3.2、说明

    第三方的注解(Spring、Mybatis框架中的注解),这些也是比较常见的,功能相对也比较简单,但是对于编程而言,大大的减少代码量,简化了,程序在形式上的编写。

Spring:@Autowired @Service @Repository

@Autowired:可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作。通过 @Autowired的使用来消除 set ,get方法。

public class UserManagerImpl implements UserManager {
   @Autowired
   private UserDao userDao;
}

@Service:用于标注业务层组件。定义某个类为一个bean,则在这个类的类名前一行使用@Service(“XXX”),就相当于讲这个类定义为一个bean,bean名称为XXX。而无需去xml文件内去配置。
@Repository:用于标注数据访问组件,即DAO组件。

二、注解分类

2.1、说明

1、按照运行机制分为

源码注解:注解只在源码中存在,编译成.class文件就不存在了
编译时注解:注解在源码和.class文件中都存在(如:JDK内置系统注解:@Override、@Deprecated、@Suppvisewarnings
运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解(如:Spring中@Autowried 程序运行时,把成员变量自动注入)

2、按照来源分为:

JDK内置系统注解、自定义注解、第三方注解

3.元注解

给注解进行注解,注解的注解

三、自定义注解

3.1、Java自定义注解

3.1.1、说明要求

  • 使用@interface关键字定义注解
  • 成员以无参无异常方式声明
  • 可以用default为成员指定一个默认值
  • 如果注解只有一个成员,则成员名必须取名value(),在使用时可以忽略成员名和赋值号(=);
  • 成员类型是受限的,合法的类型包括原始类型String,Class,Annotation,Enumeration
  • 注解类可以没有成员,没有成员的注解称为标识注解

3.1.2、图示

0156.png

3.1.3、元注解

  • @Documented 生成javadoc时包含注解
  • @Inherited 允许子类继承
  • @Retention(RetentionPolicy.RUNTIME):声明周期
    • SOURCE只在源码显示,编译时会丢弃;
    • CLASS编译时会记录到class中,运行时忽略;
    • RUNTIME运行时存在,可以通过反射读取]
  • @Target({ElementType.METHOD})
    • CONSTRUCTOR构造方法声明;
    • FIELD字段声明;
    • LOCAL_VARIABLE局部变量声明;
    • METHOD方法声明;
    • PACKAGE包声明;
    • PARAMETER参数声明;
    • TYPE类接口

3.1.4、图示

0157.png

3.2、使用自定义注解

3.2.1、格式

    自定义注解的使用

@<注解名>(<成员名1>=<成员值1>,<成员名1>=<成员值1>,…)

3.2.2、示例

@Description(desc="I am HelloWorld",author="guoguo")
public String HelloWorld(){
    return "今天天气很晴朗";
}

3.3、关于自定义注解的代码演示说明

3.3.1、说明

    自定义注解时,如果注解只有一个成员,则成员名必须为value(),在使用是可以忽略成员名称赋值号(=)。

0158.png

3.3.2、详解

  • 说明1
    @Description(desc=”the name method”)。不会报错。但不符合规范,既然只有一个成员,就要使用value作为成员名,改成:

    public @interface Description{
     String value();
    }
    


    然后在使用注解的时候,直接赋值,不写成员名和等号:@Description(“the name method”)
    以上方式,约定俗成,比较直观。

  • 说明2
    没有成员的注解——标志注解(比如常见的@Override),在使用的时候不加括号。

  • 说明3
    当一个方法被规定为({ElementType.METHOD})就表示只能用于方法的注解,如果用在类上面,就会报错(添加 ElementType.TYPE 则可适用于类)。这里没有包含关系,例如只写ElementType.TYPE 则不能对方法进行注解,只能对类或接口进行注解。
  • 说明4
    @Documented(属于标识注解,生成javadoc时会包含注解)

3.4、解析注解

3.4.1、说明

    通过反射获取类、方法或成员的运行时注解信息,从而实现动态控制程序运行的逻辑

    [@Inherited ](/Inherited ) 只能实现类的继承,而接口是无法继承的。即接口的注解无法影响到实现接口的类上面。另外,父类的方法的注解也无法被子类继承。 

3.4.2、代码实现

public static void main(String[] args){
    //1. 使用类加载器加载类
    Class c = Class.forName("anno.test.Child");
    //2. 找到类上面的注解,isAnnotationParse()判断类上是否存在Description这样的注解
    boolean isExist=c.isAnnotationPresent(Description.class);//
    if(isExist){
        //3.获得注解实例
        Description d = (Description)c.getAnnotation(Description.class);
        System.out.println(d.value());
    }
    //4.找到方法上的注解
    // 获取方法上的注解,首先,遍历所有方法,通过方法对象的isAnnotation查看是否有自定义注解
    // 如果存在则输出方法的自定义注解的信息。
    Method[] mts = c.getMethods();
    for (Method mt : mts) {
        if(mt.isAnnotationPresent(Description.class)){
            Description d2 = (Description)mt.getAnnotation(Description.class);
            System.out.println(d2.value());
        }
    }
}

3.4.3、代码实现2

    **第二种解析方法:获取这个方法的所有注解,Annotation [] as=m.getAnnotations();然后遍历该注解,如果遍历的注解是Description类型,则把遍历的注解强转为Description类型,并进行输出value()信息。**
public static void main(String[] args){
    //另外一种获取类上的注解的途径
    Annotation[] annos = c.getAnnotations();
    for (Annotation anno : annos) {
        if(anno instanceof Description){
            System.out.println(((Description) anno).value());
        }
    }
    //另一种获取方法上的注解的途径
    for (Method mt : mts){
        Annotation[] annos2 = mt.getAnnotations();
        for (Annotation anno : annos2) {
            if(anno instanceof Description){ 
                System.out.println(同上);
            }
        }
    }
}