注解和反射
01-什么是注解 Annotation
-
Annotation是从JDK5.0开始引入的新技术.
-
Annotation的作用:
- ➢不是程序本身,可以对程序作出解释.(这- -点和注释(comment)没什么区别)
- ➢可以被其他程序(比如:编译器等)读取.
-
Annotation的格式:
- ➢注解是以"@注释名"在代码中存在的,还可以添加一些参数值,例如:@SuppressWarnings(value=“unchecked”).
-
Annotation在哪里使用?
- ➢可以附加在package , class , method , field 等上面,相当于给他们添了额外的辅助信息,
我们可以通过反射机制编程实现对这些元数据的访问
- ➢可以附加在package , class , method , field 等上面,相当于给他们添了额外的辅助信息,
1-内置注解
- ➢@Override :定义在java.lang.Override中,此注释只适用于修辞方法,表示-个方法声明打算重写超类中的另一个方法声明.
- ➢@Deprecated :定义在java.lang.Deprecated中,此注释可以用于修辞方法,属性,类,表示不鼓励程序员使用这样的元素, 通常是因为它很危险或者存在更好的选择.
- ➢@SuppressWarnings :定义在javalang.SuppressWarnings中,用来抑制编译时的警告信息.与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,
我们选择性的使用就好了.- @SuppressWarningsl’“ll”)
- @SuppressWarnings(“unchecked”)
- @SuppressWarnings(value={“unchecked”,“deprecation”})
2-元注解
- 元注解的作用就是负 责注解其他注解, Java定义了4个标准的meta- annotation类型,他们被用来
提供对其他annotation类型作说明. - 这些类型和它们所支持的类在java.lang .annotation包中可以找到.( @Target , @Retention,@Documented , @Inherited )
- ➢@Target: 用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
- ➢@Retention :表示需要在什么级别保存该注释信息,用于描述注解的生命周期
- ➢(SOURCE < CLASS < RUNTIME)
- ➢@Document:说明该注解将被包含在javadoc中
- ➢@Inherited: 说明子类可以继承父类中的该注解
- @SuppressWarnings(value={“unchecked”,“deprecation”})
import java.lang.annotation.*;
public class Test01 {
@MyAnnotation
public String toSstring() {
return super.toString();
}
}
//Target 表示注解可以用在哪些地方
@Target(ElementType.METHOD)
//Retention表示注解在什么地方有效
//runtime>class>source
@Retention(RetentionPolicy.RUNTIME)
//Documented 表示是否将注解生成到Javadoc中
@Documented
//Inherited 表示子类可以继承父类的注解
@Inherited
@interface MyAnnotation{
}
3-自定义注解
- ➢使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
- ➢分析:
- @ interface用来声明一个注解,格式: public @ interface注解名{定义内容}
- 其中的每一一个方法实际上是声明了一个配置参数.
- 方法的名称就是参数的名称.
- 返回值类型就是参数的类型(返回值只能是基本类型,Class , String , enum ).
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员, - -般参数名为value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class Test02 {
//如果自定义注解里没有设置默认值,使用注解时必须赋值
@MyAnnotation2(name = "",age = 1)
public void test(){}
//value可以省略类型
@MyAnnotation3("小明")
public void test2(){}
}
//自定义注解
//作用域: TYPE:类.METHOD:方法
@Target({ElementType.TYPE,ElementType.METHOD})
//什么时候有效, RUNTIME运行时
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
//定义参数时, 类型 名字();
String name();
int age();
String name2() default "";
int age2() default 1;
}
//作用域: TYPE:类.METHOD:方法
@Target({ElementType.TYPE,ElementType.METHOD})
//什么时候有效, RUNTIME运行时
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
//定义参数时, 类型 名字();
String value();
}
02-反射机制 Reflection
动态语言
- ➢是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被
引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代
码可以根据某些条件改变自身结构。 - ➢主要动态语言: Object-C、 C#、JavaScript、 PHP、 Python等。
静态语言
- ➢与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、 C、C++。
- ➢Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一 定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!
Java Reflection
- ➢Reflection (反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借
助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及
方法。
Class C = Class.forName("java lang.String")
- ➢加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象 (一个类只有
一一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对
象看到类的结构。这个对象就像一面镜子, 透过这个镜子看到类的结构,所以,
我们形象的称之为:反射
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IDxOFAHH-1639023733671)(image-att\02-1.png)]
java反射机制提供的功能
- ➢在运行时判断任意一个对象所属的类
- ➢在运行时构造任意一个类的对象
- ➢在运行时判断任意一个类所具有的成员变 量和方法
- ➢在运行时获取泛型信息
- ➢在运行时调用任意一个对象的成员变量和方法
- ➢在运行时处理注解
- ➢生成动态代理 等等
java反射的优点和缺点
优点:
- 可以实现动态创建对象和编译, 体现出很强大的灵活性
缺点:
- 对性能有影响; 使用反射基本上是一种解释操作, 我们可以告诉JVM ,希望做什么并且他满足我们的要求, 这类操作总是慢于直接执行相同的操作
反射相关的主要API
java.lang.Class:
代表一个类java.lang.reflect.Method:
代表类的方法java.lang.reflect.Field:
代表类的成员变量java.lang.reflect.Constructor:
代表类的构造器…
1, Class类
对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。
- ➢Class本身也是一 个类
- ➢Class 对象只能由系统建立对象
- ➢一个加载的类在JVM中只会有一个Class实例
- ➢一个Class对象对应的是一个加载到JVM中的一一个.class文件
- ➢每个类的实例都会记得自己是由哪个Class实例所生成
- ➢通过Class可以完整地得到一-个类中的所有被加载的结构
- ➢Class类是Reflection的根源, 针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
2, Class类的常用方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jwN6O0g9-1639023733672)(image-att\02-2.png)]
3, 获取Class类的实例
- 1 若已知具体的类, 通过类的class属性获取,该方法最为安全可靠, 程序性能最高
Class class = Person.class
- 2 已知某个类的实例, 调用该实例的getClass()方法获取Class对象
Class class = person.getClass();
- 3 已知一个雷的全类名, 且该类在类路径下, 可通过Class类的静态方法forName()获取, 可能抛出ClassNotFoundException
Class class = Class.forName("demo01.Student");
- 4 内置基本数据类型可以直接用类名.Type
- 还可以利用ClassLoader
package com.ccc.reflection;
public class Test03 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println(person.name);
//方式一 , 通过对象获得
Class c1 = person.getClass();
System.out.println(c1.hashCode());//2333
//方式二, forName获得
Class c2 = Class.forName("com.ccc.reflection.Student");
System.out.println(c2.hashCode());//2333
//方式三, 通过类名.class获得
Class c3 = Student.class;
System.out.println(c3.hashCode());//2333
//方式四, 基本内置类型的包装类都有一个Type属性
Class c4 = Integer.TYPE;
System.out.println(c4.hashCode());//int
//获得父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);//com.ccc.reflection.Person
}
}
class Person{
public String name ;
public Person() {}
public Person(String name) {
this.name = name;
}
class Student extends Person{
public Student() {
this.name = "学生";
}
}
class Teacher extends Person{
public Teacher() {
this.name = "老师";
}
}
3-1, 哪些类型可以有Class对象
- ➢class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
- ➢interface: 接口
- ➢[]:数组
- ➢enum:枚举
- ➢annotation: 注解@interface
- ➢primitive type:基本数据类型
- ➢void
package com.ccc.reflection;
import java.lang.annotation.ElementType;
//所有类型的class
public class Test04 {
public static void main(String[] args) {
Class c1 = Object.class;//类
Class c2 = Comparable.class;//接口
Class c3 = String[].class;//一维数组
Class c4 = int[][].class;//二维数组
Class c5 = Override.class;//注解
Class c6 = ElementType.class;//枚举
Class c7 = Integer.class;//基本数据类型
Class c8 = void.class;// void
Class c9 = Class.class; //
System.out.println(c1);//class java.lang.Object
System.out.println(c2);//interface java.lang.Comparable
System.out.println(c3);//class [Ljava.lang.String;
System.out.println(c4);//class [[I
System.out.println(c5);//interface java.lang.Override
System.out.println(c6);//class java.lang.annotation.ElementType
System.out.println(c7);//class java.lang.Integer
System.out.println(c8);//void
System.out.println(c9);//class java.lang.Class
//只要元素类型和维度一样, 就是同一个Class
int[] a= new int[10];
int[] b= new int[100];
System.out.println(a.getClass()==b.getClass());//true
}
}
4, 类的加载过程
当程序主动使用某个类时, 如果该类还未被加载到内存中,则系统会通过以下四个步骤来对该类进行初始化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HeZm7irD-1639023733673)(image-att\02-4.png)]
5, 类的加载与ClassLoader理解
- 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构然后生成一个代表这个类的java.lang.Class对象.
- 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程
- 验证:确保加载的类信息符合JM规范,没有安全方面的问题
- 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
- 初始化:
- 执行类构造器()方法的过程。类构造器 ()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器) 。
- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虚拟机会保证一个类的 ()方法在多线程环境中被正确加锁和同步。
public class Test05 {
public static void main(String[] args) {
A a = new A();
System.out.println(a.m);
/*
1. 加载到内存, 会产生一个类对应Class 对象
2. 链接, 链接结束后m=0
3. 初始化
<clinit>(){
System.out.println("A静态代码块");
m =300;
m = 100;
}
m =100;
*/
}
}
class A{
static {
System.out.println("A静态代码块");
m =300;
}
static int m= 100;
public A() {
System.out.println("A构造器");
}
}
6, 什么时候会发生类初始化
- 类的主动引用(一定会发生类的初始化)
- 当虚拟机启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
- 类的被动引用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导
致子类初始化 - 通过数组定义类引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导
package com.ccc.reflection;
//测试类什么时候会初始化
public class Test06 {
static {
System.out.println("Main类代码块被调用");
}
public static void main(String[] args) throws ClassNotFoundException {
//一定会发生类的初始化:
//1 new 一个类时 , 如果没有初始化父类, 会先初始化它的父类
//Son son = new Son();//Main -- Father -- Son 都被初始化
//2 调用类的静态成员( 除了final常量 )和静态方法时
//System.out.println(Son.s);//Main -- Father -- Son 都被初始化 50
//3 使用java.lang.reflect包方法对类进行反射调用时
//Class.forName("com.ccc.reflection.Son");//Main -- Father -- Son 都被初始化
//不会发生类的初始化:
//1 当子类引用父类的静态变量时
//System.out.println(Son.f);//Main -- Father 都被初始化 100 子类没被初始化
//2 通过数组定义类引用
//Son[] s = new Son[5]; //Main 被初始化
//3 引用final常量不会触发此类的初始化
System.out.println(Son.S); //Main 被初始化 200
}
}
class Father{
static int f = 100;
static {
System.out.println("Father代码块被调用");
}
}
class Son extends Father{
static int s = 50;
static final int S = 200;
static {
System.out.println("Son代码块被调用");
}
}
7, 类加载器的作用
- 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
- 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维 持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AH4LREdr-1639023733673)(image-att\2-7.png)]
package com.ccc.reflection;
//类加载器
public class Test07 {
public static void main(String[] args) throws ClassNotFoundException {
//获得系统类的加载器
ClassLoader loader = ClassLoader.getSystemClassLoader();
System.out.println(loader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//获得系统类加载器的父类加载器-- 扩展类加载器
ClassLoader parent = loader.getParent();
System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@1b6d3586
//获取扩展类加载器的父类加载器-- 根加载器(c / c++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);//null 根加载器
//测试当前类是哪个加载器加载的
ClassLoader classLoader = Class.forName("com.ccc.reflection.Test07").getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//测试JDK内置的类是谁加载的
classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);//null 根加载器
//如何获得系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
//双亲委派机制
/*
并不是从下往上找,开始时一级一级往上委托寻找,从最高级(rt.jar)里找,找到了就直接加载这个,没找到再往下找
*/
}
}
03- 创建运行时类的对象
1, 通过反射获取运行时类的完整结构
Field, Method, Constructor, Superclass, Interface, Annotation
- 实现的全部接口
- 所继承的父类
- 全部的构造器
- 全部的方法
- 全部的Field
- 注解
package com.ccc.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
//获得类的信息
public class Test08 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.ccc.reflection.User");
//获得类的名字
System.out.println(c1.getName()); //包名+ 类名
System.out.println(c1.getSimpleName());// 获得类名
//获得类的属性
Field[] fields = c1.getFields(); //只能找到public属性
fields = c1.getDeclaredFields();//找到全部的属性
for (Field field : fields) {
System.out.println(field);
}
//获得指定属性的值
Field name = c1.getDeclaredField("name");
System.out.println(name);
//获得类的方法
Method[] methods = c1.getMethods();//获得本类和父类的全部public方法
Method[] declaredMethods = c1.getDeclaredMethods();//获得本类的所有方法
//获得指定方法
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
//获得指定的构造器
Constructor[] constructors = c1.getConstructors();
Constructor[] declaredConstructors = c1.getDeclaredConstructors();
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
System.out.println(declaredConstructor);
}
}
2, 有了Class对象, 可以做什么
- 创建类的对象:调用Class对象的newInstance()方法
- 1)类必须有一个无参数的构造器。
- 2)类的构造器的访问权限需要足够
- **思考?**难道没有无参的构造器就不能创建对象了吗?只要在操作的时候明确的调用类中的构造器并将参数传递进去之后,才可以实例化操作。
- 步骤如下:
- 1)通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器
- 2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
- 3)通过Constructor实例化对象
3, 调用指定的方法
- 通过反射,调用类中的方法,通过Method类完成。
- ①通过Class类的getMethod(String name, Class…parameterTypes)方法取得个Method对象,并设置此方法操作时所需要的参数类型。
- ②之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
Object invoke ( Object obj , 值)
- Object对应原方法的返回值,若原方法无返回值,此时返回null
- 若原方法若为静态方法,此时形参Object obj可为null
- 若原方法形参列表为空,则Object] args为null
- 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。
setAccessible
-
Method和Field, Constructor对象都有setAccessible()方法。
-
setAccessible——设置可访问的(设置访问性):true可访问,false不可访问
-
参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
- 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true.
- 使得原本无法访问的私有成员也可以访问
-
参数值为false则指示反射的对象应该实施Java语言访问检查
package com.ccc.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
//动态创建对象, 反射
public class Test09 {
public static void main(String[] args) throws Exception {
//获得Class对象
Class c1 = Class.forName("com.ccc.reflection.User");
//构造一个对象
//User user = (User) c1.newInstance();//本质是调用了类的无参构造, 没有无参构造异常:InstantiationException
//System.out.println(user);
//通过构造器创建一个对象
//Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
//User user = (User) constructor.newInstance("小明", 11, 11);
//System.out.println(user);//User{name='小明', id=11, age=11}
//通过反射调用普通方法
User user = (User) c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke(激活) 参数: (对象, "值")
setName.invoke(user, "小红");
System.out.println(user);//User{name='小红', id=0, age=0}
//通过反射操作属性
User user1 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
//setAccessible——设置可访问的(设置访问性):true可访问,false不可访问
name.setAccessible(true);
name.set(user1,"小刚");//报错, 不能操作private属性, 设置访问性才行
System.out.println(user1.getName());//小刚
}
}
性能测试
package com.ccc.reflection;
import java.lang.reflect.Method;
//分析性能问题
public class Test10 {
//普通方法
public static void test(){
User user = new User();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
user.getName();
}
long end = System.currentTimeMillis();
System.out.println("普通方法用时:"+(end-start)+"ms");
}
//反射方式调用
public static void test1() throws Exception {
User user = new User();
Class<User> c1 = User.class;
Method getName = c1.getDeclaredMethod("getName", null);
long start = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
getName.invoke(user,null);
}
long end = System.currentTimeMillis();
System.out.println("反射用时:"+(end-start)+"ms");
}
//反射方式调用 关闭检测
public static void test2() throws Exception {
User user = new User();
Class<User> c1 = User.class;
Method getName = c1.getDeclaredMethod("getName", null);
getName.setAccessible(true);
long start = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
getName.invoke(user,null);
}
long end = System.currentTimeMillis();
System.out.println("反射(关闭检测)用时:"+(end-start)+"ms");
}
public static void main(String[] args) throws Exception {
test();//普通方法用时:3ms
test1();//反射用时:172ms
test2();//反射(关闭检测)用时:107ms
}
}
04-反射操作泛型
- Java采用泛型擦除的机制来引入泛型, Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除
- 为了通过反射操作这些类型, Java新增了ParameterizedType , GenericArrayType
TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原
始类型齐名的类型 - ParameterizedType :表示一种参数化类型,比如Collection
- GenericArrayType :表示一种元素类型是参数化类型或者类型变量的数组类型
- TypeVariable :是各种类型变量的公共父接口
- WildcardType :代表一种通配符类型表达式
package com.ccc.reflection;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
public class Test11 {
public void test01(Map<String,User> map , List<User> list){
System.out.println("test01");
}
public Map<String,User> test02(){
System.out.println("test01");return null;
}
public static void main(String[] args) throws NoSuchMethodException {
//获取方法
Method method = Test11.class.getMethod("test01",Map.class,List.class);
//获取泛型的参数类型
Type[] genericParameterTypes = method.getGenericParameterTypes();
// Map, List
for (Type genericParameterType : genericParameterTypes) {
//判断泛型参数类型 是否等于参数化类型
if (genericParameterType instanceof ParameterizedType) {
//如果是,就获取参数信息getActualTypeArguments();
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
//打印
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
method = Test11.class.getMethod("test02",null);
//获取泛型的返回值类型
Type genericReturnType = method.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType) {
//如果是,就获取参数信息getActualTypeArguments();
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
//打印
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
05-反射操作注解
- getAnnotations
- getAnnottation
package com.ccc.reflection;
import java.lang.annotation.*;
import java.lang.reflect.Field;
//反射操作注解
public class Test12 {
public static void main(String[] args) throws Exception {
Class c1 = Class.forName("com.ccc.reflection.Student2");
//通过反射获取注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);//@com.ccc.reflection.Table(value=db_student)
}
//获得注解的value值
Table table = (Table) c1.getAnnotation(Table.class);
String value = table.value();
System.out.println(value);//db_student
//获得指定的注解
Field field = c1.getDeclaredField("name");
Field1 annotation = field.getAnnotation(Field1.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@Table("db_student")
class Student2{
@Field1(columnName = "db_id",type = "int",length = 10)
private int id;
@Field1(columnName = "db_name",type = "varchar",length = 10)
private String name;
@Field1(columnName = "db_age",type = "int",length = 10)
private int age;
public Student2() {
}
public Student2(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}
//类名的注解
@Target(ElementType.TYPE)//类名
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}
@Target(ElementType.FIELD)//属性
@Retention(RetentionPolicy.RUNTIME)
//属性的注解
@interface Field1{
String columnName();
String type();
int length();
}