反编译工具-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虚拟机)解释或编译成机器码执行。在这个过程中,多态性和动态绑定等特性使得运行时的实际方法调用在字节码层面并不明显。
相关文章:

反编译工具-Jclasslib的使用,与Java方法调用的探索
这里写目录标题 前言IDEA下查看字节码的两种方法使用idea自带的插件工具安装插件 为什么没有看出方法调用关系原因分析工厂举例 知识补充语言java可移植性 总结 前言 画时序图的时候,我想验证下方法的调用是否写的正确。方法调用不仅涉及到程序的基本逻辑流程&#…...

力扣 简单 876.快慢指针
文章目录 题目介绍题解 题目介绍 题解 class Solution {public ListNode middleNode(ListNode head) {ListNode slow head, fast head;while(fast ! null && fast.next ! null){slow slow.next;fast fast.next.next;}return slow;} }...

FineReport 计算同比增长
1、数据库查询 SELECTt1.年,t1.月,t1.总金额 AS 同期金额,t1.仓库名称,t2.总金额 AS 上期金额 FROMtest t1LEFT JOIN test t2 ON ( t1.年 t2.年 1 ) AND t1.月 t2.月 AND t1.仓库名称 t2.仓库名称2、配置字段 月份字段加后缀 月 数据列加后缀 计算同比增长率 if(LEN(B3)0 …...

从0开始深度学习(12)——多层感知机的逐步实现
依然以Fashion-MNIST图像分类数据集为例,手动实现多层感知机和激活函数的编写,大部分代码均在从0开始深度学习(9)——softmax回归的逐步实现中实现过 1 读取数据 import torch from torchvision import transforms import torchv…...

如何利用OpenCV和yolo实现人脸检测
在之前的blog里面,我们有介绍OpenCV和yolo的区别,本文就人脸检测为例,分别介绍下OpenCV和yolo的实现方式。 OpenCV实现人脸检测 一、安装 OpenCV 首先确保你已经安装了 OpenCV 库。可以通过以下方式安装: 使用包管理工具安装&…...

015集——c# 实现CAD excel交互(CAD—C#二次开发入门)
第一步:添加引用 程序集—>扩展 namespace WindowsFormsApp2 {public partial class Form1 : Form{public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){}private void 获取当前excel_Click(object sender, EventArgs e…...

【计网笔记】以太网
经典以太网 总线拓扑 物理层 Manchester编码 数据链路层 MAC子层 MAC帧 DIX格式与IEEE802.3格式 IEEE802.3格式兼容DIX格式 前导码(帧开始定界符SOF) 8字节 前7字节均为0xAA第8字节为0xAB前7字节的Manchester编码将产生稳定方波,用于…...

Java 入门基础篇14 - java面向对象思想以及特性
学习目标: 一、目标 面向对象思想类和对象对象的创建和使用属性和方法封装 开始学习: 二、编程思想 2.1 什么是编程思想 做人有做人的原则,编程也有编程的原则。这些编程的原则,就叫做编程思想。 2.2 面向过程和面向对象 二…...

第15篇:网络架构优化与综合案例分析
目录 引言 15.1 网络性能优化的方法与工具 15.1.1 带宽管理与流量控制 15.1.2 负载均衡 15.1.3 缓存优化 15.2 网络故障的排查与解决 15.2.1 常用的网络故障排查工具 15.2.2 网络故障排查案例 15.3 网络安全架构的综合设计案例 15.3.1 企业网络安全架构的要求 15.3.…...

UI自动化测试实战
补充:Selenium主要用于Web页面的自动化测试,它可以模拟用户的各种操作,如点击、输入、滚动等,来测试网页的功能。而Appium是一个开源的移动端自动化测试工具。 一、自动化测试实战章节 自动化测试流程测试用例编写项目自动化测试…...

东方智者颜廷利:以哲学思想促进世界和谐与无私奉献
【本社讯】在全球化的今天,东方智慧与哲学思想正逐渐成为促进世界和谐与理解的重要力量。近日,祖籍齐鲁大地山东济南的东方智者颜廷利以其深邃的哲学思想和对人类社会的深刻洞察,引起了国际社会的广泛关注。 颜廷利,一位致力于哲学研究与实践的智者,他的思想跨越古今,融合了东…...

基于 springboot vue停车场管理系统 设计与实现
博主介绍:专注于Java(springboot ssm 等开发框架) vue .net php phython node.js uniapp 微信小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设,从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不…...

如何验证ssl私钥和证书是否匹配?
从证书(CRT)文件提取公钥 openssl x509 -in server.crt -pubkey -noout | openssl sha256从证书签名请求(CSR)文件提取公钥 openssl req -in server.csr -pubkey -noout | openssl sha256从私钥(KEY)文件…...

MongoDB的基本操作
🌷数据库准备 🎈Mongoshell 1.在指定目录下创建mongodb文件夹、其子文件log和data以及mongodb.log cd /home/ubuntu mkdir -p mongodb/data mkdir -p mongodb/log touch mongodb/log/mongodb.log 执行mongodb命令启动mongdb服务 mongod --dbpath /h…...

spring mvc后端实现过程
文章目录 一、Spring mvc1、controller1.1、LoginController011.2、LoginController 2、service2.1、LoginService2.1、LoginInimplements 3、dao3.1、LoginMapper3.1、LoginMapper.xml 4、实体类 一、Spring mvc 1、controller 控制器层、处理用户的请求和响应, …...

102005
import os os.environ["CUDA_VISIBLE_DEVICES"] "0" # 设定使用的 GPUimport tensorflow as tf from dataset import generate_data import numpy as np from model import enhancednet# 检查 TensorFlow 是否可以识别 GPU gpus tf.config.list_physica…...

Cisco ACI环境给Leaf配置OOB带外管理IP方法
可以通过GUI 或CLI进行配置 通过CLI更简单,和配置传统交换机差不多, ACI中共有3大组件 APIC 控制器 SPINE 核心 LEAF 接入 下面我们将3种角色的带外IP配置方法都列出来 1 APIC配置带外IP This example shows how to configure out-of-band managemen…...

免费送源码:Java+B/S+MySQL springboot电影推荐系统 计算机毕业设计原创定制
摘 要 随着互联网与移动互联网迅速普及,网络上的电影娱乐信息数量相当庞大,人们对获取感兴趣的电影娱乐信息的需求越来越大,个性化的电影推荐系统成为一个热门。然而电影信息的表示相当复杂,己有的相似度计算方法与推荐算法都各有优势&#…...

数据清洗(脚本)
使用脚本清洗数据时,可以根据具体的数据问题选择编程语言,如Shell、Python、SQL等。这里我以 Python(Pandas库) 和 SQL 为例,演示如何通过脚本进行数据清洗。 1. 使用 Python(Pandas库) 进行数…...

jmeter中发送post请求遇到的问题
用jmeter发送post请求,把请求参数放在Body Data处,参数都写得正确,但没想到结果每次都报错,直接响应结果乱七八糟,改成用Parameters,反而不乱报错了。 上图 请求里如下 另外一些请求也是这样 这个响应结果也是错误的…...

Java中使用protobuf
一、简介 Protocal Buffers(简称protobuf)是谷歌的一项技术,用于结构化的数据序列化、反序列化。 Protocol Buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。 Protocol B…...

2020款Macbook Pro A2251无法充电无法开机定位及修复
问题背景 up主有一台2020年的Macbook Pro,带Touch Bar,16G512G,四核I5,型号A2251 应该是一周没充电了,之前还用的好好的,后来有一天出差想带上 打开没电,手头上有个小米的66W快充头,…...

Spring Cloud --- 引入Gateway网关
引入Gateway网关 介绍 Spring Cloud Gateway 组件的核心是一系列的过滤器,通过这些过滤器可以将客户端发送的请求转发(路由)到对应的微服务。 Spring Cloud Gateway 是加在整个微服务最前沿的防火墙和代理器,隐藏微服务结点 IP 端口信息,从…...

ESP32-C3实现定时器的启停(Arduino IDE)
1概述 ESP32-C3微控制器有多个定时器,这些定时器可用于各种用途,包括计时、生成PWM信号、测量输入信号的频率等。以下是ESP32-C3上可用的定时器资源: 两个硬件定时器: 定时器0:这是一个通用定时器,通常用于…...

centos升级g++使其支持c++17
centos升级g使其支持c17 升级g的原因现象原因 升级g方法更新镜像源yum升级g版本 总结 升级g的原因 现象 编译最新版本的jsoncpp报一下错误 jsontest.h:87:37: error: ‘hexfloat’ is not a member of ‘std’oss << std::setprecision(16) << std::hexfloat &l…...

Pytest日志收集器配置
前言 在pytest框架中,日志记录(logging)是一个强大的功能,它允许我们在测试期间记录信息、警告、错误等,从而帮助调试和监控测试进度。 pytest与Python标准库中的logging模块完美集成,因此你可以很容易地在…...

Morris算法(大数据作业)
我只能说,概率证明真的好难啊!(;′⌒) 这也证明我的概率论真的学的很差劲,有时间一定要补补/(ㄒoㄒ)/~~ 算法不难证明难! 当一个数足够大时,能不能用更少的空间来近似表示这个整数n,于是&…...

TCP/IP协议 【三次握手】过程简要描述
当建立TCP连接时,三次握手的作用简要描述如下: 第一次握手(客户端向服务器发送SYN包):客户端发送SYN包给服务器,确认服务器是否在线并等待响应。 第二次握手(服务器向客户端发送SYNACK包&…...

docker 数据管理,数据持久化详解 二 数据卷容器
数据卷和数据卷容器核心区别 持久性对比 数据卷:当您直接在启动容器时指定了一个数据卷(例如,使用docker run -v /data),这个数据卷会自动创建,并且其内容会在容器停止或删除后继续存在。您可以随时通过Do…...

Logrotate:Linux系统日志轮转和管理的实用指南
Logrotate是Linux系统中用于自动化管理日志文件的强大工具,它能够高效、安全地轮转、压缩和清理日志文件,从而有效控制日志文件大小,节省磁盘空间,并显著提升系统可维护性和安全性。本文档将提供Logrotate的实用指南,涵…...