注解(也被称为原数据)为我们在代码中添加信息提供了一种形式化方法,使我们在稍后的某个时刻非常方便地使用这些数据。
Java模前内置了三种标准注解与四种元注解。其中三种标注注解为:
标准注解 | 解释 |
---|---|
@Override | 表示当前类将覆盖超类中的方法。如果拼写错误或者方法签名对应出错,编译器会发出提示。 |
@Deprecated | 如果程序员使用了注解为它的元素,编译器会发出警告信息。 |
@SupressWarnings | 关闭不当的编译警告信息。 |
四种元注解如下:
- @Target:注解应用的位置
关键字 | 位置 |
---|---|
CONSTRUCTORE | 构造器声明 |
FIELD | 域声明 |
LOCAL_VARIABLE | 局部变量 |
METHOD | 方法声明 |
PACKAGE | 包声明 |
PARAMETER | 参数声明 |
TYPE | 类、接口或enum声明 |
- @Retention:注解的保留级别
关键字 | 含义 |
---|---|
SOURCE | 注解将被编译器丢弃 |
CLASS | 注解在class文件中可用,但会被JVM丢弃 |
RUNTIME | VM在运行时也会保留注解,可以通过反射机制来获取注解信息 |
-
@Documented:此注解将包含在Javadoc中。
-
@Inherited:允许子类继承父类的注解。
20.1 基本语法
- 定义注解类
@Target(ElementType.Method)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase{
public int id;
public String description() default "no description";
}
- 使用注解
//: annotations/PasswordUtils.java
import java.util.*;
public class PasswordUtils {
@UseCase(id = 47, description =
"Passwords must contain at least one numeric")
public boolean validatePassword(String password) {
return (password.matches("\\w*\\d\\w*"));
}
@UseCase(id = 48)
public String encryptPassword(String password) {
return new StringBuilder(password).reverse().toString();
}
@UseCase(id = 49, description =
"New passwords can't equal previously used ones")
public boolean checkForNewPassword(
List<String> prevPasswords, String password) {
return !prevPasswords.contains(password);
}
} ///:~
20.2 编写注解处理器
对于编写的注解,可以利用Java反射机制来编写注解处理器。主要用到两个反射方法:getDeclaredMethods与getAnnotations()。他们都属于AnnotatedElement接口。
import java.lang.reflect.*;
import java.util.*;
public class UseCaseTracker {
public static void
trackUseCases(List<Integer> useCases, Class<?> cl) {
for(Method m : cl.getDeclaredMethods()) {
UseCase uc = m.getAnnotation(UseCase.class);
if(uc != null) {
System.out.println("Found Use Case:" + uc.id() +
" " + uc.description());
useCases.remove(new Integer(uc.id()));
}
}
for(int i : useCases) {
System.out.println("Warning: Missing use case-" + i);
}
}
public static void main(String[] args) {
List<Integer> useCases = new ArrayList<Integer>();
Collections.addAll(useCases, 47, 48, 49, 50);
trackUseCases(useCases, PasswordUtils.class);
}
} /* Output:
Found Use Case:47 Passwords must contain at least one numeric
Found Use Case:48 no description
Found Use Case:49 New passwords can't equal previously used ones
Warning: Missing use case-50
*///:~
20.2.1 注解元素
可以使用的注解元素主要包括如下类型:
- 基本类型
- String
- Class
- enum
- Annotation
- 以上类型的数组
20.2.2 默认值的限制
元素必须有默认值或者在注解时提供确切的值,无法使用null作为默认值,可以用default关键字设置默认值。
20.2.3 生成外部文件
使用注解可以将所有信息保存在JavaBean源文件中,从而解决与XML文件的同步问题。
例:使用注解定义一个简单的Java Bean
import java.lang.annotation.*;
@Target(ElementType.TYPE) // Applies to classes only
@Retention(RetentionPolicy.RUNTIME)
public @interface DBTable {
public String name() default "";
} ///:~
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Constraints {
boolean primaryKey() default false;
boolean allowNull() default true;
boolean unique() default false;
} ///:~
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLInteger {
String name() default "";
Constraints constraints() default @Constraints;
} ///:~
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SQLString {
int value() default 0;
String name() default "";
Constraints constraints() default @Constraints;
} ///:~
@DBTable(name = "MEMBER")
public class Member {
@SQLString(30) String firstName;
@SQLString(50) String lastName;
@SQLInteger Integer age;
@SQLString(value = 30,
constraints = @Constraints(primaryKey = true))
String handle;
static int memberCount;
public String getHandle() { return handle; }
public String getFirstName() { return firstName; }
public String getLastName() { return lastName; }
public String toString() { return handle; }
public Integer getAge() { return age; }
} ///:~
当某元素是唯一需要赋值的,则无需使用键值对的方式进行一一赋值。
20.5 基于注解的单元测试
可以使用**@Unit注解测试框架进行测试。其中最基本的测试是使用@Test**来标记测试方法。
package annotations;
import net.mindview.atunit.*;
import net.mindview.util.*;
public class AtUnitExample1 {
public String methodOne() {
return "This is methodOne";
}
public int methodTwo() {
System.out.println("This is methodTwo");
return 2;
}
@Test boolean methodOneTest() {
return methodOne().equals("This is methodOne");
}
@Test boolean m2() { return methodTwo() == 2; }
@Test private boolean m3() { return true; }
// Shows output for failure:
@Test boolean failureTest() { return false; }
@Test boolean anotherDisappointment() { return false; }
public static void main(String[] args) throws Exception {
OSExecute.command(
"java net.mindview.atunit.AtUnit AtUnitExample1");
}
} /* Output:
annotations.AtUnitExample1
. methodOneTest
. m2 This is methodTwo
. m3
. failureTest (failed)
. anotherDisappointment (failed)
(5 tests)
>>> 2 FAILURES <<<
annotations.AtUnitExample1: failureTest
annotations.AtUnitExample1: anotherDisappointment
*///:~
20.5.4 移除测试代码
使用开源的Javassit工具类。