注解学习笔记

Java注解详解

注解学习笔记


一、注解认识

1、注解的概述

注释你还记得么?开个玩笑而已!

注释是给人看的,而注解是给程序看的!

注解是用来替代配置文件的!你回忆一下,我们以前总是要写一些配置文件,例如web.xml你还记得么?里面要写<servlet>和<servlet-mapping>!谁来读配置文件呢?当然是Tomcat!谁来写配置文件呢?当然是我们来写了!

Servlet3.0中就可以使用注解来代替配置文件,开发者就不用再写配置文件了,而是写注解,然后Tomcat来读取注解。

 

注解也是类,需要定义了才能使用!

举例:在Servlet3.0中有一个注解类为@WebServlet,然后我们就可以在Servlet中使用@WebServlet中使用这个注解了。这个注解就是用来替代<servlet>了。然后Tomcat会通过反射来读取注解中的信息!

说明:

从 JDK 5.0 开始, Java 增加了对元数据(MetaData) 的支持, 也就是 Annotation(注解)。

Annotation其实就是代码里的特殊标记, 它用于替代配置文件,也就是说,传统方式通过配置文件告诉类如何运行,有了注解技术后,开发人员可以通过注解告诉类如何运行。在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。

2、掌握注解技术的要点:

如何定义注解?

如何反射注解,并根据反射的注解信息,决定如何去运行类?

3、注解应用图示

注解就相当于一个你的源程序中要调用的一个类,要在源程序中应用某个注解,得先准备好了这个注解类。就像你要调用某个类,得先有开发好这个类。

二、注解分类

1、分类说明

按来源分JDK自带注解和自定义注解

2、JDK自带注解

Java中的基本注解:

@Override:作用在方法上的注解。当方法不是重写父类的方法时会报错;

@Deprecated:作用在方法上。标记该方法为作废方法(已过时);

@SuppressWarnings:作用在方法上,压制警告。

 

3、自定义注解(定义注解类)

(1)如何定义?

定义新的 Annotation 类型使用 @interface 关键字

说明:

1】注解也是类,需要定义了才能使用!

2】定义注解类不能使用classenum,也不能使用interface,而是使用@interface

正确使用举例: public @interface MyAnn{}

(2)声明注解的属性

注解属性的作用:原来写在配置文件中的信息,可以通过注解的属性进行描述。

Annotation 的属性声明方式:String name();

属性默认值声明方式:String name() default “xxx”;

