首页 > 编程语言 >java反射机制

java反射机制

时间:2024-10-19 21:19:34浏览次数:3  
标签:反射 java invoke clazz public 机制 Runtime 方法 Class

什么是java反射呢,先来看Oracle官方的解释:

“Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.” ( 反射使Java代码能够发现有关已加载类的字段、方法和构造函数的信息,并在安全 限制内使用反射的字段、方法和构造函数对其底层对应的对象进行操作。)

通过java反射我们可以在运行的时候获取每一个类型的成员和成员的信息,通过“反射”,我们可以将java这种静态代码加上动态特性。

反射的作用

  • 在运行时返回任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法,可以通过反射调用 private 方法
  • 在运行时调用任意一个对象的方法

获取类对象

java反射通常操作的是class对象,所以我们需要先获取java的对象,通常获取java对象有4种方法:

  • Class.forName
  • 类名.class
  • obj.getclass()
  • classLoader.loadClass()

1.forname方法

当我们需要使用一个类的时候,需要import进行导入才能进行使用,而forname就不需要

forname方法有两种重载

Class<?> forName(String name)
Class<?> forName(String name, **boolean** initialize, ClassLoader loader)

第⼀个参数是类名;第⼆个参数表示是否初始化;第三个参数是 ClassLoader 。

第二个是否初始化参数属于类的初始化,并不会去调用构造函数

类的初始化参考链接:

类的初始化-CSDN博客

使用:

Class.forname(java.lang.Runtime)

2.类名.class方法

使用:

类名.class  如 Runtime.class

3.getClass()方法

不同于以上两种方法getClass方法必须要明确具体的类,然后创建对象。

4. getSystemClassLoader().loadClass() ⽅法

使用:

ClassLoader.getSystemClassLoader().loadClass("java.lang.Runtime");

获取类方法

获取某个Class对象的方法集合,有以下4种方法

  • getDeclaredMethods()
  • getDeclaredMethod()
  • getMethods()
  • getMethod()

1.getDeclaredMethods()

getDeclaredMethods()方法返回类或接口声明的所有方法,包括:public、protected、private和默认方法,但不包括继承的方法

用法:

Method[] methods = clazz.getDeclaredMethods()

2.getDeclaredMethod()

getDeclaredMethod()方法只能返回一个特定的方法,此方法返回类或接口声明的所有方法,包括:public、protected、private和默认方法,但不包括继承的方法

getDeclaredMethod()方法参数:

public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

如上我们可以看到getDeclaredMethod有两个参数,第一个参数是要返回的方法,第二个参数是要返回的方法类型的参数

用法:

#以exec方法为例
Method method = name.getDeclaredMethod("exec", String.class);

注释中的代码涉及到Runtime的单例模式,这个问题我们后面说

3.getMethods()

getMethods() ⽅法返回某个类的所有public⽅法,包括其继承类的public⽅法

用法:

Method[] methods = clazz.getMethods()

4.getMethod()

getMethods() 方法只能返回一个特定的方法,此⽅法返回某个类的所有public⽅法,包括其继承类的public⽅法

getMethod()方法参数:

public Method getMethod(String name, Class<?>... parameterTypes)

如上我们可以看到getMethod有两个参数,第一个参数是要返回的方法,第二个参数是要返回的方法类型的参数,因java中存在重载第二个参数是为了解决这个多重载的问题,如不存在重载问题,那么第二个参数可以省略。

用法:

#以exec方法为例
Method method = clazz.getMethod("exec", String.class);

构造任意类对象

我们先看一串代码

public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
    Class clazz = Class.forName("java.lang.Runtime");
    clazz.getMethod("exec", String.class).invoke(clazz.newInstance(),"calc.exe");
}

invoke 是执⾏函数的⽅法,newInstance是实例化类对象的⽅法 ,这个方法我们后面会讲这里先用一下,上述代码是一串反射代码通过反射运行exec函数的方法,但是我们运行发现会报错

我们查看报错发现提示是“Main类无法访问带有修饰符“private”的java.lang.Runtime类的成员”

我们看下"java.lang.Runtime"类的源码

