当前位置: 首页 > news >正文

反编译工具-Jclasslib的使用,与Java方法调用的探索

这里写目录标题

  • 前言
  • IDEA下查看字节码的两种方法
    • 使用idea自带的插件工具
    • 安装插件
  • 为什么没有看出方法调用关系
    • 原因分析
    • 工厂举例
  • 知识补充
    • 语言
    • java可移植性
  • 总结

前言

    画时序图的时候,我想验证下方法的调用是否写的正确。方法调用不仅涉及到程序的基本逻辑流程,还深刻影响着程序的性能、内存管理以及异常处理等多个方面。然而,仅凭源代码层面的阅读和理解,往往难以窥见方法调用背后复杂的运行时行为,这时就可以通过查看字节码的方式去理解。

    先解释一下字节码是什么,作为Java虚拟机(JVM)直接执行的语言,是Java源代码经过编译器编译后生成的中间代码形式。它介于高级语言和机器语言之间,既保留了高级语言的可读性,又具备机器语言的高效执行特性(后面知识扩充有详细讲解)。通过分析和解读字节码,可以直观地看到方法调用的具体实现细节,包括参数传递、栈帧管理、局部变量表操作等底层操作,这对于调试、性能优化乃至安全分析都大有裨益。

    时序图作为一种常用的UML(统一建模语言)图表类型,能够清晰地展示对象之间按时间顺序的消息传递和交互行为。通过绘制时序图,可以直观地理解方法调用的顺序、参与对象及其状态变化,这对于设计复杂系统、验证业务逻辑尤为重要。然而,时序图通常是基于设计阶段的假设和预期构建的,其准确性需要通过实际运行时的行为来验证。

    本文主要想介绍一下通过IDEA如何查看Java字节码文件。(后面还有大转折哦~)

IDEA下查看字节码的两种方法

使用idea自带的插件工具

    首选打开Java源文件,确保你的Java源文件已经被编译成.class文件。通常,IDEA会在你构建项目时自动进行编译。

view下面的show bytecode,这是IntelliJ IDEA中一个用于查看Java字节码的工具
在这里插入图片描述
之后就会出现字节码文件的窗口,这个窗口默认是悬浮的,你可以通过点击右上角的“钉子”图标将其固定住,以便更方便地查看。
在这里插入图片描述
这种方法比较简单,直接view菜单下就有

安装插件

    在IntelliJ IDEA中,可以通过菜单栏的“File”->“Settings”(或使用快捷键Ctrl+Alt+S)打开设置面板。在这里插入图片描述
    在设置面板中,找到并点击“Plugins”选项。在插件市场的搜索框中输入“jclasslib”,然后回车进行搜索。
在搜索结果中找到Jclasslib插件,并点击“Install”按钮进行安装。
在这里插入图片描述

如果弹出对话框,选择“Accept”或“同意”以继续安装。
在这里插入图片描述
安装完成后,需要重启IDE以使插件生效。

这里是已经安装好了–Jclasslib Bytecode Viewer
在这里插入图片描述
重启之后,
选中要反编译的类
在这里插入图片描述
view中选中 Show bytecode with jclasslib
在这里插入图片描述
就会弹出字节码窗口
在这里插入图片描述
下图是常量池
    作用:保存了字符串常量、类或接口名、字段名等符号引用和字面值常量。常量池是字节码文件中占用空间最大的部分之一,它避免了相同内容的重复定义,从而节省了空间。
    结构:每个常量池项都有一个索引,通过索引可以快速访问到对应的常量。常量池中的数据项包括标志位(表示常量的类型)、长度和有效值等。
在这里插入图片描述在这里插入图片描述

查看方法调用,只看到调了接口和抽象类
在这里插入图片描述

    知道多态的一定都知道,多态的一个关键特性就是运行时(Runtime)动态绑定方法调用。这意味着,当通过父类的引用调用一个被子类重写(Override)的方法时,实际调用的是子类中的实现,而不是父类中的实现。

    具体来说,多态允许一个父类类型的变量引用一个子类的对象。==在编译时(Compile Time),这个引用被视为父类类型,但在运行时(Runtime),JVM会根据对象的实际类型来确定调用哪个具体的方法。==这种机制使得程序能够表现出更加灵活和动态的行为。

