前言
之前我常用的语言是 PHP,受限于语言特性,说实话 PHP 被很多公司边缘化了,感觉后面发展空间和路子并不是很大,加上很多解决方案还是 Java 的比较多,且资料更加健全,我一头扎入了 Java 的学习中。
注解和反射在我们在现在的框架中常常看到,查看框架的底层也会发现反射的身影,如果我们不了解这些通用的方法,显然有点说不过去。边学边记录,记录自己的学习路线。
注解
基本概念
- Annotation 是从 JDK5.0 开始引入的新技术
- 作用:
- 不是程序本身,可以对程序做出解释(这一点和注释没有什么区别)
- 可以被其他程序(比如:编译器等)读取
- Annotation 的格式:
- 注解以“@注解名”在代码中存在,还可以添加一些参数值,例如:@SuppressWarnings(value=”unchecked”)
- Annotation 在哪里使用?
- 可以附加在 package、class、method、field 等上面,相当于给爱他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问。
内置注解
@Override
:定义在java.lang.Override
中,此注解只适用于修饰方法,表示一个方法声明打算重写超类中的一个方法声明。@Deprecated
:定义在java.lang.Deprecated
中,此注解可用于修饰方法,属性,类,表示不鼓励大家使用这个元素,通常是因为它很危险或者有更好的选择。@SuppressWarning
:定义在java.lang.SuppressWarning
中,用来抑制编译时的警告信息- 与前面两个注解有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,我们选择性使用就好了。
- @SuppressWarning(“all”)
- @SuppressWarning(“unchecked”)
- @SuppressWarning(value={“unchecked”, “deprecation”})
- 等等……
- 与前面两个注解有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,我们选择性使用就好了。
元注解
基本概念
- 元注解的作用就是负责注解其他注解,Java 定义了 4 个标准的 meta-annotation 类型,他们被用来提供对其他 annotation 类型作说明。
- 这些类型和它们所支持的类在
java.lang.annotation
包中找到。(@Target,@Retention,@Documented,@Inherited)- @Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
- @Retention:表示需要在什么级别保存该注解信息,用于描述注解的声明周期(SOURCE < CLASS < RUNTIME)
- @Documented:说明该注解将包含在 JavaDoc 中
- @Inherited:说明子类可以继承父类中的该注解
@Target 用于描述注解的作用范围
我们查看源代码可以知道,Target 可以传入一个值 value
这个 value 的类型为 ElementType
1 | public Target { |
2 | ElementType[] value(); |
3 | } |
让我们看看这个ElementType
枚举类包含的值,表示注解作用在什么上面:
TYPE:作用在类、接口,包括注解、枚举
1
// 我们先定义一个注解,文件为 MyAnnotationDemo01.java
2
package com.yubulang.demo01;
3
4
import java.lang.annotation.ElementType;
5
import java.lang.annotation.Target;
6
7
(value = {ElementType.TYPE})
8
public MyAnnotationDemo01 {
9
}
然后我们定义这个注解可以使用的对象,我是用的 idea,如果注解不支持这个类型,注解是会有红线提示。
1
package com.yubulang.demo01;
2
3
// 作用在类上
4
01
5
public class AnnotationDemo {
6
7
}
8
9
// 作用在接口上
10
01
11
interface TestInterface {
12
13
}
14
15
// 作用在注解上
16
01
17
TestAnnotation {
18
19
}
20
21
// 作用在枚举类上
22
01
23
enum TestEnum {
24
HELLO,
25
WORLD,
26
}
FIELD:作用在属性字段,包括枚举和常量
1
// 定义一个注解
2
package com.yubulang.demo02;
3
4
import java.lang.annotation.ElementType;
5
import java.lang.annotation.Target;
6
7
(ElementType.FIELD)
8
public MyAnnotationDemo01 {
9
}
然后我们定义这个注解可以使用的对象:
1
package com.yubulang.demo02;
2
3
public class AnnotationDemo01 {
4
// 作用在name属性
5
01
6
protected String name;
7
8
// 作用在常量上
9
01
10
protected final String id = "0001";
11
12
enum HelloEnum {
13
THING01,
14
THING02
15
}
16
17
// 作用在枚举类型
18
01
19
protected HelloEnum helloEnum = HelloEnum.THING01;
20
}
METHOD:作用在方法
1
// 定义一个注解
2
package com.yubulang.demo03;
3
4
import java.lang.annotation.ElementType;
5
import java.lang.annotation.Target;
6
7
(ElementType.METHOD)
8
public MyAnnotationDemo01 {
9
}
然后我们定义这个注解可以使用的对象:
1
package com.yubulang.demo03;
2
3
public class AnnotationDemo01 {
4
// 作用于方法
5
01
6
public void test() {
7
8
}
9
}
PARAMETER:作用在方法参数上
1
// 定义一个注解
2
package com.yubulang.demo04;
3
4
import java.lang.annotation.ElementType;
5
import java.lang.annotation.Target;
6
7
(ElementType.PARAMETER)
8
public MyAnnotationDemo01 {
9
}
然后我们定义这个注解可以使用的对象:
1
package com.yubulang.demo04;
2
3
public class AnnotationDemo {
4
// 作用在方法的参数上
5
public void test(@MyAnnotationDemo01 String name) {
6
7
}
8
}
CONSTRUCTOR:作用在构造方法上
1
// 定义一个注解
2
package com.yubulang.demo05;
3
4
import java.lang.annotation.ElementType;
5
import java.lang.annotation.Target;
6
7
(ElementType.CONSTRUCTOR)
8
public MyAnnotationDemo01 {
9
}
然后我们定义这个注解可以使用的对象:
1
package com.yubulang.demo05;
2
3
public class AnnotationDemo {
4
// 作用在构造函数上
5
01
6
public AnnotationDemo() {
7
8
}
9
10
// 这里会报错,因为不是构造方法
11
// @MyAnnotationDemo01
12
// public void test() {
13
//
14
// }
15
}
LOCAL_VARIABLE:作用在局部变量上
1
// 定义一个注解
2
package com.yubulang.demo06;
3
4
import java.lang.annotation.ElementType;
5
import java.lang.annotation.Target;
6
7
(ElementType.LOCAL_VARIABLE)
8
public MyAnnotationDemo01 {
9
}
然后我们定义这个注解可以使用的对象:
1
package com.yubulang.demo06;
2
3
public class AnnotationDemo {
4
public void test() {
5
// 作用在局部变量上
6
01
7
String name = "李雷与韩梅梅";
8
}
9
}
ANNOTATION_TYPE:作用在注解上
1
// 定义一个注解
2
package com.yubulang.demo07;
3
4
import java.lang.annotation.ElementType;
5
import java.lang.annotation.Target;
6
7
(ElementType.ANNOTATION_TYPE)
8
public MyAnnotationDemo {
9
}
然后我们定义这个注解可以使用的对象:
1
package com.yubulang.demo07;
2
3
// 作用在注解上
4
5
public AnnotationDemo {
6
7
}
PACKAGE:作用在包上
1
// 定义一个注解
2
package com.yubulang.demo08;
3
4
import java.lang.annotation.ElementType;
5
import java.lang.annotation.Target;
6
7
(ElementType.PACKAGE)
8
public MyAnnotationDemo {
9
}
然后我们定义这个注解可以使用的对象,这里有个特殊我们需要创建一个
package-info.java
这个是包的描述文件。1
// package-info.java
2
// 作用在包上
3
4
package com.yubulang.demo08;
TYPE_PARAMETER:作用在定义泛型的类型参数上
1
// 定义一个注解
2
package com.yubulang.demo09;
3
4
import java.lang.annotation.ElementType;
5
import java.lang.annotation.Target;
6
7
(ElementType.TYPE_PARAMETER)
8
public MyAnnotationDemo {
9
}
然后我们定义这个注解可以使用的对象:
1
package com.yubulang.demo09;
2
3
// 作用在定义泛型类型参数上
4
class Demo<@MyAnnotationDemo T> {
5
private T data;
6
}
7
8
public class AnnotationDemo {
9
public void test() {
10
Demo<String> demo = new Demo<>();
11
}
12
}
TYPE_USE:作用在泛型类型使用参数上
1
// 定义一个注解
2
package com.yubulang.demo10;
3
4
import java.lang.annotation.ElementType;
5
import java.lang.annotation.Target;
6
7
(ElementType.TYPE_USE)
8
public MyAnnotationDemo {
9
}
然后我们定义这个注解可以使用的对象:
1
package com.yubulang.demo10;
2
3
class Demo<T> {
4
private T data;
5
}
6
7
public class AnnotationDemo {
8
// 作用在泛型类型使用时
9
Demo<new Demo<>(); String> demo =
10
}
@Retention 用于表示注解的生命周期
我们查看源代码可以知道,Retention 可以传入 value
这个值的类型为RetentionPolicy
枚举类
1 | public Retention { |
2 | /** |
3 | * Returns the retention policy. |
4 | * @return the retention policy |
5 | */ |
6 | RetentionPolicy value(); |
7 | } |
我们一般写代码的时候是源代码阶段(SOURCE),编译成 CLASS 的时候就是(CLASS),运行的时候就是(RUNTIME)。作用域由大到小应该是:RUNTIME > CLASS > SOURCE。
这个理解起来想容易,我们来使用一下:
1 | // 例子一============================= |
2 | package com.yubulang.demo11; |
3 | |
4 | import java.lang.annotation.Retention; |
5 | import java.lang.annotation.RetentionPolicy; |
6 | |
7 | // 只有在源代码时 |
8 | (RetentionPolicy.SOURCE) |
9 | public MyAnnotationDemo01 { |
10 | } |
11 | |
12 | // 例子二============================= |
13 | package com.yubulang.demo11; |
14 | |
15 | import java.lang.annotation.Retention; |
16 | import java.lang.annotation.RetentionPolicy; |
17 | |
18 | // 注解在java文件编译成为class依然有效 |
19 | (RetentionPolicy.CLASS) |
20 | public MyAnnotationDemo02 { |
21 | } |
22 | |
23 | // 例子三============================= |
24 | package com.yubulang.demo11; |
25 | |
26 | import java.lang.annotation.Retention; |
27 | import java.lang.annotation.RetentionPolicy; |
28 | |
29 | // 注解在运行时依然有效 |
30 | (RetentionPolicy.RUNTIME) |
31 | public MyAnnotationDemo03 { |
32 | } |
@Documented 说明该注解将包含在 javadoc 中
默认的javadoc
工具,是不包括注解的。但是如果声明注解时指定了 @Documented
这个注解就会被 javadoc
之类的工具处理,所以注解类型信息也会被包括在生成文档中,是一个标记注解,没有成员。
@Inherited 子类会继承父类的注解
1 | package com.yubulang.demo12; |
2 | |
3 | import java.lang.annotation.*; |
4 | |
5 | (ElementType.TYPE) |
6 | (RetentionPolicy.RUNTIME) |
7 |
|
8 | InheritedTest { |
9 | String value(); |
10 | } |
11 | |
12 | "拥有Inherited") ( |
13 | class Person { |
14 | public void methodOne() { |
15 | } |
16 | |
17 | public void methodTwo() { |
18 | } |
19 | } |
20 | |
21 | class Student extends Person { |
22 | |
23 | } |
24 | |
25 | (ElementType.TYPE) |
26 | (RetentionPolicy.RUNTIME) |
27 | IsNotInherited { |
28 | String value(); |
29 | } |
30 | |
31 | "未拥有Inherited") ( |
32 | class NotInheritedAnnotationPerson { |
33 | public void methodOne() { |
34 | } |
35 | |
36 | public void methodTwo() { |
37 | } |
38 | } |
39 | |
40 | class NotInheritedAnnotationStudent extends NotInheritedAnnotationPerson { |
41 | |
42 | } |
43 | |
44 | public class Test02 { |
45 | public static void main(String[] args) { |
46 | Class<Student> studentClass = Student.class; |
47 | |
48 | if (studentClass.isAnnotationPresent(InheritedTest.class)) { |
49 | System.out.println(studentClass.getAnnotation(InheritedTest.class).value()); |
50 | } |
51 | |
52 | Class<NotInheritedAnnotationStudent> nStudentClass = NotInheritedAnnotationStudent.class; |
53 | |
54 | if (nStudentClass.isAnnotationPresent(IsNotInherited.class)) { |
55 | System.out.println(nStudentClass.getAnnotation(IsNotInherited.class).value()); |
56 | } |
57 | } |
58 | } |
自定义注解
- 使用@interface 自定义注解时,自动继承了
java.lang.annotation.Annotation
接口 - 分析:
- @interface 用来声明一个注解,格式:
public @interface 注解名 {定义内容}
- 其中的每一个方法实际上是声明了一个配置参数
- 方法的名称就是参数的名称
- 返回值类型就是参数类型(返回值只能是基本类型,Class,String,enum)
- 可以通过 default 来声明参数的默认值
- 如果只有一个参数成员,一般命名为 value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0 作为默认值
- @interface 用来声明一个注解,格式:
知道了这些基础的概念,我觉得还是要通过一些代码,才能巩固。废话就不多说我们看代码。
1 | package com.yubulang.demo12; |
2 | |
3 | import java.lang.annotation.ElementType; |
4 | import java.lang.annotation.Retention; |
5 | import java.lang.annotation.RetentionPolicy; |
6 | import java.lang.annotation.Target; |
7 | |
8 | public class Test01 { |
9 | // 没有 default 默认值,我们必须给注解赋值 |
10 | // 有 default "" 可以显式赋值,也可以不赋值 |
11 | "大哥", schools = {"哈理工", "哈工大"}) 01(name = |
12 | public void test() { |
13 | } |
14 | |
15 | "大哥") 02( |
16 | public void test02() { |
17 | } |
18 | } |
19 | |
20 | ({ElementType.TYPE, ElementType.METHOD}) |
21 | (RetentionPolicy.RUNTIME) |
22 | MyAnnotation01 { |
23 | // 注解的参数:参数类型 + 参数名() |
24 | String name() default ""; |
25 | |
26 | int age() default 0; |
27 | |
28 | int id() default -1; // 如果默认值为 -1,代表不存在 |
29 | |
30 | String[] schools() default {}; |
31 | } |
32 | |
33 | ({ElementType.TYPE, ElementType.METHOD}) |
34 | (RetentionPolicy.RUNTIME) |
35 | MyAnnotation02 { |
36 | String value(); // 方法名为value,才能省略,其他的不行 |
37 | } |
总结
知道了怎么定义注解只是一个开始,我们想要注解去处理一些具体的事儿,还需要反射的配合才能实现。万丈高楼平地起,今天就到这里。