newInstance的本质就是调用这个类的的无参构造器,故主要问题出在newInstance调用Runtime类的无参构造器的时候,无法调用从而产生的问题。

newInstance使用不成功的原因:

  • 单例模式
  • 使用的类没有无参构造器
  • 使用的类构造函数是私有的

单例模式

什么是单例模式呢?

例如Runtime类就是一个很典型的单例模式,我们来看Runtime类的代码:

public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    public static Runtime getRuntime() {
        return currentRuntime;
    }

    private Runtime() {}

我们看如上代码可以看到我们的Runtime无参构造器是私有的,但是他实例化成了currentRuntime,然后在getRuntime方法中返回了,那么newInstance遇到单例方法的时候可以调用getRuntime方法,看如下代码:

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Class clazz = Class.forName("java.lang.Runtime");
    clazz.getMethod("exec", String.class).invoke(clazz.getMethod("getRuntime").invoke(clazz), "calc");
}

我们将上述代码进行拆解一下

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        Class clazz = Class.forName("java.lang.Runtime");
        Method method = clazz.getMethod("exec", String.class);
        Method method1 = clazz.getMethod("getRuntime");
        Object invoke1 = method1.invoke(clazz);
        method.invoke(invoke1, "calc.exe");
    }

这里需要说下Object invoke1 = method1.invoke(clazz);这串代码中invoke里面使用的是一个参数对象,为什么不是一个类对象呢,因为getRuntime方法是一个静态方法,invoke第一个参数传入的时候需要注意,如是一个普通方法需要传入类对象,如果是一个静态方法则直接传入一个类就好了,这块儿到后面invoke会详细讲解。

使用的类没有无参构造器

如使用的类没有无参构造器的时候我们应该如何解决呢,我推荐一种解决方式是调用有参的构造器,这里我们需要用到一个新的方法 getConstructor

getConstructor

我们先来看下getConstructor构造器源码

    public Constructor<T> getConstructor(Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return getConstructor0(parameterTypes, Member.PUBLIC);
    }

我们发现getConstructor只有一个参数,这个参数是我们要调用的有参构造器的参数类型

我们以ProcessBuilder为例看下列代码:

如对ProcessBuilder不了解的请参考链接:Java进程ProcessBuilder类的介绍及使用,ProcessBuilder调用外部程序执行shell命令Linux命令,示例调用本地FFMPEG命令执行视频转码和剪辑-CSDN博客

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, IOException {
    Class clazz = Class.forName("java.lang.ProcessBuilder");
    ((ProcessBuilder)clazz.getConstructor(List.class).newInstance(Arrays.asList("calc.exe"))).start();
}

如不了解Arrays.aslist还有ArrayList的参考链接:

Arrays.asList() 详解-CSDN博客

如何在 Java 中创建 ArrayList 列表?-阿里云开发者社区

因上述代码惨在一个ProcessBuilder类的强转,我们用原来的方法进行编写

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, IOException {
    Class clazz = Class.forName("java.lang.ProcessBuilder");
    clazz.getMethod("start").invoke(clazz.getConstructor(List.class).newInstance(Arrays.asList("clac.exe")));
}

我们看下getConstructor下的newInstance方法的源码

   public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }

我们发现newInstance方法的传参是传入的有参构造器所传入的参数

使用的类构造函数是私有的

前面我们讲单例方法的时候我们使用getRuntime函数来解决Runtime类构造函数是私有的问题

那么我们还有一个方法,就是用 getDeclaredConstructor()方法来解决

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, IOException {
        Class clazz = Class.forName("java.lang.Runtime");

        //获取构造方法
        Constructor declaredConstructor = clazz.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        clazz.getMethod("exec", String.class).invoke(declaredConstructor.newInstance(), "calc.exe");
    }

setAccessible方法是修改他的作用域。

执行函数的方法

invoke

我们先来看下invoke源码

public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
    }

invoke方法有两个参数,第一个参数是Object类,也就是调用该方法的对象,第二个参数是调用的方法所要传的参

需要注意的是第一个参数:

  • 如果这个方法是一个普通方法,那么第一个参数是类对象
  • 如果这个方法是一个静态方法,那么第一个参数是类

