文章目录
- 1.注解概述
- 2.几个常用的JDK内置的注解
- 2.1 @Deprecated
- 2.2 @Override
- 2.3 @SuppressWarnings
- 2.4 @FunctionalInterface
- 3.自定义注解
- 3.1 注解也可以定义属性
- 3.2 注解的使用规则补充
- 4.元注解
- 4.1 @Retention
- 4.2 @Target
- 4.3 @Documented
- 4.4 @Inherited
- 4.5 @Repeatable
- 5.通过反射获取注解
- 6.一个小练习
1.注解概述
-
什么是注解?
①注解是JDK1.5才引入的。
②注解可以标注在 类上,属性上,方法上 等。
③注解可以做到在不改变代码逻辑的前提下在代码中嵌入补充信息。 -
注解与注释
注释:给程序员看的,编译器编译时会忽略注释。
注解:给编译器看的,或给其它程序看的,程序根据有没有这个注解来决定不同的处理方式。 -
注解的重要性
框架实现原理:框架 = 反射 + 注解 + 设计模式。
2.几个常用的JDK内置的注解
2.1 @Deprecated
@Deprecated用来标记过时的元素(比如类、方法、属性),在编译阶段遇到这个注解时会发出提醒警告,告诉开发者正在调用一个过时的元素比如过时的类、过时的方法、过时的属性等。
package annotationtest;
public class AnnotationTest01 {
public static void main(String[] args) {
MyClass01 myClass01 = new MyClass01();
String m = myClass01.m;
myClass01.doSome();
}
}
@Deprecated
class MyClass01{
@Deprecated
public String m;
@Deprecated
public void doSome(){
}
}
在IDEA中会有删除线及对应警告:
2.2 @Override
@Override只能修饰实例方法,被标注的方法必须是个重写方法,否则就会编译失败。
如下代码,尝试重写equals方法,但是传入的是String类型的参数,且标注在其他位置上也编译报错:
package annotationtest;
public class AnnotationTest02 {
@Override
public static int num = 100;
@Override
// public boolean equals(Object obj) {
public boolean equals(String obj) {
return super.equals(obj);
}
@Override
public void m(){
}
}
编译报错:
2.3 @SuppressWarnings
@SuppressWarnings(抑制警告的注解):在实际开发中,建议尽量不要忽略警告,而是真正的去解决警告。
该注解常见的属性值(并非所有):
① @SuppressWarnings(“rawtypes”):抑制未使用泛型的警告;
② @SuppressWarnings(“resource”):抑制未关闭资源的警告;
③ @SuppressWarnings(“deprecation”):抑制使用了已过时资源时的警告;
④ @SuppressWarnings(“all”):抑制所有警告。
package annotationtest;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("all")
public class AnnotationTest03 {
public static void main(String[] args) throws Exception {
@SuppressWarnings("rawtypes")
List list = new ArrayList();
@SuppressWarnings("resource")
FileInputStream fileInputStream = new FileInputStream("D:\\1.txt");
@SuppressWarnings("deprecation")
MyClass01 myClass01 = new MyClass01();
}
}
2.4 @FunctionalInterface
@FunctionalInterface“函数式接口”的注解,这个是 JDK1.8 版本引入的新特性。若一个接口使用@FunctionalInterface标注,则该接口就有且只能存在一个抽象方法,否则就会发生编译错误。(注意:接口中的默认方法或静态方法可以有多个。)
package annotationtest;
public class AnnotationTest04 {
public static void main(String[] args) {
}
}
@FunctionalInterface
interface Flyable{
void fly();
void run();
}
多于一个抽象方法则编译失败:
可以有其他默认方法和静态方法:
3.自定义注解
- 使用
@interface
来定义注解。 - 默认情况下注解可以出现在类上、方法上、属性上、构造方法上、方法参数上等…
- 所有自定义的注解,它的父类是:
java.lang.annotation.Annotation
。
自定义注解:
package annotationtest;
public @interface MyAnnotation {
}
测试代码:
package annotationtest;
@MyAnnotation
public class AnnotationTest05 {
@MyAnnotation
private String name;
@MyAnnotation
public void doSome(){
}
public void toDO(@MyAnnotation String name, @MyAnnotation String password){
}
}
3.1 注解也可以定义属性
- 注解也可以定义属性,不过属性定义时,属性名后面必须加一个小括号。
属性的类型只能是:
byte,short,int,long,float,double,boolean,char、String、Class、枚举类型、注解类型,以及以上所有类型的一维数组形式。 - 注解在使用时必须给属性赋值,除非你使用了
default
关键字为属性指定了默认值。
给注解加上属性:
package annotationtest;
public @interface MyAnnotation01 {
byte b() default 1;
short s();
int i();
long l();
float f();
double d();
boolean flag() default true;
char c();
String name();
Class clazz();
Season season() default Season.AUTUMN;
MyAnnotation annotation() default @MyAnnotation;
String[] words();
}
测试代码:
package annotationtest;
@MyAnnotation01(s=2,i=10,l=10l, f=20.1f,d=22.2,c=1, name="zhangsan",clazz=Integer.class,words={"day","month","year"})
public class AnnotationTest06 {
@MyAnnotation01(s=2,i=10,l=10l, f=20.1f,d=22.2,c=1, name="zhangsan",clazz=Integer.class,words={"day","month","year"})
private int m;
}
3.2 注解的使用规则补充
- 如果属性只有一个,并且属性名是value时,使用注解时value可以省略不写。
- 如果属性是一个数组,且名为value,使用注解时,数组值只有一个,数组的大括号是可以省略的。
定义注解Table,其中只有一个属性,且属性名为value:
package annotationtest;
public @interface Table {
String value();
}
定义注解Parameter ,其中只有一个Strings数组属性,且名为value:
package annotationtest;
public @interface Parameter {
String[] value();
}
测试代码:
package annotationtest;
@Table("t_user")
@Parameter("java")
public class AnnotationTest07 {
}
4.元注解
用来标注注解的注解叫做元注解。(也是JDK内置的注解。)
常用的元注解:
- @Retention:设置注解的保持性
- @Target:设置注解可以出现的位置
- @Documented:设置注解是否可以生成到帮助文档中
- @Inherited:设置注解是否支持继承
- @Repeatable:设置注解在某一个元素上是否可以重复使用(Java8的新特性。)
4.1 @Retention
Retention英文意思有保留、保持的意思,它表示注解存在阶段是保留在源代码(编译期),字节码(类加载)或者运行时(JVM中运行)。
在@Retention注解中使用枚举属性RetentionPolicy来表示注解保留时期:
- @Retention(RetentionPolicy.SOURCE):注解仅存在于源代码中,在字节码文件中不包含。
- @Retention(RetentionPolicy.CLASS):注解在字节码文件中存在,但运行时无法获得(默认)。
- @Retention(RetentionPolicy.RUNTIME):注解在字节码文件中存在,且运行时可通过反射获取。
package annotationtest.meta.meta01;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.CLASS)
public @interface MyAnnotation {
}
测试代码:
package annotationtest.meta.meta01;
import java.lang.annotation.Annotation;
@MyAnnotation
public class Test {
public static void main(String[] args) throws Exception{
//获取类
Class clazz = Class.forName("annotationtest.meta.meta01.Test");
//获取类上的注解
Annotation annotation = clazz.getAnnotation(MyAnnotation.class);
System.out.println(annotation);
}
}
运行结果:
修改@Retention注解:
package annotationtest.meta.meta01;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
再次测试结果:
4.2 @Target
用于描述注解可以使用的位置,该注解使用ElementType枚举类型用于描述注解可以出现的位置,
ElementType有如下枚举值:
- @Target(ElementType.TYPE):作用于接口、类、枚举、注解。
- @Target(ElementType.FIELD):作用于属性、枚举的常量。
- @Target(ElementType.METHOD):作用于方法。
- @Target(ElementType.PARAMETER):作用于方法参数。
- @Target(ElementType.CONSTRUCTOR):作用于构造方法。
- @Target(ElementType.LOCAL_VARIABLE):作用于局部变量。
- @Target(ElementType.ANNOTATION_TYPE):作用于注解。
- @Target(ElementType.PACKAGE):作用于包。
- @Target(ElementType.TYPE_PARAMETER):作用于泛型,即泛型方法、泛型类和泛型接口。
- @Target(ElementType.TYPE_USE):作用于任意类型。
定义只能用于方法上的注解:
package annotationtest.meta.meta02;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
public @interface MyAnnotation {
}
测试(使用在类上时编译失败):
4.3 @Documented
Documented的英文意思是文档。使用javadoc.exe工具可以从程序源代码中抽取类、方法、属性等注释形成一个源代码配套的API帮助文档,而该工具抽取时默认不包括注解内容。如果使用的注解被@Documented标注,那么该注解就能被javadoc.exe工具提取到API文档。
4.4 @Inherited
Inherited的英文意思是继承,但是这个继承和我们平时理解的继承大同小异,一个被@Inherited注解了的注解修饰了一个父类,则它的子类也继承了父类的注解。
定义可继承注解:
package annotationtest.meta.meta03;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME) //这个例子中为了测试时能够获取Cat上的注解,所以需要设置保持性
@Inherited
public @interface MyAnnotation {
}
Animal 类上加注解:
package annotationtest.meta.meta03;
@MyAnnotation
public class Animal {
}
Cat类继承Animal类:
package annotationtest.meta.meta03;
public class Cat extends Animal {
}
测试代码:
package annotationtest.meta.meta03;
public class Test {
public static void main(String[] args) {
Class<Cat> catClass = Cat.class;
MyAnnotation annotation = catClass.getAnnotation(MyAnnotation.class);
System.out.println(annotation);
}
}
运行结果:
4.5 @Repeatable
Repeatable表示可重复的含义,该注解属于JDK1.8版本的新特性,表示可以可以重复使用该注解。
定义可重复注解:
package annotationtest.meta.meta04;
import java.lang.annotation.Repeatable;
@Repeatable(Authors.class) //必须加上一个参数,这个参数也是一个注解,是一个以注解Author数组为属性的注解
public @interface Author {
String name();
}
辅助注解:
package annotationtest.meta.meta04;
public @interface Authors {
Author[] value(); //这里属性名必须为value
}
测试:
5.通过反射获取注解
1)获取类上的所有注解:Annotation[] annotations = clazz.getAnnotations();
2)获取类上指定的某个注解:
clazz.isAnnotationPresent(AnnotationTest01.class)
AnnotationTest01 an = clazz.getAnnotation(AnnotationTest01.class);
3)获取属性上的所有注解:Annotation[] annotations = field.getAnnotations();
4)获取属性上指定的某个注解:
field.isAnnotationPresent(AnnotationTest02.class)
AnnotationTest02 an = field.getAnnotation(AnnotationTest02.class);
5) 获取方法上的所有注解:Annotation[] annotations = method.getAnnotations();
6)获取方法上指定的某个注解:
method.isAnnotationPresent(AnnotationTest02.class)
AnnotationTest02 an = method.getAnnotation(AnnotationTest02.class);
Annotation1注解:
package annotationtest.reflectannotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target({ElementType.TYPE,ElementType.FIELD})
public @interface Annotation1 {
String name();
int age();
}
Annotation2注解:
package annotationtest.reflectannotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Annotation2 {
String email();
}
测试代码:
package annotationtest.reflectannotation;
import SuperTest.superTest03.A;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
@Annotation1(name = "zhangsan",age = 23)
@Annotation2(email = "wewerwerer23@126.com")
public class ReflectAnnotationTest {
@Annotation1(name = "lisi", age = 29)
public int sum;
public static void main(String[] args) throws Exception {
//获取类
Class<ReflectAnnotationTest> aClass = ReflectAnnotationTest.class;
System.out.println("获取本类上所有注解:");
//获取类上所有注解
Annotation[] annotations = aClass.getAnnotations();
//遍历
for (Annotation annotation: annotations) {
System.out.println(annotation);
}
System.out.println("\n获取类上的Annotation1注解:");
//判断类上是否有某个注解
if(aClass.isAnnotationPresent(Annotation1.class)){
//获取对应注解及属性
Annotation1 annotation = aClass.getAnnotation(Annotation1.class);
System.out.println(annotation);
System.out.println(annotation.age());
System.out.println(annotation.name());
}
//获取属性上的注解
//1.先获取属性
Field field = aClass.getDeclaredField("sum");
System.out.println("\n获取属性"+ field.getName() +"上的Annotation1注解:");
//2.判断该属性上是否有某个注解
if(field.isAnnotationPresent(Annotation1.class)){
//3.获取属性上的注解及其属性
Annotation1 annotation1 = field.getAnnotation(Annotation1.class);
System.out.println(annotation1);
System.out.println(annotation1.name());
System.out.println(annotation1.age());
}
}
}
运行结果:
6.一个小练习
- 储备知识:
数据库是用来组织数据的,数据库使用表来组织数据。
一张表应该有表名,例如:t_user
一张表中应该有很多字段,每个字段有字段名和数据类型,例如age字段是int类型。
数据库中整数对应的类型是:int。字符串对应的类型是:varchar。
建表语句如下:
编写程序扫描一个包下所有的类,凡是被 @Table 注解标注的类都要生成一条建表语句,表名在 @Table 注解中指定。被@Table 标注的类中的属性被 @Column 注解标注,在 @Column 注解中描述字段的名称和字段的数据类型。
Table注解:
package finaltest.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 凡是被@Table注解标注的类需要生成建表语句
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
/**
* 用来指定表名
* @return 表名
*/
String value();
}
Column注解:
package finaltest.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 该注解用于标注一个类中的属性,被标注的属性参与建表
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
/**
* 字段的名字
* @return 字段的名字
*/
String name() default "";
/**
* 字段的数据类型
* @return 字段的数据类型
*/
String type() default "varchar";
}
User类:
package finaltest.a;
import finaltest.annotation.Column;
import finaltest.annotation.Table;
@Table("t_user")
public class User {
@Column(name = "cid")
private String uid;
@Column(name = "uname")
private String uname;
@Column(name = "password")
private String password;
@Column(name = "age", type = "int")
private int age;
private String eamil;
}
Customer类:
package finaltest.c;
import finaltest.annotation.Column;
import finaltest.annotation.Table;
@Table("t_customer")
public class Customer {
@Column(name = "cid")
private String cid;
@Column(name = "name")
private String name;
@Column(name = "age", type = "int")
private int age;
private String address;
}
Vip类:
package finaltest.a.b;
import finaltest.annotation.Column;
import finaltest.annotation.Table;
@Table("t_vip")
public class Vip {
@Column(name="vid")
private String vid;
@Column(name="name")
private String name;
@Column(name="grade")
private String grade;
}
测试类:
package finaltest;
import finaltest.annotation.Column;
import finaltest.annotation.Table;
import java.io.File;
import java.lang.reflect.Field;
public class Test {
private static String rootPath;
private static StringBuilder sb = new StringBuilder(); //定义成全局变量更合理
public static void main(String[] args) {
//获取当前类路径的根路径
rootPath = Thread.currentThread().getContextClassLoader().getResource(".").getPath();
File file = new File(rootPath);
try {
generateCreateStatement(file);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void generateCreateStatement(File file) throws Exception {
if(file.isFile()){ //file是一个文件的时候递归结束
String classFileAbsolutePath = file.getAbsolutePath();
//只针对字节码文件操作
if(classFileAbsolutePath.endsWith(".class")){
String className = classFileAbsolutePath
.substring(rootPath.length()-1, classFileAbsolutePath.length() - ".class".length())
.replace("\\",".");
Class clazz = Class.forName(className);
//判断类上是否有Table注解
if (clazz.isAnnotationPresent(Table.class)){
Table tableAnnotation = (Table) clazz.getAnnotation(Table.class);
//获取表名
String tableName = tableAnnotation.value();
sb.append("create table ");
sb.append(tableName);
sb.append("(\n");
//获取所有声明的属性
Field[] fields = clazz.getDeclaredFields();
//遍历
for (Field field: fields) {
//判断属性上是否有Column属性
if(field.isAnnotationPresent(Column.class)){
Column columnAnnotation = field.getAnnotation(Column.class);
//属性名
String fieldName = columnAnnotation.name();
//属性类型
String fieldType = columnAnnotation.type();
sb.append("\t");
sb.append(fieldName);
sb.append(" ");
sb.append(fieldType);
sb.append(",\n");
}
}
//删除当前拼接的最后一个逗号
sb.deleteCharAt(sb.length()-2);
sb.append(");\n");
}
}
System.out.println(sb);
return;
}
//获取当前目录下所有文件
File[] files = file.listFiles();
for (File f : files){
//递归
generateCreateStatement(f);
}
}
}
运行结果: