文章目录
- 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{@Deprecatedpublic String m;@Deprecatedpublic void doSome(){}
}
在IDEA中会有删除线及对应警告:
2.2 @Override
@Override只能修饰实例方法,被标注的方法必须是个重写方法,否则就会编译失败。
如下代码,尝试重写equals方法,但是传入的是String类型的参数,且标注在其他位置上也编译报错:
package annotationtest;public class AnnotationTest02 {@Overridepublic static int num = 100;@Override
// public boolean equals(Object obj) {public boolean equals(String obj) {return super.equals(obj);}@Overridepublic 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 {@MyAnnotationprivate String name;@MyAnnotationpublic 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);}}
}
运行结果: