Java基础知识_注解

注解最好要有Servlet基础,因为单纯的Java开发用不上注解,但是如果开发过Servlet,就对@WebServlet不会陌生

现在开发都推崇使用注解来开发,这样就可以免去写XML配置了,是一项十分方便的技术

学习注解可以更好的理解注解是怎么工作的,看见注解了就可以想到它的运行原理了

一、什么是注解

注解:Annotation

注解其实就是代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相对应的处理。

 

二、为什么我们需要用到注解?

传统的方式,我们是通过配置文件XML来告诉类是如何运行的。

有了注解技术以后,我们就可以通过注解告诉类如何运行。

@WebServlet(name = "BuyServlet",urlPatterns = "/BuyServlet")

明显的可以看出,这样是非常直观的,并且Servlet规范是推崇这种配置方式的

 

三、基本Annotation

在java.lang包下存在着5个基本的Annotation,其中有3个Annotation我们是非常常见的了。

3.1@Override

重写注解

如果我们使用IDE重写父类的方法,我们就可以它了,那它有什么用呢??

@Override是告诉编译器要检查该方法是实现父类的,可以帮我们避免一些低级的错误

比如,我们在实现equals()方法的时候,把equals()打错了,那么编译器就会发现该方法并不是实现父类的,与@Override冲突,于是就会给予错误。

3.2 @Deprecated

过时注解

该注解也非常常见,Java在设计的时候,可能觉得某些方法设计的不好,为了兼容以前的程序,是不能直接把它抛弃的,于是就设置它为过时的。

Date对象中的toLocalString()就被设置成过时了

1 @Deprecated
2 public String toLocalString(){
3     DateFormat formatter = DateFormat.getDateTimeInstance();
4     return formatter.format(this);
5 }

当我们在程序中调用它的时候,在IDE上会出现一条横杠,说明该方法是过时的

 

3.3 @SuppressWarnings

抑制编译器警告注解

该注解在我们写程序的时候并不是很常见,我们可以用它来让编译器不给予我们警告

当我们在使用集合的时候,如果没有指定泛型,那么会提示安全检查的警告

1 public static void main(String [] args){
2     ArrayList arrayList = new ArrayList();
3     System.out.println(arrayList);
4 }

如果我们在类上添加了@SuppressWarning这个注解,那么编译器就不会给我们警告了

 

3.4 @SafeVarargs

Java7 堆污染警告

什么是堆污染呢??当把一个不是泛型的集合赋值给一个带泛型集合的时候,这种情况就很容易发生堆污染

这个注解也是用来抑制编译器的注解,用的地方并不多。

 

3.5 @FunctionalInterface

@FuctionalInteface 用来指定该接口是函数式接口

 

四、自定义注解基础

上面讲解的是java.lang包下的5个注解,我们是可以自己来写注解,给方法或类注入信息

4.1 标记Annotation

没有任何成员变量的注解称为标记注解,Override就是一个标记注解

1 //有点像定义一个接口一样,只不过它多了一个@
2 public @interface MyAnnotation{
3 
4 
5 }

 

 

4.2 元数据Annotation

我们自定义的注解是可以带成员变量的,定义带称为变量的注解称为元数据Annotation

在注解中定义成员变量,语法类似于声明方法一样

1 public @interface MyAnnotation{
2 `//定义了两个成员变量
3     String username();
4     int age();      
5 }

注意:在注解上定义的成员变量只能是String、数组、Class、枚举类、注解

有的人可能会奇怪,为什么注解上还要定义注解成员变量??听起来就很复杂了

上边已经说了,注解的作用就是给类和方法注入信息。那么我们经常使用XML文件,告诉程序怎么运行。XML经常会有嵌套的情况。

<>
<作者>hhh</作者>
<价钱>100</价钱>
</>

那么,当我们在使用注解的时候,也可能需要有嵌套的时候,所以就允许了注解上可以定义成员变量为注解

 

4.3 使用自定义注解

上面我们已经定义了一个注解,下面我们来使用它吧

4.3.1 常规使用

下面我有一个add的方法,需要用username和age参数,我们通过注解来让该方法拥有这两个变量

1 //注解拥有什么属性,在修饰的时候就要给出相对应的值
2 @MyAnnotation(username = "chy" ,age = 20)
3 public void add(String username ,int age){
4 
5 }

4.3.2默认值

当然啦,我们可以在注解声明属性的时候,给出默认值。那么修饰的时候,就可以不用具体的指定了。

1 public @interface MyAnnotation{
2 //定义了两个成员变量
3 String username() default "hhh";
4 int age() default 23;
5 }

在修饰的时候就不需要给出具体的值了

@MyAnnotation()
public void add(String username,int age){

}

4.3.3 注解属性为value

还有一种特殊情况,如果注解上只有一个属性,并且属性的名称为value,那么在使用的使用的时候,我们可以不写value,直接赋值给它就行了

public @interface MyAnnotation2{

String value();

}

使用注解,可以不指定value,直接赋值

@MyAnnotation2("hhh")

public void find(String id){

}

 

4.4 把自定义注解的基本信息注入到方法上

上面我们已经使用到了注解,但是目前为止注解上的信息和方法上的信息是没有任何关联的。我们使用Servlet注解的时候,仅仅调用注解,那么注解的就生效了。这是Web容器把内部实现了。我们自己写的自定义注解是需要我们自己来处理的。

那么现在问题来了,我们怎么把注解上的信息注入到方法上呢???我们利用的是反射技术

步骤可以分为三步

  反射出该类的方法

  通过方法得到得到注解上的具体信息

  将注解上的信息注入到方法上。

  

 1 //反射出该类的方法
 2 Class aClass = Demo.class;
 3 Method method = aClass.getMethod("add",String.class,int.class);
 4 
 5 //通过该方法得到注解上的具体信息
 6 MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
 7 String username = annotation.username();
 8 int age = annotation.age();
 9 
10 //将注解上的信息注入到方法上
11 Object o = aClass.newInstance();
12 method.invoke(o,username,age);

当我们执行的时候,我们会发现异常

此时,我们需要在自定义注解上加入下面这句代码,下面会讲到,为什么要加入这句代码

1 @Retention(RetentionPolicy.RUNTIME)

再次执行的时候,我们就会发现,可以通过注解来把信息注入到方法中了。

 

五、JDK的元Annotation

前面我们已经介绍了java.lang包下的几个基本Annotation了,在JDK中除了java.lang包下有Annotation,在java.lang.annotation下也有几个常用的元Annotation

在annotation包下有好几个元Annotation都是用于修饰其他的Annotation定义

 

5.1 @Retention

上面在将注解信息注入到方法中的时候,我们最后加上了@Retention的注解,不然就会报错了,那它是干什么用的呢?

@Rentention只能用于修饰其他的Annotation,用于指定被修饰的Annotation被保留多长时间。

@Rentention包含了一个RetentionPolicy类型的value变量,所以在使用它的时候,必须要为value成员变量赋值

1 public enum RetentionPolicy{
2 SOURCE,CLASS,RUNTIME
3 }

java文件有三个时期,编译,class,允许。@Retention默认是class

前面我们是使用反射来得到注解上的信息的,因为@Retention默认是class

前面我们是使用反射来得到注解上的信息的,因为@Retention默认是class,而反射是在运行期间来获取信息的。因此就获取不到Annotation的信息了。于是,就得在自定义注解上修改它得RetentionPolicy

 

5.2 @Target

@Target 也是只能用于修饰另外得Annotation,它用于指定被修饰得Annotation用于修饰哪个程序单元

@Target是只有一个value成员变量得,该成员变量得值是以下得:

 1 public enum ElementType {
 2     /** Class, interface (including annotation type), or enum declaration */
 3     TYPE,
 4 
 5     /** Field declaration (includes enum constants) */
 6     FIELD,
 7 
 8     /** Method declaration */
 9     METHOD,
10 
11     /** Parameter declaration */
12     PARAMETER,
13 
14     /** Constructor declaration */
15     CONSTRUCTOR,
16 
17     /** Local variable declaration */
18     LOCAL_VARIABLE,
19 
20     /** Annotation type declaration */
21     ANNOTATION_TYPE,
22 
23     /** Package declaration */
24     PACKAGE
25 }

如果@Target指定得是ElementType.ANNOTATION_TYPE,那么该被修饰的Annotation只能修饰Annotation

 

5.3 @Documented

@Documented用于指定该被Annotation修饰的Annotation类将被javadoc工具提取成文档

该元Annotation用的挺少的

 

5.4 @Inherited

@Inherited也是用来修饰其他的Annotation的,被修饰过的Annotation将具有继承性。

例子

1、@xxx是我自定义的注解,我现在使用@xxx注解在Base类上使用

2、使用@Inherited修饰@xxx注解

3、当有类继承了Base类的时候,该实现类自动拥有@xxx注解

 

六、注入对象到方法或成员变量上

前面我们已经可以使用注解将基本的信息注入到方法上了,现在我们要使用的是将对象注入到方法上。

上面已经说过了,注解只能定义String,枚举类,Double之类的成员变量,那么怎么把对象注入到方法上呢

6.1.2模拟场景

Person类,定义username和age属性,拥有uername和age的getter和setter方法

 1 public class Person {
 2 
 3     private String username;
 4     private int age;
 5 
 6     public String getUsername() {
 7         return username;
 8     }
 9 
10     public void setUsername(String username) {
11         this.username = username;
12     }
13 
14     public int getAge() {
15         return age;
16     }
17 
18     public void setAge(int age) {
19         this.age = age;
20     }
21 }

PersonDao类,PersonDao类定义了Person对象,拥有person的setter和getter方法

 1 public class PersonDao {
 2 
 3     private Person person;
 4 
 5     public Person getPerson() {
 6         return person;
 7     }
 8 
 9     public void setPerson(Person person) {
10         this.person = person;
11     }
12 }

现在我要做的就是:使用注解将Person对象注入到setPerson()方法中,从而设置了PersonDao类的person属性

public class PersonDao {

    private Person person;

    public Person getPerson() {
        return person;
    }


    //将username为hhh,age为20的Person对象注入到setPerson方法中
    @InjectPerson(username = "hhh",age = 20)
    public void setPerson(Person person) {

        this.person = person;
    }
}

 

步骤:

1、自定义一个注解,属性是和JavaBean类一致的。

1 //注入工具是通过反射来得到注解的信息的,于是保留域必须使用RunTime
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface InjectPerson {
4 
5     String username();
6     int age();
7 }

2、编写注入工具

 

 1   //1.使用内省【后边需要得到属性的写方法】,得到想要注入的属性
 2         PropertyDescriptor descriptor = new PropertyDescriptor("person", PersonDao.class);
 3 
 4         //2.得到要想注入属性的具体对象
 5         Person person = (Person) descriptor.getPropertyType().newInstance();
 6 
 7         //3.得到该属性的写方法【setPerson()】
 8         Method method = descriptor.getWriteMethod();
 9 
10         //4.得到写方法的注解
11         Annotation annotation = method.getAnnotation(InjectPerson.class);
12 
13         //5.得到注解上的信息【注解的成员变量就是用方法来定义的】
14         Method[] methods = annotation.getClass().getMethods();
15 
16         //6.将注解上的信息填充到person对象上
17 
18         for (Method m : methods) {
19 
20             //得到注解上属性的名字【age或name】
21             String name = m.getName();
22 
23             //看看Person对象有没有与之对应的方法【setAge(),setName()】
24             try {
25 
26                 //6.1这里假设:有与之对应的写方法,得到写方法
27                 PropertyDescriptor descriptor1 = new PropertyDescriptor(name, Person.class);
28                 Method method1 = descriptor1.getWriteMethod();//setAge(), setName()
29 
30                 //得到注解中的值
31                 Object o = m.invoke(annotation, null);
32 
33                 //调用Person对象的setter方法,将注解上的值设置进去
34                 method1.invoke(person, o);
35 
36             } catch (Exception e) {
37 
38                 //6.2 Person对象没有与之对应的方法,会跳到catch来。我们要让它继续遍历注解就好了
39                 continue;
40             }
41         }
42 
43         //当程序遍历完之后,person对象已经填充完数据了
44 
45         //7.将person对象赋给PersonDao【通过写方法】
46         PersonDao personDao = new PersonDao();
47         method.invoke(personDao, person);
48 
49         System.out.println(personDao.getPerson().getUsername());
50         System.out.println(personDao.getPerson().getAge());

总结一下步骤

其实我们是这样把对象注入到方法中的

1、得到想要类中注入的属性

2、得到该属性的对象

3、得到属性对应的写方法

4、通过写方法得到注解

5、获取注解的详细信息

6、将注解的信息注入到对象上

7、调用属性写方法。将已填充数据的对象注入到方法中。

 

6.2 把对象注入到成员变量

上面已经说了如何将对象注入到方法上了,那么注入到成员变量上也是非常简单的

步骤

1、在成员变量上使用注解

 1 public class PersonDao {
 2 
 3     @InjectPerson(username = "zhongfucheng",age = 20) private Person person;
 4 
 5     public Person getPerson() {
 6         return person;
 7     }
 8 
 9     public void setPerson(Person person) {
10 
11         this.person = person;
12     }
13 }

2、编写注入工具

 1 //1.得到想要注入的属性
 2         Field field = PersonDao.class.getDeclaredField("person");
 3 
 4         //2.得到属性的具体对象
 5         Person person = (Person) field.getType().newInstance();
 6 
 7         //3.得到属性上的注解
 8         Annotation annotation = field.getAnnotation(InjectPerson.class);
 9 
10         //4.得到注解的属性【注解上的属性使用方法来表示的】
11         Method[] methods = annotation.getClass().getMethods();
12 
13         //5.将注入的属性填充到person对象上
14         for (Method method : methods) {
15 
16             //5.1得到注解属性的名字
17             String name = method.getName();
18 
19             //查看一下Person对象上有没有与之对应的写方法
20             try {
21 
22                 //如果有
23                 PropertyDescriptor descriptor = new PropertyDescriptor(name, Person.class);
24 
25                 //得到Person对象上的写方法
26                 Method method1 = descriptor.getWriteMethod();
27 
28                 //得到注解上的值
29                 Object o = method.invoke(annotation, null);
30 
31                 //填充person对象
32                 method1.invoke(person, o);
33             } catch (IntrospectionException e) {
34 
35                 //如果没有想对应的属性,继续循环
36                 continue;
37             }
38         }
39 
40         //循环完之后,person就已经填充好数据了
41 
42 
43         //6.把person对象设置到PersonDao中
44         PersonDao personDao = new PersonDao();
45         field.setAccessible(true);
46         field.set(personDao, person);
47 
48         System.out.println(personDao.getPerson().getUsername());

七、总结

1、注入对象的步骤:得到想要注入的对象属性,通过属性得到注解的 信息,通过属性的写方法将注解的信息注入到对象上最后将对象夫给类

2、注解其实就两个作用:让编译器检查代码、将数据注入到方法、成员变量、类上

3、在JDK中注解分为了

  • 基本Annotation

    • 在lang包下,用于常用于标记该方法,抑制编译器警告等

  • 元Annotaion

    • 在annotaion包下,常用于修饰其他的Annotation定义

 

posted @ 2019-09-04 21:55  chyblogs  阅读(204)  评论(0)    收藏  举报