JavaSE 进阶 - 第26章 注解
1、注解
1.1、注解,或者叫做注释类型,英文单词是:Annotation
1.2、注解Annotation是一种引用数据类型。编译之后也是生成xxx.class文件。
1.3、怎么自定义注解呢?语法格式?
[修饰符列表] @interface 注解类型名{
}
1.4、注解怎么使用,用在什么地方?
第一:注解使用时的语法格式是:
@注解类型名
第二:注解可以出现在类上、属性上、方法上、变量上等....
注解还可以出现在注解类型上。
默认情况下,注解可以出现在任意位置。
2、JDK内置的注解
java.lang包下的注释类型(注解):
2.1、Override注解
Override :表示一个方法声明打算重写超类中的另一个方法声明。
(表示方法重写)
源代码:
public @interface Override {
}
标识性注解,给编译器做参考的。
编译器看到方法上有这个注解的时候,编译器会自动检查该方法是否重写了父类的方法。
如果没有重写,报错。
@Override这个注解只能注解方法。
@Override这个注解是给编译器参考的,和运行阶段没有关系!!!
2.2、Deprecated
Deprecated :用 @Deprecated 注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。
(表示该元素已过时)
2.3、SuppressWarnings (了解即可)
SuppressWarnings:指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。
2.4、元注解
(1)什么是元注解?
用来标注“注解类型”的“注解”,称为元注解。
(2)常见的元注解有哪些?
Target
Retention
【注: 这两个注解是java.lang.annotation包下面的 】
(3)关于Target注解:
Target英文翻译为“目标”
这是一个元注解,用来标注“注解类型”的“注解”
这个Target注解用来标注“被标注的注解”可以出现在哪些位置上。
@Target(ElementType.METHOD):表示“被标注的注解”只能出现在方法上。
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
表示该注解可以出现在:
构造方法上,字段上,局部变量上,方法上,包上,模块上,参数上,类上
(4)关于Retention注解:
Retention英文翻译为“保留”
这是一个元注解,用来标注“注解类型”的“注解”
这个Retention注解用来标注“被标注的注解”最终保存在哪里。
@Retention(RetentionPolicy.SOURCE):表示该注解只被保留在java源文件中。
@Retention(RetentionPolicy.CLASS): 表示该注解被保存在class文件中。
@Retention(RetentionPolicy.RUNTIME):表示该注解被保存在class文件中,并且可以被反射机制所读取。
3、注解中定义属性元素
3.1、我们通常在注解当中可以定义属性,然后在使用该注解时,在注解的后面给属性赋值
public @interface MyAnnotation {
//这个是MyAnnotation的name属性。
//看着像1个方法,但实际上我们称之为属性name。
String name();
//颜色属性
String color();
//年龄属性
int age() default 25; //属性指定默认值
}
public class MyAnnotationTest {
// 报错的原因:如果一个注解当中有属性,那么必须给属性赋值。
//(除非该属性使用default指定了默认值。)
/*@MyAnnotation
public void doSome(){
}*/
//@MyAnnotation(属性名=属性值,属性名=属性值,属性名=属性值)
@MyAnnotation(name = "zhangsan", color = "红色")
public void doSome(){
}
}
3.2、如果一个注解只有一个属性的话,并且属性的名字是value,那么在使用的时候,该属性名可以省略。
public @interface MyAnnotation {
// 指定一个value属性。
String value();
}
public class MyAnnotationTest {
// 报错原因:没有指定属性的值。
/*@MyAnnotation
public void doSome(){
}*/
@MyAnnotation(value = "hehe")
public void doSome(){
}
@MyAnnotation("haha") //直接给值,省略该属性名
public void doOther(){
}
}
3.3、注解当中的属性的类型?
属性的类型可以是:
byte、short、int、 long、 float、 double、 boolean、 char、 String、 Class、 枚举类型
以及以上每一种的数组形式。
注:枚举,在【第20章 常用类】讲过
//枚举
public enum Season {
SPRING,SUMMER,AUTUMN,WINTER
}
public @interface MyAnnotation {
int value1();
String value2();
int[] value3(); //int的数字
String[] value4();
Season value5(); //枚举类型
Season[] value6();
Class parameterType(); //Class类型
Class[] parameterTypes();
}
public @interface OtherAnnotation {
/*
年龄属性
*/
int age();
/*
邮箱地址属性,支持多个
*/
String[] email();
/**
* 季节数组,Season是枚举类型
* @return
*/
Season[] seasonArray();
}
public class OtherAnnotationTest {
// 数组是大括号
@OtherAnnotation(age = 25, email = {"zhangsan@123.com", "zhangsan@sohu.com"}, seasonArray = Season.WINTER)
public void doSome(){
}
// 如果数组中只有1个元素:大括号可以省略。
@OtherAnnotation(age = 25, email = "zhangsan@123.com", seasonArray = {Season.SPRING, Season.SUMMER})
public void doOther(){
}
}
3.4、现在要能看得懂Retention的源代码了
——————————————————————————————————————————————
//元注解
public @interface Retention {
//属性
RetentionPolicy value();
}
——————————————————————————————————————————————
// RetentionPolicy的源代码:
public enum RetentionPolicy { //枚举
SOURCE,
CLASS,
RUNTIME
}
——————————————————————————————————————————————
//@Retention(value=RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME) // 用来标注MyAnnotation注解的Retention注解
public @interface MyAnnotation{
}
——————————————————————————————————————————————
4、反射注解
4.1、反射注解
【annotation5/MyAnnotation.java + MyAnnotationTest.java +ReflectAnnotationTest.java】
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//只允许该注解可以标注类、方法
@Target({ElementType.TYPE, ElementType.METHOD})
// 希望这个注解可以被反射
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
//value属性。
String value() default "北京大兴区"; //属性指定默认值
}
@MyAnnotation("上海浦东区") //可以出现在类上
public class MyAnnotationTest {
//@MyAnnotation
//不能出现在属性上
int i;
//@MyAnnotation
//不能出现在构造方法上
public MyAnnotationTest(){
}
@MyAnnotation //可以出现在方法上
public void doSome(){
//@MyAnnotation
//不能出现在局部变量上
int i;
}
}
public class ReflectAnnotationTest {
public static void main(String[] args) throws Exception{
// 获取这个类
Class c = Class.forName("com.yuming.java.annotation5.MyAnnotationTest");
// 判断类上面是否有@MyAnnotation
//System.out.println(c.isAnnotationPresent(MyAnnotation.class)); // true
if(c.isAnnotationPresent(MyAnnotation.class)){
// 获取该注解对象
MyAnnotation myAnnotation = (MyAnnotation) c.getAnnotation(MyAnnotation.class);
System.out.println("类上面的注解对象" + myAnnotation); // @com.yuming.java.annotation5.MyAnnotation()
// 获取注解对象的属性怎么办?和调接口没区别。
String value = myAnnotation.value();
System.out.println(value);
}
// 判断String类上面是否存在这个注解
Class stringClass = Class.forName("java.lang.String");
System.out.println(stringClass.isAnnotationPresent(MyAnnotation.class)); // false
}
}
/*
类上面的注解对象@com.yuming.java.annotation5.MyAnnotation(value="上海浦东区")
上海浦东区
false
*/
4.2、通过反射获取注解对象属性的值
【annotation6/MyAnnotation.java + MyAnnotationTest.java 】
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // 希望这个注解可以被反射
@Target(ElementType.METHOD) //只允许该注解可以标注方法
public @interface MyAnnotation {
//username属性
String username();
//password属性
String password();
}
import java.lang.reflect.Method;
public class MyAnnotationTest {
@MyAnnotation(username = "admin", password = "123")
public void doSome(){
}
public static void main(String[] args) throws Exception{
// 获取MyAnnotationTest的doSome()方法上面的注解信息。
Class c = Class.forName("com.yuming.java.annotation6.MyAnnotationTest");
// 获取doSome()方法
Method doSomeMethod = c.getDeclaredMethod("doSome");
// 判断该方法上是否存在这个注解
if(doSomeMethod.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation myAnnotation = doSomeMethod.getAnnotation(MyAnnotation.class);
System.out.println(myAnnotation.username());
System.out.println(myAnnotation.password());
}
}
}
/*
admin
123
*/
5、注解在开发中有什么用呢?
需求:
假设有这样一个注解,叫做: @MustHasIdPropertyAnnotation
这个注解只能出现在类上面,当这个类上有这个注解的时候,要求这个类中必须有一个int类型的id属性。
如果没有这个属性就报异常,如果有这个属性则正常执行!
【annotation7/MustHasIdPropertyAnnotation.java + HasNotIdPropertyException.java
+ User.java + Test.java】
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 表示这个注解只能出现在类上面
@Target(ElementType.TYPE)
// 该注解可以被反射机制读取到
@Retention(RetentionPolicy.RUNTIME)
public @interface MustHasIdPropertyAnnotation {
}
// 这个注解@MustHasIdPropertyAnnotation 用来标注类,被标注的类中必须有一个int类型的id属性,没有就报异常。
//自定义异常
public class HasNotIdPropertyException extends RuntimeException {
public HasNotIdPropertyException(){ //无参构造方法
}
public HasNotIdPropertyException(String s){ //有参数构造方法
super(s);
}
}
@MustHasIdPropertyAnnotation
public class User {
int id;// 可以自行修改这里,来测试
String name;
String password;
}
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Exception{
// 获取类
Class userClass = Class.forName("com.yuming.java.annotation7.User");
// 判断类上是否存在 注解
if(userClass.isAnnotationPresent(MustHasIdPropertyAnnotation.class)){
// 当一个类上面有@MustHasIdPropertyAnnotation注解的时候,要求类中必须存在int类型的id属性
// 如果没有int类型的id属性则报异常。
// 获取类的属性
Field[] fields = userClass.getDeclaredFields();
boolean isOk = false; // 给一个默认的标记
for(Field field : fields){
if("id".equals(field.getName()) && "int".equals(field.getType().getSimpleName())){
// 表示这个类是合法的类。有@MustHasIdPropertyAnnotation注解,则这个类中必须有int类型的id
isOk = true; // 表示合法
break;
}
}
// 判断是否合法
if(!isOk){
throw new HasNotIdPropertyException("被@MustHasIdPropertyAnnotation注解标注的类中必须要有一个int类型的id属性!");
}
}
}
}