肖sir_Java注解__22
Java注解
1.1 注解介绍
从Java 5 版本之后可以在源代码中嵌入一些补充信息,这种补充信息称为注解(Annotation),注解是 Java 平台中非常重要的一部分。注解都是 @ 符号开头,
例如我们在学习方法重写时使用过的 @Override 注解。同 Class 和 Interface 一样,注解也属于一种类型。
Annotation 可以翻译为“注解”或“注释”,一般翻译为“注解”,因为“注释”一词已经用于说明“//”、“/**...*/”和“/*...*/”等符号了,这里的“注释”是英文Comment 翻译。
说明:注解并不能改变程序的运行结果,也不会影响程序运行的性能。有些注解可以在编译时给用户提示或警告,有的注解可以在运行时读写字节码文件信息。
1.2 注解介绍
注解的作用:
1,生成帮助文档。这是最常见的,也是 Java 最早提供的注解。常用的有 @see、@param 和 @return 等;
2,跟踪代码依赖性,实现替代配置文件功能。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量;
3,在编译时进行格式检查。如把 @Override 注解放在方法前,如果这个方法并不是重写了父类方法,则编译时就能检查出。
说明:无论是哪一种注解,本质上都一种数据类型,是一种接口类型。到 Java 8 为止 Java SE 提供了 11 个内置注解。其中有 5 个是基本注解,它们来自于 java.lang包。有 6 个是元注解,它们来自于 java.lang.annotation 包,自定义注解会用到元注解
1.3 注解介绍
5个基本注解:
@Override、@Deprecated、@SuppressWarnings、
@SafeVarargs 和 @FunctionalInterface
6个元注解:
@Documented、@Target、@Retention 和 @Inherited
@Repeatable 和 @Native
1.4 注解介绍
@Override 注解:
该注解是用来指定方法重写的,只能修饰方法并且只能用于方法重写,不能修饰其它的元素。它可以强制一个子类必须重写父类方法或者实现接口的方法。
@Override 的作用是告诉编译器检查这个方法,保证父类要包含一个被该方法重写的方法,否则就会编译出错。这样可以帮助程序员避免一些低级错误。
比如:
public class Person {
@Override
public String toString() {
return “Person [====重写Object类的toString方法====]";
}
}
1.5 注解介绍
@Deprecated 可以用来注解类、接口、成员方法和成员变量等,用于表示某个元素
(类、方法等)已过时。当其他程序使用已过时的元素时,编译器将会给出警告。
比如:
@Deprecated
public class Person {
@Deprecated
protected String name;
@Deprecated
public void setNameAndAge(String name, int age) {
this.name = name;
this.age = age;
}
}
1.6 注解介绍
@SuppressWarnings 注解指示被该注解修饰的程序元素(以及该程序元素中的所有
子元素)取消显示指定的编译器警告,且会一直作用于该程序元素的所有子元素。
@SuppressWarnings注解的使用有以下三种:
抑制单类型的警告:@SuppressWarnings("unchecked")
抑制多类型的警告:@SuppressWarnings("unchecked","rawtypes")
抑制所有类型的警告:@SuppressWarnings("all")
比如:
@SuppressWarnings({ "deprecation" })
public static void main(String[] args) {
Person p = new Person();
p.setNameAndAge("多测师", 20);
p.name = "duoceshi";
}
1.7 注解介绍
@SuppressWarnings中的关键字:
1.8 注解介绍
@SafeVarargs 注解抑制编译器警告,但不适用于非 static 或非 final 声明的方法
比如:
public class HelloWorld {
public static void main(String[] args) {
// 传递可变参数,参数是泛型集合
display(10, 20, 30);
// 传递可变参数,参数是非泛型集合
display("10", 20, 30); // 没有@SafeVarargs会有编译警告
}
@SafeVarargs
public static <T> void display(T... array) {
for (T arg : array) {
System.out.println(arg.getClass().getName() + ":" + arg);
}
}
}
1.9 注解介绍
@FunctionalInterface 就是用来指定某个接口必须是函数式接口,
所以 @FunInterface 只能修饰接口,不能修饰其它程序元素。
函数式接口就是为 Java 8 的 Lambda 表达式准备的,Java 8 允许使用 Lambda 表
达式创建函数式接口的实例,因此 Java 8 专门增加了 @FunctionalInterface。
比如:
@FunctionalInterface
public interface FunInterface {
void test(); // 只定义一个抽象方法
}
说明:使用了@FunctionalInterface注解修饰的接口只能包含一个抽象方法。包含多
个则会报错。
2.1 自定义注解
声明自定义注解使用@interface 关键字(interface 关键字前加 @ 符号)实现。
定义注解和定义类相似,注解前面的访问修饰符和类一样有两种,分别是公有访问权限(public)和默认访问权限。一个源程序文件中可以声明多个注解,但只能有一个是公有访问权限的注解。且源程序文件命名和公有访问权限的注解名一致。
自定义注解分类:
1,标记注解:没有定义成员变量的注解类型被称为标记注解。这种注解仅利用自身的存在与否来提供信息,如前面介绍的 @Override、@Test 等都是标记注解。
2,元数据注解:包含成员变量的注解,因为它们可以接受更多的元数据,所以也被称为元数据注解。
2.2 自定义注解
自定义标记注解
例如:
// 定义一个简单的标记注解
public @interface Lesson22_2 {
}
@Lesson22_2 // 使用自定义注解
class LessonDemo{
}
2.3 自定义注解
自定义元数据注解
例如:
public @interface MyTag {
// 定义带两个成员变量的注解,注解中的成员变量以方法的形式来定义
String name();
int age();
}
注解中的成员变量也可以有默认值,可使用 default 关键字,例如:
public @interface MyTag {
// 定义了两个成员变量的注解 使用default为两个成员变量指定初始值
String name() default "多测师";
int age() default 5;
}
2.4 自定义注解
使用自定义元数据注解,例如:
public class Test {
// 使用带成员变量的注解时,需要为成员变量赋值
@MyTag(name="dcs", age=6)
public void info() {
}
}
如果为注解的成员变量指定了默认值,那么使用该注解时就可以不为这些成员变量赋
值,而是直接使用默认值。
public class Test {
@MyTag
public void info() {
}
} //如果为 MyTag 的成员变量指定了值,则默认值不会起作用。
3.1 元注解作用及使用
元注解是负责对其它注解进行说明的注解,自定义注解时可以使用元注解。Java 5 定义了 4 个注解,分别是 @Documented、@Target、@Retention 和 @Inherited。
Java 8 又增加了 @Repeatable 和 @Native 两个注解。
@Documented 是一个标记注解,没有成员变量。用 @Documented 注解修饰的注
解类会被 JavaDoc 工具提取成文档。
@Retention 用于描述注解的生命周期,也就是该注解被保留的时间长短。
@Inherited 是一个标记注解,用来指定该注解可以被继承。
@Repeatable 注解是 Java 8 新增加的,它允许在相同的程序元素中重复注解。
@Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生
成工具使用
3.2 元注解作用及使用
@Target 注解用来指定一个注解的使用范围,即被 @Target 修饰的注解可以用在什
么地方。@Target 注解有一个成员变量(value)用来设置适用目标,
value 是 java.lang.annotation.ElementType 枚举类型的数组。
ElementType常用的枚举常量:
3.3 元注解作用及使用
自定义一个 MyTarget 注解,使用范围为方法,代码如下所示。
@Target({ ElementType.METHOD })
public @interface MyTarget {
}
class Test {
@MyTarget
public void demo(){
// @MyTarget 只能修饰方法,修饰类或成员变量则会报错
}
}
3.4 元注解作用及使用
@Retention 用于描述注解的生命周期,也就是该注解被保留的时间长短。
@Retention 注解中的成员变量(value)用来设置保留策略,value 是 java.lang.an
notation.RetentionPolicy 枚举类型,RetentionPolicy 有 3 个枚举常量,如下所示。
SOURCE:在源文件中有效(即源文件保留)
CLASS:在 class 文件中有效(即 class 保留)
RUNTIME:在运行时有效(即运行时保留)
生命周期大小排序为 SOURCE < CLASS < RUNTIME,前者能使用的地方后者一定也能使用。如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用CLASS 注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解
3.5 元注解作用及使用
自定义注解:
//注解的使用范围为类,方法,成员变量
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
// RUNTIME:在运行时有效(即运行时保留)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String name() default "defaultValue"; // 默认值
}
4.1 通过反射获取注解信息
1,自定义注解:
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String name() default "defaultValue"; // 默认值
}
4.2 通过反射获取注解信息
2,定义一个类,使用注解:
//注解里使用了默认值可不填参数
@MyAnnotation(name = "I am A class")
public class Dcs {
@MyAnnotation(name = "I am a field")
public String name;
@MyAnnotation(name = "I am a method")
public void testMethod() {
System.out.println("testMethod");
}
}
4.3 通过反射获取注解信息
3,测试代码:
public static void main(String[] args) throws Exception {
Class<Dcs> c = Dcs.class;
// 获取类上的所有注解
Annotation[] a1 = c.getAnnotations();
// 获取类上的指定注解
MyAnnotation a2 = c.getAnnotation(MyAnnotation.class);
// 获取字段上的注解
Field n = c.getField("name");
MyAnnotation a3 = n.getAnnotation(MyAnnotation.class);
//获取方法上的注解
Method method = c.getMethod("testMethod");
MyAnnotation a4 = method.getAnnotation(MyAnnotation.class);
// 获取注解里的信息
String name = a4.name();
}
课后练习题
1,自定义一个学生注解,注解中有3个成员变量,name,age,sex。3个成员变量都有默认值,name=张三,age=18,sex=男。注解的修饰范围为类。新建一个学生类,在学生类上使用自定义注解。使用反射机制创建学生对象,并根据注解里面的成员变量的值来初始化学生对象。