Java 注解

前言

本文主要讲解 JDK 中的注解如何使用及如何自定义注解,内容相对简单。希望通过阅读本文能够帮助对注解存在疑惑你。

一、什么是注解

如果要问什么是注解,可能有很同学不知道该如何描述,像经常看到的@Override、@Controller ... 等等,都是注解,但是他们有什么用呢?这里先通过一个大家比较熟悉的注释来简单说明一下。

1. 注释

注释,不用多说,经常用,就是给某段程序加一段描述,让人可以快速了解这段代码是做了个什么事情。通常写法有以下几种:

// 

/**
*
*/
 

那么简单概述一下,即

注释,是用文字描述程序,给程序员看的

2. 注解

回过头来,再来看注解。先看一下官方的定义:

注解(Annotation),也叫元数据,一种代码级别的说明,它是 JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释

相信还没有弄明白注解的同学,看完后依然是一脸懵逼,那么再来用我们自己的话来概述:

注解:是用来说明程序的,给计算机看的注释,没错,它也是起到了注释的作用

二、注解的作用

了解了注解的定义,看一下它的作用

作用的分类:

1. 编写文档:通过代码里的标识的注解生成文档【生成 doc 文档】

2. 代码分析:通过代码里标识的注解对代码进行分析【使用反射】

3. 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】

1. 编译检查

这个比较简单,举个例子看一下,它是如何进行编译检查的。

首先创建一个 Java 类User.java

public class User {
	
	private String id;
	private String name;

	@Override
	public String toString() {
		return "User{" +
				"id='" + id + '\'' +
				", name='" + name + '\'' +
				'}';
	}
}

 很简单,主要看一下 toString方法,它上面有一个@Override注解,重写了 Object类的 toString 方法。当我们将方法名字稍微改一下,如下图:

 可以看到,编译报错了,因为父类并没有toString1 这个方法。这就是它的编译检查作用。

2. JavaDoc 文档演示

还是基于 User类进行演示,新增一个add方法,并加上对应的注释:

/**
 * 注解 JavaDoc 描述
 *
 * @author: <a href="568227120@qq.com">搬运 Gong</a>
 * @version: 1.0
 */
public class User {

	private String id;
	private String name;

	@Override
	public String toString() {
		return "User{" +
				"id='" + id + '\'' +
				", name='" + name + '\'' +
				'}';
	}

	/**
	 * 两个整数相加
	 *
	 * @param a 第一个整数
	 * @param b 第二个整数
	 * @return: int  返回的结果
	 */
	public int add(int a, int b) {
		return a + b;
	}
}

 下面使用 JavaDoc 命令进行doc 文档生成:

javadoc User.java

 

 找到 index.html,打开查看

 我们发现,通过注解加入的一些描述,都被放到了 JavaDoc中。

3. 利用反射分析代码

      这一块放到下面进行讲解,需要配合注解的使用一起来看。

三、JDK 中预定义的注解

@Override:检查被该注解标注的方法是否是继承自父类

@Deprecated: 表示注释的内容过时(过时了,但是依然可以使用)

@SuppressWarings: 压制警告      

 

像图中的那些黄色警告,都可以使用 @SuppressWarings 来进行压制,可以放到方法上面,也可以放到类上面,比如:

 这样是不是就看着舒服多了。

四、自定义注解及使用

 看了很多注解,那么我们是不是可以写自己的注解呢?又改如何去写?

1. 注解的格式

//元注解
public @interface 注解名称 {
       //属性列表
}

知道了注解的格式后,来写一个自定义的注解:

public @interface MyAnno {
}

这样就好了,可以使用了:

@MyAnno
public int add(int a, int b) {
	return a + b;
}

可以使用后,有的同学又会问了,那这个注解有啥用呢?别急,下面来分析一下,这个注解到底是个什么东西,先把它进行编译,然后再反编译后查看:

javac MyAnno.java

