菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
23
0

Java 注解

原创
05/13 14:22
阅读数 91381

Java 注解

 

注解(Annotation)就像一个标签,用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。

JDK预置注解包括:
@Deprecated,即将废弃的标记;
@Override,表示当前的方法定义将覆盖超类中的方法;
@SuppressWarnings,阻止警告的意思
@SafeVarargs,参数安全类型注解
@FunctionalInterface,函数式接口注解
 
 

元注解

元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。
 
@Retention,注解的存活时间,取值如下:
  • RetentionPolicy.SOURCE,注解只在源码阶段保留,将被编译器丢弃;
  • RetentionPolicy.CLASS,注解保留到编译期间,将被VM丢弃;
  • RetentionPolicy.RUNTIME,注解保留到运行期间,因此可以通过反射机制读取注解的信息;
 
@Target,限定注解应用的场景,注解可以加在包、类,属性、方法,方法的参数以及局部变量上,取值如下:
  • ElementType.ANNOTATION_TYPE,可以给一个注解进行注解
  • ElementType.CONSTRUCTOR,可以给构造方法进行注解
  • ElementType.FIELD,可以给属性进行注解
  • ElementType.LOCAL_VARIABLE,可以给局部变量进行注解
  • ElementType.METHOD,可以给方法进行注解
  • ElementType.PACKAGE,可以给一个包进行注解
  • ElementType.PARAMETER,可以给一个方法内的参数进行注解
  • ElementType.TYPE,可以给一个类型进行注解,比如类、接口、枚举
 
@Repeatable,注解可以被多次应用
@Inherited,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
@Documented,将注解中的元素包含到Javadoc中;
 
 

注解定义

在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum、Annotation)。可以通过default来声明参数的默认值。
 
一个例子,注解定义和应用
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
    int id();
    String description() default "no description";
}


public class PasswordUtils { @UseCase(id = 47, description = "password must contain at lease 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); } }

 

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class UseCaseTracker {

    @Autowired
    private PasswordUtils passwordUtils;

    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);
        }
    }

    @Test
    public void test() {
        List<Integer> useCases = new ArrayList<>();
        Collections.addAll(useCases, 47, 48, 49, 50);
        trackUseCases(useCases, PasswordUtils.class);
    }
}

输出

Found Use Case: 48 no description
Found Use Case: 49 new passwords can't equal previously used ones
Found Use Case: 47 password must contain at lease one numeric
Warning: Missing use case-50

 

上面用到了两个反射方法:getDeclaredMethods() 和 getAnnotation(),它们都属于AnnotatedElement接口(Class, Mehod, Filed 等类都实现了该接口);
getAnnotation() 方法返回指定类型的注解对象,在这里就是UseCase。如果被注解的方法上没有该类型的注解,则返回null值。
然后我们通过调用id()和  description() 方法从返回的UseCase对象中提取元素的值。其中,encryptPassword()方法在注解的时候没有指定description的值,因此取到的是默认值。
 
关于注解元素的默认值:
  • 注解的元素可以有默认值,但不能有不确定的值,也就是说,元素要么具有默认值,要么在使用注解时提供元素的值。
  • 对于非基本类型的元素,无论是在源代码中声明时,或是在注解接口中定义默认值时,都不能以null作为其值,这个约束使得很难表现一个元素的存在或缺失状态,因为在每个注解的声明中,所有的元素都存在,并且都具有相应的值。为了绕开这个约束,只能自定义一些特殊的值来表示元素不存在,例如:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
    int id() default -1;
    String description() default "";
}

 

 

 

 

发表评论

0/200
23 点赞
0 评论
收藏
为你推荐 换一批