写点什么

阿里 P8 教你 Java 注解与反射

发布于: 2021 年 08 月 09 日
阿里P8教你Java注解与反射

我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的 JavaLib」第一时间阅读最新文章喔!


Ⅰ 什么是注解

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。


注解以@注解名的形式存在于代码中,例如@Override,还可以添加一些参数值,例如@Auth(value = "super")

Ⅱ 内置注解

Java 有 10 个内置 注解,6 个注解是作用在代码上的,4 个注解是负责注解其他注解的(即元注解),元注解提供对其他注解的类型说明。


Ⅲ 自定义注解

使用 @interface在这里插入代码片定义注解,而且自动继承java.lang.annotation.Annotation接口。


  • 格式为 public @interface 注解名 {定义内容}

  • 其中每一个方法实际是声明了一个参数

  • 方法的名称就是参数的名称

  • 返回值类型就是参数的类型,而且返回值类型只能是基本类型,Class,String,enum。

  • 可以通过 default 关键字来声明参数的默认值,一般会使用空字符串或者 0

  • 如果只有一个参数,一般参数名为 value,而且使用注解时,赋值可以不显示写出参数名,直接写参数值

  • 定义了参数,如果没有默认值,就一定要显示赋值

Ⅳ 注解案例

import java.lang.annotation.*;
/** * @author Mr.nobody * @Description 自定义注解 * @date 2020/8/30 */@Target(ElementType.METHOD) // 此注解只能用在方法上。@Retention(RetentionPolicy.RUNTIME) // 此注解保存在运行时,可以通过反射访问。@Inherited // 说明子类可以集成父类中的此注解。@Documented // 此注解包含在用户文档中。public @interface CustomAnnotation { String value(); // 使用时需要显示赋值 int id() default 0; // 有默认值,使用时可以不赋值}
复制代码


/** * @author Mr.nobody * @Description 测试注解 * @date 2020/8/30 */public class TestAnnotation {
// @CustomAnnotation(value = "test") 只能注解在方法上,这里会报错 private String str = "Hello World!";
@CustomAnnotation(value = "test") public static void main(String[] args) { System.out.println(str); }}
复制代码

Ⅴ Java 反射机制

讲解反射前,我们先来谈谈静态语言和动态语言。

动态语言是一类在运行时可以改变其结构的语言。例如新的函数,对象,甚至代码可以被引进,已有的函数可以被删除或者结构上的一些变化。简单说即是在运行时代码可以根据某些条件改变自身结构。动态语言主要 C#,Object-C,JavaScript,PHP,Python 等。

静态语言是运行时结构不可改变的,例如 Java,C,C++等。

Java 不是动态语言,但是她可以称为准动态语言,因为 Java 可以利用反射机制获得类似动态语言的特性,Java 的动态性让它在编程时更加灵活。


反射机制允许程序在执行期借助于 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性以及方法等。类在被加载完之后,会在堆内存的方法区中生成一个 Class 类型的对象,一个类只有一个 Class 对象,这个对象包含了类的结构信息。我们可以通过这个对象看到类的结构。例如可以通过如下方法获取 String 类的 Class 对象:Class c = Class.forName("java.lang.String"); 当然,每个类都隐式继承 Object 类,Object 类有个getClass()方法也能获取 Class 对象。

5.1 Java 反射机制提供的功能

  1. 在运行时判断任意一个对象所属的类

  2. 在运行时构造任意一个类的对象

  3. 在运行时判断任意一个类具有的成员变量和方法

  4. 在运行时获取泛型信息

  5. 在运行时调用任意一个对象的成员变量和方法

  6. 在运行时处理注解

  7. 生成动态代理

  8. ...

5.2 Java 反射机制的优缺点

  • 优点:实现动态创建对象和编译,有更加的灵活性。

  • 缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉 JVM,我们想要做什么然后它满足我们的要求,这类操作总是慢于直接执行相同的操作。

