Java基础10—注解

1. 内置注解

  1. @Override

    @Override修饰一个方法,表示该方法不是当前类首先声明的,而是在某个父类或实现的接口中声明的,当前类“重写”了该方法

  2. @Deprecated

    @Deprecated可以修饰的范围很广,包括类、方法、字段、参数等,它表示对应的代码已经过时了,程序员不应该使用它,不过,它是一种警告,而不是强制性的。

  3. @SuppressWarnings

    @SuppressWarnings表示压制Java的编译警告,它有一个必填参数,表示压制哪种类型的警告

1.1 注解的本质

没错,注解的本质就是一个继承了 Annotation 接口的接口。有关这一点,你可以去反编译任意一个注解类,你会得到结果的。

一个注解准确意义上来说,只不过是一种特殊的注释而已,如果没有解析它的代码,它可能连注释都不如。

1.2 元注解

元注解是用于修饰注解的注解,通常用在注解的定义上,JAVA 中有以下几个元注解:

Target

Target注解用来说明那些被它所注解的注解类可修饰的对象范围:注解可以用于修饰 packages、types(类、接口、枚举、注解类)、类成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数),在定义注解类时使用了@Target 能够更加清晰的知道它能够被用来修饰哪些对象,它的取值范围定义在ElementType 枚举中。

public enum ElementType {
 
    TYPE, // 类、接口、枚举类
 
    FIELD, // 成员变量(包括:枚举常量)
 
    METHOD, // 成员方法
 
    PARAMETER, // 方法参数
 
    CONSTRUCTOR, // 构造方法
 
    LOCAL_VARIABLE, // 局部变量
 
    ANNOTATION_TYPE, // 注解类
 
    PACKAGE, // 可用于修饰:包
 
    TYPE_PARAMETER, // 类型参数,JDK 1.8 新增
 
    TYPE_USE // 使用类型的任何地方,JDK 1.8 新增
 
}

Retention注解

Reteniton注解用来限定那些被它所注解的注解类在注解到其他类上以后,可被保留到何时,一共有三种策略,定义在RetentionPolicy枚举中。

public enum RetentionPolicy {
    SOURCE,    // 源文件保留
    CLASS,       // 编译期保留,默认值
    RUNTIME   // 运行期保留,可通过反射去获取注解信息
}

Documented

Documented注解的作用是:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。

Inherited

Inherited注解的作用是:使被它修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解)。

1.3 注解的工作原理

首先,我们通过键值对的形式可以为注解属性赋值,像这样:@Hello(value = “hello”)。

接着,你用注解修饰某个元素,编译器将在编译期扫描每个类或者方法上的注解,会做一个基本的检查,你的这个注解是否允许作用在当前位置,最后会将注解信息写入元素的属性表。

然后,当你进行反射的时候,虚拟机将所有生命周期在 RUNTIME 的注解取出来放到一个 map 中,并创建一个 AnnotationInvocationHandler 实例,把这个 map 传递给它。

最后,虚拟机将采用 JDK 动态代理机制生成一个目标注解的代理类,并初始化好处理器。

那么这样,一个注解的实例就创建出来了,它本质上就是一个代理类,你应当去理解好 AnnotationInvocationHandler 中 invoke 方法的实现逻辑,这是核心。一句话概括就是,通过方法名返回注解属性值


2. 创建注解

@Target(ElementType.METHOD)			// 注解的目标
@Retention(RetentionPolicy.SOURCE)	// 表示注解信息保留到什么时候(源代码,字节码,运行时)
public @interface xxx{

}

3. 查看注解信息

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
static @interface QueryParam{
    String value();
}

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
static @interface DefaultValue{
    String value() default "";
}

public void hello(@QueryParam("action") String action, @QueryParam("sort") @DefaultValue("asc") String sort){
    
}

public static void main(String[] args) throws NoSuchMethodException {
    Class<?> cls = MethodAnnotations.class;
    Method clsMethod = cls.getMethod("hello", new Class[]{String.class, String.class});

    Annotation[][] annts = clsMethod.getParameterAnnotations();
    for(int i = 0; i < annts.length; ++i){
        System.out.println(i+1);
        Annotation[] anntArr = annts[i];
        
        for(Annotation annt: anntArr){
            if(annt instanceof QueryParam){
                QueryParam qp = (QueryParam) annt;
                System.out.println(qp.annotationType().getSimpleName()+":"+ qp.value());
            }else if(annt instanceof DefaultValue){
                DefaultValue dv = (DefaultValue)annt;
                System.out.println(dv.annotationType().getSimpleName()+":"+ dv.value());
            }
        }
    }
}

4. 注解的使用:定制序列化

// test
public class test{
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    @interface Format{
        String pattern() default "yyyy-MM-dd HH:mm:ss";
        String timezone() default "GMT+8";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    @interface Label{
        String value() default "";
    }

    @AllArgsConstructor
    @NoArgsConstructor
    @ToString
    @Getter
    @Setter
    static class Student{
        @Label("姓名")
        String name;

        @Label("出生日期")
        @Format(pattern = "yyyy/MM/dd")
        Date born;
    }

    public static void main(String[] args) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Student zhangsan = new Student("张三", sdf.parse("1990-12-12"));

        System.out.println(SimpleFormatter.format(zhangsan));
    }
}

// simpleFormatter
public class SimpleFormatter {
    private static Object formatDate(Field f, Object value) {
        SampleAnnotation.Format format = f.getAnnotation(SampleAnnotation.Format.class);
        if (format != null) {
            SimpleDateFormat sdf = new SimpleDateFormat(format.pattern());
            sdf.setTimeZone(TimeZone.getTimeZone(format.timezone()));
            return sdf.format(value);
        }
        return value;
    }

    public static String format(Object obj) {
        try {
            Class<?> cls = obj.getClass();
            StringBuilder sb = new StringBuilder();
            for (Field f : cls.getDeclaredFields()) {
                if (!f.isAccessible()) {
                    f.setAccessible(true);
                }
                SampleAnnotation.Label label = f.getAnnotation(SampleAnnotation.Label.class);
                String name = label != null ? label.value() : f.getName();
                Object value = f.get(obj);
                if (value != null && f.getType() == Date.class) {
                    value = formatDate(f, value);
                }
                sb.append(name + ":" + value + "\n");
            }
            return sb.toString();
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
    
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值