JavaseLearn25-注解
JavaseLearn25-注解
1.注解概述
-
注解,或者说叫注释类型。(Annotation)
-
注解Annotation是一种引用数据类型:
- 编译后也是生成xxx.class文件
-
自定义注释的语法格式:
-
[修饰符列表] @interface 注解类型名{ }
-
public @interface MyAnnotation { }
-
-
注解怎么用,用在哪儿?
-
注解使用时的语法格式是:@注解类型名。如:@Override
-
注解可以出现在类上、属性上、方法上、变量上等。
-
注解还可以出现在注解类型上。
-
package com.tsccg.java; /** * @Author: TSCCG * @Date: 2021/07/14 20:03 */ @MyAnnotation01 public class AnnotitationDemo01 { @MyAnnotation01 private String name; @MyAnnotation01 public AnnotitationDemo01(@MyAnnotation01 String name) { this.name = name; } @MyAnnotation01 public void m1() { @MyAnnotation01 int i = 10; } @MyAnnotation01 public static void m2() { } } @MyAnnotation01 interface Anno{ } @MyAnnotation01 enum Season{ /** * 春,夏,秋,冬 */ @MyAnnotation01 SPRING,SUMMER,AUTUMN,WINTER } /** * 自定义注解 */ @MyAnnotation01 @interface MyAnnotation01 { } /** * 另一个自定义注解 */ @MyAnnotation01 @interface AnotherAnnotation{ }
-
2.JDK自带的注解
java.lang包下的注解类型
- Override(掌握)
- 表示一个方法声明打算重写父类中的另一个方法声明。
- Deprecated(掌握)
- 标示所注解的程序元素已过时。
- SuppressWarnings(知道即可)
- 指示应该在注释元素,以及包含在该注释元素中的所有程序元素中,取消显示指示的编译器警告。
3.Override注解
- 只能注解方法。
- 标识性注解,给编译器做参考用。
- 编译器看到方法上有这个注解的时候,编译器会自动检查该方法是否重写了父类的方法。
- 如果没有重写,则报错。
- 只在编译阶段起作用,与运行期无关。
public class AnnotationDemo02 {
@Override
public String toString(){
return null;
}
}
4.元注解
4.1什么是元注解?
我们先想一下,为什么Override注解只能使用在方法上呢?
我们可以查看一下Override源码:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
可见,有另一个注解@Target用来注解Override,作用是让Override只能用于方法上。
这种用于注解注解类型的注解,我们称之为元注解。
4.2常见的元注解
- Target
- Retention
4.3关于Target注解
- 这是一个元注解,用于标注”注解类型”的“注解”。
- Target注解用于限定其所注解的“注解类型”可以出现在哪些位置上。
- @Target(ElementType.METHOD):表示被注解的“注解类型”只能出现在方法上。
4.4关于Retention注解
- 这是一个元注解,用于标注”注解类型”的“注解”。
- 用于限定被其所注解的“注解类型”最终保存的位置。
- @Retention(RetentionPolicy.SOURCE):表示该注解最终被保留在java源文件里。
- @Retention(RetentionPolicy.CLASS):表示该注解最终被保留在class文件里。
- @Retention(RetentionPolicy.RUNTIME):表示该注解最终被保留在class文件里,并且可以被反射机制所读取。
5.Deprecated注解
Deprecated的作用:
- Deprecated用于标示一个程序元素已过时。
-
主要向其他程序员传达一个信息,告知已过时,有更好的解决方案。
6.注解中的属性
6.1使用含有属性的注解
定义如下注解:
注解中有三个属性,其中一个定义了默认值。
package com.tsccg.java.annotation;
/**
* @Author: TSCCG
* @Date: 2021/07/14 20:13
*/
public @interface MyAnnotation {
//看似方法,实则为注解属性
String name();
int age();
//定义默认值
String address() default "中国";
}
在另一个类中使用该注解:
如果直接在方法上方用:@MyAnnotation 会报错。
需要填写注解属性:@MyAnnotation(name = "张三",age = 20)
address属性因为已设默认值,不写也没关系。
public class AnnotationDemo04 {
/*
报错,需要填写注解属性
@MyAnnotation
public void m1() {
}*/
//正确使用方式
//@MyAnnotation(属性名1 = 属性值1,属性名2 = 属性值2)
@MyAnnotation(name = "张三",age = 20)
public void m1() {
}
}
6.2当属性名为value时可省略
当属性名不是value时,使用时必须写上该属性名。
当一个注解中属性名为value,并且有且仅有一个属性,使用时可以不写属性名。(源码中有很多这样的注解)
当一个注解中有多个属性时,即使有属性名为value,使用时也不可以省略该属性名。
package com.tsccg.java.annotation;
/**
* @Author: TSCCG
* @Date: 2021/07/14 22:11
*/
public class AnnotationDemo04 {
//当属性名不是value时,使用时必须写上该属性名
@MyAnnotation1(name = "张三")
public void m1() {
}
//当一个注解中属性名为value,并且有且仅有一个属性,使用时可以不写属性名(源码中有很多这样的注解)
@MyAnnotation2("啦啦啦")
public void m2() {
}
//当一个注解中有多个属性时,即使有属性名为value,使用时也不可以省略该属性名
@MyAnnotation3(value = "啦啦啦",age = 20)
public void m3() {
}
}
@interface MyAnnotation1 {
String name();
}
@interface MyAnnotation2 {
String value();
}
@interface MyAnnotation3 {
String value();
int age();
}
6.3注解中的属性的类型
注解中属性的属性可以是:
byte、short、int、long、float、double、boolean、char
String、Class、枚举类型
以及以上所有类型的数组。
package com.tsccg.java.annotation.myannotation;
/**
* @Author: TSCCG
* @Date: 2021/07/15 10:29
* 注解里可以使用什么类型的属性
*/
public @interface MyAnnotation02 {
/*
注解中属性的属性可以是:
byte、short、int、long、float、double、boolean、char
String、Class、枚举类型
以及以上所有类型的数组
*/
int value1();
String value2();
Season02 value3();
Class value4();
int[] value5();
String[] value6();
Season02[] value7();
Class[] value8();
//Object value();不可使用
}
/**
* 定义一个枚举类型
*/
enum Season02 {
//春夏秋冬
SPRING,SUMMER,AUTOMN,WINTER
}
6.4使用含有数组属性的注解
自定义注解,含有数组属性:
package com.tsccg.java.annotation.myannotation;
/**
* @Author: TSCCG
* @Date: 2021/07/15 10:44
*/
public @interface MyAnnotation03 {
int age();
String[] address();
Season03[] seasons();
}
/**
* 定义一个枚举类型
*/
enum Season03 {
//春夏秋冬
SPRING,SUMMER,AUTOMN,WINTER
}
测试类:
package com.tsccg.java.annotation.myannotation;
/**
* @Author: TSCCG
* @Date: 2021/07/15 10:28
*/
public class AnnotationDemo05 {
@MyAnnotation03(age = 20,address = {"广州","河南"},
seasons = {Season03.SPRING,Season03.SUMMER})
public void m1() {
}
//当数组中只有一个元素时,可省略大括号
@MyAnnotation03(age = 20,address = "北京",
seasons =Season03.AUTOMN)
public void m2() {
}
}
7.元注解Target和Retention的源码
7.1Retention源码
//元注解
public @interface Retention {
//value属性,有且仅有一个属性,使用时可省略属性名
RetentionPolicy value();
}
RetentionPolicy源码:
是一个枚举类型
public enum RetentionPolicy {
SOURCE,
CLASS,
RUNTIME
}
7.2Target源码
//元注解
public @interface Target {
//属性,是一个数组
ElementType[] value();
}
ElementType源码:
是一个枚举类型,包含一个程序中的各个位置
public enum ElementType {
TYPE,
FIELD,
METHOD,
PARAMETER,
CONSTRUCTOR,
LOCAL_VARIABLE,
ANNOTATION_TYPE,
PACKAGE,
TYPE_PARAMETER,
TYPE_USE
}
7.3元注解的使用
标注一个注解只能使用在方法和属性上,并且能够被反射机制所访问到:
package com.tsccg.java.annotation.myannotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author: TSCCG
* @Date: 2021/07/15 11:15
*/
//标注一个注解只能使用在方法和属性上,并且能够被反射机制所访问到
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation04 {
}
8.反射注解
8.1通过反射机制获取注解
自定义注解:
限制只能用于方法和属性上,并且能够被反射机制所访问
//标注一个注解只能使用在方法和属性上,并且能够被反射机制所访问到
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation04 {
}
反射:
package com.tsccg.java.annotation.annodemo;
import com.tsccg.java.annotation.myannotation.MyAnnotation04;
/**
* @Author: TSCCG
* @Date: 2021/07/15 16:17
* 反射注解
* 通过反射获取注解
*/
@MyAnnotation04
public class ReflectAnnotationDemo01 {
public static void main(String[] args) throws Exception{
//获取类的Class
Class c = Class.forName("com.tsccg.java.annotation.annodemo.ReflectAnnotationDemo01");
//该类的上面是否有@MyAnnotation04
boolean isAnno = c.isAnnotationPresent(MyAnnotation04.class);
System.out.println(isAnno);
//如果该类被MyAnnotation04所注解,执行括号内代码
if (isAnno) {
//获取该注解
MyAnnotation04 myAnnotation04 =
(MyAnnotation04)c.getAnnotation(MyAnnotation04.class);
System.out.println(myAnnotation04);
}
}
}
结果:
true
@com.tsccg.java.annotation.myannotation.MyAnnotation04
8.2通过反射获取注解中的属性值
自定义注解:
package com.tsccg.java.annotation.myannotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author: TSCCG
* @Date: 2021/07/15 17:08
*/
//标注一个注解只能使用在方法和属性上,并且能够被反射机制所访问到
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation05 {
String name();
int age();
String address() default "中国";
}
反射获取属性值:
package com.tsccg.java.annotation.annodemo;
import com.tsccg.java.annotation.myannotation.MyAnnotation05;
import java.lang.reflect.Method;
/**
* @Author: TSCCG
* @Date: 2021/07/15 16:52
*/
public class ReflectAnnotationDemo02 {
@MyAnnotation05(name = "张三", age = 22,address = "河南")
public void m1() {
}
public static void main(String[] args) throws Exception {
//获取类
Class cra = Class.forName("com.tsccg.java.annotation.annodemo.ReflectAnnotationDemo02");
//获取类中被注解方法
Method m1 = cra.getDeclaredMethod("m1");
//判断该方法上面是否有@MyAnnotation05
if (m1.isAnnotationPresent(MyAnnotation05.class)) {
//获取注解对象
MyAnnotation05 myAnnotation05 = m1.getAnnotation(MyAnnotation05.class);
//输出注解中的属性值
System.out.println(myAnnotation05.name());
System.out.println(myAnnotation05.age());
System.out.println(myAnnotation05.address());
}
}
}
结果:
张三
22
河南
9.注解在开发中的作用
9.1作用分析
注解在程序中等同于一种标记。
如果程序元素上有这个注解会怎样,没有这个注解又会怎样。
下面通过一个例子体会:
9.2实例体会
9.2.1需求
自定义一个注解:@IdAnnotation。
- 要求只能出现在类上面。
- 并且当一个类上有这个注解时,要求这个类中必须有一个int类型的id属性。
- 如果有则正常执行,如果没有则报异常。
9.2.2自定义注解
package com.tsccg.java.annotation.myannotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author: TSCCG
* @Date: 2021/07/15 17:33
* 并且当一个类上有这个注解时,要求这个类中必须有一个int类型的id属性。
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface IdAnnotation {
int id();
}
9.2.3功能实现类
package com.tsccg.java.annotation.annodemo;
import com.tsccg.java.annotation.myannotation.IdAnnotation;
import java.lang.reflect.Field;
/**
* @Author: TSCCG
* @Date: 2021/07/15 16:14
* 当一个类上有这个注解时,要求这个类中必须有一个int类型的id属性。
* 如果有则正常执行,如果没有则报异常。
*/
public class AnnotationDemo06 {
public static void main(String[] args) throws Exception{
//获取被注解类
Class userclass = Class.forName("com.tsccg.java.annotation.annodemo.User");
//定义一个boolean类型来代表最终的结果,默认设置为false
boolean hasId = false;
//判断被注解类上是否存在@IdAnnotation
if (userclass.isAnnotationPresent(IdAnnotation.class)) {
//通过反射获取被注解类中所有的属性
Field[] fields = userclass.getDeclaredFields();
//遍历所有属性
for (Field field : fields) {
//判断,如果当前的属性名为"id"且属性类型为int,则设置hasId为true,并终止遍历
if ("id".equals(field.getName()) &&
"int".equals(field.getType().getSimpleName())) {
hasId = true;
break;
}
}
//判断最终结果,如果hasId仍为false抛出异常
if (!hasId) {
throw new HasNotIdException("类中属性没有int类型的id");
}
}
}
}
/**
* 被注解类
*/
@IdAnnotation(id = 001)
class User {
int id;
String name;
int age;
}
/**
* 自定义异常
*/
class HasNotIdException extends Exception {
public HasNotIdException() {
}
public HasNotIdException(String s) {
super(s);
}
}
9.2.4执行结果
如果被注解类中有int类型的id属性:程序正常执行,不报异常。
如果被注解类中没有int类型的id属性:
Exception in thread "main" com.tsccg.java.annotation.annodemo.HasNotIdException: 类中属性没有int类型的id
at com.tsccg.java.annotation.annodemo.AnnotationDemo06.main(AnnotationDemo06.java:33)