5.3 Java 反射相关的主要 API

  • java.lang.Class:代表一个类

  • java.lang.reflect.Method:代表类的方法

  • java.lang.reflect.Field:代表类的成员变量

  • java.lang.reflect.Constructor:代表类的构造器

  • ...

5.4 Class 类

通过 Class 对象可以得知某个类的属性,方法,构造器,注解,以及实现了哪些接口等等信息。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定的结构(class,interface,enum,annotation,private type,void,[])的有关信息。


  • Class 本身也是一个类

  • Class 对象只能系统建立

  • 一个加载的类在 JVM 中只有一个 Class 对象

  • 一个 Class 对象一般对应的是一个加载到 JVM 中的一个.class 文件

  • 每个类的实例都会记得自己是右哪个 Class 实例所生成的

  • 通过 Class 可以完整地得到一个类中的所有被加载的结构

  • Class 类是 Reflection 的根源,针对任何你想动态加载,运行的类,唯有先获得相应的 Class 对象


Class 类的常用方法:


  • static forName(String name):返回指定类名 name 的 Class 对象

  • Object newInstance():调用缺省构造方法,返回 Class 对象的一个实例

  • getName():返回 Class 对象所表示的实体(类,接口,数组类或 void)的名称。

  • Class getSuperClass():返回当前 Class 对象的父类的 Class 对象

  • Class[] getInterfaces():获取当前 Class 对象的接口

  • ClassLoader getClassLoader():获取当前类的类加载器

  • Constructor[] getConstructors():获取一个包含某些 Constructor 对象的数组

  • Method getMethod(String name, Class.. T):返回一个 Method 对象,此对象的形参类型为 paramType

  • Field[] getDeclaredFields():返回 Field 对象的一个数组


获取运行时类的完整结构


通过反射可以获取运行时类的完整结构:


  1. Field

  2. Method

  3. Constructor

  4. Superclass

  5. Interface

  6. Annatation

  7. ...


调用指定的方法:Object invoke(Object obj, Object ... args)


  • 第一个 Object 对应原方法的返回值,若原方法无返回值,则返回 null

  • 若原方法为静态方法,则参数 obj 可为 null

  • 若原方法形参列表为空,则参数 args 为 null

  • 若原方法声明为 private,则调用 invoke 方法前,需要显示调用方法对象的 setAccessible(true)方法,才可访问 private 方法。


package com.nobody;
import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;
/** * @author Mr.nobody * @Description * @date 2020/9/4 */public class Test01 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { Class<?> aClass = Class.forName("com.nobody.Student");
System.out.println(aClass.getName()); System.out.println(aClass.getSimpleName());
System.out.println("-----------------------"); System.out.println("获取public的属性"); Field[] fields = aClass.getFields(); for (Field field : fields) { System.out.println(field); }
System.out.println("-----------------------"); System.out.println("获取全部的属性"); Field[] declaredFields = aClass.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField); }
System.out.println("-----------------------"); System.out.println("获取指定名称的属性"); Field name = aClass.getDeclaredField("name"); System.out.println(name);
System.out.println("-----------------------"); System.out.println("获取本类和父类的全部public方法"); Method[] methods = aClass.getMethods(); for (Method method : methods) { System.out.println(method); }
System.out.println("-----------------------"); System.out.println("获取本类的方法"); Method[] declaredMethods = aClass.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); }
System.out.println("-----------------------"); System.out.println("获得指定名字的方法"); Method getName = aClass.getDeclaredMethod("getName"); System.out.println(getName); Method setName = aClass.getDeclaredMethod("setName", String.class); System.out.println(setName);
System.out.println("-----------------------"); System.out.println("获得构造器"); Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); }
System.out.println("-----------------------"); System.out.println("获取指定的构造器"); Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class); System.out.println(declaredConstructor);
System.out.println("-----------------------"); System.out.println("生成类的实例"); Student student = (Student) aClass.newInstance(); Student student1 = (Student) declaredConstructor.newInstance("小明", 20); System.out.println(student); System.out.println(student1);
System.out.println("-----------------------"); System.out.println("调用实例的方法"); setName.invoke(student, "小花"); System.out.println(student);
System.out.println("-----------------------"); System.out.println("使用实例的属性"); name.setAccessible(true); // 因为student的name变量是私有的,所以要加此行代码,关闭安全检测 name.set(student, "小红"); System.out.println(student); }
}
class Student {
private String name;
public int age;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
private void testMethod01() {
}
public Student() { }
public Student(String name, int age) { this.name = name; this.age = age; }
@Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; }}
复制代码