得到 class 文件后,使用 javap 来反编译查看注解的内容:

这是反编译后的内容,可以看到,它的本质就是一个接口,集成的 java.lang.annotation.Annotation 接口。

既然它的本质就是一个接口,那我们就可以在它的里面写接口里面可以写的东西,比如定义常量、方法等,如下:

public @interface MyAnno {
	
	public static String str = "Annotation";
	
	String show1();
}

属性:在接口中定义的抽象方法

返回结果必须是如下类型:

1. 基本数据类型

2. String 类型

3. 枚举类型

4. 注解

5. 以上的数组

注意,不可以是 void !

2. 注解的使用

自定义了注解之后,开始使用注解:

public @interface MyAnno {

	String show1();
	String[] show2();
	int age() default 18;
}
// 自定义注解中的 age 由于给了默认值,所以可以不写,写了话,以传入的值为准
@MyAnno(show1 = "show1",show2 = {"show2.1","show2.2"})
public int add(int a, int b) {
	return a + b;
}

属性赋值注意点:

1. 如果定义的属性,使用 default 关键字给默认属性初始值,可以在使用注解时不赋值

2. 如果只有一个属性需要赋值,而该属性的名称是 value ,那么赋值的时候value可以省略

3. 数组赋值的时候,值使用{}包裹,如果数组中只有一个值,那么{}可以省略

 3. 元注解

在看代码中的注解时,都会发现,它们的定义上面有另外3-4 个注解,这几个注解有什么作用呢?它们是 JDK 中给我们提供的 4 个元注解,含义分别是:

@Target : 描述当前注解能够作用的位置
    ElementType.TYPE :  可以作用在类上
    ElementType.METHOD: 可以作用在方法上
    ElementType.FIELD: 可以作用在成员变量上

@Retention : 描述注解被保留到的阶段
    SOURCE < CLASS < RUNTIME
    SOURCE : 表示当前注解只在代码阶段有效
    CLASS : 表示该注解会被保留到字节码阶段
    RUNTIME : 表示该注解会被保留到运行阶段 JVM
    通常,自定义的注解都会使用 RetentionPolicy.RUNTIME

@Documented : 描述注解是否被抽取到 JavaDoc api 中

@inherited : 描述注解是否可以被子类继承

4. 自定义注解使用案例

假设我们定义一个注解,它的作用就是根据传入的类名和类中方法,去执行对应的类方法,那么可以这样实现:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InvokeAnno {

	/**
	 * 需要执行的类名
	 * @return
	 */
	String className();

	/**
	 * 需要执行的类方法
	 * @return
	 */
	String classMethod();
}

然后,再写两个测试类:

public class Worker1 {

	public void workHome() {
		System.out.println("work1执行 workHome 方法...");
	}
}


public class Worker2 {

	public void workHome() {
		System.out.println("work2执行 workHome 方法...");
	}
}

在写一个使用注解的测试类:

@InvokeAnno(className = "com.custom.anno.Worker1",classMethod = "workHome")
public class MyAnnoTest {

	public static void main(String[] args) throws Exception {
		// 获取类对象
		Class<MyAnnoTest> clazz = MyAnnoTest.class;
		// 获取类中指定注解
		InvokeAnno annotation = clazz.getAnnotation(InvokeAnno.class);
		// 获取注解中的 className 属性
		String className = annotation.className();
		// 获取注解中的 classMethod 属性
		String classMethod = annotation.classMethod();
		// 通过反射,获取对应的类对象
		Class<?> clazz1 = Class.forName(className);
		// 获取类对象中指定的方法
		Method method = clazz1.getDeclaredMethod(classMethod);
		// 执行指定类中的指定方法
		method.invoke(clazz1.newInstance());
	}
}

可以看到,利用反射和注解完成了我们需求。通常情况下,注解和反射都是在一起使用的。

希望通过阅读本文,能够帮助小伙伴加深对 Java 注解的理解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值