文章目录
什么是注解
注解(Annotation)是从JDK5.0开始弓|入的新技术。
注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象$Proxy1。
注解的作用
- 不是程序本身,可以对程序作出解释。(这一点, 跟注释没什么区别)
- 可以被其他程序(此如:编译器等)读取。(注解信息处理流程,是注解和注释的重大区别。如果没有注解信息处理流程,则注解毫无意义)
注解的格式
- 注解是以"@注释名"在代码中存在的,还可以添加一些参数值,例如:
@SuppressWarnings(value= "unchecked")
。
注解在哪里使用
- 可以附加在package, class, method, field等上面,相当于给它们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问。
内置注解
@Override
- 定义在java.lang.Override中 ,此注解只适用于修辞方法,表示一个方法声明打算重写父类中的一个方法。
- 不是重写时使用这个注解会报错,比如方法名字与父类定义的名字不相同时。
@Deprecated
- 定义在java.lang.Deprecated中,此注解可用于修辞方法、属性、类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。
@SuppressWarnings
- 定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息。
- 与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数值都是已经定义好了的, 我们选择性的使用就好了,参数如下:
参数 | 说明 |
---|---|
deprecation | 使用了过时的类和方法的警告 |
unchecked | 执行了未检查的转换时的警告,如使用集合时未指定泛型 |
fallthrough | 当在switch语句使用时发生case穿透 |
path | 在路径、源文件路径等中有不存在路径的警告 |
serial | 当在可序列化的类上缺少serialVersionUID定义时的警告 |
finally | 任何finally子句不能完成时的警告 |
all | 关于以上所有情况的警告 |
- 使用单个参数的用法:
@SuppressWarnings("unchecked")
- 使用多个参数的用法:
@SuppressWarnings(value={"unchecked", " deprecation"})
测试代码
import java.util.ArrayList;
import java.util.List;
public class Demo01 /*extends Object*/{
@Override
public String toString() {
return super.toString();
}
@Deprecated
public static void text001(){
System.out.println("text001");
}
public static void text002(){
List list1 = new ArrayList();
List list2 = new ArrayList();
}
}
自定义注解
- 使用@interface自定义注解时,自动继承 java.lang.annotation.Annotation接口
要点:
- @interface用来声明一 个 注解,格式为:
public @interface注解名{定义体}
- 其中的每一个方法实际上是声明了一个配置参数。
- 方法的名称就是参数的名称
- 返回值类型就是参数的类型(返回值类型只能是基本类型、Class、 String、enum. )。
- 可以通过default来声明参数的默认值。
- 如果只有一个参数成员,一 般参数名为value
元注解
- 元注解的作用就是负责注解其他注解。 Java定义了4个标准的meta-annotation类型,它们被用来提供对其它annotation类型作说明。
- 这些类型和它们所支持的类在java.lang.annotation包中可以找到
@Target
@Retention
@Documented
@Inherited
其中前两个比较常见,下面进行介绍。
@Target
- 作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
- 用法:
@Target(value= ElementType. TYPE)
@Retention
- 作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期。
取值 | 作用 |
---|---|
SOURCE | 在源文件中有效(源文件保留) |
CLASS | 在class文件中有效(class保留) |
RUNTIME | 在运行时有效(运行时保留) |
代码实现:
自定义注解类1:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value={ElementType.METHOD,ElementType.TYPE})//描述注解的使用范围
@Retention(RetentionPolicy.RUNTIME) //描述注解的生命周期
public @interface MyAnnotation01 {
//这不是方法,注解的参数名都是带括号的
//default用于声明参数的默认值
String studentName() default "";
int age() default 0;
int id() default -1;
String[] schools() default {"清华大学","北京大学"};
}
自定义注解类2
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value={ElementType.METHOD,ElementType.TYPE})//描述注解的使用范围
@Retention(RetentionPolicy.RUNTIME) //描述注解的生命周期
public @interface MyAnnotation02 {
String value();
}
测试代码:
@MyAnnotation01
public class Demo02 {
@MyAnnotation01(age=19,studentName="小高", id=1001,
schools={"复旦大学","南开大学"})
public void test(){
}
@MyAnnotation02(value="abc")
//@MyAnnotation02("abc") 只有一个参数时,这样写也可以
public void test2(){
}
}
通过反射机制读取注解
注解是元素的说明,但是如果注解仅仅是对元素进行说明,不能让其他程序运行时读取的话,这样和注释是没有区别的。这样注解就没有了意义。
下面举一个例子体现读取注解的作用,我们常常将对象的信息存储在表中,一个表对应一个类,一条记录对应一个对象,一个列对应一个属性。
我们用程序(一般用现成的框架,也可以自己写)将对象存储到表中时,需要生成SQL语句;这时,我们需要知道存储到哪一个表中,每一个属性对应哪一列,如果需要创建这个表,我们还需要知道列的属性和长度等等。
以上我们需要的信息可以用注解声明,所以在存储对象信息时,我们就需要读取注解。这时,可以用到反射机制。
下面模拟将sxtStudent类对象存储到数据库tb_student表中;共四步:
- 定义注解
- 在类中使用注解
- 编写读取注解的程序(也可以用框架),获得想要的信息
- 根据获得的表名和字段信息,拼出SQL语句,使用JDBC执行这个SQL,在数据库中生成相应的表或者是将数据添加到相应的表中。
定义注解:
声明表的注解:
package 注解;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* 此注解用于声明存放到哪一个表中
*/
@Target(value={ElementType.TYPE})//描述注解的使用范围
@Retention(RetentionPolicy.RUNTIME) //描述注解的生命周期
public @interface SxtTable {
String value();
}
声明属性的注解:
package 注解;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* 此注解用于声明属性对应的列名,类型,长度
*/
@Target(value={ElementType.FIELD})//描述注解的使用范围
@Retention(RetentionPolicy.RUNTIME) //描述注解的生命周期
public @interface SxtField {
String columnName();
String type();
int length();
}
在类中使用注解
package 注解;
@SxtTable("tb_student")
public class sxtStudent {
@SxtField(columnName="id", type="int", length=10)
private int id;
@SxtField(columnName="sname", type="varchar", length=10)
private String studentName;
@SxtField(columnName="age", type="int", length=3)
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
编写读取注解的程序
package 注解;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
/**
* 使用反射读取注解的信息
* @author Administrator
*
*/
public class Demo03 {
public static void main(String[] args) {
//使用反射获取sxtStudent类 注解使用在那一个类就获取那一个类进行读取
try {
Class clazz = Class.forName("注解.sxtStudent");
//获得类的所有有效注解
Annotation[] annotations = clazz.getAnnotations();
for(Annotation an:annotations){
System.out.println(an);
}
//获得类的指定的注解
SxtTable st = (SxtTable) clazz.getAnnotation(SxtTable.class);
System.out.println(st.value());
//先通过反射获取类的studentName属性
Field f = clazz.getDeclaredField("studentName");
//在通过属性f获取它的注解
SxtField sxtField = f.getAnnotation(SxtField.class);
System.out.println(sxtField.columnName()+"--"+sxtField.type()+"--"+sxtField.length());
} catch (Exception e) {
e.printStackTrace();
}
}
}