传送门==>B站遇见狂神说–反射和注解
笔记和练习只是跟着视频整理的;但是有的知识点并没有整理进来.
1.什么是注解
Annotation: 从JDK5开始,Java增加对元数据的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。
- 不是程序本身,只是向程序作出解释,这一点与注释(comment)没太大区别
- 可以被其他程序(例如:编译器)读取
注解的格式: 以 @注释名 在代码中;还可以添加参数值,例如
@SuppressWarnings(value=“unchecked”).
注解的使用位置 :可附加在package,class,method,field…;可通过反射机制编程实现对这些元数据的访问.
例如: 之前经常用到的重写方法注解 @Override
在学习拉莫达表达式时见过的 函数式接口注解 @FunctionalInterface
已弃用的方法/类/包: 弃用标识注解 @Deprecated
2.内置注解
@Override 定义在java.lang.Override 中,仅适用于注释方法;表示该方法是重写超类中的一个对应方法声明.
@Deprecated 定义在java.lang.Deprecated 中,可注释 方法,属性,类, 标示已过时(弃用);一般被该注解注释的元素中间会有一条线划过.;只是不推荐使用,而不是不能使用.
@SuppressWarnings 定义在java.lang.SuppressWarnings中,被该注解修饰的元素以及该元素的所有子元素取消显示编译器警告,例如修饰一个类,那他的字段,方法都是显示警告;
String[] value()----表示使用时需要指定参数;
案例 @SuppressWarnings(value={“deprecation”, “unchecked”}) 表示不再建议使用警告"和"未检查的转换时的警告"不再进行检查。
参数字段 | 说明 |
---|
deprecation | 使用了不赞成使用的类或方法时的警告 | unchecked | 执行了未检查的转换时的警告 | fallthrough | 当 Switch 程序块直接通往下一种情况而没有 Break 时的警告。 | path | 在类路径、源文件路径等中有不存在的路径时的警告。 | serial | 当在可序列化的类上缺少 serialVersionUID 定义时的警告。 | finally | 任何 finally 子句不能正常完成时的警告。 | all | 关于所有情况的警告。 |
3.元注解
元注解是负责为其他注解做注解;位于java.lang.annotation包下;
@Target:描述注解的使用范围(作用域)
参数: TYPE —标注"类、接口(包括注释类型)或枚举声明"。FIELD—标注"字段声明"。 METHOD —标注"方法"。PARAMETER —标注"参数"。CONSTRUCTOR —标注"构造方法"。 LOCAL_VARIABLE —标注"局部变量"。
@Retention 表示需要在什么级别保存该注释信息;用于描述注解的声明周期(SOURCE(源码级别 )<CLASS(class级别 )<RUNTIME运行时级别 ) ;一般在自定义注解时用runtime;
SOURCE:仅在源文件中,当Java文件编译为class文件的时,注解不能使用; CLASS:注解被保留到class文件,当虚拟机加载class文件,就不能使用; RUNTIME:虚拟机加载class文件之后,还可使用该注解
@Document 说明该注解会被包含在javadoc文档;
@Inherited 表示子类可以继承父类的这个注解
4.自定义注解
@interface 使用自定义注解 ,自动继承java.lang.annotation.Annotation接口
- @interface用来声明一个注解,格式为:public @interface 注解名{定义内容}
- 注解使用参数时; 参数类型+参数名( );
- 在使用注解,进行赋值时,可以不在乎顺序;因为有参数名
- 每个方法实际上声明了一个配置参数;
- 方法的名称就是参数名称;
- 返回值类型就是参数类型(基本类型学;Class,String,enum);
- 可通过default来声明参数的默认值;经常使用空字符串或0作为默认值;
如果默认值为-1,表示不存在; - 若定义注解时;只有一个参数成员;建议参数名为value( ),那么在使用这个注解时,参数赋值还能省略value()指向;
- 注解元素要有值,不然就写上默认值.
练习自定义注解
public class MyClass {
@MyAnnotation(id = 100,age = 21,name = "小智",course = {"Java"})
public void myMethod(){
}
@MyAnnotation1("小智")
public void myMethod1(){
}
}
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
String name() default "";
int age() default 0;
int id() default -1;
String[] course() default {"C++"};
}
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation1{
String value();
}
5.反射概述
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
动态语言 在运行时可以改变其结构的语言:例如新的函数,对象,甚至是代码都可被引进;已经存在的函数可以被删除或者其他的结构改变; 也就是在运行时的代码可以根据某些条件改变自身结构; 主要的动态语言:Object_C;C#,PHP,Python,JavaScript;
静态语言 运行时结构不可变的语言,例如Java,C,C++; 虽然Java不是动态语言,但 Java作为 准动态语言 ;有动态性,可使用反射机制获得类似动态语言的特性;
java reflection 反射(reflection)使得Java变为类似动态语言;反射机制允许程序在执行期借助于反射API获取任何类的内部信息,并且可以直接操作任意对象的内部属性以及方法.
Class c=Class.forName(“java.lang.String”)
一个类在内存中只有一个Class对象;一个类被加载后,整个结构都会被封装到Class对象中
加载类之后,在堆内存的方法区中产生了一个Class类型的对象 (注意一个类只有一个Class对象);此对象包含了完整的类结构信息;可以通过这个对象看到类的结构.
正常方式: 引入需要的"包类"名称 —>通过new实例化 —>取得实例化对象
反射方式: 实例化对象 —>getClass( )方法 —>得到完整的"包类"名称
反射通俗来讲就是通过对象反射求出类的名称.
6.获得反射对象
Java反射机制提供的功能
(1)在运行时判断任意一个对象所属的类; (2)在运行时构造任意一个类的对象; (3)在运行时(判断/调用)任意一个类所具有的成员变量和方法; (4) 在运行时获取泛型信息; (5)在运行时可处理注解,(6)可以生成动态代理;
Java反射的优缺点
优点 :可实现动态创建对象和编译 缺点 :对于性能有影响;使用反射基本上是一种解释操作;总是慢于直接执行相同的操作.
java.lang.Class :代表一个类; java.lang.reflect.Method :代表类的方法 java.lang.reflect.Field :代表类的成员变量 java.lang.reflect.Constructor :代表类的构造器
练习:
public class MyClass {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> name1 = Class.forName("com.xiaozhi.day05_javareflection.User");
System.out.println(name1);
Class<?> name2 = Class.forName("com.xiaozhi.day05_javareflection.User");
Class<?> name3 = Class.forName("com.xiaozhi.day05_javareflection.User");
Class<?> name4 = Class.forName("com.xiaozhi.day05_javareflection.User");
System.out.println(name1.hashCode());
System.out.println(name2.hashCode());
System.out.println(name3.hashCode());
System.out.println(name4.hashCode());
}
}
class User{
String name;
int age;
int id;
public User(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
}
7.获取Class类的方式
Class类: 对象反射后可得到的信息: 类的属性,方法和构造器,这个类实现的接口; 对于每个类;jre都保留了一个不变的Class类型的对象; 一个Class对象包含了特定的结构(class/interface/enum/annotation/primitive type/void[])有关信息
Class作为一个类,它只能由系统创建对象;JVM中一个加载的类只有一个Class实例 ; 而一个Class对象对应的是一个加载到JVM中的一个.class字节码文件; 每个类的实例都会标记自己的Class实例来源; 通过Class可完整得到一个类中的所有被加载结构;
常用方法 | 注释 |
---|
static Class.forName() | 返回指定name的Class对象 | getName() | 返回Class对象表示的实体(类,接口,数组类,void[] )的名称 | Object newInstance() | 调用缼省构造函数,返回Class对象实例 | Class getSuperClass() | 返回当前Class对象的父类Class对象 | Class[] getinterfaces() | 返回当前Class对象的接口 | ClassLoader getClassLoader() | 获取类的类加载器 | Constructor[]getConstructors() | 返回包含Constructor对象的数组 | Method getMethod(String name,Class…T) | 返回一个Method对象,形参为paramType | Field[] getDeclaredFields() | 返回Field对象的一个数组 |
如何获取Class类的对象呢; (1)若已知实体类;可通过类的class属性 获取,安全可靠,性能高;
Class Class对象=实体类.class;
(2)已知实体类的实例,调用实例的getClass()方法 获取Class对象;
Class Class对象=实体类对象.getClass();
(3)已知类的全类名,且在该类的类路径在,可使用Class类的静态方法forName() 获取,但是会抛出异常 ClassNotFoundException
Class Class对象=Class.forName(“类的路径”);
(4)内置基本数据类型可以直接用类名.Type
练习:
public class Demo{
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);
System.out.println(c1.hashCode());
Class c2= Class.forName("com.xiaozhi.day06_studyclass.Student");
System.out.println(c2);
System.out.println(c2.hashCode());
Class c3 = Student.class;
System.out.println(c3);
System.out.println(c3.hashCode());
Class superclass = c1.getSuperclass();
System.out.println("父类 "+superclass);
Class<Character> type = Character.TYPE;
System.out.println(type);
}
}
class Person{
String name;
public Person(String name) {
this.name = name;
}
public Person() {
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student() {
this.name="我是学生";
}
}
8.所有类型的Class对象
具有Class对象的; class(外部类,常用内部类,静态内部类,局部内部类,匿名内部类 ); interface(接口 ); [ ] (数组);enum(枚举 ); annotation(注解 ) ;primitive type(基本数据类型 ) ;void.
对于数组;只要数组的类型维度一样;Class就一样;
练习:
public class Demo {
public static void main(String[] args) {
Class c1=String.class;
System.out.println(c1);
Class c2=Runnable.class;
System.out.println(c2);
Class c3=String[].class;
System.out.println(c3);
Class c4=String[][].class;
System.out.println(c4);
Class c5=Target.class;
System.out.println(c5);
Class c6= ElementType.class;
System.out.println(c6);
Class c7=void.class;
System.out.println(c7);
Class c8=int.class;
System.out.println(c8);
System.out.println("===============");
int[] arr0=new int[10];
int[][] arr1=new int[1][2];
int[][] arr2=new int[10][100];
System.out.println(arr0.getClass().hashCode());
System.out.println(arr1.getClass().hashCode());
System.out.println(arr2.getClass().hashCode());
}
}
9.类加载内存分析
JAVA内存简化;
堆:存放 new 的对象和数组 可被所有线程共享,不会存放别的对象引用
栈:存放基本变量类型(包含这个基本类型的具体数值);以及引用对象的变量(存放的是 引用 在 堆中的具体地址)
方法区:存放 class和static变量; 可以被所有的线程共享
类加载过程; 当程序主动使用某个类时,若该类还未被加载到内存中,则系统会通过 加载–> 链接–> 初始化.进行初始化;
加载(Load): 将类的class字节码文件读入内存,将这些静态数据转换为方法区的运行时数据结构,且创建一个java.lang.Class对象代表这个类;由类加载器完成;
链接(link): 将类的二进制数据合并到jvm的运行状态; (1)首先确保加载的类信息符合jvm规范; (2)正式在方法区中为类变量分配内存且设置类变量默认初始值;(3)虚拟机常量池内的符号引用(常量名) 替换 为直接引用地址.
初始化(initialize):执行类构造器 <clinit>() 方法的过程. (1)类构造器 <clinit> ()方法是由编译期自动收集类中所有的类变量的赋值行为和静态代码块中的语句合并产生.(类构造器是构造类信息,不是用来构造该类对象的) (2)当初始化类时,若发现其父类还未初始化,先触发父类初始化; (3)虚拟机确保类的<clinit> ()方法在多线程环境中会加锁同步
练习
public class Demo {
public static void main(String[] args) {
Demo2 demo2=new Demo2();
System.out.println(demo2.a);
}
}
class Demo2{
static {
System.out.println("Demo2的静态代码块初始化");
a=200;
}
static int a=10;
public Demo2() {
System.out.println("Demo2的构造方法初始化");
}
}
10.分析类的初始化
类的主动引用(会发生类的初始化)
(1)当虚拟机启动,先初始化main方法所在的类; (2)new 类的对象 ; (3)调用类的静态成员(final常量除外 )或者静态方法; (4)使用java.lang.reflect包的方法对类进行反射调用; (5)继承关系中,先初始化父类;
类的被动引用(不会发生类的初始化)
(1)当访问静态域时,只有真正声明此静态域的类才被初始化; 例如:通过子类引用父类的静态变量,并不会导致子类初始化; (2)通过数组定义的类引用,不会触发此类的初始化; (3)引用常量不会触发此类的初始化,这是由于常量在链接 时就存入调用类的常量池中
练习
主动引用;创建子类时,先加载的是父类构造初始化;
public class Demo {
static {
System.out.println("main方法被加载");
}
public static void main(String[] args) {
Son son=new Son();
}
}
class Father{
static int a=10;
static {
System.out.println("父类被加载");
}
}
class Son extends Father{
static {
System.out.println("子类被加载");
s=100;
}
static int s=10;
final static int M=2000;
}
输出:
main方法被加载
父类被加载
子类被加载
注释掉;使用反射机制;也是主动引用.
Class.forName("com.xiaozhi.day08_initclass.Son");
输出
main方法被加载
父类被加载
子类被加载
注释掉;试试 被动引用;子类去调用父类的静态属性;
System.out.println(Son.a);
输出:
main方法被加载
父类被加载
10
注释掉;试试 被动引用,创建数组
Father[] f=new Father[15];
输出
main方法被加载
注释掉,试试被动引用,调用子类的常量
System.out.println(Son.M);
输出
main方法被加载
2000
11.类加载器
类缓存:标准的JavaSe类加载器可按要求查找类,一旦某个类被加载到类加载器中,将维持加载(缓存一段时间),但JVM可以回收这些Class对象.
源程序(.java文件) —>Java编译器 —>字节码(.class)文件 —>类装载器 —>字节码校验器 —>解释器 —>操作系统平台
类加载器 把类装载进内存
引导类加载器 使用C++编写的,作为JVM自带的类加载器,负责Java平台核心库,用来装载核心类库;注意该加载器无法直接获取 .
扩展类加载器 负责jre/lib/ext目录下的jar包或者 java.ext.dirs指定目录下的jar包装入工作库.
系统类加载器 负责java -classpath 或者 java.class.path 指定的目录下的类与jar包装入库
练习
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
ClassLoader systemClassLoaderParent = systemClassLoader.getParent();
System.out.println(systemClassLoaderParent);
ClassLoader parent = systemClassLoaderParent.getParent();
System.out.println(parent);
ClassLoader loader = Class.forName("com.xiaozhi.day09_aboutclassloader.Demo").getClassLoader();
System.out.println(loader);
ClassLoader loader1 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(loader1);
System.out.println(System.getProperty("java.class.path"));
}
}
12.获取类的运行时结构
通过反射可获取运行时类的完整结构.
getName() 获取类的具体类名 getSimpleName() 获得类名;
getField(“指定属性”) 仅获取指定public属性 getFields( ) 仅获取public属性
getDeclaredField(“指定属性”) 获取指定属性 getDeclaredFields( ):可获取到所有属性
getMethods( ) :可获取类的以及父类的所有public方法 getDeclaredMethods() :仅获取本类的所有方法
getMethod("方法名" ,参数类型.class ):获取指定的方法;注意(若为无参方法则在参数类型处使用null )
getConstructors();获取public 构造方法 getDeclaredConstructors():获取所有构造方法 getDeclaredConstructor(类型.class,类型.class)获取指定的构造方法;
练习
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class<?> c = Class.forName("com.xiaozhi.day10_classruntime.MyClass");
String name = c.getName();
System.out.println(name);
String simpleName = c.getSimpleName();
System.out.println(simpleName);
System.out.println("----------------------------");
Field[] fields = c.getFields();
Field[] fields1 = c.getDeclaredFields();
for (Field field : fields1) {
System.out.println(field);
}
Field age = c.getDeclaredField("age");
System.out.println(age);
System.out.println("----------------------------");
Method[] methods = c.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("----------------------------");
Method[] declaredMethods = c.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("----------------------------");
Method getAge = c.getMethod("getAge", null);
System.out.println(getAge);
Method setAge = c.getMethod("setAge", int.class);
System.out.println(setAge);
System.out.println("----------------------------");
Constructor<?>[] constructors = c.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
Constructor<?>[] declaredConstructors = c.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
Constructor<?> myConstructor = c.getDeclaredConstructor( String.class, int.class);
System.out.println(myConstructor);
}
}
class MyClass{
private String name;
private int age;
public MyClass() {
}
public MyClass(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "MyClass{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
13.动态创建对象执行方法
使用反射获取类的Class对象;调用newInstance() ;创建对象 实际上是调用了类的无参构造器;
通过构造器创建对象; 先得到Class对象;在通过Class对象调用获取构造器; 然后 通过得到的构造器调用newInstance() 方法;创建对象
使用反射,可以调用类的方法; 用Class对象调用getMethod( 方法名,类型) ,获取Method 对象; 使用Object invoke(对象,设置的值) 方法激活;即可调用.
对于Object invoke(对象,设置的值) 方法; Object作为原方法的返回值;若原方法无返回值,则返回null ; 若原方法为静态方法 ,则参数:对象可以为null ; 若原方法是无参数的 ,则设置的值为null ; 若原方法为private私有修饰的 ,在调用invoke()之前 ,应该先调用方法对象的setAccessible(true)方法 .
元素为private私有的;不能直接操作私有属性,要先关闭程序的安全监测; 使用方法setAccessible(true) 设置为可通过检查;该方法默认为false
setAccessible方法时用来启动/禁用访问安全检查
练习;
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class c = Class.forName("com.xiaozhi.day11_creatobjformethod.MyClass");
MyClass myClass = (MyClass)c.newInstance();
Constructor declaredConstructor = c.getDeclaredConstructor(String.class, int.class);
MyClass myClass1 = (MyClass) declaredConstructor.newInstance("小智", 21);
MyClass myClass2 = new MyClass();
Method setAge = c.getMethod("setAge", int.class);
setAge.invoke(myClass2, 88);
MyClass myClass3 = new MyClass();
Field age = c.getDeclaredField("age");
age.setAccessible(true);
age.set(myClass3,1000);
System.out.println(myClass3.getAge());
}
}
class MyClass{
private String name;
private int age;
public MyClass() {
}
public MyClass(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "MyClass{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
14.性能对比分析
练习
public class Demo {
public static void test() {
MyClass myClass=new MyClass();
long start=System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
myClass.getAge();
}
long end=System.currentTimeMillis();
System.out.println("普通方法调用=>"+(end-start)+"ms");
}
public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
MyClass myClass = new MyClass();
Class c = myClass.getClass();
Method method = c.getDeclaredMethod("getAge",null);
long start=System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
method.invoke(myClass,null);
}
long end=System.currentTimeMillis();
System.out.println("反射方式调用=>"+(end-start)+"ms");
}
public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
MyClass myClass = new MyClass();
Class c = myClass.getClass();
Method method = c.getDeclaredMethod("getAge",null);
method.setAccessible(true);
long start=System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
method.invoke(myClass,null);
}
long end=System.currentTimeMillis();
System.out.println("反射方式且关闭监测调用=>"+(end-start)+"ms");
}
public static void main(String[] args) {
test();
try {
test2();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
try {
test3();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
15.获取泛型信息
java采用了泛型擦除 机制来引入泛型;泛型仅为编译器javac使用;确保数据的安全性以及避免强制类型转换问题;编译完成后就会擦除.
为通过反射操作这些类型;java新增ParameterizedType,GenericArrayType,TypeVariable,WildcardType 表示不能被归一到Class类中的类型但有和原始类型同名的类型;
ParameterizedType,表示参数化类型;例Collection< String> GenericArrayType,表示元素类型为参数化类型或类型变量的数组类型; TypeVariable, 各种类型变量的公共父接口; WildcardType ; 通配符类型表达式
练习
public class Demo {
public void test1(Map<MyClass,String> map, List<MyClass> list){
System.out.println("方法一");
}
public Map<MyClass,String> test2(){
System.out.println("方法二");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Method method1 = Demo.class.getMethod("test1", Map.class, List.class);
Type[] types = method1.getGenericParameterTypes();
for (Type type : types) {
System.out.println("方法1=>"+type);
if(type instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("方法1真实类型=>"+actualTypeArgument);
}
}
}
System.out.println("----------------------------------------------");
Method method2 = Demo.class.getMethod("test2",null);
Type types2 = method2.getGenericReturnType();
if(types2 instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) types2).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("方法2真实类型=>"+actualTypeArgument);
}
}
}
}
16.获取注解信息
首先得到类的Class对象; 调用getAnnotation(指定注解) 方法;获取指定的注解对象; 然后使用注解对象.参数值;调用获取注解的参数值;
练习
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c = Class.forName("com.xiaozhi.day14.MyClass");
Annotation[] annotations = c.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
ClassAnnotation annotation = (ClassAnnotation)c.getAnnotation(ClassAnnotation.class);
System.out.println(annotation.value());
Field name = c.getDeclaredField("name");
name.setAccessible(true);
FieldAnnotation annotation1 = name.getAnnotation(FieldAnnotation.class);
System.out.println(annotation1.columnName());
System.out.println(annotation1.type());
System.out.println(annotation1.length());
}
}
@ClassAnnotation("小智")
class MyClass{
@FieldAnnotation(columnName = "id",type = "int",length = 10)
private int id;
@FieldAnnotation(columnName = "age",type = "int",length = 3)
private int age;
@FieldAnnotation(columnName = "name",type = "String",length = 10)
private String name;
public MyClass() {
}
public MyClass(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "MyClass{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface ClassAnnotation{
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldAnnotation{
String columnName();
String type();
int length();
}
|