JavaSE——注解

First Post:

Last Update:

Word Count:
2k

Read Time:
8 min

注解(注释类型)

注解Annotation是一种引用数据类型。编译之后也是生成xxx.class文件

自定义注解

[修饰符列表] @interface 注解类型名{

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 默认情况下,注解可以出现在任意位置。

@MyAnnotation
public class AnnotationTest01 {

@MyAnnotation
private int no;

@MyAnnotation
public AnnotationTest01(){}

@MyAnnotation
public static void m1(){
@MyAnnotation
int i = 100;
}

@MyAnnotation
public void m2(@MyAnnotation
String name,
@MyAnnotation
int k){

}
}

@MyAnnotation
interface MyInterface {

}

@MyAnnotation
enum Season {
SPRING,SUMMER,AUTUMN,WINTER
}

JDK内置的注解

java.lang包下的注释类型:

掌握:
Deprecated 用 @Deprecated 注释的程序元素,
不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。

掌握:
Override 表示一个方法声明打算重写超类中的另一个方法声明。

不用掌握:
SuppressWarnings 指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。

@Override

源代码:
public @interface Override {
}

标识性注解,给编译器做参考的。
编译器看到方法上有这个注解的时候,编译器会自动检查该方法是否重写了父类的方法。
如果没有重写,报错。

这个注解只是在编译阶段起作用,和运行期无关!

@Override这个注解只能注解方法。
@Override这个注解是给编译器参考的,和运行阶段没有关系。
凡是java中的方法带有这个注解的,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译器报错。

1
2
3
4
5
6
7
8
9
10
11
public class AnnotationTest02 {

//@Override
private int no;

@Override
public String toString() {
return "toString";
}

}

@Deprecated

Deprecated 用 @Deprecated 注释的程序元素,
不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.bjpowernode.java.annotation;

// 表示这个类已过时。
@Deprecated
public class AnnotationTest03 {

@Deprecated
private String s;

public static void main(String[] args) {
AnnotationTest03 at = new AnnotationTest03();
at.doSome();
}

@Deprecated
public void doSome(){
System.out.println("do something!");
}

// Deprecated这个注解标注的元素已过时。
// 这个注解主要是向其它程序员传达一个信息,告知已过时,有更好的解决方案存在。
@Deprecated
public static void doOther(){
System.out.println("do other...");
}
}

class T {
public static void main(String[] args) {
AnnotationTest03 at = new AnnotationTest03();
at.doSome();

AnnotationTest03.doOther();

try {
Class c = Class.forName("java.util.Date");
Object obj = c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
}

注解中定义属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyAnnotationTest {

// 报错的原因:如果一个注解当中有属性,那么必须给属性赋值。(除非该属性使用default指定了默认值。)
/*@MyAnnotation
public void doSome(){

}*/

//@MyAnnotation(属性名=属性值,属性名=属性值,属性名=属性值)
//指定name属性的值就好了。
@MyAnnotation(name = "zhangsan", color = "红色")
public void doSome(){

}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public @interface MyAnnotation {

/**
* 我们通常在注解当中可以定义属性,以下这个是MyAnnotation的name属性。
* 看着像1个方法,但实际上我们称之为属性name。
* @return
*/
String name();

/*
颜色属性
*/
String color();

/*
年龄属性
*/
int age() default 25; //属性指定默认值

}

如果一个注解的属性的名字是value,并且只有一个属性的话,在使用的时候,该属性名可以省略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyAnnotationTest {

// 报错原因:没有指定属性的值。
/*@MyAnnotation
public void doSome(){

}*/

@MyAnnotation(value = "hehe")
public void doSome(){

}

@MyAnnotation("haha")
public void doOther(){

}
}
1
2
3
4
5
6
7
8
9
public @interface MyAnnotation {

/*
指定一个value属性。
*/
String value();

//String email();
}

注解当中的属性可以是哪一种类型?
属性的类型可以是:
byte short int long float double boolean char String Class 枚举类型
以及以上每一种的数组形式。

元注解

用来标注“注解类型”的“注解”,称为元注解。

常见的元注解 Target
Retention

关于Target注解:

这是一个元注解,用来标注“注解类型”的“注解”
这个Target注解用来标注“被标注的注解”可以出现在哪些位置上。

@Target(ElementType.METHOD):表示“被标注的注解”只能出现在方法上。
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
表示该注解可以出现在:
构造方法上
字段上
局部变量上
方法上
….
类上…

关于Retention注解:

这是一个元注解,用来标注“注解类型”的“注解”
这个Retention注解用来标注“被标注的注解”最终保存在哪里。

@Retention(RetentionPolicy.SOURCE):表示该注解只被保留在java源文件中。
@Retention(RetentionPolicy.CLASS):表示该注解被保存在class文件中。
@Retention(RetentionPolicy.RUNTIME):表示该注解被保存在class文件中,并且可以被反射机制所读取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.bjpowernode.java.annotation5;

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 "北京大兴区";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ReflectAnnotationTest {
public static void main(String[] args) throws Exception{
// 获取这个类
Class c = Class.forName("com.bjpowernode.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.bjpowernode.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
}
}

通过反射机制获取注解对象属性的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.lang.reflect.Method;

public class MyAnnotationTest {

@MyAnnotation(username = "admin", password = "456456")
public void doSome(){

}

public static void main(String[] args) throws Exception{
// 获取MyAnnotationTest的doSome()方法上面的注解信息。
Class c = Class.forName("com.bjpowernode.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());
}
}

}

注解在开发中的作用

需求:
假设有这样一个注解,叫做:@Id
这个注解只能出现在类上面,当这个类上有这个注解的时候,
要求这个类中必须有一个int类型的id属性。如果没有这个属性就报异常。如果有这个属性则正常执行!

1
2
3
4
5
6
7
8
public class HasNotIdPropertyException extends RuntimeException {
public HasNotIdPropertyException(){

}
public HasNotIdPropertyException(String s){
super(s);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 {

}

// 这个注解@Id用来标注类,被标注的类中必须有一个int类型的id属性,没有就报异常。
1
2
3
4
5
6
@MustHasIdPropertyAnnotation
public class User {
int id;
String name;
String password;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.lang.reflect.Field;

public class Test {
public static void main(String[] args) throws Exception{
// 获取类
Class userClass = Class.forName("com.bjpowernode.java.annotation7.User");
// 判断类上是否存在Id注解
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())){
// 表示这个类是合法的类。有@Id注解,则这个类中必须有int类型的id
isOk = true; // 表示合法
break;
}
}

// 判断是否合法
if(!isOk){
throw new HasNotIdPropertyException("被@MustHasIdPropertyAnnotation注解标注的类中必须要有一个int类型的id属性!");
}

}
}
}