[Java开发之路](15)注解

1. 简介

注解(也被称为元数据),为我们在代码中添加信息提供了一种形式化的方法。注解在一定程度上是把元数据与源代码文件结合在一起,而不是保存在外部文档中这一大趋势之下所催生的。

它可以提供用来完整的描述程序所需的信息,而这些信息是无法使用Java来表达的。因此,注解使得我们能够以将编译器来测试和验证的格式,存储有关程序的额外信息。注解可以用来生成描述符文件,甚至是新的类定义。通过使用注解,我们可以将这些元数据保存在Java源代码中,并利用Annotation
API为自己的注解构造处理工具。

注解可以生成更加干净易读的代码以及编译器类型检查等等。

注解(annotation)实在实际的源代码级别保存所有的信息,而不是某种注释性文字(comment),这使得代码更加简洁,便于维护。

2. 注解分类

按照运行机制分类 描述
源码注解 注解只在源码中存在,编译成.class文件就不存在了
编译时注解 注解只在源码和.class文件中都存在(例如:@override)
运行时注解 在运行阶段还起作用,甚至影响运行逻辑的注解(例如:@Autowired)

3. 内置注解:

(1)@override

表示当前的方法定义将覆盖超类中的方法。如果你不小心拼写错误,或者方法签名对不上被覆盖的方法,编译器就会发出错误提示。

(2)@Deprecated

如果程序员使用了注解为它的元素,那么编译器会发出警告信息。

(3)@SuppressWarnings

关闭不当的编译器警告信息(在java SE5 之前,也可以使用这个注解,不过被忽略不起作用)

4. 基本语法

4.1 定义注解

可以看到注解的定义很像接口的定义。事实上,与其他任何Java接口一样,注解也会被编译成class文件。


package com.qunar.annotation;

 

import java.lang.annotation.Documented;

import java.lang.annotation.ElementType;

import java.lang.annotation.Inherited;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

 

public class Annotation {

// 定义Description注解

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Inherited

@Documented

// 使用@interface 关键字定义注解

public @interface Description{

// 成员以无参无异常方式声明

String desc();

String author();

// 可以使用default关键字为成员指定一个默认值

int age() default 18;

}

}

除了@符号以外,@Description的定义很像一个接口。定义注解的时候会需要一些元注解,如@Target和@Retention。@Target用来定义你的注解将用于什么地方(是一个方法上还是一个类上),@Retention用来定义该注解在哪一个级别上可用(在源代码上或者是类文件上或者是运行时),具体下面讲解。

4.2 注解元素

注解@Description中包含int元素age,以及String元素desc和author。注解元素可以使用的类型如下:

  • 所有基本数据类型(int,float,boolean等)
  • String
  • Class
  • enum
  • Annotation
  • 以上类型的数组

如果你使用了其他类型,那么编译器就会报错。注意,也不允许使用任何包装类型,不过由于自动打包的存在,这算不上什么限制。注解也可以作为元素的类型,也就是注解可以嵌套。

4.3 默认值限制

编译器对元素的默认值有些过分的挑剔。

首先,元素不能有不确定的值,也就是说元素必须要么有默认值,要么使用注解时提供元素的值。

其次,对于非基本类型的元素,无论是在源代码中声明时,或者是在注解接口中定义默认值时,都不能以null作为其值。为了这个约束,我们只能自己定义一些特殊的值,例如空字符串或者负数,来表示某个元素不存在。

4.4 元注解

元注解只负责注解其他的注解。

元注解 参数 描述

@Taget

CONSTRUCTOR 构造器的声明

           表示注解可以用于什么地方

FIELD 域声明
METHOD 方法声明
PACKAGE 包声明
PARAMETER 参数声明
TYPE 类,接口或enum声明
LOCAL_VARIABLE 局部变量声明  

@Retention
SOURCE 注解只在源码中存在,编译成.class文件就不存在了
        表示需要在什么级别保存该注解信息