输出结果:


com.nobody.StudentStudent-----------------------获取public的属性public int com.nobody.Student.age-----------------------获取全部的属性private java.lang.String com.nobody.Student.namepublic int com.nobody.Student.age-----------------------获取指定名称的属性private java.lang.String com.nobody.Student.name-----------------------获取本类和父类的全部public方法public java.lang.String com.nobody.Student.toString()public java.lang.String com.nobody.Student.getName()public void com.nobody.Student.setName(java.lang.String)public final void java.lang.Object.wait() throws java.lang.InterruptedExceptionpublic final void java.lang.Object.wait(long,int) throws java.lang.InterruptedExceptionpublic final native void java.lang.Object.wait(long) throws java.lang.InterruptedExceptionpublic boolean java.lang.Object.equals(java.lang.Object)public native int java.lang.Object.hashCode()public final native java.lang.Class java.lang.Object.getClass()public final native void java.lang.Object.notify()public final native void java.lang.Object.notifyAll()-----------------------获取本类的方法public java.lang.String com.nobody.Student.toString()public java.lang.String com.nobody.Student.getName()public void com.nobody.Student.setName(java.lang.String)private void com.nobody.Student.testMethod01()-----------------------获得指定名字的方法public java.lang.String com.nobody.Student.getName()public void com.nobody.Student.setName(java.lang.String)-----------------------获得构造器public com.nobody.Student()public com.nobody.Student(java.lang.String,int)-----------------------获取指定的构造器public com.nobody.Student(java.lang.String,int)-----------------------生成类的实例Student{name='null', age=0}Student{name='小明', age=20}-----------------------调用实例的方法Student{name='小花', age=0}-----------------------使用实例的属性Student{name='小红', age=0}
复制代码


获取 Class 类的实例


  1. 若已知具体的类,可通过 class 属性获取,此方法最为安全可靠,程序性能最高。Class clazz = User.class;

  2. 若已知某个类的实例,可调用此实例的 getClass()方法获取。Class clazz = user.getClass();

  3. 若已知一个类的全类名,且此类在类路径下,可通过 Class 类的静态方法 forName()方法获取,可能会抛出 ClassNotFoundException。Class clazz = Class.forName("com.nobody.User");

  4. 内置基本数据类型可以直接通过类名.Type 获取

  5. 可以通过 ClassLoader 获取


package com.nobody;
/** * @author Mr.nobody * @Description * @date 2020/9/2 */public class User extends Person {
private String name;
public static void main(String[] args) throws ClassNotFoundException {
User user = new User();
Class<User> userClass = User.class; Class<? extends User> aClass = user.getClass(); Class<?> aClass1 = Class.forName("com.nobody.User");
System.out.println(userClass.hashCode()); System.out.println(aClass.hashCode()); System.out.println(aClass1.hashCode());
Class<? super User> superclass = userClass.getSuperclass(); System.out.println(superclass);
Class<Integer> type = Integer.TYPE; System.out.println(type);
}}
class Person {
}
复制代码


输出结果:


685325104685325104685325104class com.nobody.Personint
复制代码


有 Class 对象的类型


  1. class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。

  2. interface:接口

  3. []:数组

  4. enum:枚举

  5. annotation:注解

  6. primitive type:基本数据类型(例如 Integer,Double 等)

  7. void:空类型


