javac
是 Java 编程语言的编译器,它是 Java Development Kit (JDK) 的一部分。javac
命令用于将 Java 源代码文件(扩展名为 .java
)编译成 Java 字节码文件(扩展名为 .class
),这些字节码文件随后可以由 Java 虚拟机 (JVM) 执行。以下是对 javac
指令的详细介绍:
javac [options] [sourcefiles] [classes] [args]
MyProgram.java
的源文件,使用以下命令:javac MyProgram.java
-d
目录:指定编译器输出 .class
文件的目标目录。如果目录不存在,javac
将尝试创建它。-classpath
或 -cp
路径:设置查找类文件和注释处理器的路径。-sourcepath
路径:指定查找输入源文件的位置。-target
版本:指定生成特定版本的 Java VM 的类文件。例如,如果想生成与 Java 8 兼容的类文件,可以使用 -target 1.8
。-source
版本:提供与 -target
选项相对应的源代码的版本支持。-g
:生成所有调试信息,包括局部变量的信息。-Xlint
:提供对源代码的更全面检查,报告更多警告和潜在的问题。-encoding
编码:指定源文件使用的字符编码,例如 UTF-8
。-bootclasspath
路径:覆盖引导类文件的位置。这在与非标准的 Java 运行时环境 (JRE) 一起使用时特别有用。-processor
:指定要使用的注释处理器的名称。-J
选项:直接将选项传递给 JVM。javac
会报告这些错误,并在可能的情况下给出修改建议。javac
的行为和可用选项可能会根据 JDK 的版本略有不同。建议查看使用的 JDK 版本的官方文档了解具体细节。javac
通常在命令行环境中使用,但也可以在集成开发环境 (IDE) 如 Eclipse 或 IntelliJ IDEA 中间接使用。了解 javac
的这些方面对于 Java 开发者来说是很重要的,因为它是 Java 开发过程中的基本工具之一。通过有效地使用 javac
,开发者可以确保他们的 Java 程序被正确编译,且在目标 Java 虚拟机上运行无误。
javap
是 Java Development Kit (JDK) 中的一个命令行工具,它被用作 Java 类文件的反汇编程序。这意味着可以使用 javap
来查看编译后的 Java 字节码,或者更具体地说,查看 .class
文件中的信息。这对于理解 Java 字节码、调试、以及学习 Java 编译器如何工作非常有帮助。
javap [options] [classes]
MyClass.class
的类文件,使用以下命令:javap MyClass
注意,不需要指定 .class
扩展名。-c
:显示方法的字节码。这是最常用的选项,因为它允许我们看到 Java 源代码是如何被编译成字节码的。-p
或 -private
:显示所有类和成员,包括私有的。-v
或 -verbose
:提供关于类文件结构的详细信息,包括常量池。-s
:显示内部类型签名。-l
:输出行号和本地变量表。-classpath
或 -cp
路径:设置查找类文件的路径。-bootclasspath
路径:覆盖引导类文件的位置。-b
:反汇编备用(备份)类文件。-sysinfo
:显示系统信息(包括 Java 环境属性)。javap
输出的内容包括类的声明、继承的父类、实现的接口、构造函数、方法、字段等。-c
选项,我们将看到每个方法的字节码,这对于理解 Java 字节码非常有帮助。javap
可以帮助理解为什么编译的 Java 代码在运行时表现异常。javap
仅提供对字节码的静态分析。它不能告诉我们程序的运行时行为。了解 javap
对于希望深入理解 Java 字节码的开发者和研究者来说是非常重要的。通过 javap
,我们可以更好地理解 Java 程序的底层工作原理。
在 Java 字节码中,方法描述符 descriptor: ([Ljava/lang/String;)V
是用来描述方法签名的一部分。这个描述符具体来说包含两个部分:参数类型和返回类型。
参数类型:([Ljava/lang/String;)
[
表示数组。Ljava/lang/String;
表示 String
类型,其中 L
表示引用类型,java/lang/String
是 String
类的内部名称,用斜杠 /
而不是点 .
分隔包名和类名。返回类型:V
V
表示 void
类型。在 Java 字节码中,void
类型用 V
来表示。因此,([Ljava/lang/String;)V
描述的是一个方法,它接受一个 String
类型数组作为参数(这是 main
方法的标准形式),并且没有返回值(void
)。在 Java 源代码中,这对应于如下签名:
public static void main(String[] args)
理解 Java 字节码的方法描述符对于深入理解 Java 虚拟机(JVM)和 Java 字节码的工作原理是很重要的。这种低级表示形式揭示了 Java 源代码是如何被转换为字节码,进而在 JVM 上执行的。
将下面这段Java代码编译为class字节码文件,然后反汇编class文件,观察生成的字节码。
public class Demo1_22 {
public static void main(String[] args) {
String s1 = "a";
String s2 = "b";
String s3 = "ab";
}
}
将.java编译为.class,然后反汇编.class文件
javac .\Demo1_22.java
javap -v .\Demo1_22.class
反汇编出的内容如下:
Classfile /D:/ideaProject/JVM_Detect/src/per/mjn/Demo1_22.class
Last modified 2023年11月18日; size 312 bytes
MD5 checksum 3ae0972c52c7e5643f0a0ba3dccd4758
Compiled from "Demo1_22.java"
public class per.mjn.Demo1_22
minor version: 0
major version: 55
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #5 // per/mjn/Demo1_22
super_class: #6 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."":()V
#2 = String #16 // a
#3 = String #17 // b
#4 = String #18 // ab
#5 = Class #19 // per/mjn/Demo1_22
#6 = Class #20 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 Demo1_22.java
#15 = NameAndType #7:#8 // "":()V
#16 = Utf8 a
#17 = Utf8 b
#18 = Utf8 ab
#19 = Utf8 per/mjn/Demo1_22
#20 = Utf8 java/lang/Object
{
public per.mjn.Demo1_22();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=4, args_size=1
0: ldc #2 // String a
2: astore_1
3: ldc #3 // String b
5: astore_2
6: ldc #4 // String ab
8: astore_3
9: return
LineNumberTable:
line 5: 0
line 6: 3
line 7: 6
line 8: 9
}
SourceFile: "Demo1_22.java"
以上代码显示了 per.mjn.Demo1_22
类的详细结构。它包括类的定义、常量池、两个方法(构造函数和 main
方法)以及源文件信息。下面,我们来逐部分进行解释。
文件信息:
Classfile /D:/ideaProject/JVM_Detect/src/per/mjn/Demo1_22.class
:类文件的路径。Last modified 2023年11月18日; size 312 bytes
:最后修改时间和文件大小。MD5 checksum
:文件的 MD5 校验和。Compiled from "Demo1_22.java"
:源文件名称。类信息:
public class per.mjn.Demo1_22
:公共类 per.mjn.Demo1_22
。minor version: 0, major version: 55
:类文件格式的版本号,这里是 Java 11(major version 55)。flags: (0x0021) ACC_PUBLIC, ACC_SUPER
:类的访问标志,表示这是一个公共类,并使用 super
关键字。this_class: #5, super_class: #6
:类自身和父类的引用。interfaces: 0, fields: 0, methods: 2, attributes: 1
:类实现的接口数量、字段数量、方法数量和属性数量。#1 = Methodref #6.#15
指向 Object
类的构造函数,#2 = String #16
是字符串 “a” 的引用。构造函数 per.mjn.Demo1_22()
:
public
:公共访问级别。descriptor: ()V
:无参数,返回 void
。Object
的构造函数,然后返回。main
方法 public static void main(java.lang.String[])
:
public static
:公共静态方法。descriptor: ([Ljava/lang/String;)V
:接受一个字符串数组参数,返回 void
。SourceFile: "Demo1_22.java"
:指明源文件名。这个字节码文件提供了 Demo1_22
类的结构和行为的低级视图。它有助于理解 Java 编译器是如何将源代码转换为字节码的,以及这些字节码是如何在 Java 虚拟机上执行的。这对于深入理解 Java 和 JVM 的工作原理是非常有价值的。
在 Java 字节码中,方法描述符 descriptor: ([Ljava/lang/String;)V
是用来描述方法签名的一部分。这个描述符具体来说包含两个部分:参数类型和返回类型。
参数类型:([Ljava/lang/String;)
[
表示数组。Ljava/lang/String;
表示 String
类型,其中 L
表示引用类型,java/lang/String
是 String
类的内部名称,用斜杠 /
而不是点 .
分隔包名和类名。返回类型:V
V
表示 void
类型。在 Java 字节码中,void
类型用 V
来表示。因此,([Ljava/lang/String;)V
描述的是一个方法,它接受一个 String
类型数组作为参数(这是 main
方法的标准形式),并且没有返回值(void
)。在 Java 源代码中,这对应于如下签名:
public static void main(String[] args)
理解 Java 字节码的方法描述符对于深入理解 Java 虚拟机(JVM)和 Java 字节码的工作原理是很重要的。这种低级表示形式揭示了 Java 源代码是如何被转换为字节码,进而在 JVM 上执行的。