Java 是否可以重新引用任何最终字符串变量。请告诉我在给定的程序中发生了什么
我已经用两种不同的方式编写了一个相同的程序,它们都给了我不同的输出。我不明白为什么。 请纠正我。 在第一个程序中,我得到这个输出 原件:乌梅什 更改:Xmesh 在第二个程序中,我得到了这个输出 原件:乌梅什 更改:乌梅什 方案-1Java 是否可以重新引用任何最终字符串变量。请告诉我在给定的程序中发生了什么,java,Java,我已经用两种不同的方式编写了一个相同的程序,它们都给了我不同的输出。我不明白为什么。 请纠正我。 在第一个程序中,我得到这个输出 原件:乌梅什 更改:Xmesh 在第二个程序中,我得到了这个输出 原件:乌梅什 更改:乌梅什 方案-1 import java.lang.reflect.Field; public class SomeClass { public static void main(final String[] args) throws Throwable { final
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
所引用的内容),您所做的是修改它所引用的字符串。尽管正式的字符串是不可变的,但您使用反射作为后门来修改Oracle JDK中String
实现的未记录内部(其他JDK的实现可能不同,导致代码失败)。但是s
参考是不变的。即使使用反射,也不能更改final
局部变量的值。(您可以通过反射更改final
字段,但这样做会导致出现与本例中相同的不一致。)
在第二个示例中,由于s
是一个final
变量,因此您在main
中给它一个文本值,就编译器而言,它是一个编译时常量,因为String
是正式不可变的。编译器(非常)了解字符串,并围绕字符串进行了相当多的优化,例如将“a”+“b”+“c”
转换为简单的“abc”
。所以稍后,当它看到“Original:+s
,它可以很高兴地用“Original:Umesh”
来代替它。最后,当它看到更改时:“+s
可以用“Changed:Umesh”
替换它。它最终就像您实际编写的System.out.println(“Original:Umesh”)代码>和系统输出打印项次(“更改:Umesh”)源代码中的code>
编译器无法在第一个示例中执行此操作,因为s
是函数的参数,而不是在main
中声明的final
您可以看到字节码中的差异。编译它们中的每一个,然后通过javac-psomeclass
反汇编它们。下面是我得到的(我称它们为Example1
和Example2
):
$javap-c示例1
从“Example1.java”编译而来
公共课示例1{
公共示例1();
代码:
0:aload_0
1:invokespecial#1//方法java/lang/Object。“:()V
4:返回
publicstaticvoidmain(java.lang.String[])抛出java.lang.Throwable;
代码:
0:ldc#2//String-Umesh
2:invokestatic#3//方法changeString:(Ljava/lang/String;)V
5:返回
静态void changeString(java.lang.String)抛出java.lang.Throwable;
代码:
0:getstatic#4//fieldjava/lang/System.out:Ljava/io/PrintStream;
3:new#5//class java/lang/StringBuilder
6:dup
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//class 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//class“[C”
46:checkcast#16//class“[C”
49:astore_2
50:aload_2
51:iconst_0
52:bipush 88
54:卡斯托
55:getstatic#4//Field java/lang/System.out:Ljava/io/PrintStream;
58:new#5//class java/lang/StringBuilder
61:dup
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”编译而来
公共课示例2{
公共示例2();
代码:
0:aload_0
1:invokespecial#1//方法java/lang/Object。“:()V
4:返回
publicstaticvoidmain(java.lang.String[])抛出java.lang.Throwable;
代码:
0:getstatic#2//Field java/lang/Sys
$ javap -c Example1
Compiled from "Example1.java"
public class Example1 {
public Example1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Throwable;
Code:
0: ldc #2 // String Umesh
2: invokestatic #3 // Method changeString:(Ljava/lang/String;)V
5: return
static void changeString(java.lang.String) throws java.lang.Throwable;
Code:
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #5 // class java/lang/StringBuilder
6: dup
7: invokespecial #6 // Method java/lang/StringBuilder."":()V
10: ldc #7 // String Original:
12: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_0
16: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
25: ldc #11 // class java/lang/String
27: ldc #12 // String value
29: invokevirtual #13 // Method java/lang/Class.getDeclaredField:(Ljava/lang/String;)Ljava/lang/reflect/Field;
32: astore_1
33: aload_1
34: iconst_1
35: invokevirtual #14 // Method java/lang/reflect/Field.setAccessible:(Z)V
38: aload_1
39: aload_0
40: invokevirtual #15 // Method java/lang/reflect/Field.get:(Ljava/lang/Object;)Ljava/lang/Object;
43: checkcast #16 // class "[C"
46: checkcast #16 // class "[C"
49: astore_2
50: aload_2
51: iconst_0
52: bipush 88
54: castore
55: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
58: new #5 // class java/lang/StringBuilder
61: dup
62: invokespecial #6 // Method java/lang/StringBuilder."":()V
65: ldc #17 // String Changed:
67: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
70: aload_0
71: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
74: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
77: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
80: return
}
$ javap -c Example2
Compiled from "Example2.java"
public class Example2 {
public Example2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Throwable;
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Original: Umesh
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: ldc #5 // class java/lang/String
10: ldc #6 // String value
12: invokevirtual #7 // Method java/lang/Class.getDeclaredField:(Ljava/lang/String;)Ljava/lang/reflect/Field;
15: astore_2
16: aload_2
17: iconst_1
18: invokevirtual #8 // Method java/lang/reflect/Field.setAccessible:(Z)V
21: aload_2
22: ldc #9 // String Umesh
24: invokevirtual #10 // Method java/lang/reflect/Field.get:(Ljava/lang/Object;)Ljava/lang/Object;
27: checkcast #11 // class "[C"
30: checkcast #11 // class "[C"
33: astore_3
34: aload_3
35: iconst_0
36: bipush 88
38: castore
39: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
42: ldc #12 // String Changed: Umesh
44: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
47: return
}