java 注解的基础详细介绍

发布时间 - 2026-01-11 03:08:13    点击率:

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、能写一个简单的注解处理器解析注解。

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


# java  # 注解  # 注解的使用方法  # 注解详解  # mybatis自动生成@Table、@Column、@Id注解的方法  # JAVA注解相关知识总结  # 解决springboot jpa @Column columnDefinition等属性失效问题  # 默认值  # 类中  # 主键  # 可以看出  # 使用了  # 将此  # 四种  # 走着  # 快捷方式  # 的是  # 括号内  # 但由于  # 就会  # 如果你  # 也不  # 都有  # 在这里  # 好了  # 让我们  # 多个 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 绝密ChatGPT指令:手把手教你生成HR无法拒绝的求职信  Swift中循环语句中的转移语句 break 和 continue  网站优化排名时,需要考虑哪些问题呢?  Laravel如何自定义分页视图?(Pagination示例)  ChatGPT常用指令模板大全 新手快速上手的万能Prompt合集  Bootstrap整体框架之CSS12栅格系统  东莞专业网站制作公司有哪些,东莞招聘网站哪个好?  Laravel怎么做数据加密_Laravel内置Crypt门面的加密与解密功能  JS中对数组元素进行增删改移的方法总结  Android自定义listview布局实现上拉加载下拉刷新功能  Win11怎么设置默认图片查看器_Windows11照片应用关联设置  jQuery validate插件功能与用法详解  Laravel如何使用Blade组件和插槽?(Component代码示例)  西安市网站制作公司,哪个相亲网站比较好?西安比较好的相亲网站?  如何选择可靠的免备案建站服务器?  VIVO手机上del键无效OnKeyListener不响应的原因及解决方法  Laravel怎么连接多个数据库_Laravel多数据库连接配置  Laravel Asset编译怎么配置_Laravel Vite前端构建工具使用  音乐网站服务器如何优化API响应速度?  JavaScript如何实现继承_有哪些常用方法  HTML5段落标签p和br怎么选_文本排版常用标签对比【解答】  昵图网官网入口 昵图网素材平台官方入口  打造顶配客厅影院,这份100寸电视推荐名单请查收  北京专业网站制作设计师招聘,北京白云观官方网站?  Laravel如何安装Breeze扩展包_Laravel用户注册登录功能快速实现【流程】  实现点击下箭头变上箭头来回切换的两种方法【推荐】  Laravel怎么返回JSON格式数据_Laravel API资源Response响应格式化【技巧】  软银砸40亿美元收购DigitalBridge 强化AI资料中心布局  Android自定义控件实现温度旋转按钮效果  Angular 表单中正确绑定输入值以确保提交与验证正常工作  微信小程序 五星评分(包括半颗星评分)实例代码  如何在HTML表单中获取用户输入并结合JavaScript动态控制复利计算循环  laravel怎么配置Redis作为缓存驱动_laravel Redis缓存配置教程  如何用狗爹虚拟主机快速搭建网站?  微信小程序 闭包写法详细介绍  深圳网站制作平台,深圳市做网站好的公司有哪些?  Laravel如何实现全文搜索_Laravel Scout集成Algolia或Meilisearch教程  如何彻底卸载建站之星软件?  悟空识字怎么关闭自动续费_悟空识字取消会员自动扣费步骤  香港服务器租用每月最低只需15元?  Laravel如何升级到最新版本?(升级指南和步骤)  Linux系统命令中tree命令详解  宙斯浏览器视频悬浮窗怎么开启 边看视频边操作其他应用教程  html5怎么画眼睛_HT5用Canvas或SVG画眼球瞳孔加JS控制动态【绘制】  Laravel如何使用集合(Collections)进行数据处理_Laravel Collection常用方法与技巧  javascript如何操作浏览器历史记录_怎样实现无刷新导航  百度浏览器如何管理插件 百度浏览器插件管理方法  谷歌Google入口永久地址_Google搜索引擎官网首页永久入口  如何快速搭建高效香港服务器网站?  Laravel怎么实现模型属性转换Casting_Laravel自动将JSON字段转为数组【技巧】