java中《自定义注解》的使用
Posted 辉常努腻
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java中《自定义注解》的使用相关的知识,希望对你有一定的参考价值。
注解
1.概念和定义
-
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释
补充导读:相信很多同学看完之后是整不明白的,为什么呢是因为我们用专业名词去解释了专业名词,所以你才会不懂的,那么我们该怎么办呢
补充导读:相当大家都听说过这样一句话:“乔布斯重新定义了智能手机,罗永浩重新定义了傻逼”,有人肯定会说这个跟咱们今天的课也不沾边啊,其实不然。诚然咱们讲的是编程课,但是概念是可以类比的去理解的,
所谓的注解说白了就是一种“标记”,就像一说到智能手机就是乔布斯,一说到炫富就是郭美美,一说到好男人就是曾小贤。。。。。,都是标记 -
为什么引入注解呢?
曾经我们的商业项目开发的时候我们都是用xml实现配置的,这种方式的特点是耦合度低,但是麻烦,但是不知道是什么时候有一些编码人员和架构师开始使用注解来代替原来的xml配置使得书写简单了,但是从某些角度而言是耦合度提升了,那么我们是不是要全部放弃XML,全部改为注解呢?答案是否定的,为什么呢?那么我们就一起看看注解和XML的对比吧
注解
优点:简化配置使用起来直观且容易,提升开发效率,类型安全
缺点:改变实现类比xml困难xml
优点:类与类间的松藕合,容易扩展、更换对象间的关系一目了然
缺点:配置冗长,且还要额外多维护一份配置,类型不安全,compile(编译)无法帮忙校验,运行期才会发现错误小结:xml和注解都要会
现状:xml(配置文件)比注解使用的要多一些,
友情提示:先学会xml形式的,那么再来学注解的基本上就是水到渠成的事,
在商业项目开发的时候经常是xml和注解一起配合使用的 -
注解的作用:
- 生成文档:这是最常见的,也是java 最早提供的注解。常用的有@param @return 等
- 跟踪代码依赖性,实现替代配置文件功能:比如Spring框架依赖注入,将大量注解替换掉配置的xml文件,具有很大用处
- 在编译时进行格式检查:如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。
-
注解的分类:
- JDK中提供的标准注解:其实就是JDK官方已经写好的且具有特定功能的注解
- 元注解:由元数据修饰的注解就是元注解(注解的注解),就是标签的标签
- 自定义注解:就是开发者根据注解的规则自己编写的注解,且经常要使用上元注解
-
Java中注解是用来干做什么的:java就是通过注解来表示元数据
-
什么是元数据:元数据是指用来描述数据的数据,更通俗一点,就是描述代码间关系,或者代码与其他资源(例如数据库表)之间内在联系的数据。在一些技术框架,如struts、EJB、hibernate就不知不觉用到了元数据。对struts来说,元数据指的是struts-config.xml,对于servlet来说元数据是指servlet-mapping,其本质就是一种和Class,枚举等一样的一种的Java的数据类型,其最根本的本质就是一个接口
2.JDK中的几个标准注解
- JDK1.5中有三个标准注解,当然1.5之后有又增加了一些,那么我们今天就以1.5中的这三个基本的注解做例子进行讲解
他们分别为:- @override:重写,覆盖
- @Deprecated:过时的,弃用的
- @SuppressWarnings:压缩警告/忽略警告
示例代码:
package com.Li.anno;
/**
* @desc 注解的学习 电话类
* @author Li Ya Hui
* @time 2021年6月22日 上午9:11:36
*/
public class Phone {
public void call()
{
System.out.println("电话:可以通话!");
}
@Deprecated //注解 Deprecated为过时代码的意思
public void sendLittleMessage()
{
System.out.println("电话:可以发短信");
}
@Override
public String toString() {
return "Phone [toString()= @Override 是一个注解 且作用为重写 ]";
}
}
package com.Li.anno;
/**
* @desc 测试类,测试JDK中的三个标准注解
* @author Li Ya Hui
* @time 2021年6月22日 上午9:17:44
*/
public class Test {
/**
* @SuppressWarnings:压缩警告:告诉编译器忽略指定的警告,不用在编译完成后出现警告信息
* 分类:@SuppressWarnings(""):抑制单类型的警告
* 例如当你的代码中有使用@Deprecated的时候就会产生这种类型的警告:@SuppressWarnings("deprecation")
* 例如当你的代码中有使用没有确定泛型的容器的时候会产生这种类型的警告:unchecked抑制未检查的转化
* 那么两者组合在一起就是抑制多类型的警告,那么这样岂不是很麻烦吗,诚然。但是你可以用抑制所有类型的警告这样的话就变的清爽了
* @SuppressWarnings({}):抑制多类型的警告
* @SuppressWarnings("all"):抑制所有类型的警告,但是不建议多使用,因为这样的话就降低了编译器的自检能力,可能会出现意想不到的错误
* 小结:根据自己的需要巧妙的根据IDE的提示去使用@SuppressWarnings这个注解
*/
// SuppressWarnings 抑制警告
@SuppressWarnings("deprecation")
public static void main(String[] args) {
Phone pe = new Phone();
pe.call();
pe.sendLittleMessage();
System.out.println(pe.toString());
}
}
小结:
* @SuppressWarnings:压缩警告:告诉编译器忽略指定的警告,不用在编译完成后出现警告信息
* 分类:@SuppressWarnings(""):
* 抑制单类型的警告
* 例如当你的代码中有使用@Deprecated的时候就会产生这种类型的警告:
* @SuppressWarnings("deprecation")
* 例如当你的代码中有使用没有确定泛型的容器的时候会产生这种类型的警告:
* unchecked抑制未检查的转化
* 那么两者组合在一起就是抑制多类型的警告,那么这样岂不是很麻烦吗,诚然。
* 但是你可以用抑制所有类型的警告这样的话就变的清爽了`@SuppressWarnings("all")`
* @SuppressWarnings({}):
* 抑制多类型的警告
* @SuppressWarnings("all"):
* 抑制所有类型的警告,但是不建议多使用,因为这样的话就降低了编译器的自检能力,可能会出现意想不到的错误
* 小结:根据自己的需要巧妙的根据IDE的提示去使用@SuppressWarnings这个注解
3.元注解
-
什么是元注解:java.lang.annotation提供了四种元注解,专门注解其他的注解(在自定义注解的时候,需要使用到元注解)
PS:元注解:就是注解的注解 -
元注解包含4种:
1)@Documented –注解是否将包含在JavaDoc中(是否能转为电子文档)
2)@Retention –什么时候使用该注解
3)@Target –注解用于什么地方
4)@Inherited – 是否允许子类继承该注解 -
种元注解的详细:
- @Retention– 定义该注解的生命周期
● RetentionPolicy.SOURCE : 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
● RetentionPolicy.CLASS : 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式
● RetentionPolicy.RUNTIME : 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。 - @Target – 表示该注解用于什么地方。默认值为任何元素,表示该注解用于什么地方。可用ElementType参数包括
● ElementType.CONSTRUCTOR:用于描述构造器
● ElementType.FIELD:成员变量、对象、属性(包括enum实例)
● ElementType.LOCAL_VARIABLE:用于描述局部变量
● ElementType.METHOD:用于描述方法
● ElementType.PACKAGE:用于描述包
● ElementType.PARAMETER:用于描述参数
● ElementType.TYPE:用于描述类、接口(包括注解类型) 或enum声明 - @Documented–一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。
- @Inherited – 定义该注释和子类的关系
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
- @Retention– 定义该注解的生命周期
4.自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
分析:
-
@interface 用来声明一个注解,格式:public @interface 注解名 {定义内容}
-
其中的每一个方法实际上是声明了一个配置参数
-
方法名的名称就是参数的名称
-
返回值类型就是参数的类型(返回值之能)
-
可以通过default来声明参数的默认值
-
如果只有一个参数成员,一般参数名为value
-
注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值
4.1.自定义注解应用举例之分散写
创建自定义注解和注解处理类(器)
FriutName.java 第一个自定义注解:FruitName,其意思为水果名字
package com.Li.anno2;
/**
* @desc 第一个自定义注解:FruitName,其意思为水果名字
* @author wyh
* @time 2018-12-28 上午10:36:03
*
*/
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value = { ElementType.FIELD }) //表示该注解用于什么地方。默认值为任何元素 field 成员变量、对象、属性(包括enum实例)
@Retention(value = RetentionPolicy.RUNTIME) //表示该注解的生命周期, runtime始终不会丢弃 ,运行期间也保留该注释
@Documented //一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。
public @interface FruitName {
//默认值是三
String value() default "123";
}
FriutColor.java 第二个自定义注解:FruitColor,其意思为水果颜色
package com.Li.anno2;
/**
* @desc 第二个自定义注解:FruitColor,其意思为水果颜色
* @author wyh
* @time 2018-12-28 上午10:39:08
*
*/
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value = { ElementType.FIELD })//使用的范围为属性
@Retention(value = RetentionPolicy.RUNTIME)//注解的声明周期为始终不放弃
@Documented
public @interface FriutColor {
/**
* 1.创建一个枚举注解
* @author Li Ya Hui
* @time 2021年6月22日 下午8:43:50
*/
public enum Color{RED,BLUE,GREEN};//声明一个枚举类型,且给值为红色,绿色,蓝色
/**
* 2.为当前的自定义注解FruitColor设定一个属性,通过default这个关键字设定该属性的默认值为红色
* @return
*/
Color firuitColor() default Color.RED;//默认为红色
}
FruitProcessor.java 注解处理器
package com.Li.anno2;
/**
* @desc 注解处理器
* @author LiYaHui
* @time 2018-12-28 上午10:47:07
*
*/
import java.lang.reflect.Field;
public class FruitProcessor {
public static void getFruitInfo(Class<?>clazz)
{
//1.声明两个变量,且赋初值
String strFruitName="水果的名字:";
String strFruitColor="水果的颜色:";
//2.利用反射获取传递类的属性的集合
Field[] fields = clazz.getFields();
//2.迭代每一个属性,目的是为了取出使用了我们自定义的两个注解的值
for (Field field : fields)
{
//2.1判断当前传入的注解类对象是否是注解
if(field.isAnnotationPresent(FruitName.class))
{
FruitName fruitName = field.getAnnotation(FruitName.class);
strFruitName=strFruitName+fruitName.value();
System.out.println(strFruitName);
}
else if (field.isAnnotationPresent(FriutColor.class))
{
FriutColor fruitColor = field.getAnnotation(FriutColor.class);
strFruitColor=strFruitColor+fruitColor.firuitColor();
System.out.println(strFruitColor);
}
}
}
}
4.2.自定义注解应用举例之写在一起
创建自定义注解和注解处理类(器)
LitChi.java 荔枝类—自定义注解之合在一起写
package com.Li.anno3;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @desc 荔枝类---自定义注解之合在一起写
* @author Li Ya Hui
*/
public class LitChi {
@FruitName(value="荔枝")
public String name;
public String toStr()
{
return "当前水果的名字";
}
/**
* @desc 1.在LitChi类中自定义了一个注解,且名字叫FruitName
* @author Li Ya Hui
* @time 2021年6月22日 下午9:28:54
*/
@Target(value = {ElementType.FIELD}) //运行目标
@Retention(value = RetentionPolicy.RUNTIME) //运行周期
@Documented
@interface FruitName{
/**
* @desc 2.设置一个属性,且该属性的名字为value,且类型为String 类型,且默认值为“”
* @return
*/
String value() default "";
}
}
Test.java 测试自定义注解
package com.Li.anno3;
// 反映
import java.lang.reflect.Field;
import com.Li.anno3.LitChi.FruitName;
/**
* @desc 测试自定义注解
* @author Li Ya Hui
* @time 2021年6月22日 下午9:36:17
*/
public class Test {
public static void main(String[] args) {
Class<LitChi> lch = LitChi.class;
Field[] fields = lch.getFields();
for (Field field : fields)
{
System.out.println(field);//获取到LitChi类中的属性
//判断LitChi类中的属性是否被自定义注解FruitName修饰
System.out.println(field.isAnnotationPresent(FruitName.class));
if (field.isAnnotationPresent(FruitName.class))
{
//获取LitChi类中的fruitName这个属性被标记/修饰的自定义注解对象
FruitName fruitName=field.getAnnotation(FruitName.class);
System.out.println(fruitName.value());//获取到给自定义注解中的属性value赋的值
}
}
}
}
Documented注解的作用及其javadoc文档生成工具的使用
Documented注解的作用及其javadoc文档生成工具的使用
作用:生成文档
生成文档的步骤:
- 准备:先进入到LitChi的文件目录下, 然后在该目录下切换出cmd黑窗口
- 输入指令:javac -encoding utf-8 -d . LitChi.java 其目的是为了将该Java文件进行编译
- 输入指令:javadoc -d doc LitChi.java 其目的是为了生成电子文档
Documented注解的作用及其javadoc文档生成工具的使用
作用:生成文档
铺垫:为了能够顺利的实现生成电子文档:我们需要将LitChi.java中的中文给删掉(强烈建议复制粘贴此处的代码)
package com.rj.bd.anno3;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class LitChi
{
@FriutName("lizhi")
public String fruitName;
public String toString()
{
return "It is :"+fruitName ;
}
}
@Target(value = { ElementType.FIELD })
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@interface FriutName{
public String value() default "" ;
}
生成文档的步骤:
0)准备:先进入到LitChi的文件目录下, 然后在该目录下切换出cmd黑窗口
1)输入指令:javac -encoding utf-8 -d . LitChi.java 其目的是为了将该Java文件进行编译
2 ) 输入指令:javadoc -d doc LitChi.java 其目的是为了生成电子文档
4.3.自定义注解应用举例之复杂数据类型
我们所需建的类
- CarAnnotation.java 自定义注解一:复杂数据类型的属性 @Target(value= {ElementType.METHOD})
- CarBodyAnnotation2.java 自定义注解二:里面可以设定字符串类型的属性 @Target(FIELD)
- CarTypes.java 枚举:车的类型
- Car.java 车:第一个使用注解的类
- CarBody.java 车身信息:用上第二个自定义的注解
- CarProcessor.java 注解处理器类
- Test.java 测试类
CarAnnotation.java 自定义注解一:复杂数据类型的属性 @Target(value= {ElementType.METHOD})
package com.Li.anno4;
/**
* @desc 自定义注解一:复杂数据类型的属性 设定复杂的数据类型作为变量,且使用的案例是车的注解
* @author Li Ya Hui
* @time 2021年6月23日 上午10:17:48
*/
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Documented
@Retention(RUNTIME)
@Target(value= {ElementType.METHOD}) //注解作用于方法上
public @interface CarAnnotation {
String name();//1.字符串
String engineType() default "汽油";//2.带默认值的字符串
int carDoor() default 5;//3.int型
CarTypes carTypes() default CarTypes.COMMON;//4.枚举型
Class clazz();//5.类类型
CarBodyAnnotation2 my(); //6.注解类型,(在注解中包含注解)
int[] arr() default{1,2,3};//7.一元数组类型
CarTypes [] carTypeArray(); //枚举数组
}
CarBodyAnnotation2.java 自定义注解二:里面可以设定字符串类型的属性 @Target(FIELD)
package com.Li.anno4;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* @desc 自定义注解二:里面可以设定字符串类型的属性,且是为了将来能用在CarAnnotation这个注解中
* @author Li Ya Hui
* @time 2021年6月23日 上午10:19:49
*/
@Documented
@Retention(RUNTIME)
@Target(FIELD)
public @interface CarBodyAnnotation2 {
String material();//车身的材料
String info();//车身的信息
}
CarTypes.java 枚举:车的类型
package com.Li.anno4;
/**
* @desc 枚举:车的类型
* @author Li Ya Hui
* @time 2021年6月23日 上午10:21:27
*/
public enum CarTypes {
COMMON,//普通汽车
SUV,//越野车
CRV,//城市越野
MINI;//迷你
}
Car.java 车:第一个使用注解的类
package com.Li.anno4;
/**
* @desc 第一个使用注解的类
* @author Li Ya Hui
* @time 2021年6月23日 上午10:24:05
*/
public class Car {
@CarAnnotation(carTypeArray = { CarTypes.COMMON,CarTypes.SUV }, clazz =CarBody.class, arr={3,4,5}, name = "林肯",carDoor=4,engineType="电动", my = @CarBodyAnnotation2(info = "展台的样车", material = "不锈钢") )
public void show(String name,String engineType,int carDoor,int[] arr,CarTypes[] carTypeArray)
{
}
}
CarBody.java 车身信息:用上第二个自定义的注解
package com.Li.anno4;
/**
* 车身信息:用上第二个自定义的注解
* @author Li Ya Hui
* @time 2021年6月23日 上午10:25:17
*/
public class CarBody {
@CarBodyAnnotation2(info="4.2米长,2.6米宽",material = "钛铝合金")
public String carBodyInfo;
}
CarProcessor.java 注解处理器类
package com.Li.anno4;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @desc 注解处理器类
* @author Li Ya Hui
* @time 2021年6月23日 上午10:27:21
*/
public class 以上是关于java中《自定义注解》的使用的主要内容,如果未能解决你的问题,请参考以下文章