为什么没有看出方法调用关系

原因分析

    关于上面的这点,再细致分析一下:
    时序图主要展示的是对象之间的交互顺序以及这些交互发生的时间顺序,它确实是描述运行时关系调用的有力工具。然而,反编译后的字节码与时序图所呈现的信息在抽象层次和用途上存在显著差异。

    反编译后的字节码是Java程序源代码经过编译后生成的中间代码,字节码主要描述了程序的结构、操作码以及操作数等信息,但它并不直接展示对象之间的交互顺序或运行时调用关系。

    要理解运行时的调用关系,通常需要借助调试工具、日志记录、性能分析工具或专门的运行时分析工具。这些工具可以在程序执行时捕获对象之间的交互信息,包括方法调用、消息传递、异常处理等、

    也就是说时序图用于描述和展示运行时的对象交互顺序,而反编译后的字节码虽然包含了程序的结构和操作信息,但并不直接展示这些运行时交互。要理解运行时的调用关系,需要借助其他工具和方法。(这点我后续会再写一篇文章)

工厂举例

再来看一个例子:

// 父类
class Animal {void makeSound() {System.out.println("Some generic animal sound");}
}// 子类1
class Dog extends Animal {@Overridevoid makeSound() {System.out.println("Woof");}
}// 子类2
class Cat extends Animal {@Overridevoid makeSound() {System.out.println("Meow");}
}// 工厂
class AnimalFactory {static Animal createAnimal(String type) {if (type.equals("dog")) {return new Dog();} else if (type.equals("cat")) {return new Cat();} else {return new Animal();}}
}public class Main {public static void main(String[] args) {Animal myDog = AnimalFactory.createAnimal("dog");Animal myCat = AnimalFactory.createAnimal("cat");myDog.makeSound(); // 输出: WoofmyCat.makeSound(); // 输出: Meow}
}

    在这个例子中,AnimalFactory 类中的createAnimal方法是一个简单工厂,它根据传入的字符串参数返回不同类型的 Animal 对象。在main方法中,创建了两个 Animal类型的引用 myDog 和 myCat,但实际上它们分别指向了 Dog 和 Cat对象。

查看这段代码的字节码,会看到类似以下的内容(简化版):

// 省略了其他部分的字节码...
public static void main(java.lang.String[]);Code:0: ldc           #7                  // String dog2: invokestatic  #8                  // Method AnimalFactory.createAnimal:(Ljava/lang/String;)LAnimal;5: astore_16: ldc           #9                  // String cat8: invokestatic  #8                  // Method AnimalFactory.createAnimal:(Ljava/lang/String;)LAnimal;11: astore_212: aload_113: invokevirtual #10                 // Method Animal.makeSound:()V16: aload_217: invokevirtual #10                 // Method Animal.makeSound:()V20: return

    在字节码中, 一样只能看到对 Animal`类的makeSound方法的调用(invokevirtual #10),而无法直接看到这些调用实际上是在Dog 或 Cat对象上执行的。这是因为多态性的实现是在运行时由JVM动态绑定的,而不是在编译时静态确定的。

    因此,虽然字节码提供了程序的结构和操作信息,但它并不直接展示运行时的多态性和方法调用关系。要理解这些运行时行为,需要运行程序并观察其实际输出,或者使用调试工具来跟踪程序的执行过程。

知识补充

语言

一、高级语言
    高级语言是一种接近人类自然语言和数学语言的编程语言,它使得程序员能够使用相对简单的语法和语义来编写程序。高级语言具有高度的抽象性和可读性,能够大大简化编程过程,提高开发效率。常见的高级语言包括Java、Python、C++等。

二、中间码(中间语言)
    中间码(或中间语言)是介于高级语言和机器语言之间的一种特殊形式的代码。它是编译器在将高级语言源代码翻译成机器语言目标代码的过程中产生的一种中间表示。中间码具有逻辑结构清晰、不依赖目标机结构等特点,使得编译器能够对其进行优化,生成更高质量的机器代码。常见的中间码形式包括后缀式、抽象语法树、三地址码等。.class文件就是一种中间码,它包含了Java程序编译后的字节码。这些字节码不是直接由计算机的CPU执行,而是由Java虚拟机(JVM)解释和执行。这种中间码的设计使得Java程序具有跨平台的特性,因为只要目标平台上有兼容的JVM,.class文件文件就可以在该平台上运行,而无需对源代码进行任何修改或重新编译。

三、机器语言
    机器语言是计算机能够直接识别和执行的二进制指令集。它由一系列由0和1组成的二进制代码构成,这些代码直接对应于计算机硬件中的电路和逻辑设计。机器语言是计算机硬件与软件之间的桥梁,是计算机能够执行任何程序的基础。然而,由于机器语言的复杂性和难以记忆性,它并不适合人类直接编写程序。
在这里插入图片描述

java可移植性

    不同的操作系统拥有各自专属的JVM(Java虚拟机),而Java编写的程序正是通过这些JVM来执行。由于JVM能够在各种操作系统上实现Java字节码(.class文件)的跨平台运行,因此,Java程序无需针对特定系统进行修改或重新编译,即可在不同的操作系统上无缝运行。这一特性使得Java成为了一种极具可移植性的编程语言,开发者只需编写一次Java代码,便能在多种平台上轻松部署和运行。
在这里插入图片描述

总结

    在软件开发过程中,如何确保所绘制的时序图准确地反映了程序的实际运行时行为?为了解决这一问题,我引入了反编译工具Jclasslib,该工具能够帮助我们查看Java程序的字节码,从而间接地了解程序的结构和方法调用情况。然后我详细介绍了Jclasslib的使用方法,包括如何安装、配置以及利用该工具查看Java类的字节码。

    然而,在深入探索字节码的过程中,我发现了一个有趣的现象:字节码文件中并不能直接看到运行时的方法调用。这一发现引发了我对于高级语言、中间码和机器语言之间关系的深入思考。文章对此进行了详细解释,指出高级语言编写的代码经过编译后生成中间码(如Java字节码),而中间码再由JVM(Java虚拟机)解释或编译成机器码执行。在这个过程中,多态性和动态绑定等特性使得运行时的实际方法调用在字节码层面并不明显。

http://www.lryc.cn/news/464784.html

相关文章:

  • 力扣 简单 876.快慢指针
  • FineReport 计算同比增长
  • 从0开始深度学习(12)——多层感知机的逐步实现
  • 如何利用OpenCV和yolo实现人脸检测
  • 015集——c# 实现CAD excel交互(CAD—C#二次开发入门)
  • 【计网笔记】以太网
  • Java 入门基础篇14 - java面向对象思想以及特性
  • 第15篇:网络架构优化与综合案例分析
  • UI自动化测试实战
  • 东方智者颜廷利:以哲学思想促进世界和谐与无私奉献
  • 基于 springboot vue停车场管理系统 设计与实现
  • 如何验证ssl私钥和证书是否匹配?
  • MongoDB的基本操作
  • spring mvc后端实现过程
  • 102005
  • Cisco ACI环境给Leaf配置OOB带外管理IP方法
  • 免费送源码:Java+B/S+MySQL springboot电影推荐系统 计算机毕业设计原创定制
  • 数据清洗(脚本)
  • jmeter中发送post请求遇到的问题
  • Java中使用protobuf
  • 2020款Macbook Pro A2251无法充电无法开机定位及修复
  • Spring Cloud --- 引入Gateway网关
  • ESP32-C3实现定时器的启停(Arduino IDE)
  • centos升级g++使其支持c++17
  • Pytest日志收集器配置
  • Morris算法(大数据作业)
  • TCP/IP协议 【三次握手】过程简要描述
  • docker 数据管理,数据持久化详解 二 数据卷容器
  • Logrotate:Linux系统日志轮转和管理的实用指南
  • 八股面试3(自用)