javaSE高级篇6 — 注解( 附:注解底层解析 ) —— 更新完毕
注解 ———— 英文:annotation
1、注解长什么样子?
- @xxxxxxx( 一些信息 ) ————— 这个信息可有可无
2、注解可以放在什么地方?
- 类本身的上面、属性的上面、方法的上面、参数的前面
3、注解的作用是什么?
- 1、用来标识一个事物( 方法、属性、参数...)有特殊的含义
- 2、用来消灭配置文件
- 详细点就是这样的:
- 1)、用来充当注释的作用,仅仅相当于是一个文字的说明
- 如:@Deprecated ———— 表示该方法是废弃的
- 1)、用来充当注释的作用,仅仅相当于是一个文字的说明
- 详细点就是这样的:
- 2)、用来做代码的检测
- 如:@override ———— 表示检测该方法是否 是重写的
- 2)、用来做代码的检测
- 3)、携带一些信息 ( 内容 )
- 如:Properties 和 xml
- 3)、携带一些信息 ( 内容 )
4、java中提供的现有注解
- 1)、@Depracated ———— 用于说明方法是废弃的,这种备注之后,方法会有一根横线( 可以用,只是不建议,不然出了问题不能怪java的设计 ———这就相当于是甩锅,别人设计java也要考虑用了会不会出问题嘛 ) ———— 自己设计方法的时候也可以这么玩儿啊,当然,也得身居得有那个层次,不然自己设计的方法就用来搞项目的而已,还设计个锤子的废弃方法
- 举个例子:Data工具类
- 举个例子:Data工具类
- 2)、@Override ———— 用来检测该方法是否 是重写的 ( 是检测啊,别乱用啊 ,不是什么地方都可以用的 ) ———— 这个已经见过太多次了
- 举个例子:让一个类实现Comparable接口
-
如果我自己乱整一个方法,然后用这个注解得不得吃诶?
-
是会报错的哟,所以这个@Override是带有检测的
-
-
- 举个例子:让一个类实现Comparable接口
- 3)、@SuppressWarnings( 携带的信息 )
- 携带信息的类型只能是String[ ],即:写法为 ———— @SuppressWarnings( {" 某某信息 "} ) ———— 当然:如果{ }这里面的元素只有一个的话,可以省略{ },如:@SuppressWarnings( 某一个信息 )
- 这个信息的内容可为以下信息
- (1)、unUsed ———— 表示变量定义后未被使用
- 举个例子:
-
当然:不建议玩儿啊,因为,这是编辑器告诉自己,这个变量未被使用,要是加了这个的话,那么那些变量用了,那些变量没用都不知道了
-
- 举个例子:
- (1)、unUsed ———— 表示变量定义后未被使用
- 这个信息的内容可为以下信息
- (2)、serial ———— 指的是类实现了序列化接口,但是不添加序列化ID号
-
- (3)、rawtypes ———— 表示集合不用泛型
- (2)、serial ———— 指的是类实现了序列化接口,但是不添加序列化ID号
- (4)、unchecked ———— 表示出现泛型问题不检测 ———— 但是这个东西不建议玩儿,所以不展示了,有兴趣的可以故意把泛型弄错,然后加@suppressWarnings( " unchecked " ) 试一下就知道效果是怎么样的了
- (5)、all ———— 包含了上述的所有问题( 也不建议使用,所以也不展示了 )
5、注解中能够携带的信息是哪些?
- 注解中可以携带信息,也可以不携带 ———— 如:@Override 和 suppressWarning( { " " } )
- 如果要携带信息的话,那么只能是如下这几种类型
- 1)、基本数据类型 如:( 100 )
- 2)、String类型 如:( " 纳尼 " )
- 3)、枚举类型 如:( enum ) ———— 这里是说enum类型,不是说携带的是enum这几个字母
- 4)、注解类型 如:( @xxxx )
- 5)、数组类型 如:( [ ] ) ———— 注意:这个数组里面的类型,又只能是前四种类型
6、自定义注解
- 想要自定义注解的话,需要用到@interface,注意啊:这不是接口啊,多了一个@符号呢,举个例子:
-
从这可以看出:注解也是一种类结构,只是把class关键字改成了@interface而已
- 同时:注解这玩意儿和接口贼像,所以可以对照着接口来记嘛,接口有的东西,注解应该也有 ( 接口中有什么? ———— 属性和抽象方法呗,所以注解有吗? )
- 测试一下咯:接口中的属性是public static final 修饰的常量( 虽然我在面向对象编程篇中讲过 public static final 可以去掉,默认就是这个,但是还是需要再次说明,不写不代表没有修饰符 )———— 那么就来看一下@interface注解中的属性是什么样的?
-
但是:虽然可以这么玩儿,但是很少使用 ^ _ ^
- @interface注解中的方法是怎么样的?
- 照样对照接口来玩儿 ———— 接口中的方法是:public abstract类型的方法 ( 这两个修饰符也是可以省略不写的 ) ,但是:接口中的方法可以没有返回值,如:void test(),那么@interface中的方法是什么修饰的、有没有返回值? ———— 来测试一下嘛
-
从这个测试中就可以得出结论:
- @interface中的方法是public abstract修饰的,同时:这个方法必须有返回值
-
- 照样对照接口来玩儿 ———— 接口中的方法是:public abstract类型的方法 ( 这两个修饰符也是可以省略不写的 ) ,但是:接口中的方法可以没有返回值,如:void test(),那么@interface中的方法是什么修饰的、有没有返回值? ———— 来测试一下嘛
- 另外这个方法其实是可以设置默认值的,不过需要使用default关键字
- 好处:使用注解的使用就不用强制要求赋值了,利用反射解析就可以使用默认值( 同时:这也就告知了 注解是怎么实现的,即:利用反射解析出来的 ,后续会进行演示 )
- 另外这个方法其实是可以设置默认值的,不过需要使用default关键字
- 最后补充一点:虽然@interface中有属性 和 方法( 已经测试过了 ),但是:注解中只能有属性( 即:这个抽象方法也是属性,只是这个属性其实就是我们以前所说的抽象方法而已,至于叫法为什么是这样,是因为学术性的问题,所以对于@interface注解,它里面有什么,只有属性( 虽然实质是抽象方法 ) ) ———— 这个很重要
- 因此:总结一下 怎么自定义注解、注解中有什么?
- 1)、通过@interface实现自定义注解
- 2)、public static final修饰的属性( 当然:这里一定是这样吗?【 可以自行尝试一下 】—— 会有新发现 ) ———— 同时:搞这个属性很少见( 不常用 )
- 3)、public abstract修饰的 有返回值的 方法
- 补充一点:如果有返回值,则:这个返回值类型必须是前面讲的那些类型 ( 基本、String、枚举、注解、数组 )
- 因此:总结一下 怎么自定义注解、注解中有什么?
- 自定义了自己的注解之后,可以用吗?
- 还不可以嘛,所以在这里需要再学几个小东西 ———— 元注解
- 自定义了自己的注解之后,可以用吗?
7、元注解
- 元注解是什么?
- 就是用来给自定义的注解 再 注解 ( 即:解释自定义注解的 )
- 必会的两个元注解
- 1)、@Target ( { ElementType[ ] value } ) ———— 目标 ,即:是解释自定义的注解是放在什么地方的 ( 类上、属性上、方法上、参数前面 )
- 注意参数:是一个ElementType类型的,什么意思? ———— 看一下源码
-
发现是个枚举类 ———— 那就好办了,说明这里面存储的就是一些固定的对象嘛,那就直接用 ElementType. 调用嘛
// 1、@Target( { } ) ———— 这个是说:声明我的这个注解 能够作用在哪些地方( 类上、属性上、方法上( 含构造 )、参数的前面 )
// @Target( { ElementType.FIELD } ) // 这表明:这个自定义注解可以放在属性上
// @Target( { ElementType.METHOD } ) // 这表明:这个自定义注解可以方法上————想要看其他放置位置可以直接 ElementType. 就出来了
@Target( { ElementType.FIELD , ElementType.METHOD } )
-
- 注意参数:是一个ElementType类型的,什么意思? ———— 看一下源码
- 2)、Retention ( RetentionPolicy value ) ———— 这个是解释:自定义的注解持续到什么地方都还有效【 还存在 】( 即:自定义注解的作用域 )
- java代码经过的几个阶段
-
同时也注意:这个Retention中的参数是RetentionPolicy类型( 这也是一个枚举类 ,因此:一样的通过 类名. 就可以调用了),由这个图就可以理解作用域了,即:自定义的这个注解要一直持续到哪一个阶段都还存在 ( 有效 ),过了这个阶段,自定义的注解就没了( 被回收了 )
// 2、@Retention( ) ———— 这个是说:声明的这个注解 在什么作用域也存在( 作用域: 其实真正需要的注明的阶段是 —— 源码阶段、字节码阶段、运行阶段 )
// @Retention( RetentionPolicy.SOURCE ) // 这个是说:这个自定义注解 在源码阶段也存在( 过了这个阶段就被JVM丢弃 )
// @Retention( RetentionPolicy.CLASS ) // 这个是说:这个自定义注解 在字节码阶段也可存在
// @Retention( RetentionPolicy.RUNTIME ) // 这个是说:这个自定义注解 在运行阶段也还存在
@Retention( RetentionPolicy.RUNTIME )
-
- java代码经过的几个阶段
- 了解即可的元注解
- 3)、@Inherited ———— 这个元注解是让自定义的注解,可以被子类继承
- 4)、@Decumented ———— 这个元注解是说 自定义的注解可以被文档记录 ( 即:以前在基础篇中说过的文档注释,不是最后可以被生成一个文档吗 ( 最典型就是API,它就是一个生成文档 ),因此:这个元注解的作用就是为了:生成文档的时候也可以把自定义注解生成进去
8、有了上述的知识,那么就来模拟自定义一个注解
package cn.xieGongZi.customAnnotation; import java.lang.annotation.*; // 自定义注解 @Target( {ElementType.FIELD,ElementType.METHOD} ) // 这个自定义注解可以方法属性上、方法上 @Retention(RetentionPolicy.RUNTIME) // 这个自定义注解可以一直存在于运行时 @Inherited // 这个自定义注解可以被子类继承 @Documented // 这个注解可以被文档记录 @interface MyAnnotation { // 注解中有的东西 ———— 属性( 即:抽象方法 )
String value(); // 一个自定义的注解也就完成了
// 现在也就开始用这个自定义的注解
}package cn.xieGongZi.customAnnotation;
// 使用自定义注解
public class UseMyAnnotation {
@MyAnnotation( value = "邪公子" )
private String name;
@MyAnnotation( value = "19" )
private Integer age;
@MyAnnotation( value = "女" )
private String sex;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public String getSex() { return sex; }
public void setSex(String sex) { this.sex = sex; }
@Override
public String toString() {
return "UseMyAnnotation{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}package cn.xieGongZi.customAnnotation;
// 开始解析注解
public class resolvAnnotation { public static void main(String[] args) { UseMyAnnotation useMyAnnotation = new UseMyAnnotation(); System.out.println( useMyAnnotation.getName() );
System.out.println( useMyAnnotation.getSex() );
System.out.println( useMyAnnotation.getAge() );
}
}效果如下:
-
哦豁 ~ 没有值!!!!这尼玛注解是假的吧,别急,还差最后一步,也是底层之中需要做的事情,利用反射把注解中的值真正赋给属性
package cn.xieGongZi.customAnnotation; import java.lang.reflect.Field; // 开始解析注解
public class resolvAnnotation { public static void main(String[] args) throws IllegalAccessException { UseMyAnnotation useMyAnnotation = new UseMyAnnotation(); // System.out.println( useMyAnnotation.getName() );
// System.out.println( useMyAnnotation.getSex() );
// System.out.println( useMyAnnotation.getAge() ); // 获取类对象
Class<UseMyAnnotation> aClass = UseMyAnnotation.class; // 获取这个类的属性( 私有的嘛,所以需要用如下方法 ———— 在反射中已经讲解过了
Field[] fields = aClass.getDeclaredFields(); // 这就获取这个类的所有属性了 ( 包括private修饰的 ) // 然后查看这个属性头上有没有存在注解
// 但是这是一个属性组成的数组涩 ———— 所以需要遍历,这样才可以查看每一个属性头上有没有注解嘛
for (Field field : fields) { // 现在才查看这一个属性上面有没有存在自定义的注解 ———— 自定义注解是MyAnnotation涩,所以获取这个注解的类对象
// 因此:获取这个注解的类对象
MyAnnotation fieldClass = field.getAnnotation( MyAnnotation.class );// 这里面需要一个注解的类对象,因此:获取自定义注解的类对象 // 看看这个属性头上有没有注解
if ( fieldClass != null ){ // 这就说明,这个属性头上存在自定义的注解 // 那么获取这个自定义注解携带的数据
String value = fieldClass.value(); // 获取自定义注解携带的数据后,准备开始给相应属性赋值
// 但是: 从自定义注解中获得的数据是一个字符串,而每一个属性的类型也不一样,因此:需要再做一件事
// 那就是判断属性对应的数据类型是什么
// 但是属性是private修饰的,所以还得提供修改private属性的权限涩
field.setAccessible(true);
Class fieldType = field.getType(); // 现在才给对应类型的属性赋值
// fieldType就是每一个属性 的对应数据类型 ———— 现在开始考虑用什么选择语句涩 ———— Switch ? if ?
// Switch支持的是byte、short、int 及对应包装类、String、枚举,所以好像不满足所有的需求
// 因此:选择if
if ( fieldType == String.class ){ // 由于获取到的fieldType是一个Class类型的
// 因此:属性的对应数据类型也要用它的Class类型 // 给String类型的属性赋值
field.set( useMyAnnotation,value ); // 这里建议使用set(),别用setXxx() ———— 其中Xxx是要转的对应数据类型,如:setInt()
// 因为set是支持任意类型( 还不容易报错 ) ———— 其中:set()里面需要传递两个参数,
// 第一个参数是属性所在的 类的对象、第二个是注解中携带的那个数据( 即:要传递的那个值 )
// 另外,set()方法需要处理异常
} if ( fieldType == Integer.class ){ // 注意:如果属性是int类型的,那么:属性的类型记得用integer
// 虽然不是必须,但:建议用,好处就在这里体现1出来了,不然可以尝试用int、byte试一下就知道效果了
// 因此,这又回到以前说过的:属性若是基本数据类型 则:最好用对应的包装类
// 然后这里有个坑儿,因为我们定义的类 属性age是Integer类型,而注解中携带的数据是String类型的,所以这里还需要进行一件事
// 把String 转为 Integer
field.set( useMyAnnotation,Integer.parseInt(value) );
} // 其他类型就是一样的,这样对应类型赋值就可以了,这里由于我自定义的useAnnotation类中只有String 和 int 类型,所以就只展示这两种
}
} System.out.println( " UseAnnotation类中的各个属性值如下:\n" + useMyAnnotation );
}
}效果图如下 ( 这就把注解中携带的数据 真正地 给了对应属性 ):
-
-
最后:再补充两点
- 第一点:前面已经提到了,就是自定义的注解中,那个属性 ( 即:抽象方法 )是可以设置默认值的,但是需要使用default关键字
- 使用默认值的好处就是,在使用注解的时候,不需要强制要求给自定义注解中的属性 ( 抽象方法 ) 赋值
- 如:我上面是只定义了一个value()属性,所以在使用这个注解时,只需要传递一个值给这个value()即可
- 但是,如果我在自定义注解中再定义一个属性 ( 抽象方法 )———— 此时没有使用default关键字
- 那么,在使用这个自定义注解时,就必须传递两个值 ( 这两个值分别给了自定义注解中的那两个没用default修饰的属性 )
- 如:我上面是只定义了一个value()属性,所以在使用这个注解时,只需要传递一个值给这个value()即可
- 使用默认值的好处就是,在使用注解的时候,不需要强制要求给自定义注解中的属性 ( 抽象方法 ) 赋值
- 怎么使用这个default关键字
- 第二点就是:如果自定义注解中的属性 ( 抽象方法 )名是value,那么:在使用这个自定义注解传值时( 即:value = ....... ),不需要写value =
- 如:
原因:
- 底层中用的就是这个名字,所以不需要
- 如:
至此,注解的相关知识就完毕了,掌握这些知识之后,
其他没提到的注解知识就都没问题了,那些就是一看就懂、还带会用了
javaSE高级篇6 — 注解( 附:注解底层解析 ) —— 更新完毕的更多相关文章
- javaSE高级篇4 — 反射机制( 含类加载器 ) — 更新完毕
反射机制 1.反射机制是什么?----英文单词是:reflect.在java.lang包下---这才是java最牛逼的技术 首先提前知道一句话----在java中,有了对象,于是有了类,那么有了类之后 ...
- javaSE高级篇5 — java8新特性详解———更新完毕
java8新特性 在前面已经见过一些东西了,但是:挖得有坑儿 1.lambda表达式 lambda表达式是jdk1.8引入的全新语法特性 它支持的是:只有单个抽象方法的函数式接口.什么意思? 就是说: ...
- 高级篇 KZ002.反射读取注解[未封装]
创建自定义注解 package com.hanpang.java; /** * 注解说明: 方法的文档注释 * * @Author: 胖先生 * @Create: 2016-04-27 10:29 * ...
- javaSE高级篇7 — 设计原则和设计模式 — 设计模式慢慢更( 这是思想层次篇 )
1.什么是设计原则? 设计原则就是面向对象的原则嘛,即:OOP原则 换句话说:就是为了处理类与类之间的关系( 包括接口.类中的方法 ) 2.OOP设计原则有哪些? 1).开闭原则:就是指对拓展开放.对 ...
- javaSE高级篇2 — 流技术 — 更新完毕
1.先认识一个类----File类 前言:IO相关的一些常识 I / O----输入输出 I 输入 input 0 输出 output I / o 按数据的流动方向来分- ...
- javaSE高级篇1 — 异常与多线程基础
1.异常的体系结构 注:Throwable是一个类,不是一个接口,这个类里面是描述的一些Error和Exception的共性,如图所示: 异常 / 错误是什么意思? 定义:指的是程序运行过程中,可能 ...
- javaSE高级篇3 — 网络编程 — 更新完毕
网络编程基础知识 先来思考两个问题( 在这里先不解决 ) 如何准确的找到一台 或 多台主机? 找到之后如何进行通讯? 网络编程中的几个要素 IP 和 端口号 网络通讯协议:TCP / UDP 最后一句 ...
- javaSE基础知识(走向编程的门口)— 更新完毕
前言:玩儿编程最重要的一点:不要怕麻烦,感觉是在浪费时间: 能动手绝不哔哔:只要脑袋不傻,编程都是"一看就会,一练就废",开始学的时候,就算再基础的东西都建议手敲一遍 要有囫囵吞枣 ...
- 浅谈 Java Xml 底层解析方式
XML 使用DTD(document type definition)文档类型来标记数据和定义数据,格式统一且跨平台和语言,已成为业界公认的标准. 目前 XML 描述数据龙头老大的地位渐渐受到 Jso ...
随机推荐
- 你真的了解电子邮件系统的组成和结构吗?(SMTP、POP3、IMAP、MIME……)
文章转自:https://blog.csdn.net/weixin_43914604/article/details/105896201 学习课程:<2019王道考研计算机网络> 学习目的 ...
- 集合先从ArrayList开始
本篇文章非常建议直接从经典Demo开始哦~ 一.ArrayList简介 ArrayList 的底层是数组队列,相当于动态数组.与 Java 中的数组相比,它的容量能动态增长.在添加大量元素前,应用程序 ...
- AXI协议中的模棱两可的含义的解释(Cachable和Bufferable)
转载:https://blog.csdn.net/hit_shaoqi/article/details/53243173 Cachable和Bufferable 一个Master发出一个读写的requ ...
- SpringBoot2.x请求注解简单介绍(4)
1.新建项目,项目中实战讲解注解作用 2.pom.xml依赖配置 <properties> <project.build.sourceEncoding>UTF-8</pr ...
- 使用python操作HDF5文件
HDF Hierarchical Data Format,又称HDF5 在深度学习中,通常会使用巨量的数据或图片来训练网络.对于如此大的数据集,如果对于每张图片都单独从硬盘读取.预处理.之后再送入网络 ...
- 组件通过props属性传值
组件之间的传值 组件是一个单独功能模块的封装,有属于自己的data和methods,一个组件的 data 选项必须是一个函数 为什么必须是函数:因为只有当data是函数时,不同实例调用同一个组件时才会 ...
- IO流(二)
一:字符流 字符输入流 写入文件字符流 import java.io.FileWriter; import java.io.IOException; //fileWriter public class ...
- 升级npm后版本依然没有变 原来是全局npm设置的锅
最近准备给家里的老爷机打一个 react 的环境 win7系统还不算老~ 不过!由于很多年以前装的node了版本很低,所以赶紧去官网 下了一个 最新的稳定版本的. 卸载和安装都费了老大力了. 以为光明 ...
- loto示波器实践——超声波测距模块
我们这里用到的超声波测距模块,一般是用于arduino智能小车自动避障的.经常见到的应用是使用单片机或者stm32和这种模块结合进行开发的. 我们使用LOTO示波器可以更直观和快速的看到超声波测量距离 ...
- myeclipse重写快捷键
shift+alt+s 点击Override/Implments methods