代码示例:

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Class clazz = Class.forName("java.lang.Runtime");
    clazz.getMethod("exec", String.class).invoke(clazz.getMethod("getRuntime").invoke(clazz), "calc");
}

标签:反射,java,invoke,clazz,public,机制,Runtime,方法,Class
From: https://blog.csdn.net/m0_62484818/article/details/143024087

相关文章

  • [Java基础] 异常处理机制
    往期回顾[Java基础]基本数据类型[Java基础]运算符[Java基础]流程控制[Java基础]面向对象编程[Java基础]集合框架[Java基础]输入输出流[Java基础]异常处理机制[Java基础]Lambda表达式目录什么是异常处理?异常分类检查型异常非检查型异常(UncheckedExcepti......
  • java计算机毕业设计springboot+vue小区物业管理系统(开题+程序+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、研究背景随着城市化进程的加速,住宅小区的规模不断扩大,小区内的管理事务日益繁杂。传统的物业管理方式依赖人工操作,效率低下且容易出错。在信息技术飞速发......
  • java计算机毕业设计广告系统(开题+程序+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、研究背景随着互联网的迅猛发展,广告行业发生了翻天覆地的变化。广告形式和载体日益多样化,从传统的报纸、杂志、电视到如今无处不在的互联网广告等1。海量......
  • java基于springboot的在线教学平台(源码+vue+部署文档+前后端分离等)
    收藏关注不迷路!!......
  • Java虚拟机(JVM)(7)—— 垃圾回收(1)
    文章目录前言一、方法区的回收二、垃圾判别阶段算法1、引用计数法2、可达性分析算法GCRoots对象三、常见的引用对象1、强引用2、软引用软引用的使用方法3、弱引用4、虚引用和终结器引用四、垃圾回收算法1、垃圾回收算法的评价标准2、标记清除算法3、复制算法3、......
  • 深度讲解Java中的面对象,通俗易懂
    目录1.封装(Encapsulation)2.继承(Inheritance)3.多态(Polymorphism)4.抽象(Abstraction)总结在Java中,“面向对象编程”(Object-OrientedProgramming,简称OOP)是核心概念之一。它通过使用类和对象来组织代码,允许程序员创建可以模拟现实世界事物的代码结构。Java中的面向对象编......
  • 基于Java数据库管理系统的毕业设计论文
    摘要数据库管理系统是一个快速分析数据、方便管理者管理所有数据的后台操作系统,所以将传统的图书管理方式彻底的解脱出来,可以提高管理员的工作效率,减轻管理员以往繁忙的工作,减小出错的概率。从而使人们有更多时间来获取信息、了解信息、掌握信息。数据库的开发主要包括后台......
  • 反射机制及应用
    反射机制是指程序在运行时动态地获取类的信息、创建对象、访问对象的属性和调用对象的方法的一种能力。它使得程序能够在运行时检查和修改自身的结构。这种机制广泛应用于Java等编程语言,特别是在框架设计、序列化、依赖注入等场景中。反射机制的基本特性动态加载类:可以在......
  • 【最新Java必过毕设选题】基于SSM的医院门诊互联电子病历管理信息系统(源码+万字LW+答
    作品简介 Hi,各位同学好呀!今天向大家分享一个最新完成的高质量毕业设计项目作品基于SSM的XXX管理系统项目评分(最低0分,满分5分)难度系数:3分工作量:5分创新点:3分界面美化:5分使用技术前端:html/js/css/vue后端:ssm数据库:MySql服务器:apache-tomcat编译器:Int......
  • 【最新Java必过毕设选题】基于微信小程序的智能停车场管理小程序(源码+万字LW+答辩PPT)
    作品简介 Hi,各位同学好呀!今天向大家分享一个最新完成的高质量毕业设计项目作品基于ssm+uniapp的XXX微信小程序项目评分(最低0分,满分5分)难度系数:3分工作量:5分创新点:3分界面美化:5分使用技术小程序框架:uniapp小程序开发软件:HBuilderX小程序运行软件:微信开发者......