Java Annotation详解(一): 理解和使用Annotation

JDK1.5之后,引入了元数据的概念,也就是Annotation(注释),其实它是代码里的特殊标记,这些标记可以再编译、类加载、运行时被读取,并执行相应的处理。

元数据的作用:

如果要对于元数据的作用进行分类,目前还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类:

1.  编写文档:通过代码里标识的元数据生成文档。
2.  代码分析:通过代码里标识的元数据对代码进行分析。
3.  编译检查:通过代码里标识的元数据让编译器能实现基本的编译检查。

一、 系统内建的Annotation:

    @Override 覆写的Annotation

注释能实现编译时检查,你可以为你的方法添加该注释,以声明该方法是用于覆盖父类中的方法。如果该方法不是覆盖父类的方法,将会在编译时报错。例如我们为某类重写toString()方法却写成了tostring(),并且我们为该方法添加了@Override注释,则会提示编译错误。
     @Deprecated 不赞成使用的Annotation

其作用是对不应该在使用的方法添加注释,当编程人员使用这些方法时,将会在编译时显示提示信息,不推荐在使用该方法或该类。
    @SuppressWarnings 压制安全警告的Annotation

与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数值都是已经定义好了的,我们选择性的使用就好了,参数如下:
deprecation   使用了过时的类或方法时的警告
unchecked  执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型
fallthrough   当 Switch 程序块直接通往下一种情况而没有 Break 时的警告
path   在类路径、源文件路径等中有不存在的路径时的警告
serial 当在可序列化的类上缺少 serialVersionUID 定义时的警告
finally    任何 finally 子句不能正常完成时的警告
all 关于以上所有情况的警告

在为@SuppressWarnings设置注释信息的时候,是以key-value的形式出现的,所以以上的@SuppressWarnings也可以直接使用,所以@SuppressWarnings可以使用”value={"unchecked","deprecation"}“的方式来设置。

@Deprecated
class Demo<T>{
	private T var ;
	public T getVar(){
		return this.var ;
	}
	public void setVar(T var){
		this.var = var ;
	}
};
public class SuppressWarningsAnnotationDemo03{
	// @SuppressWarnings(value={"unchecked","deprecation"})
	public static void main(String args[]){
		Demo d = new Demo() ;
		d.setVar("沉缘") ;
		System.out.println("内容:" + d.getVar()) ;
	}
};

上面,我们将 注释掉,编译后,会出现警告提示:

---------- javac ----------
注: SuppressWarningsAnnotationDemo03.java使用或覆盖了已过时的 API。
注: 有关详细信息, 请使用 -Xlint:deprecation 重新编译。
注: SuppressWarningsAnnotationDemo03.java使用了未经检查或不安全的操作。
注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。

打开@SuppressWarnings注释,再次编译,发现,警告已被抑制。

二、 自定义Annotation

定义简单的Annotation形式:

[public] @interface Annotation名称{

        数据类型  变量名称();

}

例如:

public @interface MyDefaultAnnotationNoneParam{
}

之后,就可以直接使用@Annotation名称:

@MyDefaultAnnotationNoneParam
class Demo
{
}

此时,就表示在Demo类上使用Annotation。

还可以向Annotation中设置变量,使用变量接受参数。

public @interface MyDefaultAnnotationSingleParam{
	public String value();	//接受设置的参数
}

在使用的时候,必须清楚的指定变量的名称,变量的内容:

@MyDefaultAnnotationSingleParam("沉缘")
class Demo
{
}

或者使用明确的标记,表示内容赋给哪个参数:

@MyDefaultAnnotationSingleParam(value="沉缘")
class Demo
{
}

以上的参数,是要赋给value属性的。既然可以设置一个参数,则也就可以同时设置多个参数。

public @interface MyDefaultAnnotationMoreParam{
	public String key() ;
	public String value() ;	// 接收设置的内容
}

