探索Java的DNA-JVM字节码深度解析


引言

在Java的世界里,JVM(Java虚拟机)是我们程序运行的心脏。而字节码,作为JVM的血液,携带着程序的执行指令。今天,我们将深入探索Java字节码的奥秘,一窥JVM如何将人类可读的代码转化为机器可执行的指令。


一、JVM 知识回顾


JVM是一个可以执行Java字节码的虚拟计算机,它包括类加载器、运行时数据区、执行引擎等核心组件。JVM架构保证了Java程序的平台独立性,实现了“一次编写,到处运行”的理念。

关于Java虚拟机的工作原理、作用和应用场景, 感兴趣的朋友请前往查看:Java虚拟机揭秘-底层驱动力,性能保障!


二、Class文件结构


Class文件是JVM执行字节码的载体,它以8位字节为基础单位的二进制流形式存在。Class文件的结构包括魔数、版本号、常量池、字段表集合、方法表集合等。

在这里插入图片描述


下面以一个例子逐步讲解字节码。

//Test.java
public class Test {private int a;public int num() {return a + 1;}
}

通过执行以下命令, 可以在当前所在路径下生成一个Test.class文件。

javac Test.java

以文本形式打开Test.class文件,内容如下:

cafe babe 0000 003d 0013 0a00 0200 0307
0004 0c00 0500 0601 0010 6a61 7661 2f6c
616e 672f 4f62 6a65 6374 0100 063c 696e
6974 3e01 0003 2829 5609 0008 0009 0700
0a0c 000b 000c 0100 0454 6573 7401 0001
6101 0001 4901 0004 436f 6465 0100 0f4c
696e 654e 756d 6265 7254 6162 6c65 0100
036e 756d 0100 0328 2949 0100 0a53 6f75
7263 6546 696c 6501 0009 5465 7374 2e6a
6176 6100 2100 0800 0200 0000 0100 0200
0b00 0c00 0000 0200 0100 0500 0600 0100
0d00 0000 1d00 0100 0100 0000 052a b700
01b1 0000 0001 000e 0000 0006 0001 0000
0002 0001 000f 0010 0001 000d 0000 001f
0002 0001 0000 0007 2ab4 0007 0460 ac00
0000 0100 0e00 0000 0600 0100 0000 0700
0100 1100 0000 0200 12

  • 文件开头的4个字节(“cafe babe”)称之为 魔数,唯有以"cafe babe"开头的class文件方可被虚拟机所接受,这4个字节就是字节码文件的身份识别。

  • 0000是编译器jdk版本的次版本号0


三、反编译字节码文件


使用 javap 命令反编译 Java 字节码文件是一个查看 .class 文件内部结构和字节码指令的简单方法。

用法: javap <options> <classes>

其中<options>选项包括:

-help  --help  -?        输出此用法消息-version                 版本信息-v  -verbose             输出附加信息-l                       输出行号和本地变量表-public                  仅显示公共类和成员-protected               显示受保护的/公共类和成员-package                 显示程序包/受保护的/公共类和成员 (默认)-p  -private             显示所有类和成员-c                       对代码进行反汇编-s                       输出内部类型签名-sysinfo                 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)-constants               显示最终常量-classpath <path>        指定查找用户类文件的位置-cp <path>               指定查找用户类文件的位置-bootclasspath <path>    覆盖引导类文件的位置

以下是如何使用 javap 命令的基本步骤:

  • 第一步,编译 Java 源文件: 首先,你需要有一个 .class 文件。如果你有一个 .java 源文件,可以使用 javac 命令将其编译成 .class 文件。例如:
javac Test.java

这将在当前目录下生成一个名为 Test.class 的字节码文件。

在这里插入图片描述


  • 第二步,使用 javap 命令: 打开命令行工具(在 Windows 上是 CMD 或 PowerShell,在 macOS 或 Linux 上是 Terminal),然后使用 javap 命令查看 .class 文件的内容。

    可以使用:

    javap -c Test
    

  • 第三步,查看输出: 执行 javap 命令后,你将看到 .class 文件的详细信息,包括类定义、字段、方法以及它们的字节码指令。

