environment messages:
(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);
}
}
The results are as follows:
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
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
I changed the s1, the s2, s3 reference also changed. It really mixed me up! do the s1, s2, s3's final value " abcd" has the same memory address?
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
Yes it’s wrong. As wrong as a statement can be. All objects are stored in the heap memory. Because that’s 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.
The constant pool is nothing you have to think about. All that matters, is that all string literals of the same contents, i.e. " 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 methodString.intern
(§12.5).
Since they refer to the same object, hacking into the internals to manipulate what is supposed to be immutable, will make the change observable through all references.
The array wrapped by String
objects is an implementation detail, so whether creating a new string via new String(" abcd")
will create a new array or just let the two equal strings share the same array, is an implementation detail too. If they share the same array, manipulating that array affects both strings.
The “identity hash code” is neither, a “true identity” nor an address. It’s, like the ordinary hash code, a number helping certain data structures, like HashSet
, to look up objects efficiently. Since there is no limit on the number of objects an implementation can create, but the hash code is just a 32 bit number, two objects still can have the same identity hash code. Further, objects can be moved around in the memory, but the identity hash code of an object will never change. So, obviously, it can’t be an address.
In your particular output, they two distinct string instances happened to have different identity hash codes and all strings happened to truly refer to the same array, which the behavior after the modification proved better than the hash codes.