如何查看注解实现_这就是java中的注解(下)

基本注解

在介绍完了元注解后,接下来了解一下Java中默认提供的5个基本注解,它们存在于java.lang包下:@Deprecated、@FunctionalInterface、@Override、@SafeVarargs和@SuppressWarnings。

820ccd64fed9796415e5fb884366995f.png

请注意不要和java.lang.annotation包下的6个元注解搞混淆了:

5d80ec0d46e7b9df966703d61760d368.png

@Deprecated注解

首先查看一下@Deprecated注解的源码信息,如下所示:

@Documented@Retention(RetentionPolicy.RUNTIME)@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})public @interface Deprecated {}

可以看到@Deprecated注解自jdk1.5引入,它用来标记过时的元素,这个注解在日常开发中会经常碰到。如果编译器在编译阶段遇到这个注解时会自动发出提醒警告,用于告诉开发人员正在调用一个过时的元素,如过时的方法、过时的类、过时的成员变量等(可以参看它的Target注解)。

举个例子,新建一个Envy类,里面的代码为:

public class Envy {    @Deprecated    public void hello(){        System.out.println("hello");    }    public void world(){        System.out.println("world");    }        public static void main(String[] args){        new Envy().hello();        new Envy().world();    }}

可以看到目前在该hello方法山添加了@Deprecated注解,然后在main方法内新建一个Envy对象去调用这个hello方法,可以发现编译器此时自动给该方法中间添加了横线:

df2b41128bf1c54116bec28f806ceecf.png

注意这个效果是编译器添加的,并不是程序具有的效果。注意过时方法不是不能使用,而是不建议使用。

@FunctionalInterface注解

首先查看一下@FunctionalInterface注解的源码信息,如下所示:

@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface FunctionalInterface {}

可以看到@FunctionalInterface注解自jdk1.8引入,它是一个函数式接口注解,这是1.8的一个新特性。所谓的函数式接口 (Functional Interface) 其实就是一个只具有一个方法的普通接口。看到这里,笔者立马就想到了多进程中的Runnable接口,它就是一个只含有run方法的函数式接口:

@FunctionalInterfacepublic interface Runnable {    public abstract void run();}

此时你可能有一个疑惑,函数式接口有什么用,等下一篇介绍Lambda表达式的时候就会深深体会到它的用处。

@Override注解

首先查看一下@Override注解的源码信息,如下所示:

@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}

可以看到@Override注解自jdk1.5引入,它是一个重写注解,这是使用最多的注解。当某个方法添加@Override注解后,编译器会去检查该方法是实现父类的对应方法,可以避免一些低级错误,如单词拼写错误等。举个例子,这里有一个Movie类:

public class Movie {    public void watch(){        System.out.println("watch");    }}

然后有一个ActionMovie类,该类继承了Movie类,并实现了其中的watch方法,但是由于粗心将watch写成了wath,此时编译器就会抛出提示信息:

1c637d76fed39edb42078ee0d4875fc4.png

根据这个信息就能知道出错原因是方法单词拼错了。

@SafeVarargs注解

首先查看一下@SafeVarargs注解的源码信息,如下所示:

@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})public @interface SafeVarargs {}

可以看到@SafeVarargs注解自jdk1.7引入,它是一个参数安全类型注解,大家也称之为Java 7“堆污染”警告,所谓的堆污染其实就是把一个非泛型集合赋值给一个泛型集合的时候,这时就很容易发生堆污染。如果使用@SafeVarargs注解后,它会提醒开发者不要用参数做一些不安全的操作,它的存在会阻止编译器产生unchecked这样的警告。这个用的不是很多。

@SuppressWarnings注解

首先查看一下@SuppressWarnings注解的源码信息,如下所示:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})@Retention(RetentionPolicy.SOURCE)public @interface SuppressWarnings {    String[] value();}

