您的位置:首页 > 文旅 > 旅游 > 网页翻译会被检测到吗_蚌埠seo_媒体公关_专业搜索引擎优化电话

网页翻译会被检测到吗_蚌埠seo_媒体公关_专业搜索引擎优化电话

2024/12/29 5:46:51 来源:https://blog.csdn.net/gaosw0521/article/details/144274538  浏览:    关键词:网页翻译会被检测到吗_蚌埠seo_媒体公关_专业搜索引擎优化电话
网页翻译会被检测到吗_蚌埠seo_媒体公关_专业搜索引擎优化电话

Java 语言以其“一次编写,处处运行”(Write Once, Run Anywhere)的特性而闻名。这一特性得益于 Java 虚拟机(JVM)的存在,它能够加载并执行与平台无关的字节码(Byte Code)。本文将通过 javap 命令来深入探讨 Java 字节码的结构和内容,帮助读者更好地理解 Java 程序的运行机制。

1 Java 字节码与 JVM

Java 源代码在编译后会生成 .class 文件,这些文件包含了 Java 字节码。字节码是一种中间代码,它不是直接的机器码,而是由 JVM 解释执行的指令集。JVM 在不同的操作系统上都有相应的实现,因此 Java 程序可以在不同的平台上运行。

2 使用 javap 命令反编译字节码

javap 是 JDK 自带的一个命令行工具,主要用于反编译 .class 文件。通过 javap 命令,我们可以查看类的结构、方法、字段等信息,从而更好地理解字节码的组成。

javap的基本用法:

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

以下是一个简单的 Java 类:

public class Main {private int age = 18;public int getAge() {return age;}
}

编译生成 Main.class 文件后,可以使用 javap -v -p Main.class 命令来查看字节码的详细信息。

3 字节码文件的基本信息

通过 javap 命令输出的内容,我们可以看到字节码文件的基本信息,包括文件路径、修改日期、文件大小、SHA-256 校验和等。这些信息有助于确保文件的完整性和一致性。

Classfile /Users/maweiqing/Documents/GitHub/TechSisterLearnJava/codes/TechSister/target/classes/com/itwanger/jvm/Main.classLast modified 2021年4月15日; size 385 bytesSHA-256 checksum 6688843e4f70ae8d83040dc7c8e2dd3694bf10ba7c518a6ea9b88b318a8967c6Compiled from "Main.java"
public class com.itwanger.jvm.Mainminor version: 0major version: 55flags: (0x0021) ACC_PUBLIC, ACC_SUPERthis_class: #3                          // com/itwanger/jvm/Mainsuper_class: #4                         // java/lang/Objectinterfaces: 0, fields: 1, methods: 2, attributes: 1
  • minor versionmajor version:表示字节码的版本号,这里的主版本号为 55,对应 Java 11。
  • flags:表示类的访问标志,如 ACC_PUBLICACC_SUPER
  • this_classsuper_class:分别指向当前类和父类的常量池索引。

4 常量池

常量池是字节码文件中的一个重要部分,它包含了类、字段、方法等的符号引用。常量池中的常量可以分为字面量和符号引用两类。理解常量池的结构和内容对于深入理解 Java 字节码至关重要。

4.1 常量池的基本概念

常量池可以理解为字节码文件中的资源仓库,主要存放两大类信息:

  1. 字面量(Literal):类似于 Java 中的常量概念,比如文本字符串、final 常量等。
  2. 符号引用(Symbolic References):属于编译原理方面的概念,包括以下三种:
    • 类和接口的全限定名(Fully Qualified Name)
    • 字段的名称和描述符(Descriptor)
    • 方法的名称和描述符

Java 虚拟机在加载字节码文件时进行动态链接,字段和方法的符号引用只有在运行期转换后才能获得真正的内存地址。

4.2 常量池的结构

当前字节码文件中一共有 21 个常量,它们之间是有链接的。我们采用顺藤摸瓜的方式,从上依次往下看,那些被链接的常量我们就点到为止。

注:

  • # 号后面跟的是索引,索引从 1 开始,而不是从 0 开始。设计者考虑到“如果要表达不引用任何一个常量的含义时,可以将索引值设为 0 来表示”。
  • = 号后面跟的是常量的类型,没有包含前缀 CONSTANT_ 和后缀 _info

4.3 常量池的详细解析

常量池中的常量通过索引相互引用,形成一个复杂的网络结构。以下是部分常量池的内容:

