java 注解Annotation

描述

java注解是在JDK5时引入的新特性,Java注解与普通修饰符(public、static、void等)的使用方式并没有多大区别,可以修饰java对象元素。

声明注解

//自定义声明的注解,可以提供java元素调用

@Target(ElementType.METHOD)//元注解
@Retention(RetentionPolicy.RUNTIME)//元注解
public @interface Test {

} 

元注解

用来给其他注解打标签的注解,即用来注解其他注解的注解。元注解共有6个。

@Retention:用于指定被此元注解标注的注解的保留时长,源代码如下:

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

从源代码中可以看出,其有一个属性value,返回一个枚举RetentionPolicy 类型,有3种类型:

RetentionPolicy.SOURCE: :注解信息只保留在源代码中,编译器编译源码时会将其直接丢弃。
RetentionPolicy.CLASS::注解信息保留在class文件中,但是虚拟机VM不会持有其信息。编译时获取注解信息,并据此产生java代码文件,无性能损失
RetentionPolicy.RUNTIME::注解信息保留在class文件中,而且VM也会持有此注解信息,所以可以通过反射的方式获得注解信息。 运行时获取并使用注解信息,此方式属于反射范畴,有性能损失
@Target:用于指定被此元注解标注的注解可以标注的程序元素,源码如下:

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

从源码中可以看出,其有一个属性value,返回一个枚举ElementType类型的数组

public enum ElementType {
   /**标明该注解可以用于类、接口(包括注解类型)或enum声明*/
   TYPE,

   /** 标明该注解可以用于字段(域)声明,包括enum实例 */
   FIELD,

   /** 标明该注解可以用于方法声明 */
   METHOD,

   /** 标明该注解可以用于参数声明 */
   PARAMETER,

   /** 标明注解可以用于构造函数声明 */
   CONSTRUCTOR,

   /** 标明注解可以用于局部变量声明 */
   LOCAL_VARIABLE,

   /** 标明注解可以用于注解声明(应用于另一个注解上)*/
   ANNOTATION_TYPE,

   /** 标明注解可以用于包声明 */
   PACKAGE,

   /**
    * 标明注解可以用于类型参数声明(1.8新加入)
    */
   TYPE_PARAMETER,

   /**
    * 类型使用声明(1.8新加入)
    */
   TYPE_USE
}

当注解未指定Target值时,则此注解可以用于任何元素之上,多个值使用{}包含并用逗号隔开,下面代码表示,此Annotation既可以注解构造函数、字段和方法:

@Target(value={CONSTRUCTOR, FIELD, METHOD})

@Documented:将被标注的注解生成到javadoc中。

@Inherited:其让被修饰的注解拥有被继承的能力。如下,我们有一个用@Inherited修饰的注解@InAnnotation,那么这个注解就拥有了被继承的能力。

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InAnnotation{
}

@InAnnotation
class Base{}

class Son extends Base{}

当使用此注解修饰一个基类Base, 其子类Son 并没有使用任何注解修饰,但是其已经拥有了@InAnnotation这个注解,相当于Son 已经被@InAnnotation修饰了

@Repeatable :使被修饰的注解可以重复的注解某一个程序元素。例如下面的代码中@ShuSheng这个自定义注解使用了@Repeatable修饰,所以其可以按照下面的语法重复的注解一个类。是JDK1.8新加入的

@ShuSheng(name="frank",age=18)
@ShuSheng(age = 20)
public class AnnotationDemo{}

如何定义一个重复注解呢,如下所示,我们需要先定义一个容器,例如ShuShengs ,然后将其作为参数传入@Repeatable中。

