一、概述
1、字节码文件的跨平台性
(1)Java语言:跨平台的语言(write once ,run anywhere)
当Java源代码成功编译成字节码后,如果想在不同的平台上面运行,则无须再次编译这个优势不再那么吸引人了。Python、PHP、Per1、Ruby、Lisp等有强大的解释器。·跨平台似乎已经快成为一门语言必选的特性。
(2)Java虚拟机:跨语言的平台
Java虚拟机不和包括Java在内的任何语言绑定,它只与“Class 文件”这种特定的二进制文件格式所关联。无论使用何种语言进行软件开发,只要能将源文件编译为正确的Class文件,那么这种语言就可以在Java虚拟机上执行。可以说,统一而强大的
Class文件结构,就是Java虚拟机的基石、桥梁。
https://docs.oracle.com/javase/specs/index.html
所有的JVM全部遵守Java虚拟机规范,也就是说所有的JVM环境都是一样的,。这样一来字节码文件可以在各种JVM上运行。
(3)想要让一个Java程序正确地运行在JVM中,Java源码就必须要被编译为符合JVM规范的字节码。
前端编译器的主要任务就是负责将符合Java语法规范的Java代码转换为符合JVM规范的字节码文件。
javac是一种能够将Java源码编译为字节码的前端编译器。
Javac编译器在将Java源码编译为一个有效的字节码文件过程中经历了4个步骤,分别是词法解析、语法解析、语义解析以及生成字节码。
Oracle的JDK软件包括两部分内容:
一部分是将3ava源代码编译成Java虚拟机的指令集的编译器
另一部分是用于实现ava虚拟机的运行时环境。
2、Java的前端编译器
前端编译器 VS 后端编译器
Java源代码的编译结果是字节码,那么肯定需要有一种编译器能够将Java源码编译为字节码,承担这个重要责任的就是配置在path环境变量中的javac编译器。javac是一种能够将Java源码编译为字节码的前端编译器。
HotSpot VM并没有强制要求前端编译器只能使用javac来编译字节码,其实只要编译结果符合JVW规范都可以被JVM所识别即可。在Java的前端编译器领域,除了javac之外,还有一种被大家经常用到的前端编译器,那就是内置在Eclipse中的ECJ(EclipseCompiler for Java)编译器。和Javac的全量式编译不同,ECJ是一种增量式编译器。
(1)在Eclipse中,当开发人员编写完代码后,使用“Ctrl+S”快捷键时,ECJ编译器所采取的编译方案是把未编译部分的源码逐行进行编译,而非每次都全量编译。因此ECJ的编译效率会比javac更加迅速和高效,当然编译质量和javac相比大致还是一样的。
(2)ECJ不仅是Eclipse的默认内置前端编译器,在Tomcat中同样也是使用ECJ编译器来编译jsp文件。由于ECJ编译器是采用 GPLv2 的开源协议进行源代码公开,所以,大家可以登录eclipse官网下载ECJ编译器的源码进行二次开发。
(3)默认情况下,Intelli3 IDEA使用 javac编译器。(还可以自己设置为Aspect]编译器 ajc )
前端编译器并不会直接涉及编译优化等方面的技术,而是将这些具体优化细节移交给HotSpot的JIT编译器负责。
复习:AOT(静态提前编译器,Ahead Of Time Compiler)
3、透过字节码指令看代码细节
面试题:
① 类文件结构有几个部分?
② 知道字节码吗?字节码都有哪些?Integer x = 5; int y = 5; 比较 x==y 都经过哪些步骤?
案例1:
1 public class IntegerTest {
2
3 public static void main(String[] args) {
4 Integer x = 5;
5 int y = 5;
6 System.out.println(x == y); //true
7
8 Integer i1 = 10;
9 Integer i2 = 10;
10 System.out.println(i1 == i2); //true
11
12 Integer i3 = 128;
13 Integer i4 = 128;
14 System.out.println(i3 == i4); //false
15 }
16 }
字节码信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 0 iconst_5 把 int 5 放在操作数栈 1 invokestatic # 2 <java/lang/Integer.valueOf> 调用valueOf成为包装类 4 astore_1 5 iconst_5 6 istore_2 7 getstatic # 3 <java/lang/System.out> 10 aload_1 11 invokevirtual # 4 <java/lang/Integer.intValue> 14 iload_2 15 if_icmpne 22 (+ 7 ) 18 iconst_1 19 goto 23 (+ 4 ) 22 iconst_0 23 invokevirtual # 5 <java/io/PrintStream.println> 26 bipush 10 28 invokestatic # 2 <java/lang/Integer.valueOf> 31 astore_3 32 bipush 10 34 invokestatic # 2 <java/lang/Integer.valueOf> 37 astore 4 39 getstatic # 3 <java/lang/System.out> 42 aload_3 43 aload 4 45 if_acmpne 52 (+ 7 ) 48 iconst_1 49 goto 53 (+ 4 ) 52 iconst_0 53 invokevirtual # 5 <java/io/PrintStream.println> 56 sipush 128 59 invokestatic # 2 <java/lang/Integer.valueOf> 62 astore 5 64 sipush 128 67 invokestatic # 2 <java/lang/Integer.valueOf> 70 astore 6 72 getstatic # 3 <java/lang/System.out> 75 aload 5 77 aload 6 79 if_acmpne 86 (+ 7 ) 82 iconst_1 83 goto 87 (+ 4 ) 86 iconst_0 87 invokevirtual # 5 <java/io/PrintStream.println> 90 return |
案例2:
1 public class StringTest {
2 public static void main(String[] args) {
3 String str = new String("hello") + new String("world");
4 String str1 = "helloworld";
5 System.out.println(str == str1); //false
6 }
7 }
字节码信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 0 new # 2 <java/lang/StringBuilder> 3 dup 4 invokespecial # 3 <java/lang/StringBuilder.<init>> 7 new # 4 <java/lang/String> 10 dup 11 ldc # 5 <hello> 13 invokespecial # 6 <java/lang/String.<init>> 16 invokevirtual # 7 <java/lang/StringBuilder.append> 19 new # 4 <java/lang/String> 22 dup 23 ldc # 8 <world> 25 invokespecial # 6 <java/lang/String.<init>> 28 invokevirtual # 7 <java/lang/StringBuilder.append> 31 invokevirtual # 9 <java/lang/StringBuilder.toString> 34 astore_1 35 ldc # 10 <helloworld> 37 astore_2 38 getstatic # 11 <java/lang/System.out> 41 aload_1 42 aload_2 43 if_acmpne 50 (+ 7 ) 46 iconst_1 47 goto 51 (+ 4 ) 50 iconst_0 51 invokevirtual # 12 <java/io/PrintStream.println> 54 return |
案例3:
1 class Father {
2 int x = 10;
3 public Father() {
4 this.print();
5 x = 20;
6 }
7
8 public void print() {
9 System.out.println("Father.x = " + x);
10 }
11 }
12
13 class Son extends Father {
14 int x = 30;
15 public Son() {
16 this.print();
17 x = 40;
18 }
19
20 public void print() {
21 System.out.println("Son.x = " + x);
22 }
23 }
24 public class SonTest {
25 public static void main(String[] args) {
26 Father f = new Son();
27 System.out.println(f.x);
28 }
29 }
运行结果:
1 2 3 | Son.x = 0 Son.x = 30 20 |
字节码信息:
Father类:
1 2 3 4 5 6 7 8 9 10 11 | 0 aload_0 1 invokespecial # 1 <java/lang/Object.<init>> 4 aload_0 5 bipush 10 7 putfield # 2 <com/njf/java/Father.x> 10 aload_0 11 invokevirtual # 3 <com/njf/java/Father.print> 14 aload_0 15 bipush 20 17 putfield # 2 <com/njf/java/Father.x> 20 return |
Son类:
1 2 3 4 5 6 7 8 9 10 11 | 0 aload_0 1 invokespecial # 1 <com/njf/java/Father.<init>> 4 aload_0 5 bipush 30 7 putfield # 2 <com/njf/java/Son.x> 10 aload_0 11 invokevirtual # 3 <com/njf/java/Son.print> 14 aload_0 15 bipush 40 17 putfield # 2 <com/njf/java/Son.x> 20 return |
SonTest类:
1 2 3 | 0 aload_0 1 invokespecial # 1 <java/lang/Object.<init>> 4 return |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器