既然要探讨反射,首先就要知道反射它是干什么的:
- 反射是 Java 的特征之⼀,C/C++语⾔中不存在反射
- 通过反射,我们可以在运⾏时获得程序或程序集中每⼀个类型的成员和成员的信息。
- 通过 Java 反射机制 , 可以动态的创建对象并调⽤其⽅法和属性,这也就使得反射的⽤途很⼴泛,在开发过程中使⽤Eclipse、IDEA等开发⼯具时,当我们输⼊⼀个对象或类并想调⽤它的属性或⽅法时,编译器会⾃动列出它的属性或⽅法,这是通过反射实现的;再如,JavaBean和jsp之间的调⽤也是通过反射实现的。
- 反射最重要的⽤途是开发各种通⽤框架,如Spring框架以及ORM框架,都是通过反射机制来实现的
前置概念——代理、多态
代理
之前写过一个代理篇文章,感兴趣的小伙伴可以详细去看我之前写的文章,这里我就简单对代理做个总结:
- 你没有这个功能,但你又想拥有它,可以去找一个拥有这个功能的代理去做
- 代理会调用你的功能,形成一个完整的系统
- 通过接口就知道接口里面有什么方法去调用,接口里面所有的方法就是我们想要代理的方法
多态
- 多态是面向对象编程中的一个重要特性,它允许用一个父类的引用变量来引用子类的对象。
- 在多态中,父类声明的变量可以指向子类的实例对象,在运行时动态绑定到子类的方法。这就意味着,通过使用相同的父类接口,我们可以根据不同的子类实例来执行不同的操作。
- 通过一个简单的代码例子来说明:、
定义一个父类Animal(接口规范)
//父类Animal
public class Animal {
public void makeSound(){
System.out.println("动物可以发出声音");
}
}
定义子类1 Cat
public class Cat extends Animal{
@Override
public void makeSound() {
System.out.println("猫会喵喵叫");
}
}
定义子类2 Dog
public class Dog extends Animal{
@Override
public void makeSound() {
System.out.println("狗会汪汪叫");
}
}
测试及结果
public class Test {
public static void main(String[] args) {
//父类引用指向子类对象
Animal animal1=new Cat();
Animal animal2=new Dog();
animal1.makeSound();
animal2.makeSound();
}
}
小结
上述代码就是多态的体现,这里我们没有单独创建Cat对象和Dog对象,而是通过同一个接口Animal,使用不同的实例对象(Cat和Dog),执行了不同的操作。可以看到多态使得代码更加灵活和可扩展,可以方便地增加新的子类,而无需修改现有的代码。
值得注意的是,这个Animal并不是指Java中的interface关键字定义的接口,而是指在程序设计中,我们通常将一些共性的方法和属性抽象出来,定义成父类或接口,用于规范其子类或实现类的具体行为。这个抽象出来的父类或接口就可以被看作是一种接口。
反射由来
多态虽然已经对代码的扩展性很好了,但还没有最好;因为上面的功能还是要手动删除或者添加。这里就引入了反射机制:
- Java中规则是由接口体现的。谁实现什么功能,只要跟接口对应好就行
- 实现接口就得重写里头的抽象方法
- 使用多态时,代码总是显得冗长。方法的形参是接口,具体传入的是实现类的对象。例如:
定义一个支付接口Pay,包含了一个payOnline()方法
//定义一个支付接口
public interface Pay {
void payOnline();
}
通过实现该接口的具体类‘Alipay’和‘WeChatPay’,分别实现了`payOnline()`方法。
public class Alipay implements Pay{
@Override
public void payOnline() {
System.out.println("使用支付宝支付");
}
}
public class WeChatPay implements Pay{
@Override
public void payOnline() {
System.out.println("使用微信支付");
}
}
测试类中,调用 pay 方法,并分别传入 Alipay 和 WeChatPay 对象
public class Test {
public static void main(String[] args) {
Pay aliPay=new Alipay();
Pay wechatPay=new WeChatPay();
pay(aliPay); // 调用pay方法,传入Alipay对象
pay(wechatPay); // 调用pay方法,传入WeChatPay对象
}
//方法的形参是接口,具体传入的是实现类的对象
private static void pay(Pay aliPay) {
aliPay.payOnline();
}
}
代码冗长
使用多态的方式会使代码显得冗长,需要在每次调用 pay() 方法时都要传入不同的实现类对象。
反射
既然我们说多态的方式写出的代码不行,下面我们来看一下今天的反射以它的一个优点。
反射定义
反射定义:运行状态中,任意一个类,都能知道这个类所有的属性和方法;任意一个对象,都能调用他的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java的反射机制。
可能定义说的还是比较不好理解,我举一个例子:比如在我们的一个javaWEB项目中,程序往往是动态性的。我在登录点是要做登录还是做注册,我在支付时是要用支付宝还是微信支付,这些前后端的交互要根据具体传过来的东西才能创建servlet对象。也就是说,程序只有运行起来了,服务端才知道要执行的具体任务。在这种动态运行时反射就能起到它最大的作用。
反射原理五个步骤
.java文件编译成.class文件后才能运行
- 当我们编写完一个Java项目之后,它是一个.java文件
- 每个.java文件需要被编译成一个.class文件(字节码)才能被Java虚拟机(JVM)执行和运行
.class文件被加载到JVM
- Java虚拟机将字节码文件加载到内存中,并逐行执行其中的指令。
- Java程序运行时,首先需要将需要的类加载到内存中。这个过程被称为类加载(Class Loading)。类加载不会一次性将所有的类都加载到内存中,而是按需加载。当程序需要使用某个类时,Java虚拟机会去查找并加载该类所对应的字节码文件。
- 简单总结一下类加载的步骤:
(1)加载(Loading):查找并加载类的字节码文件。字节码文件可以从本地文件系统、网络等地方获取。
(2)验证(Verification):检验所加载的字节码文件是否符合Java虚拟机规范,以确保不会对虚拟机造成安全或者运行时的危害。
(3)准备(Preparation):为类的静态变量分配内存,并初始化为默认值。
(4)解析(Resolution):将常量池中的符号引用转换为直接引用,这个过程会进行符号引用的验证、解析和替换工作。
(5)初始化(Initialization):对类进行初始化,包括执行类构造器方法的静态初始程序。在这个阶段,程序会按照静态变量的定义顺序进行初始化。
- 类加载过程是Java程序执行的一个重要阶段,它负责将程序所需要的类加载到内存中,并为其进行初始化。只有类加载完成,Java程序才能正常运行。
获取类的class对象
- Class对象是Java反射的核心,它包含了该类的所有信息,包括构造函数、方法、属性等。
- 这里我们要理解一个概念“全限定路径”,其实就是包名+类名。通过全限定路径,可以在反射中获取类的信息,如创建类的实例、调用类的方法、获取类的属性等。在使用反射时,通常使用类的全限定路径来指定要操作的类。
String str=“全限定路径”;
Class cls=Class.forName(str);
获取类的构造函数、方法和属性
在获取到Class对象之后,可以通过反射获取类的构造函数、方法和属性等信息。
利用反射获取的信息
获取 | 反射操作 | 示例代码 |
构造函数 | 使用getConstructors()方法获取所有公共构造函数。 使用getDeclaredConstructors()方法获取所有构造函数。 | Class<?> clazz = MyClass.class; Constructor<?>[] constructors = clazz.getConstructors(); |
方法 | 使用getMethods()方法获取所有公共方法(包括继承的方法)。 使用getDeclaredMethods()方法获取所有方法。 | Method[] methods = clazz.getMethods(); |
属性 | 使用getFields()方法获取所有公共属性(包括继承的属性)。 使用getDeclaredFields()方法获取所有属性。 | Field[] fields = clazz.getFields(); |
单个构造函数 | 通过构造函数的参数类型获取特定的构造函数。 | Constructor<?> constructor = clazz.getConstructor(String.class, int.class); |
单个方法 | 通过方法名和参数类型获取特定的方法。 | Method method = clazz.getMethod("methodName", parameterType1, parameterType2); |
单个属性 | 通过属性名获取特定的属性。 | Field field = clazz.getField("fieldName"); |
反射利用总结
- java中,类是用于创建对象的模板。每个类都有一个对应的Class对象,它包含了有关该类的元信息。这些Class对象位于java.lang包下。
- 通过通过获取Class对象(cat字节码信息/dog字节码信息),我们能够深入了解一个类的内部信息,如属性、方法、构造器等
- 例如我们要获取属性:Class类 → 得到具体的实例/对象 → 通过对象获取属性
获取字节码信息的四种方式
- 字节码信息只会加载一次,无论哪种方式获取的都是同一个字节码信息
1.通过对象.getClass()方法获取
Dog dog=new Dog();
Class c1=dog.getClass();
System.out.println(c1);
2.通过 类名.class 获取
Class c1=Dog.class;
System.out.println(c1);
3.使用 Class.forName("全限定路径名")
Class clazz=Class.forName("com.Reflect.Animal.Dog")
4.使用 类加载器 获取字节码信息
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class c1= classLoader.loadClass("com.Reflect.Animal.Dog");
获取被不同修饰符修饰的构造器
- 获取构造器通过Class去获取
获取当前运行时类的 被public修饰的构造器 : getConstructors()
public class GetConstructorClass {
public GetConstructorClass() {
}
public GetConstructorClass(int param) {
}
private GetConstructorClass(int num1,int num2){
}
public GetConstructorClass(int num1,int num2,int num3){
}
public static void main(String[] args) {
Class c1 = GetConstructorClass.class;
//getConstructors()方法返回了一个数组
Constructor[] constructors =c1.getConstructors();
//对constructors数组遍历
for (Constructor constructor:constructors) {
System.out.println(constructor);
}
}
}
获取当前运行时类的 全部修饰符的构造器 : getDeclaredConstructors()
Constructor[] constructors =c1.getDeclaredConstructors();
获取当前运行时类的指定构造器 : getConstructor()、getDeclaredConstructor()
- public修饰的空构造器: getConstructor
Constructor constructor1 =c1.getConstructor();
System.out.println(constructor1);
- public修饰的有参构造器: getConstructor
Constructor constructor2 =c2.getConstructor(int.class,double.class,String.class);
- private修饰的有参构造器: getDeclaredConstructor
Constructor constructor3 =c3.getDeclaredConstructor(int.class,double.class);
利用获取的构造器创建对象
import java.lang.reflect.Constructor;
public class GetConstructorClass {
int age;
double weight;
String sex;
public GetConstructorClass() {
}
public GetConstructorClass(int param) {
}
private GetConstructorClass(int num1,double num2){
}
public GetConstructorClass(int num1,double num2,String data3){
}
public static void main(String[] args) throws Exception{
Class c1 = Class.forName("com.Reflect.Constructor.GetConstructorClass");
//获取私有属性的有参构造器
Constructor constructor= c1.getDeclaredConstructor(int.class,double.class);
//由于构造器是私有的,需要设置setAccessible帮助访问
constructor.setAccessible(true);
//利用构造器创建对象,分配一个instance实例,这个类型是objec,需要向下转型为GetConstructorClass
GetConstructorClass instance= (GetConstructorClass) constructor.newInstance(18,176.3);
System.out.println(instance);
}
}
获取属性
- 获取属性和构造器是类似的,这里用到了Field类
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class GetConstructorClass {
int age;
double weight;
String sex;
public GetConstructorClass() {
}
public GetConstructorClass(int age, double weight, String sex) {
this.age = age;
this.weight = weight;
this.sex = sex;
}
public static void main(String[] args) throws Exception{
Class c1 = Class.forName("com.Reflect.Constructor.GetConstructorClass");
//获取有参构造器,它们可能是私有的,如果是私有的需要设置可访问性
Constructor constructor= c1.getDeclaredConstructor(int.class,double.class,String.class);
constructor.setAccessible(true);
//利用这个有参构造器创建对象,分配一个instance实例,这个类型是object,需要向下转型为GetConstructorClass
GetConstructorClass instance= (GetConstructorClass) constructor.newInstance(18,176.3,"man");
//获取对象的属性值需要用到Field类,和获取构造器的方式是一样的
Field ageField=c1.getDeclaredField("age");
Field weightField=c1.getDeclaredField("weight");
Field sexField=c1.getDeclaredField("sex");
//设置属性的可访问性,它们可能是私有的
ageField.setAccessible(true);
weightField.setAccessible(true);
sexField.setAccessible(true);
//通过这个创建的对象获取属性的值
int ageValue=ageField.getInt(instance);
double weightValue=weightField.getDouble(instance);
String sexValue= (String) sexField.get(instance);
System.out.println(ageValue);
System.out.println(weightValue);
System.out.println(sexValue);
}
}
获取运行时类和父类中 被public修饰的属性。
import java.lang.reflect.Field;
public class TEST {
public static void main(String[] args) throws Exception{
Class c1= Class.forName("com.Reflect.Field.sonClass");
Field[] fields=c1.getFields();
for(Field field:fields){
System.out.println(field.getName());
System.out.println(field.getType());
}
}
}
获取运行时该类中 所有属性。
import java.lang.reflect.Field;
public class TEST {
public static void main(String[] args) throws Exception{
Class c2= Class.forName("com.Reflect.Field.sonClass");
Field[] fields=c2.getDeclaredFields();
for(Field field:fields){
System.out.println(field.getName());
System.out.println(field.getType());
}
}
}
获取运行时该类中 指定属性
import java.lang.reflect.Field;
public class TEST {
public static void main(String[] args) throws Exception{
Class c3= Class.forName("com.Reflect.Field.sonClass");
Field field=c3.getField("publicSonField");
System.out.println(field);
Field field2=c3.getDeclaredField("privateSonField");
System.out.println(field2);
}
}
给获得的属性赋值——必须得通过实例对象去设置属性
import java.lang.reflect.Field;
public class TEST {
public static void main(String[] args) throws Exception{
Class c3= Class.forName("com.Reflect.Field.sonClass");
Field field=c3.getField("publicSonField");
Object instance=c3.newInstance();
field.set(instance,"private");
System.out.println("新的公有属性值:"+field.get(instance));
/*Field field2=c3.getDeclaredField("privateSonField");
System.out.println(field2);*/
}
}
获取方法
获取 运行时类和父类中被public修饰的方法
public class TEST {
public static void main(String[] args) throws ClassNotFoundException {
Class c1= Class.forName("com.Reflect.Method.SonClass");
Method[] methods=c1.getMethods();
for (Method method:methods){
System.out.println(method);
}
}
}
获取运行时类中所有方法
import java.lang.reflect.Method;
public class TEST {
public static void main(String[] args) throws ClassNotFoundException {
Class c1= Class.forName("com.Reflect.Method.SonClass");
/* Method[] methods=c1.getMethods();
for (Method method:methods){
System.out.println(method);
}*/
Method[] declaredmethods=c1.getDeclaredMethods();
for(Method m:declaredmethods){
System.out.println(m);
}
}
}
获取 指定方法(public修饰的无参)
import java.lang.reflect.Method;
public class TEST {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class c1= Class.forName("com.Reflect.Method.SonClass");
/* Method[] methods=c1.getMethods();
for (Method method:methods){
System.out.println(method);
}*/
/* Method[] declaredmethods=c1.getDeclaredMethods();
for(Method m:declaredmethods){
System.out.println(m);
}*/
Method method=c1.getMethod("publicMethodSonClass1");
System.out.println(method);
}
}
获取 指定方法(public修饰的2个参数的)
import java.lang.reflect.Method;
public class TEST {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class c1= Class.forName("com.Reflect.Method.SonClass");
/* Method[] methods=c1.getMethods();
for (Method method:methods){
System.out.println(method);
}*/
/* Method[] declaredmethods=c1.getDeclaredMethods();
for(Method m:declaredmethods){
System.out.println(m);
}*/
Method method=c1.getMethod("publicMethodSonClass2",int.class,double.class);
System.out.println(method);
}
}
获取 指定方法(private修饰的无参)
import java.lang.reflect.Method;
public class TEST {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class c1= Class.forName("com.Reflect.Method.SonClass");
/* Method[] methods=c1.getMethods();
for (Method method:methods){
System.out.println(method);
}*/
/* Method[] declaredmethods=c1.getDeclaredMethods();
for(Method m:declaredmethods){
System.out.println(m);
}*/
/*Method method=c1.getMethod("publicMethodSonClass2",int.class,double.class);
System.out.println(method);*/
Method method=c1.getDeclaredMethod("privateMethodSonClass1");
System.out.println(method);
}
}
调用获取的方法——创建实例,利用实例对象去调用方法
- 无参方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TEST {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException {
Class c1= Class.forName("com.Reflect.Method.SonClass");
/* Method[] methods=c1.getMethods();
for (Method method:methods){
System.out.println(method);
}*/
/* Method[] declaredmethods=c1.getDeclaredMethods();
for(Method m:declaredmethods){
System.out.println(m);
}*/
/*Method method=c1.getMethod("publicMethodSonClass2",int.class,double.class);
System.out.println(method);*/
Method method=c1.getDeclaredMethod("privateMethodSonClass1");
method.setAccessible(true);
//创建对象
Object instance=c1.newInstance();
try {
//通过对象调用方法
method.invoke(instance);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
- 有参方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TEST {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException {
Class c1= Class.forName("com.Reflect.Method.SonClass");
/* Method[] methods=c1.getMethods();
for (Method method:methods){
System.out.println(method);
}*/
/* Method[] declaredmethods=c1.getDeclaredMethods();
for(Method m:declaredmethods){
System.out.println(m);
}*/
/*Method method=c1.getMethod("publicMethodSonClass2",int.class,double.class);
System.out.println(method);*/
Method method=c1.getDeclaredMethod("privateMethodSonClass2",int.class,String.class);
method.setAccessible(true);
//创建对象
Object instance=c1.newInstance();
try {
//通过对象调用方法
method.invoke(instance,33,"hello");
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
参考链接
https://mikechen.cc/29968.html
https://github.com/Xiao-XuXu/Reflect