特殊属性value:如果注解中有一个名称value(比如:String value();或者String[]value();等等)的属性,那么使用注解时可以省略value=部分,如@MyAnnotation(xxx")

(3)注解支持类型

注解支持类型:String、基本数据类型、枚举、Class 、其它注解类型、以上数据类型相应一维数组

三、元注解(注解的注解)

JDK 的元 Annotation

元 Annotation指修饰Annotation的Annotation。(元注解不是我们定义的, 都是 jdk 中已经定义好的

JDK中定义了如下元Annotation:

@Retention、@Target、@Documented、@Inherited

(1)@Retention:

只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留的域,@Rentention 包含一个 RetentionPolicy 类型的成员变量, 通过这个变量指定域。

【1】RetentionPolicy.CLASS: 编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注解.这是默认值

【2】RetentionPolicy.RUNTIME:编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注解. 程序可以通过反射获取该注释

【3】RetentionPolicy.SOURCE: 编译器直接丢弃这种策略的注释

备注:一个java类有几种表现形式?

分别是:java源码,class字节码,runtime运行时三种表现形式。此三中表现形式对应着 RetentionPolicy 类型的成员变量的三种取值:RetentionPolicy.SOURCERetentionPolicy.CLASSRetentionPolicy.RUNTIME

(2)@Target:

指定注解用于修饰类的哪个成员. @Target 包含了一个名为 value,类型为ElementType的成员变量。  

(3)@Documented:

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

(4)@Inherited:

被它修饰的 Annotation 将具有继承性.如果某个类使用了被 @Inherited 修饰的 Annotation, 则其子类将自动具有该注解

 

四、注解的应用与说明

1、使用注解目标(注解使用地方)

  注解可以作用在:类(接口或枚举)、属性、方法、构造器、包、参数、局部变量

package cn.itcast.annocation;

 

@MyAnn

public class MyClass {

    @MyAnn

    private int a;

    @MyAnn

    public MyClass() {}

    @MyAnn

    public void fun1() {}

    @MyAnn

    public void fun2(@MyAnn String s) {

       @MyAnn

       int n = 10;

    }

}

 

2、注解的属性

定义注解时也可以给出属性

public @interface MyAnn {

    String value();

    int value1();

}

 

其中value,value1就是属性!你可能会说,它是一个方法!没错,它是一个方法,但我们非要称之为属性,因为把它当做属性更加好理解。

当为注解指定属性后,那么在使用注解时就必须要给属性赋值了:

@MyAnn(value1=100,value="hello")

public class MyClass {

}

 

注解的属性还可以有默认值,在使用注解时就可以不给带有默认值的属性赋值了。但没有给出默认值的属性还是要赋值的。

 

public @interface MyAnn {

    String value() default "hello world";

    int value1();

}

@MyAnn(value1=100)

public class MyClass {

}

 

在使用注解时,如果给名称为value的属性赋值,那么可以不给出属性的名称直接给出值。【因为value是特殊属性】

例子:

packageannotation;

 

public @interface MyAnn {

   String value() default "hello world";

   int value1() default 100;

}

@MyAnn()

 class MyClass {//使用正确

}

@MyAnn(value="hello")

 class MyClass2 {//使用正确

}

@MyAnn(value1=200)

 class MyClass3 {//使用正确

}

@MyAnn(value="hello",value1=200)

 class MyClass4 {//使用正确

}

@MyAnn("hello annocation"//特殊属性value的使用

 class MyClass5 {//使用正确

}

@MyAnn(300)      //使用正确

class MyClass6 {

}

@MyAnn(value1=300)   //使用错误,value1不是特殊属性,不能省略名称

 class MyClass7 {

}

@MyAnn("hello",value1=200)  //特殊属性value的使用

                        //使用错误

 class MyClass8 {

}

结论:特殊属性并不是什么说都可以缺省名称,只有在使用时,只写value的属性值是,才可以省略名称;如果在使用时有其它属性需要设置值,则value不能省略名称,否则会报错。

 

属性使用注意事项:

l  注解的属性后面要有一对圆括号,而且圆括号内不能给出东西。就像是无参的方法一样;

l  注解的属性类型只能是:基本类型、String、Enum、Class、以上类型的一维数组类型;

l  注解的属性可以有默认值,例如:int a() default 100;

l  数组的属性默认值:int[] arr() default {1,2,3},这里不能使用new int[]{1,2,3}

l  使用注解时,在给数组属性赋值时的格式:@MyAnn(arr={1,2,3});

 

3、注解的作用目标(元注解@Target使用)

在定义注解时可以限制注解的作用目录!例如让注解只能作用在类和方法上。

这需要使用元注解:@Target。该注解有一个属性value,类型为ElementType[],它是枚举类型。

 

public @interface Target {

    ElementType[] value();

}

public enum ElementType {

  TYPE,FIELD,METHOD,PARAMETED,CONSTRUCTOR,LOCAL_VARIABLE,ANNOCATION_TYPE,PACKAGE

}

 

@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)

取值(ElementType)有:

1.CONSTRUCTOR:用于描述构造器

2.FIELD:用于描述域

3.LOCAL_VARIABLE:用于描述局部变量

4.METHOD:用于描述方法

5.PACKAGE:用于描述包

6.PARAMETER:用于描述参数

7.TYPE:用于描述类、接口(包括注解类型) 或enum声明

 

 @Target:

在定义注解时,可以使用@Target注解来限制注解的作用目标:

@Target({ElementType.TYPE, ElementType.METHOD})

public @interface MyAnn {

}

 

这样MyAnn就只能作用在类和方法上的!其中ElementType.TYPE表示类和接口。

@MyAnn()

public class MyClass {

    @MyAnn()

    private int a;

   

    @MyAnn()

    public void fun() {}

}

 

4、注解的保留策略(元注解@Retention使用)

注解的保留策略是指,注解是只保留在源代码上,还是保留到class文件上,再或者是类在运行时,可以 被类加载器加载到内存中。

如果希望注解被反射,那么注解就要保留到运行时,而不是源代码或类文件上。

指定注解的保留策略需要使用元注解@Retention,它有一个value属性,类型为RetentionPolicy类型,RetentionPolicy是枚举类型:

public @interface Retention {

    RetentionPolicy value();

}

public enum RetentionPolicy {

    SOURCE, CLASS, RUNTIME

}

 

下面代码是指定注解保留到运行时

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.TYPE, ElementType.METHOD})

public @interface MyAnn {

    String value() default "hello";

    int value1() default 100;

}

 

说明:此说明是摘抄的,原文出处

来源:博客园

博主:孤傲苍狼

博文:Java基础加强总结()——注解(Annotation)

网址:https://www.cnblogs.com/xdp-gacl/p/3622275.html

当在Java源程序上加了一个注解,这个Java源程序要由javac去编译,javac把java源文件编译成.class文件,在编译成class时可能会把Java源程序上的一些注解给去掉,java编译器(javac)在处理java源程序时,可能会认为这个注解没有用了,于是就把这个注解去掉了,那么此时在编译好的class中就找不到注解了, 这是编译器编译java源程序时对注解进行处理的第一种可能情况,假设java编译器在把java源程序编译成class时,没有把java源程序中的注解去掉,那么此时在编译好的class中就可以找到注解,当程序使用编译好的class文件时,需要用类加载器把class文件加载到内存中,class文件中的东西不是字节码,class文件里面的东西由类加载器加载到内存中去,类加载器在加载class文件时,会对class文件里面的东西进行处理,如安全检查,处理完以后得到的最终在内存中的二进制的东西才是字节码,类加载器在把class文件加载到内存中时也有转换,转换时是否把class文件中的注解保留下来,这也有说法,所以说一个注解的生命周期有三个阶段:java源文件是一个阶段,class文件是一个阶段,内存中的字节码是一个阶段,javac把java源文件编译成.class文件时,有可能去掉里面的注解,类加载器把.class文件加载到内存时也有可能去掉里面的注解,因此在自定义注解时就可以使用Retention注解指明自定义注解的生命周期,自定义注解的生命周期是在RetentionPolicy.SOURCE阶段(java源文件阶段),还是在RetentionPolicy.CLASS阶段(class文件阶段),或者是在RetentionPolicy.RUNTIME阶段(内存中的字节码运行时阶段),根据JDK提供的API可以知道默认是在RetentionPolicy.CLASS阶段 (JDKAPI写到:the retention policy defaults toRetentionPolicy.CLASS.)

 

