为什么try里的int会编译成byte
Why is the int in try compiled into byte
我发现一个现象:
public class TryTest {
public static void main(String[] args) {
System.out.println(test1());
}
public static int test1() {
try {
int a = 127;
return a;
} catch (Exception e) {
}finally {
System.out.println("I am finally");
}
return 0;
}
}
编译为 .class:
public class TryTest {
public TryTest() {
}
public static void main(String[] args) {
System.out.println(test1());
}
public static int test1() {
try {
int a = 127;
byte var1 = a;
return var1;
} catch (Exception var5) {
} finally {
System.out.println("I am finally");
}
return 0;
}
}
为什么“int a”转换为“byte var1”?
目的是为了节省内存吗?
这不是没有必要吗?
我想知道编译器是如何处理这个的。
但我发现如果代码是这样的:
public static int test3() {
int a = 1;
return a;
}
编译为 .class:
public static int test3() {
int a = 1;
return a;
}
如果没有"try",则不会编译成"byte"
127的值可以存储在一个字节内,所以它试图节省内存。
如果你想看Java中编译成什么东西,你不应该看反编译的代码。将 .class
文件转换回 .java
文件涉及很多解释(甚至可以说是猜测),不应将其视为实际编译的指示。请查看 javap -v
输出,它会显示实际的字节码。您的方法的字节码如下所示(我删除了一些不必要的细节,运行 自己检查一下):
public static int test1();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=0
0: bipush 127
2: istore_0
3: iload_0
4: istore_1
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #5 // String I am finally
10: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: iload_1
14: ireturn
15: astore_0
16: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
19: ldc #5 // String I am finally
21: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
24: goto 38
27: astore_2
28: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
31: ldc #5 // String I am finally
33: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
36: aload_2
37: athrow
38: iconst_0
39: ireturn
Exception table:
from to target type
0 5 15 Class java/lang/Exception
0 5 27 any
其中没有任何内容表明 byte
中存储了任何内容(iload_*
和 istore_*
加载和存储整数值)。
唯一特定于字节的指令是 bipush
,它压入 字节值 ,但它会在堆栈上扩展为 int
值。这只是比 sipush
(推送一个 short
常量)或 ldc
(这需要将值存储在常量池中)节省几个字节。
istore_1
用于记住执行 finally 块时要 returned 的值 (8-10)。然后我们使用 iload_1
将其加载回来并 return 它。
反编译器可能认为这是一个实际变量,而实际上它只是一个由 try-catch-finally 构造引起的合成构造。
此外,您可能会注意到字节码看起来非常低效,但这仅仅是因为 javac
编译器对字节码进行了非常直接的转换,实际上没有进行任何优化。任何实际的优化(例如从不在 any 变量中实际存储 any 值,而只是 returning 常量值 127
) 将由 JVM 在 运行 时间完成。
Java 反编译器因从字节码生成外观奇怪、不正确甚至不可编译的 Java 代码而臭名昭著。您不能通过查看反编译代码来推断 Java 编译器以特定方式编译了 Java 源文件。
但你不需要。只需使用 java -p
将字节码转换为可读形式,然后查找字节码指令在 JVM 规范中的含义。
如果你在这种情况下这样做(就像@Joachim 所做的那样),你会看到字节码中没有实际转换为 byte
。反编译器出错了......但这不应该是一个大惊喜。
我对这个序列逻辑的理解
0: bipush 127
2: istore_0
3: iload_0
4: istore_1
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #5 // String I am finally
10: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: iload_1
14: ireturn
是编译器在 4 处发出了指令:将 return 表达式保存在一个临时变量中,以便它可以“内联” finally
块中的代码。在 13:它重新加载值并 returns 它。
但是 istore_1
和 iload_1
指令正在恢复然后加载 int
值。反编译器很困惑。
我发现一个现象:
public class TryTest {
public static void main(String[] args) {
System.out.println(test1());
}
public static int test1() {
try {
int a = 127;
return a;
} catch (Exception e) {
}finally {
System.out.println("I am finally");
}
return 0;
}
}
编译为 .class:
public class TryTest {
public TryTest() {
}
public static void main(String[] args) {
System.out.println(test1());
}
public static int test1() {
try {
int a = 127;
byte var1 = a;
return var1;
} catch (Exception var5) {
} finally {
System.out.println("I am finally");
}
return 0;
}
}
为什么“int a”转换为“byte var1”?
目的是为了节省内存吗?
这不是没有必要吗?
我想知道编译器是如何处理这个的。
但我发现如果代码是这样的:
public static int test3() {
int a = 1;
return a;
}
编译为 .class:
public static int test3() {
int a = 1;
return a;
}
如果没有"try",则不会编译成"byte"
127的值可以存储在一个字节内,所以它试图节省内存。
如果你想看Java中编译成什么东西,你不应该看反编译的代码。将 .class
文件转换回 .java
文件涉及很多解释(甚至可以说是猜测),不应将其视为实际编译的指示。请查看 javap -v
输出,它会显示实际的字节码。您的方法的字节码如下所示(我删除了一些不必要的细节,运行 自己检查一下):
public static int test1();
descriptor: ()I
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=0
0: bipush 127
2: istore_0
3: iload_0
4: istore_1
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #5 // String I am finally
10: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: iload_1
14: ireturn
15: astore_0
16: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
19: ldc #5 // String I am finally
21: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
24: goto 38
27: astore_2
28: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
31: ldc #5 // String I am finally
33: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
36: aload_2
37: athrow
38: iconst_0
39: ireturn
Exception table:
from to target type
0 5 15 Class java/lang/Exception
0 5 27 any
其中没有任何内容表明 byte
中存储了任何内容(iload_*
和 istore_*
加载和存储整数值)。
唯一特定于字节的指令是 bipush
,它压入 字节值 ,但它会在堆栈上扩展为 int
值。这只是比 sipush
(推送一个 short
常量)或 ldc
(这需要将值存储在常量池中)节省几个字节。
istore_1
用于记住执行 finally 块时要 returned 的值 (8-10)。然后我们使用 iload_1
将其加载回来并 return 它。
反编译器可能认为这是一个实际变量,而实际上它只是一个由 try-catch-finally 构造引起的合成构造。
此外,您可能会注意到字节码看起来非常低效,但这仅仅是因为 javac
编译器对字节码进行了非常直接的转换,实际上没有进行任何优化。任何实际的优化(例如从不在 any 变量中实际存储 any 值,而只是 returning 常量值 127
) 将由 JVM 在 运行 时间完成。
Java 反编译器因从字节码生成外观奇怪、不正确甚至不可编译的 Java 代码而臭名昭著。您不能通过查看反编译代码来推断 Java 编译器以特定方式编译了 Java 源文件。
但你不需要。只需使用 java -p
将字节码转换为可读形式,然后查找字节码指令在 JVM 规范中的含义。
如果你在这种情况下这样做(就像@Joachim 所做的那样),你会看到字节码中没有实际转换为 byte
。反编译器出错了......但这不应该是一个大惊喜。
我对这个序列逻辑的理解
0: bipush 127
2: istore_0
3: iload_0
4: istore_1
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #5 // String I am finally
10: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: iload_1
14: ireturn
是编译器在 4 处发出了指令:将 return 表达式保存在一个临时变量中,以便它可以“内联” finally
块中的代码。在 13:它重新加载值并 returns 它。
但是 istore_1
和 iload_1
指令正在恢复然后加载 int
值。反编译器很困惑。