gpt4 book ai didi

java - 是否可以重新引用任何最终的 String 变量。请告诉我给定程序中发生了什么

转载 作者:行者123 更新时间:2023-12-04 08:23:42 25 4
gpt4 key购买 nike

我用两种不同的方式编写了一个相同的程序,两者都给了我不同的输出。我不明白为什么。
请纠正我。
在第一个程序中,我得到了这个输出

原文:乌梅什

改变:Xmesh

在第二个程序中,我得到了这个输出。

原文:乌梅什

改变:乌梅什

程序-1

import java.lang.reflect.Field;

public class SomeClass {
public static void main(final String[] args) throws Throwable {
final String s = "Umesh";
changeString(s);
}

// We need a method so the compiler won't inline "s":
static void changeString(final String s) throws Throwable {

System.out.println("Original: " + s);
final Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
final char[] value = (char[]) field.get(s);
value[0] = 'X';
System.out.println("Changed: " + s);
}
}

程序 2
import java.lang.reflect.Field;

public class SomeClass {
public static void main(final String[] args) throws Throwable {
final String s = "Umesh";
System.out.println("Original: " + s);
final Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
final char[] value = (char[]) field.get(s);
value[0] = 'X';
System.out.println("Changed: " + s);
}

}

最佳答案

我会首先说你真的,真的不应该用这样的字符串来捣乱。 :-)

在您的第一个示例中,您没有“重新引用”任何内容(也就是说,您没有更改 s 所指的字符串),您所做的是修改它所指的字符串。尽管官方字符串是不可变的,但您使用反射作为后门来修改 String 未公开的内部结构在 Oracle 的 JDK 中实现(其他 JDK 可能以不同的方式实现,使该代码失败)。但是s引用不变。即使使用反射,您也无法更改 final 的值局部变量。 (您可以通过反射更改 final 字段,但这样做会导致您在此示例中看到的相同类型的不一致。)

在你的第二个例子中发生的事情是因为 sfinal变量,您在 main 内提供文字值,就编译器而言,它是一个编译时常量,因为 String是正式不变的。编译器(非常)了解字符串并围绕它们进行一些优化,例如转动 "a" + "b" + "c"简单地变成"abc" .所以后来,当它看到"Original: " + s ,它可以愉快地替换"Original: Umesh"为了那个原因。最后再一次,当它看到 "Changed: " + s它可以用 "Changed: Umesh" 代替它.它最终就像你真的写了 System.out.println("Original: Umesh");System.out.println("Changed: Umesh");在源代码中。

在第一个例子中编译器不能这样做,因为 s是函数的参数,而不是 finalmain 中宣布就在那里.

您可以看到字节码的差异。编译它们,然后通过 javac -p SomeClass 反汇编它们.这是我得到的(我称它们为 Example1Example2 ):

$ javap -c 示例1
编译自“Example1.java”
公共(public)类示例 1 {
公共(public)示例1();
代码:
0:aload_0
1: invokespecial #1//方法 java/lang/Object."":()V
4:返回

public static void main(java.lang.String[]) 抛出 java.lang.Throwable;
代码:
0: ldc#2//字符串 Umesh
2: invokestatic#3//方法 changeString:(Ljava/lang/String;)V
5:返回

static void changeString(java.lang.String) 抛出 java.lang.Throwable;
代码:
0: getstatic#4//字段 java/lang/System.out:Ljava/io/PrintStream;
3: new #5//类 java/lang/StringBuilder
6:重复
7: invokespecial #6//方法 java/lang/StringBuilder."":()V
10: ldc#7//字符串原始:
12: invokevirtual #8//方法 java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15:aload_0
16: invokevirtual #8//方法 java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #9//方法 java/lang/StringBuilder.toString:()Ljava/lang/String;
22: invokevirtual #10//方法 java/io/PrintStream.println:(Ljava/lang/String;)V
25: ldc#11//类 java/lang/String
27: ldc#12//字符串值
29: invokevirtual #13//方法 java/lang/Class.getDeclaredField:(Ljava/lang/String;)Ljava/lang/reflect/Field;
32:astore_1
33:aload_1
34:iconst_1
35: invokevirtual #14//方法 java/lang/reflect/Field.setAccessible:(Z)V
38:aload_1
39:aload_0
40: invokevirtual #15//方法 java/lang/reflect/Field.get:(Ljava/lang/Object;)Ljava/lang/Object;
43:checkcast #16//类“[C”
46: checkcast #16//类“[C”
49:astore_2
50:aload_2
51:iconst_0
52:双推88
54:蓖麻
55: getstatic#4//字段 java/lang/System.out:Ljava/io/PrintStream;
58: new #5//类 java/lang/StringBuilder
61:重复
62: invokespecial #6//方法 java/lang/StringBuilder."":()V
65:ldc#17//字符串更改:
67: invokevirtual #8//方法 java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
70:aload_0
71: invokevirtual #8//方法 java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
74: invokevirtual #9//方法 java/lang/StringBuilder.toString:()Ljava/lang/String;
77: invokevirtual #10//方法 java/io/PrintStream.println:(Ljava/lang/String;)V
80:返回
}



$ javap -c 示例2
编译自“Example2.java”
公共(public)类示例 2 {
公共(public)示例2();
代码:
0:aload_0
1: invokespecial #1//方法 java/lang/Object."":()V
4:返回

public static void main(java.lang.String[]) 抛出 java.lang.Throwable;
代码:
0: getstatic#2//字段 java/lang/System.out:Ljava/io/PrintStream;
3: ldc#3//字符串原始:Umesh
5: invokevirtual #4//方法 java/io/PrintStream.println:(Ljava/lang/String;)V
8: ldc#5//类 java/lang/String
10: ldc#6//字符串值
12: invokevirtual #7//方法 java/lang/Class.getDeclaredField:(Ljava/lang/String;)Ljava/lang/reflect/Field;
15:astore_2
16:aload_2
17:iconst_1
18: invokevirtual #8//方法 java/lang/reflect/Field.setAccessible:(Z)V
21:aload_2
22: ldc#9//字符串 Umesh
24: invokevirtual #10//方法 java/lang/reflect/Field.get:(Ljava/lang/Object;)Ljava/lang/Object;
27: checkcast #11//类“[C”
30: checkcast #11//类“[C”
33:astore_3
34:aload_3
35:iconst_0
36:双推88
38:蓖麻
39: getstatic#2//字段 java/lang/System.out:Ljava/io/PrintStream;
42:ldc#12//字符串更改:Umesh
44: invokevirtual #4//方法 java/io/PrintStream.println:(Ljava/lang/String;)V
47:返回
}

注意我们在第二个例子中没有看到任何字符串连接(StringBuilder 用法),编译器在编译阶段组合了静态字符串。

关于java - 是否可以重新引用任何最终的 String 变量。请告诉我给定程序中发生了什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32256206/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com