当前位置: 移动技术网 > IT编程>开发语言>Java > java 注解的基础详细介绍

java 注解的基础详细介绍

2019年07月19日  | 移动技术网IT编程  | 我要评论

java 注解的基础详细介绍

前言

注解是java引入的一项非常受欢迎的补充,它提供了一种结构化的,并且具有类型检查能力的新途径,从而使得程序员能够为代码加入元数据,而不会导致代码杂乱且难以阅读。使用注解能够帮助我们避免编写累赘的部署描述文件,以及其他生成的文件。

注解的语法比较简单,除了@符号的使用之外,它基本与java固有的语法一致。但由于java源码中提供的内置注解很少,所以大部分同学对注解都不是很了解,虽然我们都接触过,比如java内置的几种注解:

 @override,表示当前的方法定义将覆盖超类中的方法。
  @deprecated,表示当前方法即将废弃,不推荐使用。
  @suppresswarnings,表示忽略编译器的警告信息。

但这并不能让我们体会到注解的强大和便利,其实java还另外提供了四种注解,专门负责新注解的创建。今天我们就来学习下怎么创建和使用注解。

注解类的定义

首先看看一个注解类的定义:

@target(elementtype.field)
@retention(retentionpolicy.runtime)
public @interface constraints {
  boolean primarykey() default false;
  boolean allownull() default true;
  boolean unique() default false;
}

除了@符号以外,注解类的定义很像一个空的接口。定义注解时,会需要一些元注解,如@target和@retention,java提供了四种元注解,定义如下:

@target:表示该注解可以用于什么地方。

取值(elementtype)包括:
constructor:用于描述构造器
field:用于描述域
local_variable:用于描述局部变量
method:用于描述方法
package:用于描述包
parameter:用于描述参数
type:用于描述类、接口(包括注解类型) 或enum声明

@retention:表示需要在什么级别保存该注解信息。

取值(retentionpolicy)包括:
source:在源文件中有效(即源文件保留)
class:在class文件中有效(即class保留)
runtime:在运行时有效(即运行时保留),因此可以通过反射机制读取注解的信息。

@documented:表示将此注解包含在javadoc中。

@inherited:表示允许子类继承父类中的注解。

可以看出 定义注解格式为:
  public @interface 注解名 {定义体}

注解类中定义的元素称为注解元素,注解元素可用的类型如下:

 所有基本数据类型(int,float,boolean,byte,double,char,long,short)
 string类型
 class类型
 enum类型
 annotation类型
 以上所有类型的数组

如果你使用了其它类型,那编译器就会报错。注意,也不允许使用任何包装类型,但由于自动打包的存在,这算不上什么限制。注解也可以作为元素的类型,比如我们再定义一个注解类:

@target(elementtype.field)
@retention(retentionpolicy.runtime)
public @interface sqlstring {
  int value() default 0;
  string name() default "";
  constraints constraints() default @constraints;
  //@constraints后没有括号表明使用默认值,可以加括号,在里面定义初始值,比如@constraints(unique=true)
}

可以看出,所有的注解元素都有一个默认值。编译器对元素的默认值有些过分挑剔,首先,元素必须具有默认值;其次不能以null作为默认值。所以我们只能自己定义一些特殊的值,例如空字符串或负数,来表示某个元素不存在。

注解类的使用

注解类定义好了,怎么使用呢?

为了体现注解的便利和强大,在这里我们写一个demo,使用注解来自动生成一个建数据库表的sql命令。

另外我们再提供两个注解类:

@target(elementtype.type)
@retention(retentionpolicy.runtime)
public @interface dbtable {
  public string name() default "";
}
@target(elementtype.field)
@retention(retentionpolicy.runtime)
public @interface sqlinteger {
  string name() default "";
  constraints constraints() default @constraints;
}

好,现在我们一共有4个注解类。