此Annotation在使用时,需要设置两个参数,一个key,一个value。

@MyDefaultAnnotationMoreParam(key="Linkage",value="沉缘")
class Demo{
};

当然,我们可以设置数组进去,@SuppressWarnings就使用了数组。

public @interface MyDefaultAnnotationArrayParam{
	public String[] value() ;	// 接收设置的内容
}

接收内容本身是一个数组类型,要传递数组。

@MyDefaultAnnotationArrayParam(value={"沉缘","流烬"})
class Demo{
};

以上的定义Annotation都未指定属性的默认值,必须在使用时设置。 其实,也可以直接使用default来定义默认值:

public @interface MyDefaultAnnotationValue{
	public String key() default "Linkage" ;	// 指定好了默认值
	public String value() default "沉缘" ;	// 指定好了默认值
}

在实际的操作中,对于一个Annotation而言,有时候会固定其取值范围,只能使用固定的几个值。那么这时候实际上就需要依靠枚举:

public enum MyName{	// 定义枚举类型
	WUQING,WUYUAN,WULEI ;
}

public @interface MyDefaultAnnotationEnum{
	public MyName name() default MyName.WUQING ;	// 指定默认值
}

三、 限定注释使用范围Target

当我们的自定义注释不断的增多也比较复杂时,就会导致有些开发人员使用错误,主要表现在不该使用该注释的地方使用。为此,Java提供了一个ElementType枚举类型来控制每个注释的使用范围,比如说某些注释只能用于普通方法,而不能用于构造函数等。下面是Java定义的ElementType枚举:

package java.lang.annotation;

public enum ElementType {

  TYPE,         // Class, interface, or enum (but not annotation)

  FIELD,        // Field (including enumerated values)

  METHOD,       // Method (does not include constructors)

  PARAMETER,        // Method parameter

  CONSTRUCTOR,      // Constructor

  LOCAL_VARIABLE,   // Local variable or catch clause

  ANNOTATION_TYPE,  // Annotation Types (meta-annotations)

  PACKAGE       // Java package

}

想要使用ElementType,只需要为注释添加@Target即可:

@Target( { ElementType.METHOD, ElementType.CONSTRUCTOR })

public @interface TargetTest {

}

正如上面代码所展示的,我们只允许Greeting注释标注在普通方法和构造函数上,使用在包申明、类名等时,会提示错误信息。

四、 Retention和RetentionPolicy,注释保持性策略

public enum RetentionPolicy {

  SOURCE,// Annotation is discarded by the compiler

  CLASS,// Annotation is stored in the class file, but ignored by the VM

  RUNTIME// Annotation is stored in the class file and read by the VM

}

RetentionPolicy的使用方法的简单代码示例如下:

@Retention(RetentionPolicy.RUNTIME)

而,在RetentionPolicy的三个范围中,最需要注意的就是RUNTIME范围,因为在执行的时候起作用。

import java.lang.annotation.Retention ;
import java.lang.annotation.RetentionPolicy ;
@Retention(value=RetentionPolicy.RUNTIME) // 表示此Annotation在运行时有效
public @interface MyDefaultRententionAnnotation{
public String name() default "沉缘" ;
}

我们看下内建的Annotation的RetentionPolicy:

@Override定义采用的是@Retention(value=SOURCE),只能在源文件中出现。

@Deprecated定义采用的是@Retention(value=RUNTIME),可以在执行时出现。

@SuppressWarnings定义采用的是@Retention(value=SOURCE),只能在源文件中出现。

五、 文档化功能

Java提供的Documented元注释跟Javadoc的作用是差不多的,其实它存在的好处是开发人员可以定制Javadoc不支持的文档属性,并在开发中应用。它的使用跟前两个也是一样的,简单代码示例如下:

import java.lang.annotation.Documented ;
@Documented
public @interface MyDocumentedAnntation{
<span style="white-space:pre">	</span>public String key() default "Linkage" ;
<span style="white-space:pre">	</span>public String value() default "沉缘" ;
}

