• 十分钟彻底搞懂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冷的心,内容包含计算机网络、数据结构与算法、科技资讯和知识扫盲,期待结实各位大佬和对计算机感兴趣的小伙伴~

  • 相关阅读:
    MySQL(2)---Explain
    MySQL(1)---索引
    php 的 PHPExcel1.8.0 使用教程
    通过html5 的EventSource来进行数据推送
    centos6.6 下 安装 php7 按 nginx方式
    IIS PHP Warning: Unknown: open(c:\php\tmp\sess_xxx, O_RDWR) failed: Permission denied (13) in Unknown on line 0
    动态加载JS,并执行回调函数
    nginx 504 gateway time out
    php 账号不能同时登陆,当其它地方登陆时,当前账号失效
    php 函数中静态变量的问题
  • 原文地址:https://www.cnblogs.com/taobean/p/12364258.html
Copyright © 2020-2023  润新知