可以看到@SuppressWarnings注解自jdk1.5引入,它是一个抑制编译器警告注解。前面说过调用被@Deprecated注解修饰的方法后,编译器会发出提醒,如果开发者想忽略这种警告,那么可以在调用时添加@SuppressWarnings注解来实现这个目的:

public class Envy {    @Deprecated    public void hello(){        System.out.println("hello");    }    public void world(){        System.out.println("world");    }    @SuppressWarnings("deprecation")    public static void main(String[] args){        new Envy().hello();        new Envy().world();    }}

这样编辑器就不会去检测hello方法是否过期。同样通过一张图片来总结一下这5个基本注解:

eb1dc09a53669c2fc585c272c48ecf30.png

自定义注解的使用

将自定义信息注入到方法中

在前面介绍了如何在运行时获取注解中的信息,接下来介绍如何将自定义信息注入到方法中。这里就不再自定义获取注解中信息的方法了,而是直接使用内置的方法。自定义一个@EnvyThink注解,里面的代码为:

@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})public @interface EnvyThink {    String name() default "envy";    int age() default 22;}

接着新建一个Envy类,其中的代码为:

public class Envy {    @EnvyThink()    public void hey(String name,int age){        System.out.println("name--->"+name+""+ "age--->"+age);    }}

然后新建一个测试类EnvyTest,里面的代码为:

public class EnvyTest {    public static void main(String[] args){        try {            /**第一步,通过反射得到该类被注解修饰的方法*/            Class> aClass = Envy.class;            Method method = aClass.getDeclaredMethod("hey",String.class,int.class);            /**第二步,通过该方法来得到注解的信息*/            EnvyThink envyThink = method.getAnnotation(EnvyThink.class);            String name = envyThink.name();            int age = envyThink.age();            /**第三步,将注解的信息注入到方法内*/            Object object = aClass.newInstance();            method.invoke(object,name,age);        } catch (Exception e) {            e.printStackTrace();        }    }}

然后运行该方法,可以知道该方法的结果为:

name--->envyage--->22

将自定义对象注入到方法中

可以看到前面注入的是注解内自定义的成员属性,接下来尝试将自定义对象注入到方法中。首先解释一下什么是将自定义对象注入到方法中?举一个例子,首先定义Book实体类,里面的代码为:

public class Book {    private String name;    private double price;    public Book(){};    public Book(String name,double price){        this.name=name;        this.price=price;    }    //getter/setter/toString方法}

其次再定义一个Author类,里面的代码为:

public class Author {    private Book book;    public Book getBook(){        return book;    }    public void setBook(Book book){        this.book = book;    }}

大家肯定知道这个setBook方法内的参数book对象其实是外界传进来的,所以如果需要调用这个方法则必须使用类似如下操作:

public class AuthorTest {    public static void main(String[] args){        new Author().setBook(new Book("三国演义",128));    }}

现在想通过注解的方式来将该Book对象注入到Author中,也就是如下方式:

@BookAnno(name = "三国演义", price = 128)public void setBook(Book book){    this.book = book;}

应该如何实现呢?往下看,首先定义一个@BookAnno注解,里面定义两个属性name和price(注意属性数量和名称必须与Book完全保持一致),里面的代码为:

@Retention(RetentionPolicy.RUNTIME)public @interface BookAnno {    String name() default "三国演义";    double price() default 128;}

注意该注解必须添加@Retention(RetentionPolicy.RUNTIME)注解语句,否则无法通过反射实现相应的功能。

接着定义一个AuthorAnnoTest类,用于实现将Book对象注入到Author的setBook方法中。在该类中,需要通过内省(Introspector)来对JavaBean类属性、事件进行缺省处理。关于内省的相关介绍,后续会专门出一篇文章进行说明。AuthorAnnoTest类中的代码为:

public class AuthorAnnoTest {  public static void main(String[] args) {    try {      /** 1.使用内省机制来获取想要注入的属性 */      /** 第一个参数为待注入属性,第二个参数为该属性所依附的类 */      PropertyDescriptor descriptor = new PropertyDescriptor("book", Author.class);      /** 2.获取该属性的setPerson方法(写方法)*/      Method method = descriptor.getWriteMethod();      /** 3.获取写方法的注解*/      Annotation annotation = method.getAnnotation(BookAnno.class);      /** 4.获取注解上的信息(注意必须是方法,因为注解内成员变量的定义就是采用方法形式)*/      Method[] methods = annotation.getClass().getMethods();      /** 5.获取待注入属性的实例对象*/      Book book = (Book) descriptor.getPropertyType().newInstance();      /** 6.将注解上的信息添加到book对象上*/      for (Method meth : methods) {        /** 7.获取注解内相关属性的名称(注意这里的name不仅仅只是name属性,而是一个属性别称) */        String name = meth.getName();        /** 8.判断Book对象中是否存在各属性对应的setter方法*/        try {          /** 8.1.1 假设存在相对应的setter方法,则采用内省机制来获取对应的setter方法*/          PropertyDescriptor descriptor1 = new PropertyDescriptor(name, Book.class);          /** 8.1.2 获取各属性相对应的setter写方法 */          Method method1 = descriptor1.getWriteMethod();          /** 8.1.3 获取注解中的值*/          Object o = meth.invoke(annotation, null);          /** 8.1.4 调用Book对象的setter写方法,将前面获取的注解的值设置进去*/          method1.invoke(book, o);        } catch (Exception e) {          /** 8.2 如果Book对象中不存在各属性对应的setter方法,那么会跳过当前循环,继续遍历注解*/          continue;        }      }      /** 9、遍历完成,book对象中的各个setter方法已经成功写入了注解中的值*/      /** 9.1 通过setter方法将book对象写入到author对象中*/      Author author = new Author();      method.invoke(author, book);      /** 10.输出author对象中的name和price的信息 */      System.out.println(author.getBook().getName());      System.out.println(author.getBook().getPrice());    } catch (Exception e) {      e.printStackTrace();    }  }}

运行该方法,可以得到输出结果为:

三国演义128.0

接下来总结一下该步骤:1、使用内省机制来获取想要注入的属性;2、获取该属性的写方法;3、获取写方法的注解;4、获取注解上的信息(注意必须是方法,因为注解内成员变量的定义就是采用方法形式);5、获取待注入属性的实例对象;6、将注解上的信息添加到对象上;7、调用属性的写方法将已添加数据的对象注入到方法中;8、验证对象是否成功注入方法中。

Spring内置的@Autowried注解就是完成了类似的功能才使得用户可以直接在方法中就能使用对象。

将自定义对象注入到属性中

将自定义对象注入到属性中,这也是一个常见的使用场景,通过前面的介绍,开发者可以尝试照葫芦画瓢来实现这一功能。也就是当开发者使用如下方式:

public class Author {    @BookAnno(name = "三国演义", price = 128)    private Book book;    public Book getBook(){        return book;    }    public void setBook(Book book){        this.book = book;    }}

的代码配置时,我们依然可以像前面那样在author对象中获取注解的值。接着定义一个AuthorAnnoFiledTest类,用于实现将Book对象注入到Author的book属性中。需要注意的是首先需要使用反射机制来获取对应的属性,其次使用内省机制来获取对应的写方法。AuthorAnnoFiledTest类中的代码为:

public class AuthorAnnoFiledTest {    public static void main(String[] args){        try {            /** 1.使用反射机制来获取想要注入的属性 */            Field field = Author.class.getDeclaredField("book");            /** 2.获取属性的注解*/            Annotation annotation = field.getAnnotation(BookAnno.class);            /** 3.获取注解上的信息(注意必须是方法,因为注解内成员变量的定义就是采用方法形式)*/            Method[] methods = annotation.getClass().getMethods();            /** 4.获取待注入属性的实例对象*/            Book book = (Book) field.getType().newInstance();            /** 5.将注解上的信息添加到book对象上*/            for (Method meth : methods) {                /** 6.获取注解内相关属性的名称(注意这里的name不仅仅只是name属性,而是一个属性别称) */                String name = meth.getName();                /** 7.判断Book对象中是否存在各属性对应的setter方法*/                try {                    /** 7.1.1 假设存在相对应的setter方法,则采用内省机制来获取对应的setter方法*/                    PropertyDescriptor descriptor = new PropertyDescriptor(name, Book.class);                    /** 7.1.2 获取各属性相对应的setter写方法 */                    Method method1 = descriptor.getWriteMethod();                    /** 7.1.3 获取注解中的值*/                    Object o = meth.invoke(annotation, null);                    /** 7.1.4 调用Book对象的setter写方法,将前面获取的注解的值设置进去*/                    method1.invoke(book, o);                } catch (Exception e) {                    /** 7.2 如果Book对象中不存在各属性对应的setter方法,那么会跳过当前循环,继续遍历注解*/                    continue;                }            }            /** 8、遍历完成,book对象中的各个setter方法已经成功写入了注解中的值*/            /** 8.1 通过setter方法将book对象写入到author对象中*/            Author author = new Author();            field.setAccessible(true);            field.set(author,book);            /** 10.输出author对象中的name和price的信息 */            System.out.println(author.getBook().getName());            System.out.println(author.getBook().getPrice());        } catch (Exception e) {            e.printStackTrace();        }    }}

运行该方法,可以得到输出结果为:

三国演义128.0

接下来总结一下该步骤:1、使用反射机制来获取想要注入的属性;2、获取该属性的注解;3、获取注解上的信息(注意必须是方法,因为注解内成员变量的定义就是采用方法形式);4、获取待注入属性的实例对象;5、将注解上的信息添加到对象上;6、调用属性的写方法将已添加数据的对象注入到方法中;7、验证对象是否成功注入方法中。

注解总结

接下来对注解进行总结,注解其实就两个作用:(1)在适当的时候将数据注入到方法、成员变量、类或者其他组件中;(2)让编译器检查代码,进而发出提示信息。关于注解就先介绍到这里,后续会出一篇文章来总结Spring框架中常用的注解。

1f59faaa6007842f9293bd0630d74cb5.png
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java,可以通过自定义注解来进行数据验证。下面是一个简单的例子: 1. 定义注解 ```java @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default ""; int minLength() default 0; int maxLength() default Integer.MAX_VALUE; String regex() default ""; } ``` 这个注解可以用在的字段上,可以指定字段的值、最小长度、最大长度和正则表达式。 2. 使用注解 ```java public class User { @MyAnnotation(minLength = 3, maxLength = 10, regex = "[a-zA-Z0-9_]+") private String username; // getter and setter } ``` 在这个例子,我们给User的username字段加上了MyAnnotation注解,并指定了最小长度为3,最大长度为10,只能包含字母、数字和下划线。 3. 验证数据 ```java public class Validator { public static boolean validate(Object obj) throws IllegalAccessException { Class<?> clazz = obj.getClass(); for (Field field : clazz.getDeclaredFields()) { MyAnnotation annotation = field.getAnnotation(MyAnnotation.class); if (annotation != null) { field.setAccessible(true); String value = (String) field.get(obj); if (value == null || value.length() < annotation.minLength() || value.length() > annotation.maxLength() || !value.matches(annotation.regex())) { return false; } } } return true; } } ``` 这个Validator可以用来验证任意对象的字段是否符合注解的要求。它通过反射获取对象的所有字段,并检查是否有MyAnnotation注解,然后根据注解的要求验证字段的值。 使用方法: ```java public static void main(String[] args) throws IllegalAccessException { User user = new User(); user.setUsername("abc_123"); boolean isValid = Validator.validate(user); System.out.println(isValid); // true } ``` 在这个例子,我们创建了一个User对象,并将username设置为"abc_123",然后使用Validator来验证这个对象的所有字段是否符合注解的要求。由于username符合要求,所以验证结果为true。 这样,我们就可以通过自定义注解来进行数据验证了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值