为什么我在java中通过反射改变了字符串s1常量,其他s2,s3也改变了?

Why I change the string s1 constant by reflection in java, the other s2, s3 also changed?

环境消息:

(env) λ java -version
openjdk version "1.8.0_242"
OpenJDK Runtime Environment (build 1.8.0_242-xxxxxx_JDK_xxxxx)
OpenJDK 64-Bit Server VM (build 25.242-b08, mixed mode)

/*
 * Copyright (c) Google Technologies Co., Ltd. 2021-2021. All rights reserved.
 */

package corejava.v1ch05.practice;

import java.lang.reflect.Field;
import java.util.Random;

public class ChangeString {

    private static void printAddress(String message) throws IllegalAccessException, NoSuchFieldException {
        Field f = message.getClass().getDeclaredField("value");
        f.setAccessible(true);
        char[] v = (char[])f.get(message);

        System.out.println("message hashcode: " + message.hashCode());
        System.out.println("message real identity: " + System.identityHashCode(message));
        System.out.println("v real identity: " + System.identityHashCode(v));
        System.out.println();
    }

    private static void change(String message) throws NoSuchFieldException, IllegalAccessException {
        System.out.println(System.identityHashCode(message));

        Field f = message.getClass().getDeclaredField("value");
        System.out.print("Accessible: " + f.isAccessible());
        f.setAccessible(true);
        char[] v = (char[])f.get(message);
        System.out.println(System.identityHashCode(v));
        Random random = new Random();
        char randomizedCharacter = (char) (random.nextInt(26) + 'a');
        v[0] = randomizedCharacter;

        System.out.println();
    }

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        String s1 = " abcd";
        String s2 = " abcd";
        String s3 = new String(" abcd");

        printAddress(s1);
        printAddress(s2);
        printAddress(s3);

        change(s1);

        printAddress(s2);
        printAddress(s3);
        System.out.print(s1 + " " + s2 + " " + s3);
    }
}

结果如下:

message hashcode: 32539746
message real identity: 460141958
v real identity: 1163157884

message hashcode: 32539746
message real identity: 460141958
v real identity: 1163157884

message hashcode: 32539746
message real identity: 1956725890
v real identity: 1163157884

460141958
Accessible: false1163157884

message hashcode: 32539746
message real identity: 460141958
v real identity: 1163157884

message hashcode: 32539746
message real identity: 1956725890
v real identity: 1163157884

qabcd qabcd qabcd

据我所知,字符串常量存储在字符串常量池中。 店铺地址(可能有误!): jdk1.6方法区 jdk1.7堆内存 jdk1.8 本地内存

我改了s1,s2,s3的引用也改了。真的把我搞糊涂了! s1、s2、s3 的最终值“abcd”是否具有相同的内存地址?

As I know, the string constant is stored in the string constant pool. store location(maybe it's wrong!): jdk1.6 Method Area jdk1.7 heap memory jdk1.8 local memory

是的,这是错误的。一个陈述可能是错误的。所有对象都存储在堆内存中。因为那是 how heap memory is defined:

The Java Virtual Machine has a heap that is shared among all Java Virtual Machine threads. The heap is the run-time data area from which memory for all class instances and arrays is allocated.

常量池不是你想的。重要的是所有内容相同的字符串文字,即 " abcd"refer to the same object.

Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.29) - are "interned" so as to share unique instances, as if by execution of the method String.intern (§12.5).

由于它们引用同一个对象,侵入内部以操纵本应不可变的内容,将使更改可通过所有引用观察到。

String对象包裹的数组是一个实现细节,所以通过new String(" abcd")创建一个新字符串是否会创建一个新数组或者只是让两个相等的字符串共享同一个数组,是也是一个实现细节。如果它们共享同一个数组,则操作该数组会影响两个字符串。

“身份哈希码”既不是“真实身份”也不是地址。就像普通的哈希码一样,它是一个数字,可以帮助某些数据结构(例如 HashSet)有效地查找对象。由于实现可以创建的对象数量没有限制,但哈希码只是一个 32 位数字,因此两个对象仍然可以具有相同的身份哈希码。此外,对象可以在内存中四处移动,但对象的身份哈希码永远不会改变。所以,很明显,它不能是地址。

在您的特定输出中,它们两个不同的字符串实例碰巧具有不同的身份哈希码,并且所有字符串碰巧真正引用同一个数组,修改后的行为证明比哈希码更好。