@dbtable 代表数据库表,注解元素name表示表名;
@constraints 代表对数据表每一列的条件补充,有primarykey是不是主键,默认false,allownull是否允许为空,默认true,unique数据是否唯一,默认false;
@sqlstring 代表表中的string列,注解元素value表示列长度,name表示列名,constraints表示对列的一些约束条件;
@sqlinteger 代表表中的int列,name表示列名,constraints表示对列的一些约束条件;

下面我们定义一个简单的bean类,在其中应用以上4个注解类:

@dbtable(name = "member")
public class member {
  @sqlstring(30) string firstname;
  @sqlstring(50) string lastname;
  @sqlinteger int age;
  @sqlstring(value = 30, constraints = @constraints(primarykey = true)) string handle;
  public string getfirstname() {
    return firstname;
  }
  public string getlastname() {
    return lastname;
  }
  public int getage() {
    return age;
  }
  public string gethandle() {
    return handle;
  }
  public string tostring() {
    return handle;
  }
}

注解的元素在使用时表现为名-值对的形式,并需要置于 @注解类名 声明之后的括号内。如果没有使用括号,代表全部使用默认值。

1、类的注解@dbtable给定了值member,它将会用来作为表的名字;

2、bean的属性firstname和lastname,都被注解为@sqlstring类型,这些注解有两个有趣的地方:第一,他们都使用了嵌入的@constraints注解的默认值;第二,它们都使用了快捷方式。何谓快捷方式?如果程序员的注解中定义了名为value的元素,并且在应用该注解的时候,如果该元素是唯一需要赋值的一个元素,那么此时无需使用名-值对的这种语法,而只需在括号内给出value元素所需的值即可。这可以应用于任何合法类型的元素。当然了,这也限制了程序员必须将此元素命名为value。
bean属性age全部使用默认值,handle为主键。

3、bean属性age全部使用默认值,handle为主键。

实现注解处理器

注解类使用上了,我们还需要一个注解处理器来解析我们定义的bean,这样才能将注解转换成我们需要的操作。

以下定义解析bean的注解处理器,我们的目的是要输出一条sql语句。主要用到了反射技术。

public class tablecreator {
  public static void main(string[] args) throws exception{
//传入我们定义好的bean类名,带上包名    
class<?> cl = class.forname("annotation.member");
//检查我们传入的类是否带有@dbtable注解
    dbtable dbtable = cl.getannotation(dbtable.class);
    list<string> columndefs = new arraylist<string>();
//得到类中的所有定义的属性
    for(field filed : cl.getdeclaredfields()){
      string columnname = null;
//得到属性的注解,对一个目标可以使用多个注解
      annotation[] anns = filed.getannotations();
      if(anns.length < 1){
        continue;
      }
//sqlstring注解走着
      if(anns[0] instanceof sqlstring){
        sqlstring sstring = (sqlstring)anns[0];
//name()使用的是默认值,所以这里取属性名
        if(sstring.name().length() < 1){
          columnname = filed.getname().touppercase();
        }else{
          columnname = sstring.name();
        }
//构建sql语句
        columndefs.add(columnname + " varchar(" + sstring.value() + ")" + getconstraints(sstring.constraints()));
      }
//sqlinteger注解走着
      if(anns[0] instanceof sqlinteger){
        sqlinteger sint = (sqlinteger)anns[0];
        if(sint.name().length() < 1){
          columnname = filed.getname().touppercase();
        }else{
          columnname = sint.name();
        }
        columndefs.add(columnname + " int" + getconstraints(sint.constraints()));
      }
    }
    stringbuilder creator = new stringbuilder("create table " + dbtable.name() + "( ");
    for(string c : columndefs){
      creator.append("\n" + c + ",");
    }
    creator.deletecharat(creator.length()-1);
    creator.append(")");
    system.out.println(creator.tostring());
  }

  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;
  }
}

最后的输出:

create table member( 
firstname varchar(30), 
lastname varchar(50), 
age int, 
handle varchar(30) primary key)

总结

最后,通过这篇文章,我们要学到一下几点:

1、了解java 4种元注解的说明与使用;
2、会自定义注解类;
3、了解注解元素的定义和规则;
4、会使用注解类;
5、能写一个简单的注解处理器解析注解。

如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网