Constant pool:#1 = Methodref          #4.#18         // java/lang/Object."<init>":()V#2 = Fieldref           #3.#19         // com/itwanger/jvm/Main.age:I#3 = Class              #20            // com/itwanger/jvm/Main#4 = Class              #21            // java/lang/Object#5 = Utf8               age#6 = Utf8               I#7 = Utf8               <init>#8 = Utf8               ()V#9 = Utf8               Code#10 = Utf8               LineNumberTable#11 = Utf8               LocalVariableTable#12 = Utf8               this#13 = Utf8               Lcom/itwanger/jvm/Main;#14 = Utf8               getAge#15 = Utf8               ()I#16 = Utf8               SourceFile#17 = Utf8               Main.java#18 = NameAndType        #7:#8          // "<init>":()V#19 = NameAndType        #5:#6          // age:I#20 = Utf8               com/itwanger/jvm/Main#21 = Utf8               java/lang/Object

第 1 个常量

#1 = Methodref #4.#18 // java/lang/Object."<init>":()V
  • 类型Methodref,表明是用来定义方法的。
  • 指向:常量池中下标为 4 和 18 的常量。

第 4 个常量

#4 = Class #21 // java/lang/Object
  • 类型Class,表明是用来定义类(或者接口)的。
  • 指向:常量池中下标为 21 的常量。

第 21 个常量

#21 = Utf8 java/lang/Object
  • 类型Utf8,UTF-8 编码的字符串。
  • java/lang/Object

第 18 个常量

#18 = NameAndType #7:#8 // "<init>":()V
  • 类型NameAndType,表明是字段或者方法的部分符号引用。
  • 指向:常量池中下标为 7 和 8 的常量。

第 7 个常量

#7 = Utf8 <init>
  • 类型Utf8,UTF-8 编码的字符串。
  • <init>,表明为构造方法。

第 8 个常量

#8 = Utf8 ()V
  • 类型Utf8,UTF-8 编码的字符串。
  • ()V,表明方法的返回值为 void

组合起来的意思:Main 类使用的是默认的构造方法,来源于 Object 类。#4 指向 Class #21(即 java/lang/Object),#18 指向 NameAndType #7:#8(即 "<init>":()V)。

第 2 个常量

#2 = Fieldref #3.#19 // com/itwanger/jvm/Main.age:I
  • 类型Fieldref,表明是用来定义字段的。
  • 指向:常量池中下标为 3 和 19 的常量。

第 3 个常量

#3 = Class #20 // com/itwanger/jvm/Main
  • 类型Class,表明是用来定义类(或者接口)的。
  • 指向:常量池中下标为 20 的常量。

第 19 个常量

#19 = NameAndType #5:#6 // age:I
  • 类型NameAndType,表明是字段或者方法的部分符号引用。
  • 指向:常量池中下标为 5 和 6 的常量。

第 5 个常量

#5 = Utf8 age
  • 类型Utf8,UTF-8 编码的字符串。
  • age,表明字段名为 age

第 6 个常量

#6 = Utf8 I
  • 类型Utf8,UTF-8 编码的字符串。
  • I,表明字段的类型为 int

组合起来的意思:声明了一个类型为 int 的字段 age#3 指向 Class #20(即 com/itwanger/jvm/Main),#19 指向 NameAndType #5:#6(即 age:I)。

5 字段表集合

字段表用来描述接口或者类中声明的变量,包括类变量和成员变量,但不包含声明在方法中的局部变量。字段表中的信息包括字段的修饰符、类型和名称等。

5.1 字段的修饰符

字段的修饰符一般有以下几种:

  • 访问权限修饰符:如 publicprivateprotected
  • 静态变量修饰符:如 static
  • final 修饰符:如 final
  • 并发可见性修饰符:如 volatile
  • 序列化修饰符:如 transient

5.2 字段的类型和名称

字段的类型可以是基本数据类型(如 intfloat)、数组和对象。名称则是字段的标识符。

5.3 字段表示例

Main.class 字节码文件中,字段表的信息如下所示:

private int age;descriptor: Iflags: (0x0002) ACC_PRIVATE
  • 访问权限修饰符private,表明字段的访问权限为私有。
  • 类型int,类型描述符为 I
  • 名称age,字段的名称。

字段的访问标志和类的访问标志非常类似,ACC_PRIVATE 表示字段是私有的。

6 方法表集合

方法表是 Java 字节码文件中的一个重要部分,它用来描述接口或者类中声明的方法,包括类方法、成员方法以及构造方法。方法的修饰符和字段略有不同,例如 volatiletransient 不能用来修饰方法,而方法的修饰符多了 synchronizednativestrictfpabstract

6.1. 构造方法

构造方法是用于创建类实例的特殊方法。在 Main.class 字节码文件中,构造方法的信息如下所示:

public com.itwanger.jvm.Main();descriptor: ()Vflags: (0x0001) ACC_PUBLIC
  • 声明public com.itwanger.jvm.Main(); 这是 Main 类的构造方法,用于创建 Main 类的实例。它是公开的(public)。
  • 描述符descriptor: ()V 这表示构造方法没有参数(())并且没有返回值(V,代表 void)。
  • 访问标志flags: (0x0001) ACC_PUBLIC,表示这个构造方法是公开的,可以从其他类中访问。

6.1.1 Code 属性

构造方法的 Code 属性包含了方法的具体实现细节:

Code:stack=2, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: aload_05: bipush        187: putfield      #2                  // Field age:I10: returnLineNumberTable:line 6: 0line 7: 4LocalVariableTable:Start  Length  Slot  Name   Signature0      11     0  this   Lcom/itwanger/jvm/Main;
  • stack:最大操作数栈深度为 2。操作数栈是一个 LIFO(后进先出)栈,用于存放临时变量和中间结果。在构造方法中,bipushaload_0 指令可能会同时需要栈空间,所以需要 2 个操作数栈深度。
  • locals:局部变量所需要的存储空间为 1 个槽(slot)。局部变量表的容量以变量槽为最小单位,一个变量槽可以存放一个 32 位以内的数据类型。对于实例方法(如构造方法),局部变量表的第一个位置(索引 0)总是用于存储 this 引用。
  • args_size:方法的参数个数为 1。这是因为有一个隐藏的 this 变量,只要不是静态方法,都会有一个当前类的对象 this 存在。

6.1.2 字节码指令

  • aload_0:加载局部变量表中的第一个变量(通常是 this)。
  • invokespecial #1:调用父类的构造方法。
  • bipush 18:将整数 18 推送到操作数栈。
  • putfield #2:将操作数栈顶的值赋给字段 age
  • return:返回。

6.1.3 附加信息

  • LineNumberTable:描述源码行号与字节码行号(字节码偏移量)之间的对应关系。例如,第 6 行对应的字节码行号为 0,第 7 行对应的字节码行号为 4。
  • LocalVariableTable:描述帧栈中的局部变量与源码中定义的变量之间的关系。例如,this 变量的作用域从指令偏移量 0 开始,持续整个方法的长度(长度为 11),并且被分配到局部变量表的第一个槽位(索引 0)。

6.2 成员方法 getAge()

成员方法 getAge() 的信息如下所示:

public int getAge();descriptor: ()Iflags: (0x0001) ACC_PUBLIC
  • 声明public int getAge(); 这是 Main 类的成员方法,用于获取 age 字段的值。它是公开的(public)。
  • 描述符descriptor: ()I 这表示方法没有参数(())并且返回类型为 intI)。
  • 访问标志flags: (0x0001) ACC_PUBLIC,表示这个方法是公开的,可以从其他类中访问。

6.2.1 Code 属性

getAge() 方法的 Code 属性包含了方法的具体实现细节:

Code:stack=1, locals=1, args_size=10: aload_01: getfield      #2                  // Field age:I4: ireturnLineNumberTable:line 9: 0LocalVariableTable:Start  Length  Slot  Name   Signature0       5     0  this   Lcom/itwanger/jvm/Main;
  • stack:最大操作数栈深度为 1。
  • locals:局部变量所需要的存储空间为 1 个槽(slot)。
  • args_size:方法的参数个数为 1。这是因为有一个隐藏的 this 变量。

6.2.2 字节码指令

  • aload_0:加载局部变量表中的第一个变量(通常是 this)。
  • getfield #2:获取字段 age 的值,并将其推送到操作数栈顶。
  • ireturn:返回栈顶的整型值。

6.2.3 附加信息

  • LineNumberTable:描述源码行号与字节码行号(字节码偏移量)之间的对应关系。例如,第 9 行对应的字节码行号为 0。
  • LocalVariableTable:描述帧栈中的局部变量与源码中定义的变量之间的关系。例如,this 变量的作用域从指令偏移量 0 开始,持续整个方法的长度(长度为 5),并且被分配到局部变量表的第一个槽位(索引 0)。

7 总结

通过 javap 命令,我们可以深入了解 Java 字节码的结构和内容。字节码文件包含了类的基本信息、常量池、字段表、方法表和字节码指令等。理解这些内容不仅有助于我们更好地调试和优化 Java 程序,还能加深对 Java 虚拟机工作原理的认识。

8 思维导图

在这里插入图片描述

9 参考链接

从javap的角度轻松看懂字节码

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com