Java开发之路

文章32 |   阅读 9325 |   点赞0

来源:https://blog.csdn.net/sunnyyoona

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

x33g5p2x  于2021-03-13 发布在 其他  
字(11.2k)|赞(0)|评价(0)|浏览(308)

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 元注解

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

<table cellspacing="0" cellpadding="0" style="margin:8px 0px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:892px"><tbody><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); text-align:center; width:206px">元注解</td><td colspan="2" rowspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); text-align:center; width:429px"><span style="font-size:10.5pt; line-height:1.5">参数</span></td><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); text-align:center; width:206px">描述</td></tr><tr><td colspan="1" rowspan="7" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px"><br/><br/><br/>@Taget<br/><br/><br/></td><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">CONSTRUCTOR</td><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">构造器的声明</td><td colspan="1" rowspan="6" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px"><br/><br/>           表示注解可以用于什么地方<br/><br/><br/></td></tr><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">FIELD</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">域声明</td></tr><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">METHOD</td><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">方法声明</td></tr><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">PACKAGE</td><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">包声明</td></tr><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">PARAMETER</td><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">参数声明</td></tr><tr><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">TYPE</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">类,接口或enum声明</td></tr><tr><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">LOCAL_VARIABLE</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">局部变量声明</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px"> </td></tr><tr><td colspan="1" rowspan="3" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px"><br/>@Retention<br/></td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">SOURCE</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">注解只在源码中存在,编译成.class文件就不存在了</td><td colspan="1" rowspan="3" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px"><br/>        表示需要在什么级别保存该注解信息<br/></td></tr><tr><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">CLASS</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">注解只会在.class文件存在,会被VM丢弃</td></tr><tr><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">RUNTIME</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">VM将在运行期也保留注解,因此可以通过反射机制读取注解的信息</td></tr><tr><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">@Document</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px"> </td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px"> </td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">将此注解包含在Javadoc中</td></tr><tr><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">@Inherited</td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px"> </td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px"> </td><td colspan="1" style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:206px">允许子类继承父类中的注解</td></tr></tbody></table>

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

运行结果:

<table cellspacing="0" cellpadding="0" style="margin:8px 0px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:892px"><tbody><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:875px"><br/><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">方法注解:@com.qunar.annotation.Annotation$Description(time=2016-01-12, desc=set name for student object, author=sjf0115)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">方法注解:@com.qunar.annotation.Annotation$Description(time=2016-01-11, desc=get name from student object, author=sjf0115)</span></div></td></tr></tbody></table>

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值。

相关文章