@Repeatable(ShuShengs.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ShuSheng {
    String name() default "ben";
    int age();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ShuShengs {
    ShuSheng[] value();
}

注解元素及其数据类型

通过上述对@Test注解的定义,我们了解了注解定义的过程,由于@Test内部没有定义其他元素,所以@Test也称为标记注解(marker annotation),但在自定义注解中,一般都会包含一些元素以表示某些值,方便处理器使用

/**
 * Created by wuzejian on 2017/5/18.
 * 对应数据表注解
 */
@Target(ElementType.TYPE)//只能应用于类上
@Retention(RetentionPolicy.RUNTIME)//保存到运行时
public @interface DBTable {
    String name() default "";
}

上述定义一个名为DBTable的注解,该用于主要用于数据库表与Bean类的映射(稍后会有完整案例分析),与前面Test注解不同的是,我们声明一个String类型的name元素,其默认值为空字符,但是必须注意到对应任何元素的声明应采用方法的声明方式,同时可选择使用default提供默认值,@DBTable使用方式如下:

//在类上使用该注解

@DBTable(name = "MEMBER")
public class Member {
    //.......
}

关于注解支持的元素数据类型除了上述的String,还支持如下数据类型

所有基本类型(int,float,boolean,byte,double,char,long,short)

String

Class

enum

Annotation

上述类型的数组

倘若使用了其他数据类型,编译器将会丢出一个编译错误,注意,声明注解元素时可以使用基本类型但不允许使用任何包装类型,同时还应该注意到注解也可以作为元素的类型,也就是嵌套注解,下面的代码演示了上述类型的使用过程:

package com.zejian.annotationdemo;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by wuzejian on 2017/5/19.
 * 数据类型使用Demo
 */

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Reference{
    boolean next() default false;
}

public @interface AnnotationElementDemo {
    //枚举类型
    enum Status {FIXED,NORMAL};

    //声明枚举
    Status status() default Status.FIXED;

    //布尔类型
    boolean showSupport() default false;

    //String类型
    String name()default "";

    //class类型
    Class<?> testCase() default Void.class;

    //注解嵌套
    Reference reference() default @Reference(next=true);

    //数组类型
    long[] value();
}

注意:元素必须要么具有默认值,要么在使用注解时提供元素的值,注解不支持继承

基本注解

Java内置的注解共有5个

Override:让编译器检查被标记的方法,保证其重写了父类的某一个方法。此注解只能标记方法。源码如下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

@Deprecated:标记某些程序元素已经过时,程序员请不要再使用了。源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

@SuppressWarnings :告诉编译器不要给老子显示警告,老子不想看,老子清楚的知道自己在干什么。源码如下:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}   

其内部有一个String数组,根据传入的值来取消相应的警告: 
deprecation:使用了不赞成使用的类或方法时的警告; 
unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型; 
fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告; 
path:在类路径、源文件路径等中有不存在的路径时的警告; 
serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告; 
finally:任何 finally 子句不能正常完成时的警告; 
all:关于以上所有情况的警告。

@SafeVarargs(Java7 新增) :@SuppressWarnings可以用在各种需要取消警告的地方,而 @SafeVarargs主要用在取消参数的警告。就是说编译器如果检查到你对方法参数的操作,有可能发生问题时会给出警告,但是你很自(任)性,老子不要警告,于是你就加上了这个标签。源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {}

其实这个注解是专为取消堆污染警告设置的,因为Java7会对可能产生堆污染的代码提出警告,什么是堆污染?且看下面代码

 @SafeVarargs
 private static void method(List<String>... strLists) {
     List[] array = strLists;
     List<Integer> tmpList = Arrays.asList(42);
     array[0] = tmpList; //非法操作,但是没有警告
     String s = strLists[0].get(0); //ClassCastException at runtime!
 }

如果不使用 @SafeVarargs,这个方法在编译时候是会产生警告的 : “…使用了未经检查或不安全的操作。”,用了就不会有警告,但是在运行时会抛异常。

@FunctionalInterface(Java8 新增): 标记型注解,告诉编译器检查被标注的接口是否是一个函数接口,即检查这个接口是否只包含一个抽象方法,只有函数接口才可以使用Lambda表达式创建实例。源码如下:

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

使用注解

Annotation接口是所有注解的父接口(需要通过反编译查看),在java.lang.reflect 反射包下存在一个叫AnnotatedElement接口,其表示程序中可以接受注解的程序元素,例如 类,方法,字段,构造函数,包等等。而Java为使用反射的主要类实现了此接口,如反射包内的Constructor类、Field类、Method类、Package类和Class类。

当我们通过反射技术获取到反射包内的那些类型的实例后,就可以使用AnnotatedElement接口的中的API方法来获取注解的信息了。

<T extends Annotation> T getAnnotation(Class<T> annotationClass); : 返回该元素上存在的指定类型的注解,如果不存在则返回 null。
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass){} :返回该元素上存在的直接修饰该元素的指定类型的注解,如果不存在则返回null.
Annotation[] getAnnotations();:返回该元素上存在的所有注解。
Annotation[] getDeclaredAnnotations();:返回该元素上存在的直接修饰该元素的所有注解。
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass){}:该方法功能与前面getAnnotation方法类似,但是由于Java8 加入了重复注解功能,因此需要此方法获取修饰该程序元素的指定类型的多个Annotation
获取注解简单示例
首先我们定义了两个注解@Master与@ShuSheng,@ShuSheng是一个可重复注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Master {
}