在这里插入图片描述

  • 第四步,输出到文件: 如果你想要将 javap 的输出保存到文件中,可以使用重定向操作符 >
javap -c Test > output.txt

这将把 Test.class 文件的字节码指令输出到 output.txt 文件中。

在这里插入图片描述


请注意,javap 主要用于查看字节码指令和类的结构,而不是将 .class 文件完全反编译回 Java 源代码。

如果你需要将 .class 文件反编译为更接近原始源代码的形式,可能需要使用其他专门的反编译工具。


四、字节码文件.class详细解读


执行以下命令,可查看子节码文件内容:

javap -verbose -p Test.class
  Last modified 2024年5月26日; size 265 bytesSHA-256 checksum aeba5b65f486bc4f6ee16ec2073e4fec2053987d53cdaed540343c6230966095Compiled from "Test.java"
public class Testminor version: 0major version: 61flags: (0x0021) ACC_PUBLIC, ACC_SUPERthis_class: #8                          // Testsuper_class: #2                         // java/lang/Objectinterfaces: 0, fields: 1, methods: 2, attributes: 1
Constant pool:#1 = Methodref          #2.#3          // java/lang/Object."<init>":()V#2 = Class              #4             // java/lang/Object#3 = NameAndType        #5:#6          // "<init>":()V#4 = Utf8               java/lang/Object#5 = Utf8               <init>#6 = Utf8               ()V#7 = Fieldref           #8.#9          // Test.a:I#8 = Class              #10            // Test#9 = NameAndType        #11:#12        // a:I#10 = Utf8               Test#11 = Utf8               a#12 = Utf8               I#13 = Utf8               Code#14 = Utf8               LineNumberTable#15 = Utf8               num#16 = Utf8               ()I#17 = Utf8               SourceFile#18 = Utf8               Test.java
{private int a;descriptor: Iflags: (0x0002) ACC_PRIVATEpublic Test();descriptor: ()Vflags: (0x0001) ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 2: 0public int num();descriptor: ()Iflags: (0x0001) ACC_PUBLICCode:stack=2, locals=1, args_size=10: aload_01: getfield      #7                  // Field a:I4: iconst_15: iadd6: ireturnLineNumberTable:line 7: 0
}
SourceFile: "Test.java"

以上信息是使用 javap 命令反编译 Test.class 文件后得到的输出结果。它展示了 .class 文件的内部结构和相关信息。


下面是对输出结果的详细解析:

(1)、文件元信息

  • Last modified:文件最后修改时间。

  • size:文件大小。

  • SHA-256 checksum:文件的 SHA-256 校验和。


(2)、编译信息

  • Compiled from "Test.java":表明 Test.class 文件是由 Test.java 源文件编译而来。


(3)、类定义

  • public class Test:定义了一个名为 Test 的公共类。


(4)、版本信息

  • minor version: 0major version: 61:表示这个 .class 文件的 Java 版本信息,主版本号是 61,这对应于 Java 17。


(5)、访问标志

  • flags: (0x0021) ACC_PUBLIC, ACC_SUPER:类的标志,ACC_PUBLIC 表示这个类是公开的,ACC_SUPER 是一个默认标志,表示这个类不是 final 的。


    访问标志的含义如下:

    标志名称标志值含义
    ACC_PUBLIC0x0001是否为Public类型
    ACC_FINAL0x0010是否被声明为final,只有类可以设置
    ACC_SUPER0x0020是否允许使用invokespecial字节码指令的新语义.
    ACC_INTERFACE0x0200标志这是一个接口
    ACC_ABSTRACT0x0400是否为abstract类型,对于接口或者抽象类来说,次标志值为真,其他类型为假
    ACC_SYNTHETIC0x1000标志这个类并非由用户代码产生
    ACC_ANNOTATION0x2000标志这是一个注解
    ACC_ENUM0x4000标志这是一个枚举

(6)、常量池

