一、Java反射机制概述
1.1 静态 VS 动态语言
动态语言:
-
一类在运行时可以改变其结构的语言,比如新的函数、对象等,已有的函数可以被删除或者其他结构的变化。
-
主要的动态语言:
Object-C
、C#
、JavaScript
、PHP
、Python
等
静态语言:
-
运行时结构不变的语言,如
Java
、C
、C++
-
Java有一定的动态性,利用反射机制获得类似于动态语言的特性
1.2 Reflection
Reflection
反射是Java被视为动态语言的关键,反射机制允许程序在执行期借助Reflection API
获取任何类的内部信息,并能直接操作任何对象的内部属性以及方法。
- 加载完类之后,在堆内存的方法区中就产生一个
Class
类型对象(一个类只有一个Class
对象),这个对象就包含完整的类结构信息。我们通过这个对象看到类的结构,这个对象就像镜子一样,所以称之为反射。
1.3 反射机制的应用
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类对象
- 在运行时判断任意一个类所具有的的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
1.4 反射的优缺点
优点:
- 可以实现动态创建对象和编译,体现出很大的灵活性
缺点:
- 对性能有影响,使用反射基本上是一种解释操作,告诉JVM希望做什么并且满足要求,这类操作慢于直接执行相同操作
1.5 反射的主要API
java.lang.Class
:代表一个类java.lang.reflect.Method
:代表类的方法java.lang.reflect.Field
:代表类的成员变量java,lang.reflect.Constructor
:代表类的构造器
二、理解Class类并获取Class示例
2.1 Class类
在
Object
类中定义了一个getClass()
方法,此方法被所有子类继承
getClass
返回值的类型是一个Class
类,此类是Java反射的源头,实际上所谓反射可以通过对象反射求出类的名称Class
本身也是一个类Class
对象只能有系统建立- 一个加载的类在JVM中只有一个
Class
实例 - 一个
Class
对象对应一个加载到JVM中的一个.class
文件 - 每个类的实例都会记得自己是由哪个
Class
实例所生成 - 通过
Class
可以完整得到一个类中所有被加载的结构 Class
类是Reflection
根源,针对任何想动态加载、运行的类,唯有先获得Class
对象
2.2 Class类的常用方法
2.3 如何获取Class类的实例
实例:
/**
* @ClassName com.nick.Reflection1
* @Description 什么叫反射
* @Author wangwk-a
* @Date 2022/1/4 21:30
* @Version 1.0
*/
public class Reflection1 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println(person.name);
/* TODO: 方式一(通过对象获取Class对象) */
Class c1 = person.getClass();
/* TODO: 方式二(通过Class.forName获取Class对象) */
Class c2 = Class.forName("com.nick.Student");
/* TODO: 方式三(通过类名.class获取Class对象) */
Class c3 = Student.class;
/* TODO: 方式四(通过基本内置类型的包装类TYPE属性获取Class对象) */
Class<Integer> c4 = Integer.TYPE;
System.out.println(c4);
/* TODO: 方式五(通过Class对象获取父类的Class对象) */
Class c5 = c1.getSuperclass();
System.out.println(c1 + "的父类是" + c5);
// 一个类在内存中有一个Class对象
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
class Person {
public String name;
}
@Data
@ToString
class Student extends Person{
public Student() {
this.name = "学生";
}
}
@Data
@ToString
class Teacher extends Person{
public Teacher() {
this.name = "老师";
}
}
学生
int
class com.nick.Student的父类是class com.nick.Person
789451787
789451787
789451787
2.4 哪些类型可以有Class对象
class
:外部类,成员(成员内部类、静态内部类)、局部内部类、匿名内部类interface
:接口[]
:数组enum
:枚举annotation
:注解primitive type
:基本数据类型void
实例:
/**
* @ClassName Reflection2
* @Description 所有类型的Class对象
* @Author wangwk-a
* @Date 2022/1/4 22:10
* @Version 1.0
*/
public class Reflection2 {
public static void main(String[] args) {
// class
Class c1 = Object.class;
// interface
Class c2 = Runnable.class;
// 数组
Class c3 = String[].class;
Class c4 = String[][].class;
// 注解
Class c5 = Override.class;
// 枚举
Class c6 = ElementType.class;
// 基本数据类型
Class c7 = Integer.class;
// 空
Class c8 = void.class;
// Class
Class c9 = Class.class;
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
}
}
class java.lang.Object
interface java.lang.Runnable
class [Ljava.lang.String;
class [[Ljava.lang.String;
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
三、类的加载与ClassLoader
3.1 Java内存分析
3.2 类加载过程
当程序制动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
1. 加载
将class
字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class
2.链接:将Java类的二进制代码合并到JRE中
- 验证:确保加载的类信息符合JVM规范,没有安全问题
- 准备:正式为类变量(
static
)分配内存并设置变量默认初始值的阶段,这些内存在方法区中进行分配(所以static在类初始化之前就做了) - 解析:JVM常量池内的符号引用(常量名)替换为直接引用(地址)的过程
3.初始化
- 执行类构造器
<clinit>()
方法的过程,其是由编译期自动收集类中所有变量的赋值动作和静态代码快中的语句合并(所有的static会合并到一起)产生。(类构造器是构造类信息的,不是构造类对象的构造器) - 当初始化一个类的时候,如果发现其父类还没有初始化,则需要先触发父类的初始化
- JVM会保证一个类的构造器方法在多线程环境安全
示例:
package com.nick;
/**
* @ClassName Reflection3
* @Description 分析类加载过程
* @Author wangwk-a
* @Date 2022/1/4 22:34
* @Version 1.0
*/
public class Reflection3 {
public static void main(String[] args) {
A a = new A();
System.out.println(A.m);
}
}
class A {
static {
System.out.println("A类静态代码快初始化");
m = 300;
}
static int m = 100;
public A() {
System.out.println("A类的无参构造方法");
}
}
A类静态代码快初始化
A类的无参构造方法
100
执行过程示意图:
3.3 什么时候会发生类的初始化
-
主动引用(一定会发生类的初始化)
- 当JVM启动,先初始化mina方法所在的类
- new一个类的对象
- 调用类静态成员和静态方法
- 使用
java.lang.reflect
包方法对类进行反射调用 - 初始化一个类,如果父类没有被初始化,则先会调用父类初始化
-
被动引用(不会发生类的初始化)
- 访问静态域时,只有真正生命这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
- 通过数组定义类引用,不会触发此类初始化
- 引用常量不会触发初始化
package com.nick;
/**
* @ClassName Reflection4
* @Description 类的主动引用--一定会调用初始化
* @Author wangwk-a
* @Date 2022/1/8 20:47
* @Version 1.0
*/
public class Reflection4 {
static {
System.out.println("Main类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
/**
* case 1 : 主动引用
* result:
* Main类被加载
* 父类被加载
* 子类被加载
*/
// Son son = new Son();
/**
* case 2 : 反射也会产生初始化
* result:
* Main类被加载
* 父类被加载
* 子类被加载
*/
// Class.forName("com.nick.Son");
/**
* case 3 : 子类访问父类的静态变量,子类不会被加载
* result:
* Main类被加载
* 父类被加载
* 2
*/
// System.out.println(Son.b);
/**
* case 4 : 数组只是一个空间和空间的名字
* result:
* Main类被加载
*/
// Son[] array = new Son[5];
/**
* case 5 : 调用常量池也不会初始化,在连接的时候赋值
* result:
* Main类被加载
* 1
*/
// System.out.println(Son.M);
}
}
class Father {
static int b = 2;
static {
System.out.println("父类被加载");
}
}
class Son extends Father {
static int m = 100;
static {
System.out.println("子类被加载");
m = 300;
}
static final int M = 1;
}
3.4 类加载器的作用
类加载器的作用
将class文件字节码内容加载到内存中,并将这些静态数据转化成方法区运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class
对象,作为方法区中类数据的访问入口。
类缓存
标准的JavaSE
类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,将维持加载(缓存)一段时间,不过JVM垃圾回收机制可以回收这些Class对象。
类加载器作用是用来把类class装载进内存,JVM规范定义如下类加载器:
- 引导类加载器(Bootstap Classloader):用c++编写,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库,该加载器无法直接获取
- 扩展类加载器(Extension Classloader):负责
jre/lib/ext
目录下的jar包或者-D java.ext.dirs
指定目录下的jar包装入工作库
- 系统类加载器(System Classloader):负责
java -classpath
或-D java.class.path
所指目录下的类与jar包装入工作,是最常用的加载器
package com.nick;
/**
* @ClassName Reflection5
* @Description 获取类加载器
* @Author wangwk-a
* @Date 2022/1/8 21:10
* @Version 1.0
*/
public class Reflection5 {
public static void main(String[] args) throws ClassNotFoundException {
// 获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
// 获取扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
// 获取引导类加载器
ClassLoader bootstap = parent.getParent();
System.out.println(bootstap);
// 测试当前类是哪个加载器
ClassLoader classLoader = Class.forName("com.nick.Reflection5").getClassLoader();
System.out.println(classLoader);
// 测试JDK类加载器
ClassLoader classLoader2 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader2);
/**
* 获取系统类加载器可以加载的路径
* result:
* C:\JDK8\jre\lib\charsets.jar;
* C:\JDK8\jre\lib\deploy.jar;
* C:\JDK8\jre\lib\ext\access-bridge-64.jar;
* C:\JDK8\jre\lib\ext\cldrdata.jar;
* C:\JDK8\jre\lib\ext\dnsns.jar;
* C:\JDK8\jre\lib\ext\jaccess.jar;
* C:\JDK8\jre\lib\ext\jfxrt.jar;
* C:\JDK8\jre\lib\ext\localedata.jar;
* C:\JDK8\jre\lib\ext\nashorn.jar;
* C:\JDK8\jre\lib\ext\sunec.jar;
* C:\JDK8\jre\lib\ext\sunjce_provider.jar;
* C:\JDK8\jre\lib\ext\sunmscapi.jar;
* C:\JDK8\jre\lib\ext\sunpkcs11.jar;
* C:\JDK8\jre\lib\ext\zipfs.jar;
* C:\JDK8\jre\lib\javaws.jar;
* C:\JDK8\jre\lib\jce.jar;
* C:\JDK8\jre\lib\jfr.jar;
* C:\JDK8\jre\lib\jfxswt.jar;
* C:\JDK8\jre\lib\jsse.jar;
* C:\JDK8\jre\lib\management-agent.jar;
* C:\JDK8\jre\lib\plugin.jar;
* C:\JDK8\jre\lib\resources.jar;
* C:\JDK8\jre\lib\rt.jar;
* D:\SpringLearning\Spring\ReflectionDemo\target\classes;
* C:\Users\wangwk-a.m2\repository\org\springframework\spring-webmvc\5.3.13\spring-webmvc-5.3.13.jar;
* C:\Users\wangwk-a.m2\repository\org\springframework\spring-aop\5.3.13\spring-aop-5.3.13.jar;
* C:\Users\wangwk-a.m2\repository\org\springframework\spring-beans\5.3.13\spring-beans-5.3.13.jar;
* C:\Users\wangwk-a.m2\repository\org\springframework\spring-context\5.3.13\spring-context-5.3.13.jar;
* C:\Users\wangwk-a.m2\repository\org\springframework\spring-core\5.3.13\spring-core-5.3.13.jar;
* C:\Users\wangwk-a.m2\repository\org\springframework\spring-jcl\5.3.13\spring-jcl-5.3.13.jar;
* C:\Users\wangwk-a.m2\repository\org\springframework\spring-expression\5.3.13\spring-expression-5.3.13.jar;
* C:\Users\wangwk-a.m2\repository\org\springframework\spring-web\5.3.13\spring-web-5.3.13.jar;
* C:\Users\wangwk-a.m2\repository\mysql\mysql-connector-java\8.0.27\mysql-connector-java-8.0.27.jar;
* C:\Users\wangwk-a.m2\repository\com\google\protobuf\protobuf-java\3.11.4\protobuf-java-3.11.4.jar;
* C:\Users\wangwk-a.m2\repository\org\springframework\spring-jdbc\5.3.13\spring-jdbc-5.3.13.jar;
* C:\Users\wangwk-a.m2\repository\org\springframework\spring-tx\5.3.13\spring-tx-5.3.13.jar;
* C:\Users\wangwk-a.m2\repository\org\mybatis\mybatis\3.5.7\mybatis-3.5.7.jar;
* C:\Users\wangwk-a.m2\repository\org\mybatis\mybatis-spring\2.0.6\mybatis-spring-2.0.6.jar;
* C:\Users\wangwk-a.m2\repository\org\junit\jupiter\junit-jupiter-api\5.8.1\junit-jupiter-api-5.8.1.jar;
* C:\Users\wangwk-a.m2\repository\org\opentest4j\opentest4j\1.2.0\opentest4j-1.2.0.jar;
* C:\Users\wangwk-a.m2\repository\org\junit\platform\junit-platform-commons\1.8.1\junit-platform-commons-1.8.1.jar;
* C:\Users\wangwk-a.m2\repository\org\apiguardian\apiguardian-api\1.1.2\apiguardian-api-1.1.2.jar;
* C:\Users\wangwk-a.m2\repository\org\aspectj\aspectjweaver\1.9.7\aspectjweaver-1.9.7.jar;E:\IDEA\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar
*/
System.out.println(System.getProperty("java.class.path"));
}
}
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@2f0e140b
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null
C:\JDK8\jre\lib\charsets.jar;D:\SpringLearning\Spring\ReflectionDemo\target\classes; // 太长了这里省略
双亲委派机制
找包的时候会从下往上找(system->extension->bootstap),如果前后都找到了,前面的就无效了。例如自己写一个java.lang.String
,该类并不会被加载。
四、创建运行时类对象
4.1 获取运行时类的完整结构
通过反射获取运行时类的完整结构:
Field
、Method
、Constructor
、Superclass
、interface
、Annotation
package com.nick;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @ClassName Reflection6
* @Description 获取类的信息
* @Author wangwk-a
* @Date 2022/1/8 21:32
* @Version 1.0
*/
public class Reflection6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.nick.Teacher");
// 获得类的名字
System.out.println(c1.getName());
System.out.println(c1.getSimpleName());
/**
* 获得类的属性
* getFields 获取public属性(包括继承来的public属性)
* getDeclaredFields 只能获取在类中声明的属性
*/
Field[] publicFiles = c1.getFields();
Arrays.stream(publicFiles).forEach(f-> System.out.println(f));
System.out.println("-------------------------------------");
Field[] allFields = c1.getDeclaredFields();
Arrays.stream(allFields).forEach(f-> System.out.println(f));
System.out.println("-------------------------------------");
Field name = c1.getField("name");
System.out.println(name);
/**
* 获得类的方法
* getMethods 获得本类所有方法
* getDeclaredMethods 获得本类声明的方法
*/
System.out.println("========================================\n\n");
Method[] methods = c1.getMethods();
Arrays.stream(methods).forEach(m-> System.out.println(m));
System.out.println("-------------------------------------");
Method[] declaredMethods = c1.getDeclaredMethods();
Arrays.stream(declaredMethods).forEach(m-> System.out.println(m));
System.out.println("-------------------------------------");
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
/**
* 获得类的构造器
*/
System.out.println("========================================\n\n");
Constructor[] constructors = c1.getConstructors();
Arrays.stream(constructors).forEach(c-> System.out.println(c));
System.out.println("-------------------------------------");
Constructor[] declaredConstructors = c1.getDeclaredConstructors();
Arrays.stream(declaredConstructors).forEach(c-> System.out.println(c));
System.out.println("-------------------------------------");
Constructor constructor = c1.getDeclaredConstructor(int.class, String.class);
System.out.println(constructor);
}
}
com.nick.Teacher
Teacher
public java.lang.String com.nick.Teacher.gender
public java.lang.String com.nick.Person.name
-------------------------------------
private int com.nick.Teacher.id
public java.lang.String com.nick.Teacher.gender
-------------------------------------
public java.lang.String com.nick.Person.name
========================================
public boolean com.nick.Teacher.equals(java.lang.Object)
public java.lang.String com.nick.Teacher.toString()
public int com.nick.Teacher.hashCode()
public int com.nick.Teacher.getId()
public java.lang.String com.nick.Teacher.getGender()
public void com.nick.Teacher.setGender(java.lang.String)
public void com.nick.Teacher.setId(int)
public java.lang.String com.nick.Person.getName()
public void com.nick.Person.setName(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
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 boolean com.nick.Teacher.equals(java.lang.Object)
public java.lang.String com.nick.Teacher.toString()
public int com.nick.Teacher.hashCode()
public int com.nick.Teacher.getId()
public java.lang.String com.nick.Teacher.getGender()
public void com.nick.Teacher.setGender(java.lang.String)
public void com.nick.Teacher.setId(int)
protected boolean com.nick.Teacher.canEqual(java.lang.Object)
-------------------------------------
public java.lang.String com.nick.Person.getName()
public void com.nick.Person.setName(java.lang.String)
========================================
public com.nick.Teacher(int,java.lang.String)
public com.nick.Teacher()
-------------------------------------
public com.nick.Teacher(int,java.lang.String)
public com.nick.Teacher()
-------------------------------------
public com.nick.Teacher(int,java.lang.String)
4.2 有了Class对象,能做什么
- 创建类对象
- 调用Class对象的
newInstance
方法 - 类必须有一个无参构造器
- 类构造器的访问权限需要足够
- 调用Class对象的
难道没有无参构造器就不能创建对象了?只要在操作的时候明确调用类中的构造器,并将参数传递进去之后,才可以实例化操作。
步骤如下:
- 通过Class类的
getDeclaredConstructor(Class ...paramTypes)
取的本类指定形参的构造器 - 向构造器形参中传递一个对象进去,里面包含构造器中所需各个参数
- 通过Constructor实例化对象
package com.nick;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @ClassName Reflection7
* @Description 动态创建对象
* @Author wangwk-a
* @Date 2022/1/8 22:07
* @Version 1.0
*/
public class Reflection7 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class c1 = Class.forName("com.nick.Teacher");
// 本质上是调用了无参构造器
Teacher teacher = (Teacher) c1.newInstance();
System.out.println(teacher);
// 使用有参构造器
Constructor constructor = c1.getConstructor(int.class, String.class);
Teacher teacher2 = (Teacher) constructor.newInstance(5, "男");
System.out.println(teacher2);
// 通过反射调用方法 invoke(对象,值)
Method setGender = c1.getDeclaredMethod("setGender", String.class);
setGender.invoke(teacher2, "女");
System.out.println(teacher2);
// 通过反射操作属性 setAccessible取消安全检测
Field id = c1.getDeclaredField("id");
id.setAccessible(true);
id.set(teacher2, 1000);
System.out.println(teacher2);
}
}
Teacher{name='老师', id=0, gender='null'}
Teacher{name='老师', id=5, gender='男'}
Teacher{name='老师', id=5, gender='女'}
Teacher{name='老师', id=1000, gender='女'}
4.3 性能分析
package com.nick;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @ClassName Reflection8
* @Description 性能分析
* @Author wangwk-a
* @Date 2022/1/9 19:23
* @Version 1.0
*/
public class Reflection8 {
public static final int TIMES = 1000000000;
/**
* 普通方式调用
*/
public static void test01() {
Teacher teacher = new Teacher();
long startTime = System.currentTimeMillis();
for (int i = 0; i < TIMES; i++) {
teacher.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式执行十亿次需要:" + (endTime - startTime) + "ms");
}
/**
* 反射
*/
public static void test02() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class c1 = Class.forName("com.nick.Teacher");
Teacher t = (Teacher) c1.newInstance();
Method getName = c1.getMethod("getName", null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < TIMES; i++) {
getName.invoke(t);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行十亿次需要:" + (endTime - startTime) + "ms");
}
/**
* 反射关闭权限检查
*/
public static void test03() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class c1 = Class.forName("com.nick.Teacher");
Teacher t = (Teacher) c1.newInstance();
Method getName = c1.getMethod("getName", null);
getName.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < TIMES; i++) {
getName.invoke(t);
}
long endTime = System.currentTimeMillis();
System.out.println("反射(关闭权限检测)方式执行十亿次需要:" + (endTime - startTime) + "ms");
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
test01();
test02();
test03();
}
}
普通方式执行十亿次需要:3ms
反射方式执行十亿次需要:1737ms
反射(关闭权限检测)方式执行十亿次需要:1186ms
4.4 反射操作泛型
Java采用泛型擦除机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除
为了通过反射操作泛型,Java新增了ParameterizedType
、GenericArrayType
、TypeVariable
、WildcardType
几种类型来代表不能被归一到Class类中的类型,但是又和原始类型齐名的类型。
ParameterizedType
:表示一种参数化类型,比如Collection<String>
GenericArrayType
:表示一种元素类型是参数化类型或者类型变量的数组类型TypeVariable
:是各种类型变量的公共父接口WildcardType
:代表一种通配符类型表达式
package com.nick;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @ClassName Reflection9
* @Description 反射操作泛型
* @Author wangwk-a
* @Date 2022/1/9 19:38
* @Version 1.0
*/
public class Reflection9 {
public void test01(Map<String, Teacher> map, List<Teacher> list) {
System.out.println("test01");
}
public Map<String, Teacher> test02() {
System.out.println("test02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
// 获取泛型参数类型
Method method1 = Reflection9.class.getMethod("test01", Map.class, List.class);
Type[] genericParameterTypes = method1.getGenericParameterTypes();
Arrays.stream(genericParameterTypes).forEach(g-> {
System.out.println(g);
// 判断泛型的类型是不是一个参数化类型
if (g instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) g).getActualTypeArguments();
Arrays.stream(actualTypeArguments).forEach(a-> System.out.println("--> " + a));
}
});
// 获取方法返回值
// 获取泛型参数类型
System.out.println("\n------------------------------\n");
Method method2 = Reflection9.class.getMethod("test02", null);
Type genericReturnType = method2.getGenericReturnType();
System.out.println(genericReturnType);
if (genericReturnType instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
Arrays.stream(actualTypeArguments).forEach(a-> System.out.println("--> " + a));
}
}
}
java.util.Map<java.lang.String, com.nick.Teacher>
--> class java.lang.String
--> class com.nick.Teacher
java.util.List<com.nick.Teacher>
--> class com.nick.Teacher
------------------------------
java.util.Map<java.lang.String, com.nick.Teacher>
--> class java.lang.String
--> class com.nick.Teacher
4.5 反射操作注解
练习ORM
- ORM(Object Relationship Mapping)对象关系映射
- 类和表结构对应
- 属性和字段对应
- 对象和记录对应
要求:利用注解和反射完成类和表结构的映射关系
package com.nick;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.util.Arrays;
/**
* @ClassName Reflection10
* @Description 反射操作注解 ORM
* @Author wangwk-a
* @Date 2022/1/9 19:56
* @Version 1.0
*/
public class Reflection10 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.nick.Student2");
// 通过反射获得注解
Annotation[] annotations = c1.getAnnotations();
Arrays.stream(annotations).forEach(a->System.out.println(a));
// 获得注解value的值
TableName tableName = (TableName) c1.getAnnotation(TableName.class);
System.out.println(tableName.value());
// 获得属性上的注解
Field name = c1.getDeclaredField("name");
FieldName annotation = name.getAnnotation(FieldName.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@TableName("db_student")
class Student2 {
@FieldName(columnName = "db_id", type = "int", length = 10)
private int id;
@FieldName(columnName = "db_age", type = "int", length = 10)
private int age;
@FieldName(columnName = "db_name", type = "varchar", length = 3)
private String name;
}
/**
* 类名的注解
* @author wangwk-a
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableName {
String value();
}
/**
* 属性的注解
* @author wangwk-a
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldName {
String columnName();
String type();
int length();
}
@com.nick.TableName(value=db_student)
db_student
db_name
varchar
3