CLASS 注解只会在.class文件存在,会被VM丢弃
RUNTIME VM将在运行期也保留注解,因此可以通过反射机制读取注解的信息
@Document     将此注解包含在Javadoc中
@Inherited     允许子类继承父类中的注解

4.5 使用注解

语法:@<注解名称>(<成员名1> = <成员值1>,<成员名2> = <成员值2>,...)


package com.qunar.annotation;

 

import com.qunar.annotation.Annotation.Description;

 

public class Student {

private String name;

@Description(desc = "set name for student object" , author = "sjf0115")

public String getName() {

return name;

}

@Description(desc = "get name from student object" , author = "sjf0115", time = "2016-01-11")

public void setName(String name) {

this.name = name;

}

}

5. 解析注解

通过反射机制获取类,函数或者成员上的运行时注解信息,从而实现动态控制程序运行的逻辑。


package com.qunar.annotation;

 

import java.lang.reflect.Method;

 

import com.qunar.annotation.Annotation.Description;

 

public class ParseAnnotation {

public static void main(String[] args){

Class<?> class1 = null;

try {

// 使用类加载器加载类

class1 = Class.forName("com.qunar.annotation.Student");

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

// 判断Student类上是否有Description注解

boolean isExits = class1.isAnnotationPresent(Description.class);

if(isExits){

// 注解实例

Description desc = class1.getAnnotation(Description.class);

System.out.println("注解:" + desc.toString());

}//if

// 获取Student类上的所有方法

Method[] methods = class1.getMethods();

// 遍历所有方法

for (Method method : methods) {

// 判断方法上是否有Description注解

isExits = method.isAnnotationPresent(Description.class);

if(isExits){

Description description = method.getAnnotation(Description.class);

System.out.println("方法注解:" + description.toString());

}//if

}//for

}

}

运行结果:

方法注解:@com.qunar.annotation.Annotation$Description(time=2016-01-12, desc=set name for student object, author=sjf0115)

方法注解:@com.qunar.annotation.Annotation$Description(time=2016-01-11, desc=get name from student object, author=sjf0115)


package com.qunar.annotation;

 

import java.lang.annotation.Annotation;

import java.lang.reflect.Method;

 

import com.qunar.annotation.Annotation.Description;

 

public class ParseAnnotation {

public static void main(String[] args){

Class<?> class1 = null;

try {

// 使用类加载器加载类

class1 = Class.forName("com.qunar.annotation.Student");

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

// 判断Student类上是否有Description注解

boolean isExits = class1.isAnnotationPresent(Description.class);

if(isExits){

// 注解实例

Description desc = class1.getAnnotation(Description.class);

System.out.println("注解:" + desc.toString());

}//if

// 获取Student类上的所有方法

Method[] methods = class1.getMethods();

// 遍历所有方法

for (Method method : methods) {

// 方法上获取所有的注解

Annotation[] annotations = method.getAnnotations();

for (Annotation annotation : annotations) {

if(annotation instanceof Description){

System.out.println("Description注解:" + annotation.toString());

}//if

}//for

}//for

}

}

这两个程序都用到了反射的方法:getMethods()和getAnnotation(),它们都属于AnnotatedElement接口(Class,Method与Field等类都实现了该接口)。getAnnotation()方法返回指定类型的注解对象,在这里就是Description。如果被注解的方法上没有该类型的注解,则返回null值。

时间: 2024-10-26 06:01:45

[Java开发之路](15)注解的相关文章

【java开发系列】—— 自定义注解

之前在开发中,就总纳闷,为什么继承接口时,会出现@Override注解,有时候还会提示写注解@SuppressWarnings? 原来这是java特有的特性,注解! 那么什么是注解呢? 注解就是某种注解类型的一个实例,我们可以用它在某个类上进行标注,这样编译器在编译我们的文件时,会根据我们自己设定的方法来编译类. 注解都是什么呢?看下面这张图就明白了! 上面的图可以看出,注解大体上分为三种:标记注解,一般注解,元注解 @Override用于标识,该方法是继承自超类的.这样,当超类的方法修改后,实

java开发之路的困惑

问题描述 已经毕业快一年了,算上大四也已经有了2年左右的开发经验.现在的公司是一个比较正规软件公司,名字就不说了,每个项目大概在400万左右,目前这个项目已经验收.由于这个项目的特殊性还有比较多的开发需要完成,加上项目组上3个项目经理离职,在现场只有我一个人,需要同时兼任项目经理的职责,目前公司给我的待遇算上全部的补贴在6500左右.在开发上,能够使用公司的框架正常开发,现在的开发全部都是一些业务逻辑上的开发,总感觉没有学习到什么技术,除了写sql语句熟练了.由于在大学基础没有学好,总感觉自己的

[Java开发之路](16)学习log4j日志

1. 新建一个Java工程,导入Jar包(log4j-1.2.17.jar) Jar包下载地址:点击打开链接 2. 配置文件:创建并设置log4j.properties # 设置 log4j.rootLogger = debug,stdout,D,E   # 输出信息到控制台 log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.append

[Java开发之路](10)DOM解析XML文档

对象序列化的一个重要限制是它只是Java的解决方案:只有Java程序才能反序列化这种对象.一种更具操作性的解决方案是将数据转化为XML格式,这可以使其被各种各样的平台和语言使用. 1. 简介 DOM 是用与平台和语言无关的方式表示XML文档的官方 W3C 标准.DOM 是以层次结构组织的节点或信息片断的集合.这个层次结构允许开发人员在树中寻找特定信息.分析该结构通常需要加载整个文档和构造层次结构, 然后才能做任何工作. 由于它是基于信息层次的,因而 DOM 被认为是基于树或基于对象的.DOM 以

[Java开发之路](3)Java常用类

1.包装类 大家对基本数据类型都非常熟悉,例如 int.float.double.boolean.char 等.基本数据类型是不具备对象的特性,比如基本类型不能调用方法.功能简单...,为了让基本数据类型也具备对象的特性, Java 为每个基本数据类型都提供了一个包装类,这样我们就可以像操作对象那样来操作基本数据类型.  基本类型和包装类之间的对应关系: 包装类主要提供了两大类方法:     1. 将本类型和其他基本类型进行转换的方法     2. 将字符串和本类型及包装类互相转换的方法 pac

[Java开发之路](9)对象序列化与反序列化

1. 对象序列化 当你创建对象时,只要你需要,它会一直存在,但是程序终止时,无论何时它都不会继续存在.尽管这样做是非常有意义的,但是在某些情况下,如果程序不运行时扔能存在并且保存其信息,那将对我们非常有用.这样,在下次程序运行时,该对象将被重建并且拥有的信息与程序上次运行时它所拥有的信息相同.当然,我们也可以通过将信息写入文件或者数据库,但是如果能将一个对象声明为是"持久性"的,并为我们处理掉所有的细节,这将会显得十分方便. Java的序列化是将那些实现了Serializable接口的

[Java开发之路]反射机制

简介 Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. Java反射机制:"程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言".从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言.但是Java有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以

[Java开发之路](5)异常详解

1. 异常分类 在Java程序设计语言中,异常对象都是派生于Throwable类的一个实例.其是如果Java中的异常类不能满足需求,用户可以创建自己的异常类. 下图是Java异常层次结构的一个简化示意图. 从图上可以看出,所有的异常都是继承于Throwable类,但是在下一层立即分解为两个分支:Error和Exception. (1)Error Error描述了Java运行时系统的内部错误和资源耗尽错误.应用程序不应该抛出这种类型的错误,如果出现了这样的内部错误,除了通告用户,并尽力使程序安全的

[Java开发之路]Java字符串的10大热点问题盘点

译文链接:http://www.html5tricks.com/10-top-questions-java-string.html英文原文:Top 10 questions of Java Strings翻译作者:蒋丽丽 下面我为大家总结了10条Java开发者经常会提的关于Java字符串的问题,如果你也是Java初学者,仔细看看吧: 1.如何比较字符串,应该用"=="还是equals()? 总的来说,"=="是用来比较字符串的引用地址,而equals()才是比较字符