Constant pool
  • 常量池可以理解成Class文件中的资源仓库,包含了类文件中的各种常量引用,例如方法引用、类名、字段名等。

  • 常量池主要存放的是两大类常量:字面量(Literal)和符号引用(Symbolic References)。

  • 字面量类似于java中的常量概念,如文本字符串,final常量等。

  • 符号引用则属于编译原理方面的概念,包括以下三种:

    • 类和接口的全限定名(Fully Qualified Name)

    • 字段的名称和描述符号(Descriptor)

    • 方法的名称和描述符

JVM是在加载Class文件的时候才进行的动态链接,也就是说这些字段和方法符号引用只有在运行期转换后才能获得真正的内存入口地址。当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建或运行时解析并翻译到具体的内存地址中。

例如:

  • #1 = Methodref #2.#3:引用了 java/lang/Object 类的无参构造方法 <init>()V

  • #7 = Fieldref #8.#9:引用了 Test 类中的字段 a


(7)、字段

  • private int a;:定义了一个名为 a 的私有整型字段。

(8)、构造方法

  • public Test();:定义了一个公共的无参构造方法,用于实例化 Test 类的对象。

  • Code:构造方法的字节码指令,这里调用了父类 Object 的构造方法。


    Code:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 2: 0
    

    code内的主要属性为:

    • stack: 最大操作数栈,JVM运行时会根据这个值来分配栈帧(Frame)中的操作栈深度,此处为1。

    • locals: 局部变量所需的存储空间,单位为Slot, Slot是虚拟机为局部变量分配内存时所使用的最小单位,为4个字节大小。方法参数(包括实例方法中的隐藏参数this),显示异常处理器的参数(try catch中的catch块所定义的异常),方法体中定义的局部变量都需要使用局部变量表来存放。值得一提的是,locals的大小并不一定等于所有局部变量所占的Slot之和,因为局部变量中的Slot是可以重用的。

    • args_size: 方法参数的个数,这里是1,因为每个实例方法都会有一个隐藏参数this.

    • LineNumberTable: 该属性的作用是描述源码行号与字节码行号(字节码偏移量)之间的对应关系。可以使用 -g:none 或-g:lines选项来取消或要求生成这项信息,如果选择不生成LineNumberTable,当程序运行异常时将无法获取到发生异常的源码行号,也无法按照源码的行数来调试程序。


(9)、方法

  • public int num();:定义了一个名为 num 的公共方法,返回类型为 int

  • Codenum 方法的字节码指令,它读取字段 a 的值,加 1,然后返回结果。


  • descriptor: I

类型为I, I即是int类型,关于字节码的类型对应如下:

标识字符含义
B基本类型byte
C基本类型char
D基本类型double
F基本类型float
I基本类型int
J基本类型long
S基本类型short
Z基本类型boolean
V特殊类型void
L对象类型,以分号结尾,如Ljava/lang/Object;

(10)、行号表

  • LineNumberTable:提供了源代码行号和字节码指令之间的映射。


(11)、源文件

  • SourceFile: "Test.java":指示这个 .class 文件是由 Test.java 源文件编译而来的。

这个输出结果提供了 Test.class 文件的详细内部结构,包括字段、方法、访问控制、版本信息等。通过这些信息,可以更好地理解 Java 类的编译过程和字节码的细节。


五、分析try-catch-finally


public class TestCode {public int foo() {int x;try {x = 1;return x;} catch (Exception e) {x = 2;return x;} finally {x = 3;}}
}

执行命令:

javac TestCode.java
javap -verbose -p TestCode.class

内容如下:

  Last modified 2024年5月26日; size 418 bytesSHA-256 checksum 0d58e986cce436cf5dd634bcfabb39b75afaddaa863d596c1e874f3571832952Compiled from "TestCode.java"
