一、反射的概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
以上的总结就是什么是反射
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
二、Java中为什么需要反射?反射要解决什么问题?
Java中编译类型有两种:
- 静态编译:在编译时确定类型,绑定对象即通过。
- 动态编译:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以减低类之间的耦合性。
Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public、static等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。
Reflection可以在运行时加载、探知、使用编译期间完全未知的classes。即Java程序可以加载一个运行时才得知名称的class,获取其完整构造,并生成其对象实体、或对其fields设值、或唤起其methods。
反射(reflection)允许静态语言在运行时(runtime)检查、修改程序的结构与行为。
在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误;换句话说,程序在运行时的行为都是固定的。如果想在运行时改变,就需要反射这东西了。
实现Java反射机制的类都位于java.lang.reflect包中:
- Class类:代表一个类
- Field类:代表类的成员变量(类的属性)
- Method类:代表类的方法
- Constructor类:代表类的构造方法
- Array类:提供了动态创建数组,以及访问数组的元素的静态方法
一句话概括就是使用反射可以赋予jvm动态编译的能力,否则类的元数据信息只能用静态编译的方式实现,例如热加载,Tomcat的classloader等等都没法支持。
三、使用
先创建一个对象
package com.example.jdk8demo; import com.alibaba.fastjson.JSON; public class Student { private String name; private Integer age; private Status status; public String name1; public Integer age1; private String getName1(){ return this.name1; } private String setName(String name){ this.name = name; return "调用setName成功,设置为"+this.name; } private String setName1(String name){ this.name1 = name; return "调用setName1成功,设置为"+this.name1; } public Student(String name,Integer age){ this.name = name; this.age = age; } public Student(){ } public Student(String name,Integer age, Status status){ this.name = name; this.age = age; this.status = status; } public String getName() { return name; } public Integer getAge() { return age; } public Status getStatus(){ return status; } public enum Status{ FREE, BUSY, VOCATION } }
反射使用:
(1)获取反射对象
package com.example.jdk8demo; import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @SpringBootTest @Slf4j class FanSheTest { @Test public void test() throws Exception{ //获取反射对象 Student student = new Student(); Class studentClass1 = student.getClass(); log.info("student.getClass()反射结果{}",studentClass1.getName()); Class studentClass2 = Student.class; log.info("Student.class反射结果{}",studentClass2.getName()); log.info("Student.class==student.getClass()结果{}",studentClass1==studentClass2); Class studentClass3 = Class.forName("com.example.jdk8demo.Student"); log.info("Class.forName反射结果{}",studentClass3.getName()); log.info("Student.class==Class.forName结果{}",studentClass3==studentClass2); }
测试结果:
由测试结果可见,三种方式得到的结果是一致的
(2)获取相关内容
//获取构造函数集合 Constructor[] cons = studentClass1.getConstructors(); log.info("反射获取所有构造函数{}", JSON.toJSONString(cons)); //获取无参构造 Constructor con = studentClass1.getConstructor(); log.info("反射获取无参构造函数{}", JSON.toJSONString(con)); //获取有参构造 Constructor con1 = studentClass1.getConstructor(String.class, Integer.class); log.info("反射获取有参构造函数{}", JSON.toJSONString(con)); //获取公有字段,此处字段必须是public修饰,否则会报错 Field field = studentClass1.getField("name1"); log.info("公有字段{}", JSON.toJSONString(con));
(3)动态调用方法
//获取私有方法 Method setName1 = studentClass1.getDeclaredMethod("setName1", String.class); setName1.setAccessible(true); String s = (String)setName1.invoke(Student.class.newInstance(),"lcl"); log.info("========={}", s); //获取公有方法 Method setName = studentClass1.getDeclaredMethod("setName", String.class); String ss = (String)setName1.invoke(Student.class.newInstance(),"mm"); log.info("========={}", ss);
测试结果:
这里需要特别说明,如果动态调用的是私有方法,则需要使用 setName1.setAccessible(true); 进行解绑操作,才可以使用invoice方法调用。
声明:本文根据https://blog.csdn.net/grandgrandpa/article/details/84832343写的,里面大量复制内容,感谢博主