@Repeatable(ShuShengs.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ShuSheng {
    String name() default "ben";
    int age();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ShuShengs {
    ShuSheng[] value();
}

使用定义好的注解来修饰,如下

@Master
public class AnoBase {
}

@ShuSheng(name="frank",age=18)
@ShuSheng(age = 20)
public class AnnotationDemo extends AnoBase{
}

调用相关函数获取相应的结果 

private static void getAnnotation()

   {
       Class<?> cInstance=AnnotationDemo.class;

       //获取AnnotationDemo上的重复注解
       ShuSheng[] ssAons= cInstance.getAnnotationsByType(ShuSheng.class);
       System.out.println("重复注解:"+Arrays.asList(ssAons).toString());

       //获取AnnotationDemo上的所有注解,包括从父类继承的
       Annotation[] allAno=cInstance.getAnnotations();
       System.out.println("所有注解:"+Arrays.asList(allAno).toString());

       //判断AnnotationDemo上是否存在Master注解
       boolean isP=cInstance.isAnnotationPresent(Master.class);
       System.out.println("是否存在Master: "+isP);
   }

执行结果如下:

重复注解:[@top.ss007.ShuSheng(name=frank, age=18), @top.ss007.ShuSheng(name=ben, age=20)]
所有注解:[@top.ss007.ShuShengs(value=[@top.ss007.ShuSheng(name=frank, age=18), @top.ss007.ShuSheng(name=ben, age=20)])]
是否存在Master: false
 

自定义注解处理器(APT)

了解完注解与反射的相关API后,就可以更进一步。下面的实例自定义了一个APT,完成通过注解构建SQL语句的功能。此处代码来自此处。下面代码要求对数据库有初步认识。

先定义相关的注解

/**
 * 用来注解表
 */
@Target(ElementType.TYPE)//只能应用于类上
@Retention(RetentionPolicy.RUNTIME)//保存到运行时
public @interface DBTable {
    String name() default "";
}

/**
 * 注解Integer类型的字段
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
    //该字段对应数据库表列名
    String name() default "";
    //嵌套注解
    Constraints constraint() default @Constraints;
}

/**
 * 注解String类型的字段
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
    //对应数据库表的列名
    String name() default "";
    //列类型分配的长度,如varchar(30)的30
    int value() default 0;
    Constraints constraint() default @Constraints;
}

/**
 * 约束注解
 */
@Target(ElementType.FIELD)//只能应用在字段上
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
    //判断是否作为主键约束
    boolean primaryKey() default false;
    //判断是否允许为null
    boolean allowNull() default false;
    //判断是否唯一
    boolean unique() default false;
}

/**
 * 数据库表Member对应实例类bean
 */
@DBTable(name = "MEMBER")
public class Member {
    //主键ID
    @SQLString(name = "ID",value = 50, constraint = @Constraints(primaryKey = true))
    private String id;

    @SQLString(name = "NAME" , value = 30)
    private String name;

    @SQLInteger(name = "AGE")
    private int age;

    @SQLString(name = "DESCRIPTION" ,value = 150 , constraint = @Constraints(allowNull = true))
    private String description;//个人描述

   //省略set get.....
}

上述定义4个注解,分别是@DBTable(用于类上)、@Constraints(用于字段上)、 @SQLString(用于字段上)、@SQLString(用于字段上)并在Member类中使用这些注解,这些注解的作用的是用于帮助注解处理器生成创建数据库表MEMBER的构建语句,在这里有点需要注意的是,我们使用了嵌套注解@Constraints,该注解主要用于判断字段是否为null或者字段是否唯一。接下来就需要编写我们自己的注解处理器了。

public class TableCreator {

  public static String createTableSql(String className) throws ClassNotFoundException {
    Class<?> cl = Class.forName(className);
    DBTable dbTable = cl.getAnnotation(DBTable.class);
    //如果没有表注解,直接返回
    if(dbTable == null) {
      System.out.println(
              "No DBTable annotations in class " + className);
      return null;
    }
    String tableName = dbTable.name();
    // If the name is empty, use the Class name:
    if(tableName.length() < 1)
      tableName = cl.getName().toUpperCase();
    List<String> columnDefs = new ArrayList<String>();
    //通过Class类API获取到所有成员字段
    for(Field field : cl.getDeclaredFields()) {
      String columnName = null;
      //获取字段上的注解
      Annotation[] anns = field.getDeclaredAnnotations();
      if(anns.length < 1)
        continue; // Not a db table column

      //判断注解类型
      if(anns[0] instanceof SQLInteger) {
        SQLInteger sInt = (SQLInteger) anns[0];
        //获取字段对应列名称,如果没有就是使用字段名称替代
        if(sInt.name().length() < 1)
          columnName = field.getName().toUpperCase();
        else
          columnName = sInt.name();
        //构建语句
        columnDefs.add(columnName + " INT" +
                getConstraints(sInt.constraint()));
      }
      //判断String类型
      if(anns[0] instanceof SQLString) {
        SQLString sString = (SQLString) anns[0];
        // Use field name if name not specified.
        if(sString.name().length() < 1)
          columnName = field.getName().toUpperCase();
        else
          columnName = sString.name();
        columnDefs.add(columnName + " VARCHAR(" +
                sString.value() + ")" +
                getConstraints(sString.constraint()));
      }
    }
    //数据库表构建语句
    StringBuilder createCommand = new StringBuilder(
            "CREATE TABLE " + tableName + "(");
    for(String columnDef : columnDefs)
      createCommand.append("\n    " + columnDef + ",");

    // Remove trailing comma
    String tableCreate = createCommand.substring(
            0, createCommand.length() - 1) + ");";
    return tableCreate;
  }

  /**
   * 判断该字段是否有其他约束
   * @param con
   * @return
   */
  private static String getConstraints(Constraints con) {
    String constraints = "";
    if(!con.allowNull())
      constraints += " NOT NULL";
    if(con.primaryKey())
      constraints += " PRIMARY KEY";
    if(con.unique())
      constraints += " UNIQUE";
    return constraints;
  }

  public static void main(String[] args) throws Exception {
    String[] arg={"com.zejian.annotationdemo.Member"};
    for(String className : arg) {
      System.out.println("Table Creation SQL for " +
              className + " is :\n" + createTableSql(className));
    }
  }
}

输出结果为:

Table Creation SQL for com.zejian.annotationdemo.Member is :
CREATE TABLE MEMBER(
        ID VARCHAR(50) NOT NULL PRIMARY KEY,
        NAME VARCHAR(30) NOT NULL,
        AGE INT NOT NULL,
        DESCRIPTION VARCHAR(150)
        );
 

 https://blog.csdn.net/ShuSheng0007/article/details/80622035

https://blog.csdn.net/kaifa1321/article/details/79622715

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园的建设目标是通过数据整合、全面共享,实现校园内教学、科研、管理、服务流程的数字化、信息化、智能化和多媒体化,以提高资源利用率和管理效率,确保校园安全。 智慧校园的建设思路包括构建统一支撑平台、建立完善管理体系、大数据辅助决策和建设校园智慧环境。通过云架构的数据中心与智慧的学习、办公环境,实现日常教学活动、资源建设情况、学业水平情况的全面统计和分析,为决策提供辅助。此外,智慧校园还涵盖了多媒体教学、智慧录播、电子图书馆、VR教室等多种教学模式,以及校园网络、智慧班牌、校园广播等教务管理功能,旨在提升教学品质和管理水平。 智慧校园的详细方案设计进一步细化了教学、教务、安防和运维等多个方面的应用。例如,在智慧教学领域,通过多媒体教学、智慧录播、电子图书馆等技术,实现教学资源的共享和教学模式的创新。在智慧教务方面,校园网络、考场监控、智慧班牌等系统为校园管理提供了便捷和高效。智慧安防系统包括视频监控、一键报警、阳光厨房等,确保校园安全。智慧运维则通过综合管理平台、设备管理、能效管理和资产管理,实现校园设施的智能化管理。 智慧校园的优势和价值体现在个性化互动的智慧教学、协同高效的校园管理、无处不在的校园学习、全面感知的校园环境和轻松便捷的校园生活等方面。通过智慧校园的建设,可以促进教育资源的均衡化,提高教育质量和管理效率,同时保障校园安全和提升师生的学习体验。 总之,智慧校园解决方案通过整合现代信息技术,如云计算、大数据、物联网和人工智能,为教育行业带来了革命性的变革。它不仅提高了教育的质量和效率,还为师生创造了一个更加安全、便捷和富有智慧的学习与生活环境。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值