public class TestCodeminor version: 0major version: 61flags: (0x0021) ACC_PUBLIC, ACC_SUPERthis_class: #9                          // TestCodesuper_class: #2                         // java/lang/Objectinterfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:#1 = Methodref          #2.#3          // java/lang/Object."<init>":()V#2 = Class              #4             // java/lang/Object#3 = NameAndType        #5:#6          // "<init>":()V#4 = Utf8               java/lang/Object#5 = Utf8               <init>#6 = Utf8               ()V#7 = Class              #8             // java/lang/Exception#8 = Utf8               java/lang/Exception#9 = Class              #10            // TestCode#10 = Utf8               TestCode#11 = Utf8               Code#12 = Utf8               LineNumberTable#13 = Utf8               foo#14 = Utf8               ()I#15 = Utf8               StackMapTable#16 = Class              #17            // java/lang/Throwable#17 = Utf8               java/lang/Throwable#18 = Utf8               SourceFile#19 = Utf8               TestCode.java
{public TestCode();descriptor: ()Vflags: (0x0001) ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 1: 0public int foo();descriptor: ()Iflags: (0x0001) ACC_PUBLICCode:stack=1, locals=5, args_size=10: iconst_11: istore_12: iload_13: istore_24: iconst_35: istore_16: iload_27: ireturn8: astore_29: iconst_210: istore_111: iload_112: istore_313: iconst_314: istore_115: iload_316: ireturn17: astore        419: iconst_320: istore_121: aload         423: athrowException table:from    to  target type0     4     8   Class java/lang/Exception0     4    17   any8    13    17   any17    19    17   anyLineNumberTable:line 5: 0line 6: 2line 11: 4line 6: 6line 7: 8line 8: 9line 9: 11line 11: 13line 9: 15line 11: 17line 12: 21StackMapTable: number_of_entries = 2frame_type = 72 /* same_locals_1_stack_item */stack = [ class java/lang/Exception ]frame_type = 72 /* same_locals_1_stack_item */stack = [ class java/lang/Throwable ]
}
SourceFile: "TestCode.java"

我们重点解读名为 foo 的 Java 方法的字节码信息。

下面是对这个输出的详细解析:

  1. 方法签名
    • public int foo();:定义了一个名为 foo 的公共方法,它返回一个整数值。
  2. 方法描述符
    • descriptor: ()I:表示这个方法没有参数,并返回一个 int 类型的值。
  3. 访问标志
    • flags: (0x0001) ACC_PUBLIC:表示这个方法是公开的。
  4. 字节码指令
    • Code:包含了实际执行的方法体的字节码指令。
    • stack=1, locals=5, args_size=1:表示这个方法在执行时,操作数栈的最大深度是 1,局部变量表的大小是 5(包括 this 指针和方法参数),参数大小是 1(对于实例方法,this 指针算作第一个参数)。
  5. 字节码指令详解
    • iconst_1:将整数 1 推送到栈上。
    • istore_1:将栈顶的整数值存储到局部变量 1 中。
    • iload_1:从局部变量 1 中加载整数值到栈上。
    • istore_2:将栈顶的整数值存储到局部变量 2 中。
    • iconst_3:将整数 3 推送到栈上。
    • istore_1:将栈顶的整数值存储到局部变量 1 中。
    • iload_2:从局部变量 2 中加载整数值到栈上。
    • ireturn:将栈顶的整数值作为返回值结束方法。
    • astore_2:将栈顶的引用类型值存储到局部变量 2 中。
    • iconst_2istore_1istore_3aloadathrow:这些指令涉及到异常处理,athrow 指令会抛出异常。
  6. 异常表
    • Exception table:列出了方法中可能抛出的异常及其处理程序的位置。
    • fromtotargettype:分别表示异常发生的起始指令、结束指令、跳转目标指令和异常类型。
  7. 行号表
    • LineNumberTable:提供了源代码行号和字节码指令之间的映射,方便调试。
  8. StackMapTable
    • StackMapTable:在 JDK 7 及以后版本中,用于替代之前的 Exceptions 属性和 LineNumberTable,提供了更详细的栈映射信息,用于支持新的异常表和行号信息。
  9. 源文件
    • SourceFile:指示这个 .class 文件是由哪个 .java 源文件编译而来的。

这个输出结果提供了 foo 方法的字节码指令和相关元信息,包括方法的访问权限、返回类型、局部变量和操作数栈的使用情况、异常处理以及源代码行号映射等。通过这些信息,可以深入理解 Java 方法的执行细节和异常处理机制。