成功后,在使用此Annotation的时候,可以增加一些信息进去:

@MyDocumentedAnntation(key="Baidu",value="www.baidu.com")
public class SimpleBeanDocumented{
	/**
	 * 此方法在对象输出时调用,返回对象信息
	 */
	@MyDocumentedAnntation(key="Xinlang",value="www.sina.com")
	public String toString(){
		return "Hello World!!!" ;
	}
};

之后,在生成jdk文档的时候,使用@Document修饰的方法将被注释下来。

六、 标注继承

继承应该是Java提供的最复杂的一个元注释了,它的作用是控制注释是否会影响到子类(一个Annotation是否可以被继承下来),简单代码示例如下:

package com.test.inheriteddemo ;
import java.lang.annotation.Retention ;
import java.lang.annotation.RetentionPolicy ;
import java.lang.annotation.Documented ;
import java.lang.annotation.Inherited ;
@Documented
@Inherited
@Retention(value=RetentionPolicy.RUNTIME)
public @interface MyInheritedAnnotation{
	public String name() ;

}

使用该注释,标注一个父类Person:

package com.test.inheriteddemo ;
@MyInheritedAnnotation(name="沉缘")
public class Person{
};

按照所解释的,使用Inherited声明的Annotation是可以被子类继承下来的:

import java.lang.annotation.Annotation ;
import org.lxh.demo16.inheriteddemo.MyInheritedAnnotation ;
public class ReflectInheritedDemo{
	public static void main(String args[]) throws Exception{
		Class<?> c = null ;
		c = Class.forName("com.test.inheriteddemo.Student") ;
		Annotation ann[] = c.getAnnotations() ;	// 取得全部的Annotation
		for(Annotation a:ann){	// 输出
			System.out.println(a) ;
		}
		// 继续取得此Annotation设置的内容
		if(c.isAnnotationPresent(MyInheritedAnnotation.class)){
			MyInheritedAnnotation mda = null ;
			mda = c.getAnnotation(MyInheritedAnnotation.class) ;
			String name = mda.name() ;	// 取出name的内容
			System.out.println("name = " + name) ;
		}
	}
}
时间: 2024-08-24 13:50:45

Java Annotation详解(一): 理解和使用Annotation的相关文章

Java Annotation详解(二): 反射和Annotation

前面一篇文<Java Annotation详解(一): 理解和使用Annotation>中,我们或许会觉得,Annotation注释其实并没有多大的作用,除了几个内建的Annotation偶尔为了消除警告会使用下,自定义Annotation大家在实际的开发中应该都没有用过.其实呢,我在毕业后一年的工作里,也从未自定义使用过Annotation,只是在多处开发中使用过注释方便的内容,比如Servlet,Spring以及一些优秀的Android开源类库. 如果从简单的开发来讲,大家基本会使用一些开

Java虚拟机详解----JVM常见问题总结

[正文] 声明:本文只是做一个总结,有关jvm的详细知识可以参考本人之前的系列文章,尤其是那篇:Java虚拟机详解04----GC算法和种类.那篇文章和本文是面试时的重点. 面试必问关键词:JVM垃圾回收.类加载机制.   先把本文的目录画一个思维导图:(图的源文件在本文末尾)   一.Java引用的四种状态: 强引用: 用的最广.我们平时写代码时,new一个Object存放在堆内存,然后用一个引用指向它,这就是强引用. 如果一个对象具有强引用,那垃圾回收器绝不会回收它.当内存空间不足,Java

Java泛型详解_java