package com.nobody;
import java.lang.annotation.Documented;import java.util.List;
/** * @author Mr.nobody * @Description * @date 2020/9/2 */public class ClassTest {
public static void main(String[] args) { Class<Object> objectClass = Object.class; Class<List> listClass = List.class; Class<String[]> aClass = String[].class; Class<String[][]> aClass1 = String[][].class; Class<Override> overrideClass = Override.class; Class<Documented> documentedClass = Documented.class; Class<Integer> integerClass = Integer.class; Class<Void> voidClass = void.class; Class<Class> classClass = Class.class;
System.out.println(objectClass); System.out.println(listClass); System.out.println(aClass); System.out.println(aClass1); System.out.println(overrideClass); System.out.println(documentedClass); System.out.println(integerClass); System.out.println(voidClass); System.out.println(classClass); }}
复制代码


输出结果:


class java.lang.Objectinterface java.util.Listclass [Ljava.lang.String;class [[Ljava.lang.String;interface java.lang.Overrideinterface java.lang.annotation.Documentedclass java.lang.Integervoidclass java.lang.Class
复制代码

Ⅵ 类加载过程

当程序主动使用某个类时,如果此类还未被加载到内存中,则系统会通过以下三个步骤来对此类进行初始化。


  1. 类的加载(load):将类的 class 文件字节码加载到内存中,并将这些静态数据转换为方法区的运行时数据结构,然后生成一个代表这个类的 java.lang.Class 对象,此过程由类加载器完成。

  2. 类的链接(Link):将类的二进制代码合并到 JVM 的运行状态之中的过程。

  3. 验证:确保加载的类的信息符合 JVM 规范,确保安全;

  4. 准备:正式为类变量(static)分配内存并设置变量默认初始值的阶段,这些内存都将在方法区中进行分配;

  5. 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程;

  6. 类的初始化(Initialize):JVM 对类进行初始化。

  7. 执行类构造器<clinit>()方法的过程,类构造器<clinit>()方法是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)

  8. 当初始化一个类的时候,如果发现其父类还没被初始化,则先进行父类初始化。

  9. 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。


package com.nobody;
/** * @author Mr.nobody * @Description * @date 2020/9/3 */public class TestA {
public static void main(String[] args) { Child child = new Child(); System.out.println(">>> age = " + Child.age); }}
class Child extends Father {
static { System.out.println(">>> Child static block..."); age = 20; }
static int age = 18;
public Child() { System.out.println(">>> Child constructor..."); }
}
class Father {
static { System.out.println(">>> Father static block..."); }
public Father () { System.out.println(">>> Father constructor..."); }
}
复制代码


输出结果为:


>>> Father static block...>>> Child static block...>>> Father constructor...>>> Child constructor...>>> age = 18
复制代码


如果 Child 类的静态代码块和 static int age = 18;语句位置交换,则最后 age 的值为 20(0 -> 18 -> 20)。因为编译器会按定义顺序自动收集类中所有类变量的赋值动作静态代码块中的语句合并产生类构造器<clinit>()方法。


class Child extends Father {
static int age = 18;
static { System.out.println(">>> Child static block..."); age = 20; }
public Child() { System.out.println(">>> Child constructor..."); }}
复制代码


输出结果为:


>>> Father static block...>>> Child static block...>>> Father constructor...>>> Child constructor...>>> age = 20
复制代码

5.1 何时会发生类初始化

类的主动引用(一定会发生类的初始化)


  • 当虚拟机启动,先初始化 main 方法所在的类

  • new 一个类的对象

  • 调用类的静态成员(除 final 常量外)和静态方法

  • 使用 java.lang.reflect 包的方法对类进行反射调用

  • 当初始化一个类,如果其父类未被初始化,则先初始化其父类


类的被动引用(不会发生类的初始化)


  • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。例如,当通过子类引用父类的静态变量或静态方法,不会导致子类初始化。Child.age;,而 age 属性是在父类定义的。

  • 通过数组定义类引用,也不会触发此类的初始化。Child[] childs = new Child[5];

  • 引用常量不会触发此类的初始化,因为常量在链接阶段就存入调用类的常量池中了。

Ⅶ 类加载器 Classloader

类加载器的作用:将 class 文件字节码加载到内存中,并将这些静态数据转换为方法区的运行时数据结构,然后在堆中生成一个代表这个类的 java.lang.Class 对象,作为方法区中类数据的访问入口。


类缓存:标准的 JavaSE 类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将加载(缓存)一段时间。不过 JVM 垃圾回收机制可以回收这些 Class 对象。



package com.nobody;
/** * @author Mr.nobody * @Description * @date 2020/9/3 */public class TestClassLoader {
public static void main(String[] args) throws ClassNotFoundException {
// 获取系统类的加载器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); // 获取系统类加载器的父加载器,也就是扩展类加载器 ClassLoader parent = systemClassLoader.getParent(); // 获取扩展类加载器的父加载器,也就是根加载器,用C或C++编写 ClassLoader parent1 = parent.getParent(); System.out.println(systemClassLoader); System.out.println(parent); System.out.println(parent1);
System.out.println("----------------------------------------");
// 测试我们定义的类是哪个类加载器加载的 ClassLoader classLoader = Class.forName("com.nobody.TestClassLoader").getClassLoader(); // 测试JDK内置的的类是哪个类加载器加载的 ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader(); System.out.println(classLoader); System.out.println(classLoader1);
// 获取系统类加载器可以加载的路径 System.out.println(System.getProperty("java.class.path")); } }
复制代码


输出结果:


sun.misc.Launcher$AppClassLoader@18b4aac2sun.misc.Launcher$ExtClassLoader@1b6d3586null  // 根加载器(引导类加载器)我们获取不到,所以是null----------------------------------------sun.misc.Launcher$AppClassLoader@18b4aac2null
C:\Program Files\Java\jdk1.8.0_202\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\rt.jar;D:\IdeaProjects\nobody\project02\out\production\hello-world;D:\devTools\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar
复制代码

Ⅷ 性能对比分析

Method,Field,Constructor 都由 setAccessible()方法,它的作用是开启或禁用访问安全检查。如果代码中用到反射,而且此代码被频繁调用,为了提高反射效率,则最好禁用访问安全检查,即设置为 true。


package com.nobody;
import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;
/** * @author Mr.nobody * @Description * @date 2020/9/5 */public class Test02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { test01(); test02(); test03(); }
public static void test01() { Teacher t = new Teacher(); long start = System.currentTimeMillis(); for (int i = 0; i < 1000000000; i++) { t.getName(); } long end = System.currentTimeMillis(); System.out.println("普通方式执行10亿次消耗:" + (end - start) + "ms"); }
public static void test02() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { Teacher t = new Teacher(); Class<?> aClass = Class.forName("com.nobody.Teacher"); Method getName = aClass.getDeclaredMethod("getName"); long start = System.currentTimeMillis(); for (int i = 0; i < 1000000000; i++) { getName.invoke(t, null); } long end = System.currentTimeMillis(); System.out.println("反射方式执行10亿次消耗:" + (end - start) + "ms"); }
public static void test03() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { Teacher t = new Teacher(); Class<?> aClass = Class.forName("com.nobody.Teacher"); Method getName = aClass.getDeclaredMethod("getName"); getName.setAccessible(true); long start = System.currentTimeMillis(); for (int i = 0; i < 1000000000; i++) { getName.invoke(t, null); } long end = System.currentTimeMillis(); System.out.println("关闭安全检查反射方式执行10亿次消耗:" + (end - start) + "ms"); }
}
class Teacher {
private String name;
public String getName() { return name; }}
复制代码


输出结果:


普通方式执行1亿次消耗:6ms反射方式执行1亿次消耗:4294ms关闭安全检查反射方式执行1亿次消耗:1963ms
复制代码

Ⅸ 反射操作泛型

Java 采用泛型擦除的机制来引入泛型,Java 中的泛型仅仅是给编译器 javac 使用的,确保数据的安全性和免去强制类型转换问题,但是一旦编译完成,所有和泛型有关的类型全部擦除。


为了通过反射操作这些类型,Java 新增了ParameterizedTypeGenericArrayTypeTypeVariableWildcardType几种类型来代表不能被归到 Class 类中的类型但是又和原始类型齐名的类型。


  • ParameterizedType:表示一种参数化类型,比如 Collection<String>

  • GenericArrayType:表示种元素类型是参数化类型或者类型变量的数组类型

  • TypeVariable:是各种类型变量的公共父接口

  • WildcardType:代表种通配符类型表达式


package com.nobody;
import java.lang.reflect.Method;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.util.Map;
/** * @author Mr.nobody * @Description * @date 2020/9/5 */public class Test03 {
public void test01(Map<String, Integer> map, Person person) {
}
public Map<String, Student> test02() { return null; }
public static void main(String[] args) throws NoSuchMethodException { Method test01 = Test03.class.getDeclaredMethod("test01", Map.class, Person.class); // 获取方法test01的参数类型 Type[] genericParameterTypes = test01.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { System.out.println("<<< " + genericParameterType); // 如果参数类型等于参数化类型 if (genericParameterType instanceof ParameterizedType) { // 获得真实参数类型 Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } }
Method test02 = Test03.class.getDeclaredMethod("test02", null); // 获取方法test02的返回值类型 Type genericReturnType = test02.getGenericReturnType(); System.out.println("<<< " + genericReturnType); // 如果参数类型等于参数化类型 if (genericReturnType instanceof ParameterizedType) { // 获得真实参数类型 Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } }
}}
复制代码


输出结果:


<<< java.util.Map<java.lang.String, java.lang.Integer>class java.lang.Stringclass java.lang.Integer<<< class com.nobody.Person<<< java.util.Map<java.lang.String, com.nobody.Student>class java.lang.Stringclass com.nobody.Student
复制代码

Ⅹ 反射操作注解

通过反射我们可以获取代码中的注解,并且获取注解的属性值,下面演示如何获取类和属性的注解,解析和数据库映射的相关信息。


package com.nobody;
import java.lang.annotation.*;
/** * @author Mr.nobody * @Description * @date 2020/9/5 */public class Test04 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { Class<?> aClass = Class.forName("com.nobody.Book"); // 获得Book类的注解 Annotation[] annotations = aClass.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } // 获取类的指定注解,并且获取注解的值 Table annotation = aClass.getAnnotation(Table.class); String value = annotation.value(); System.out.println("Book类映射的数据库表名:" + value);
java.lang.reflect.Field bookName = aClass.getDeclaredField("bookName"); Field annotation1 = bookName.getAnnotation(Field.class); System.out.println("bookName属性映射的数据库字段属性 - 列名:" + annotation1.colName() + ",类型:" + annotation1.type() + ",长度:" + annotation1.length()); java.lang.reflect.Field price = aClass.getDeclaredField("price"); Field annotation2 = price.getAnnotation(Field.class); System.out.println("price属性映射的数据库字段属性 - 列名:" + annotation2.colName() + ",类型:" + annotation2.type() + ",长度:" + annotation2.length()); }}
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@interface Table { String value();}
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@interface Field { String colName();
String type();
int length();}
@Table("t_book")class Book { @Field(colName = "name", type = "varchar", length = 15) String bookName; @Field(colName = "price", type = "int", length = 10) int price;
}
复制代码


输出结果:


@com.nobody.Table(value=t_book)Book类映射的数据库表名:t_bookbookName属性映射的数据库字段属性 - 列名:name,类型:varchar,长度:15price属性映射的数据库字段属性 - 列名:price,类型:int,长度:10
复制代码


发布于: 2021 年 08 月 09 日阅读数: 20
用户头像

CSDN博客专家,微信搜一搜 - 陈皮的JavaLib 2020.02.22 加入

CSDN博客专家,专注各项技术领域的Java开发工程师,微信搜一搜【陈皮的JavaLib】,关注后学习更多技术文章和一线大厂面试资料和技术电子书籍。

评论

发布
暂无评论
阿里P8教你Java注解与反射