六、结语

字节码不仅支持Java语言,还支持所有编译到字节码的JVM语言,如Groovy、Scala、Kotlin等。此外,字节码层面的优化可以显著提高程序性能。

字节码是Java程序的灵魂,掌握了字节码,就掌握了程序性能的钥匙。本文深入探讨了Java字节码的内部结构和工作原理,然而,字节码的世界远比我们所见的要深邃。

在下一篇文章中,我们将揭开JVM调优的神秘面纱,探索如何通过字节码优化让Java程序运行如飞。敬请期待!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/21790.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【小白向】微信小程序解密反编译教程

# 前言 最近笔者有做到微信小程序的渗透测试&#xff0c;其中有一个环节就是对微信小程序的反编译进行源码分析&#xff0c;所谓微信小程序反编译&#xff0c;就是将访问的小程序进行反向编译拿到部分源码&#xff0c;然后对源码进行安全审计&#xff0c;分析出其中可能存在的…

图形学初识--颜色混合

文章目录 前言正文为什么要有颜色混合&#xff1f;颜色混合常见实现方式&#xff1f;上述颜色混合注意点 结尾&#xff1a;喜欢的小伙伴点点关注赞哦! 前言 本章节补充一下颜色混合的内容&#xff0c;主要包含&#xff1a;为什么要有颜色混合&#xff1f;颜色混合常实现方式&a…

Centos 7 安装刻录至硬件服务器

前言 在日常测试中&#xff0c;会遇到很多安装的场景&#xff0c;今天给大家讲一下centos 7 的安装&#xff0c;希望对大家有所帮助。 一.下载镜像 地址如下&#xff1a; centos官方镜像下载地址https://www.centos.org/download/ 按照需求依次点击下载 二.镜像刻录 镜像刻…

idea springboot woff/woff2/eot/ttf/svg等小图标不显示的问题 - 第515篇

历史文章&#xff08;文章累计500&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 《…

Shopify 独立站监控观测最佳实践

Shopify 简介 Shopify 是一个全球领先的电子商务平台&#xff0c;它为商家提供了一整套在线商店解决方案。自 2006 年成立以来&#xff0c;Shopify 已经帮助数百万商家在全球范围内建立和发展他们的在线业务。 监控观测 Shopify 站点对于确保业务连续性、优化用户体验和提高运…

Linux shell编程学习笔记56:date命令——显示或设置系统时间与日期

0 前言 2024年的网络安全检查又开始了&#xff0c;对于使用基于Linux的国产电脑&#xff0c;我们可以编写一个脚本来收集系统的有关信息。在收集的信息中&#xff0c;应该有一条是搜索信息的时间。 1. date命令 的功能、格式和选项说明 我们可以使用命令 date --help 来查看 d…

Kafka之Broker原理

1. 日志数据的存储 1.1 Partition 1. 为了实现横向扩展&#xff0c;把不同的数据存放在不同的 Broker 上&#xff0c;同时降低单台服务器的访问压力&#xff0c;我们把一个Topic 中的数据分隔成多个 Partition 2. 每个 Partition 中的消息是有序的&#xff0c;顺序写入&#x…

LeetCode刷题:反转链表

leetCode真题 206. 反转链表 属于基础简单题目 常见的做法有递归和while循环 递归 // 1. 递归参数和返回值public static ListNode reverseList(ListNode head) {// 1. 递归终止条件if (head null || head.next null) {return head;}// 递归逻辑ListNode last reverseL…

达梦数据库相关SQL及适配Mysql配置总结

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

Windows 系统安装 VisualSVN Server

一.下载 VisualSVN Server VisualSVN-Server 是 SVN 版本控制中服务器端要使用的软件,就是我们提交代码存在安装这个软件的电脑上,它将很多配置和服务直接帮你完成,简单好用容易上手。VisualSVN Server有三个版本,社区版免费但限15个用户,另有一般和‘企业’两个收费版本…

如何卸载ollama