5、通过反射读取注解

说明:注解信息要想在运行时仍被使用,必须设置保留策略为Runtime

即:@Retention(RetentionPolicy.RUNTIME)

(1)使用案例一

读取注解需要使用反射来完成

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.TYPE, ElementType.METHOD})

public @interface MyAnn {

    String value() default "hello";

    int value1() default 100;

}

@MyAnn(value="hello world", value1=200)

public class MyClass {

    private int a;

   

    @MyAnn("myMethod")

    public void fun() {}

}

public class Demo1 {

    public static void main(String[] args) throws Exception {

       Class clazz = MyClass.class;//获取当前类的class(字节码文件对象)

       MyAnn myAnn = (MyAnn) clazz.getAnnotation(MyAnn.class);//通过字节码文件对象获得MyClass类上使用的MyAnn的注解类对象

       System.out.println(myAnn.value());//hello world

       System.out.println(myAnn.value1());//200

      

       Method method = clazz.getMethod("fun");//通过字节码文件对象获得fun方法对象

       MyAnn myAnn1 = method.getAnnotation(MyAnn.class); //通过fun方法对象获得该方法上使用的注解类对象MyAnn

       System.out.println(myAnn1.value());//myMethod

       System.out.println(myAnn1.value1());//100

    }

}

 

(2)使用案例二

【1】注解类DbInfo
package annotation.reflect;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

//原因分析:注解信息只保留到了class文件阶段,在运行时没有保留到,所以就没有那些属性信息
//因此:注解 信息  要想在 运行时仍然可以 被 解析到, 必须要设置 保留策略为 运行时 
@Retention(RetentionPolicy.RUNTIME)
public @interface DbInfo {
	
	String driverClass();//驱动名属性
	String username();//用户名属性
	String password();//密码属性
	String url();//url属性
}
【2】JdbcUtils类(反射读取注解)

package annotation.reflect;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;

public class JdbcUtils {

	@DbInfo(driverClass="com.mysql.jdbc.Driver",username="root",password="1234",url="jdbc:mysql://localhost:3306/shoppings")
	public static Connection getConnection() throws Exception{
		/*
		 * 某个类的方法中使用了自定义主机类DbInfo,我们要如何获得?
		 * 思路:
		 * 1、首先获取某个类的class
		 * 2、然后通过class,获取当前方法getConnection
		 * 3、判断当前方法getConnection上是否有注解类
		 * 4、获取当前方法上的主机类上的属性值
		 * 
		 * 5、注册驱动,然后连接数据库
		 */
		Class clazz=Class.forName("annotation.reflect.JdbcUtils");//获取类的字节码文件对象
		Method method=clazz.getMethod("getConnection", null);
		
		if(!method.isAnnotationPresent(DbInfo.class)){//当前类没有注解类
			return null;
		}
		
		DbInfo dbInfo = method.getAnnotation(DbInfo.class);//获取注解类信息
		String driverClass = dbInfo.driverClass();
		String username = dbInfo.username();
		String password = dbInfo.password();
		String url = dbInfo.url();
		
		Class.forName(driverClass);//注册驱动
		Connection connection = DriverManager.getConnection(url, username, password);//建立连接
		
		return connection;
	}
}

【3】测试类TestJdbcUtils

package annotation.reflect;

import java.sql.Connection;
import org.junit.Test;

public class TestJdbcUtils {

	@Test
	public void test() throws Exception{
		
		Connection con=JdbcUtils.getConnection();
		System.out.println(con);
		//结果为空,但是程序没有错误
		//原因分析:注解信息只保留到了class文件阶段,在运行时没有保留到,所以就没有那些属性信息
		//因此:注解 信息  要想在 运行时仍然可以 被 解析到, 必须要设置 保留策略为 运行时 
		//设置:@Retention(RetentionPolicy.RUNTIME)
		//此时结果为:com.mysql.jdbc.JDBC4Connection@38135151  测试成功
	}
}

【4】结果

com.mysql.jdbc.JDBC4Connection@38135151

【5】说明

记得导入jdbc驱动,因为连数据库;而且那些连数据库的信息,即:JdbcUtils类中的一些数据:

@DbInfo(driverClass="com.mysql.jdbc.Driver",username="root",password="1234",url="jdbc:mysql://localhost:3306/shoppings")

要结合自己mysql的用户名,密码,数据库连接对象进行修改

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值