0

环境消息:

(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”是否具有相同的内存地址?

4

1 回答 1

2

据我所知,字符串常量存储在字符串常量池中。存储位置(可能是错的!): jdk1.6 方法区 jdk1.7 堆内存 jdk1.8 本地内存

是的,这是错误的。一个陈述可能是错误的。所有对象都存储在堆内存中。因为这就是堆内存的定义方式

Java 虚拟机有一个在所有 Java 虚拟机线程之间共享的堆。堆是为所有类实例和数组分配内存的运行时数据区域。

常量池是你不必考虑的。重要的是,所有具有相同内容的字符串文字,即" abcd"引用同一个对象

此外,字符串字面量始终指代一个 class 实例String。这是因为字符串文字 - 或者更一般地说,作为常量表达式值的字符串(第 15.29 节) - 是“内部的”,以便共享唯一实例,就像通过执行方法String.intern(第 12.5 节)一样。

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

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

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

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

于 2021-08-23T09:00:05.887 回答