文章目录 一 概述二 卸载2.1 Windows平台卸载 ollama2.2 Linux 平台卸载 ollama2.3 Docker 平台卸载 ollama 参考链接 一 概述 本文档主要讲述 ollama 如何卸载&#xff0c;适用范围包括 Windows Linux 以及 Docker 等平台的安装方式。 二 卸载 2.1 Windows平台卸载 ollama …

学习C++应该做点什么项目

C作为一门底层可操作性很强的语言&#xff0c;广泛应用于游戏开发、工业和追求性能、速度的应用。 比如腾讯&#xff0c;无论游戏&#xff0c;还是微信&#xff0c;整个鹅厂后台几乎都是 C 开发&#xff0c;对 C 开发者的需求非常大。 但问题是C入门和精通都比较困难&#xf…

有哪些挣钱软件一天能赚几十元?盘点十个能长期做下去的挣钱软件

在这个信息爆炸的时代&#xff0c;每个人都在寻找快速赚钱的秘诀。很多人做兼职副业的目标并不是获得很大的成功&#xff0c;大部分人一天能赚几十就心满意足了。 今天&#xff0c;我要带你一探究竟&#xff0c;揭秘那些能让你日赚几十元的挣钱软件。准备好了吗&#xff1f;让我…

单枪匹马月入17万美元:数字游民Pieter Levels如何成就商业传奇

了解数字游民的应该都听说过 Pieter Levels&#xff0c;可以说他是数字游民的先驱人物。 他在推特上拥有超过43万的粉丝&#xff0c;仅凭一台笔记本电脑就连续建立了多个高盈利网站&#xff0c;光是推特主页上展示的比较新的几个网站&#xff0c;每月收入加起来就高达 17.6 万…

Vulnhub项目:THE PLANETS: MERCURY

1、靶场地址 The Planets: Mercury ~ VulnHubThe Planets: Mercury, made by SirFlash. Download & walkthrough links are available.https://vulnhub.com/entry/the-planets-mercury,544/ 这好像是个系列的&#xff0c;关于星球系列&#xff0c;之前还做过一个地球的&a…

STM32-15-DMA

STM32-01-认识单片机 STM32-02-基础知识 STM32-03-HAL库 STM32-04-时钟树 STM32-05-SYSTEM文件夹 STM32-06-GPIO STM32-07-外部中断 STM32-08-串口 STM32-09-IWDG和WWDG STM32-10-定时器 STM32-11-电容触摸按键 STM32-12-OLED模块 STM32-13-MPU STM32-14-FSMC_LCD 文章目录 STM…

[原创][Delphi多线程]TThreadedQueue的经典使用案例.

[简介] 常用网名: 猪头三 出生日期: 1981.XX.XX QQ: 643439947 个人网站: 80x86汇编小站 https://www.x86asm.org 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delph…

解读信创产业根基,操作系统发展历程

信创产业根基之一操作系统 操作系统是一个关键的控制程序&#xff0c;负责协调、管理和控制计算机硬件和软件资源。作为硬件的首要软件扩展&#xff0c;它位于裸机与用户之间&#xff0c;充当了两者之间的桥梁。通过其核心程序&#xff0c;操作系统高效地管理着系统中的各类资源…

有哪些兼职软件一天能赚几十元?盘点十个能长期做下去的挣钱软件

在当今这个信息泛滥的时代&#xff0c;众人纷纷寻求迅速致富的捷径。许多人在从事兼职或副业时&#xff0c;并不期望取得巨大的成就&#xff0c;只要每天能额外收入数十元&#xff0c;便已心满意足。 今天&#xff0c;我将带领大家深入探究&#xff0c;揭开那些隐藏在日常生活…

【小海实习日记】Git使用规范

1.Git使用流程 1.1 从master分支拉一个分支&#xff0c;命名要符合规范且清晰。 1.2 commit到本地&#xff0c;push 到远端。 1.3 在Gitlab创建MR&#xff0c;选择develp分支。 1.4 如果要修改的话&#xff0c;先把Gitlab上的MR修改为Draft(修改态)&#xff0c;然后在本地修改代…