1. Why --引入泛型机制的原因     假如我们想要实现一个String数组,并且要求它可以动态改变大小,这时我们都会想到用ArrayList来聚合String对象.然而,过了一阵,我们想要实现一个大小可以改变的Date对象数组,这时我们当然希望能够重用之前写过的那个针对String对象的ArrayList实现.     在Java 5之前,ArrayList的实现大致如下: public class ArrayList { public Object get(int i) { ... }

JAVA 内部类详解及实例_java

JAVA 内部类 一.什么是内部类? 一个类的定义放在另一个类的内部,这个类就叫做内部类 二.内部类有那些特性? 1.内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号 . 2.内部类不能用普通的方式访问.内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的 . 3.内部类声明成静态的,就不能随便的访问外部类的成员变量了,此时内部类只能访问外部类的静态成员变量 . 三.内部类有哪几种? 1.成员内

Android AOP注解Annotation详解(一)

Android 注解Annotation 相关文章: Android AOP注解Annotation详解(一) Android AOP之注解处理解释器详解(二) Android AOP 注解详解及简单使用实例(三) Android AOP 等在Android上应用越来越广泛,例如框架ButterKnife,Dagger2,EventBus3等等,这里我自己总结了一个学习路程. - Java的注解Annotation - 注解处理解析器APT(Annotation Processing Tool)

Java设计模式详解之门面模式(外观模式)_java

门面模式(Facade Pattern)也叫外观模式,它隐藏系统的复杂性,并向客户端提供一个可以访问系统的接口.这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性,为子系统中的一组接口提供了一个统一的高层访问接口,这个接口使得子系统更容易被访问或使用.这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用. 简而言之,就是把一堆复杂的流程封装成一个接口供给用户更简单的使用,这个设计模式里有三个角色: 1)门面角色( facade ):

java关键字(详解)

基本类型 1 boolean 布尔型 2 byte 字节型 3 char 字符型 4 double 双精度 5 float 浮点 6 int 整型 7 long 长整型 8 short 短整型 9 null 空 10 true 真 11 false 假 程序控制语句 1 break 跳出中断 2 continue 继续 3 return 返回 4 do 运行 5 while 循环 6 if 如果 7 else 否则 8 for 循环 9 instanceof 实例 10 switch 观察 11

Java NIO 详解(一)

NIO即新的输入输出,这个库是在JDK1.4中才引入的.它在标准java代码中提供了高速的面向块的IO操作. 一.基本概念描述 1.1 I/O简介 I/O即输入输出,是计算机与外界世界的一个借口.IO操作的实际主题是操作系统.在java编程中,一般使用流的方式来处理IO,所有的IO都被视作是单个字节的移动,通过stream对象一次移动一个字节.流IO负责把对象转换为字节,然后再转换为对象. 关于Java IO相关知识请参考我的另一篇文章:Java IO 详解 1.2 什么是NIO NIO即New

HBase Java API详解

[本文转自HBase Java API详解] HBase是Hadoop的数据库,能够对大数据提供随机.实时读写访问.他是开源的,分布式的,多版本的,面向列的,存储模型. 在讲解的时候我首先给大家讲解一下HBase的整体结构,如下图: HBase Master是服务器负责管理所有的HRegion服务器,HBase Master并不存储HBase服务器的任何数据,HBase逻辑上的表可能会划分为多个HRegion,然后存储在HRegion Server群中,HBase Master Server中存

请教:朋友跟我想写一本关于JAVA虚拟机详解方面的书。

问题描述 朋友跟我想写一本关于JAVA虚拟机详解方面的书.书的内容主要包括JVM的原理,JVM源码分析等方面的问题.书本身内容清晰,层次很分明,也很通俗易懂.目前书已经写了一半,大概6章的内容..不知道怎么联系出版社,如果出版以后销路会如何.也不知道有没有多少读者会关注JAVA虚拟机方面的知识..大家给点意见,或者渠道..谢谢. 解决方案 解决方案二:顶,一直有个小理想,自己写本jvm分析的书,不过未能实现.感觉这种书很小众.jvm原理的书还可以,但代码分析的未必对大部分java程序员有多大价值