十分钟彻底搞懂Java反射

欢迎关注本人公众号:Bean冷的心,内容包含计算机网络、数据结构与算法、科技资讯和知识扫盲,期待结实各位大佬和对计算机感兴趣的小伙伴~


想要搞明白反射到底是什么,首先要知道什么是反射?反射有什么用,为什么需要反射。首先我们看一下反射的定义:

一、定义

JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

二、定义解释

这样文绉绉的定义实在是难理解,而且到底什么是在运行状态,对于任意一个实体类,都能够知道这个类的所有属性和方法呢??

我们首先看个简单例子:
在这里插入图片描述

相信大家平时肯定写过这样的代码吧,String类的对象,通过点“ . ”,就可以调用String类封装好了的方法,虽然我们很熟悉这样的方法,可是Idea是怎么知道有这些方法的呢?

答案就是反射,在解释这个问题之前,我们先来了解一下类加载机制是什么。

三、什么是类加载机制

假如我们写了一个类,叫Student,众所周知类一般是有三部分的:在这里插入图片描述
将其浓缩一下就是这样:
在这里插入图片描述
.java源文件通过javac编译成.class字节码文件之后,结构不变,还是存储在硬盘中,然后此时,我们就可以使用Student类了

Student stu = new Student();
stu.run();

可是我们常常忽略了字节码文件是如何被加载到内存当中去的,字节码文件通过一个叫做类加载器(ClassLoader)的东西,将类加载到内存当中去。类加载器中有一个Class对象,封装了类的基本信息,包括属性构造方法普通方法,所以结构如图:
在这里插入图片描述
我们再回头看前面的问题,正是因为Class对象封装了当前类所有的成员方法,所以将具体方法显示到IDEA中,就可以达到通过点“ . ”调用方法时,能看到类的所有成员方法了。

四、获取Class对象的方式

获得Class对象,总共有三种方法:

方法一
Class.forName("全类名");
方法二

类名.class获取

Student.class;
方法三

由于所有类都是继承Object类,所以Object的所有方法,子类都可以用,Object类中有一个方法就是getClass();,所以可以通过对象名调getClass();方法。

对象.getClass();
看个例子

在这里插入图片描述

打印结果:

在这里插入图片描述
可以看出来这三种方式生成的Class对象其实都是一样的。

结论

同一个字节码文件(*.class)一次编译,只会被加载一次,无论是哪种加载方法,最终生成的Class对象都是一样的。

五、使用Class对象

1.获得成员变量们
Field[] getFields()  获得所有public修饰的成员变量
Field getField(String name)  获得指定名称被public修饰的成员变量

Field[] getDeclaredFields()  获得当前包下所有方法
Field getDeclaredField(String name)  获得当前包下任一方法
2.获得构造方法们
Constructor<?>[] getConstructors()  
Constructor<T> getConstructor(Class<?>... parameterTypes)  

Constructor<?>[] getDeclaredConstructors()  
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)  
3.获得成员方法们
Method[] getMethods()  
Method getMethod(String name, Class<?>... parameterTypes)  

Method[] getDeclaredMethods()  
Method getDeclaredMethod(String name, Class<?>... parameterTypes)  
4.获得类名
String getName()

需要注意的是,虽然getDeclaredXXX()方法可以获取私有方法或属性,但是需要调用一个方法,以调用私有方法举例

method.setAccessible(true);

通过这个方法暴力破解!!!~一定要记牢了!!

六、方法调用案例

先介绍一下java.lang.reflect.Method的invoke方法:

public Object invoke(Object obj,
                     Object... args)
              throws IllegalAccessException,
                     IllegalArgumentException,
                     InvocationTargetException

invoke方法就相当于你调用你通过Student对象stu调用成员方法run,如果方法有参数,则传入方法所在对象和参数,无参数,则只传入方法所在对象。

以调用方法为例:

package com.bean.ioc.ReflectionDemo;

import java.lang.reflect.Method;

public class Demo03 {
    public static void main(String[] args) throws Exception{
        Class<Student> studentClass = Student.class;
        Student stu = new Student();
        //想找到带有参数的run方法,必须有两个必要条件,方法的名字和参数
        Method run = studentClass.getMethod("run", String.class);
        Method run1 = studentClass.getMethod("run");
        //想要调用自己写的方法,需要两个必要条件,类的对象和方法传入的参数
        run.invoke(stu,"小斌哥");
        run1.invoke(stu);
        int i = 1;
        //获取当前类的所有public成员方法
        Method[] methods = studentClass.getMethods();
        for (Method method : methods) {
            System.out.print(i+++" ");
            System.out.println(method);
//            method.setAccessible(true);加了这句话就可以获取私有方法了(暴力破解)
        }
    }
}

输出结果
小斌哥正在奔跑
我正在run
1 public static void com.bean.ioc.ReflectionDemo.Student.main(java.lang.String[]) throws java.lang.Exception
2 public void com.bean.ioc.ReflectionDemo.Student.run()
3 public void com.bean.ioc.ReflectionDemo.Student.run(java.lang.String)
4 public final void java.lang.Object.wait() throws java.lang.InterruptedException
5 public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
6 public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
7 public boolean java.lang.Object.equals(java.lang.Object)
8 public java.lang.String java.lang.Object.toString()
9 public native int java.lang.Object.hashCode()
10 public final native java.lang.Class java.lang.Object.getClass()
11 public final native void java.lang.Object.notify()
12 public final native void java.lang.Object.notifyAll()

Process finished with exit code 0

方法包括Student类的成员方法以及从Object父类继承来的方法

七、案例

好像说了这么多,也没说反射为什么占着灵魂的作用,我们来看一个例子:

需求

写一个框架,在不改变任意类的前提下,可以帮我们创建任意类的对象,并且执行其中任意的方法。

步骤

1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中
2.在程序中加载读取配置文件
3.使用反射技术来加载文件到内存
4.创建对象
5.执行方法

package com.bean.ioc.ReflectionDemo;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 框架类
 */
public class ReflectUtil {
    public static void main(String[] args) throws Exception {
        //可以创建任意类,可以执行任意方法
        /*
           前提:不修改源码的前提下
         */
        //1.加载配置文件
        //1.1创建properties文件
        Properties pro = new Properties();
        //1.2加载配置文件
        //1.2.1获取class目录下的配置文件
        ClassLoader classLoader = ReflectUtil.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        pro.load(is);

        //2.获取配置文件中定义的数据
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");

        //3.加载该类到内存
        Class<?> cls = Class.forName(className);
        //4.创建对象
        Object o = cls.newInstance();
        //5.获取方法对象
        Method method = cls.getMethod(methodName);
        //6.执行方法
        method.invoke(o);

    }
}

properties
className=com.bean.ioc.ReflectionDemo.Student
methodName=run

这样就在不改变原有代码的条件下,仅通过修改properties的文件就执行了相应的方法,可是原来的方法只需要两行就能调用run方法,为什么我们要写这么一大堆反射的东西还要弄properties呢?因为将来在开发大型项目的时候,如果修改源码中的一行代码,是需要重新测试的,通过反射加配置文件,仅仅修改物理文件properties是不会对源码产生影响的,可以节省大量的时间。

欢迎关注本人公众号:Bean冷的心,内容包含计算机网络、数据结构与算法、科技资讯和知识扫盲,期待结实各位大佬和对计算机感兴趣的小伙伴~

发布了54 篇原创文章 · 获赞 82 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/u011679785/article/details/98853403