Preface
When you mention String in Java, you always mention that String is immutable.But is this true?Can String's immutability be destroyed?
Before validating, you need to first introduce the immutable properties of String.
PS: Also mention the interview questions you have encountered here:
String str = "abc"; // Output def System.out.println(str);
String Invariant
String's immutability refers to
- String internally uses a final-modified char array value to store the value of a string
- The value of the array value is already assigned when the object is constructed
- String does not provide a method to modify values in an array value
- The method in String that needs to modify the value, such as replace, returns a new String object directly
So String is immutable.
Destroy String's Invariance
String immutability is essentially achieved around the fact that value is an immutable array of char s, but it can be completely destroyed by Java reflection.
The key codes are as follows:
String str="test"; // Reference address of str object printAddresses("str",str); try { Field field=str.getClass().getDeclaredField("value"); // char[] newChars={'h','e','l','l','o'}; // Make the value of the private property accessible field.setAccessible(true); // Replace the value of the value array field.set(str,newChars); // Replaced value System.out.println("str value:"+str); // After replacement, the reference address of the str object printAddresses("str",str); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); }
The way to print the object address above is by reference How to get the address of a Java object Sample code, as follows:
/** * Print object address * @param label * @param objects */ public void printAddresses(String label, Object... objects) { System.out.print(label + ":0x"); long last = 0; Unsafe unsafe=getUnsafe(); int offset = unsafe.arrayBaseOffset(objects.getClass()); int scale = unsafe.arrayIndexScale(objects.getClass()); switch (scale) { case 4: // 64-bit JVM long factor = 8; final long i1 = (unsafe.getInt(objects, offset) & 0xFFFFFFFFL) * factor; System.out.print(Long.toHexString(i1)); last = i1; for (int i = 1; i < objects.length; i++) { final long i2 = (unsafe.getInt(objects, offset + i * 4) & 0xFFFFFFFFL) * factor; if (i2 > last) System.out.print(", +" + Long.toHexString(i2 - last)); else System.out.print(", -" + Long.toHexString(last - i2)); last = i2; } break; case 8: throw new AssertionError("Not supported"); } System.out.println(); } /** * Obtaining Unsafe Objects by Reflection * @return */ private static Unsafe getUnsafe() { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(null); } catch (Exception e) { throw new AssertionError(e); } }
Run Results
str:0x76b4136e0 str value:hello str:0x76b4136e0
As you can see from the results, reflection allows you to modify the value of a String object without modifying its object address.
conclusion
Reflection can actually be used to destroy the immutability of String, but it is also a Java-provided feature that makes it hard to avoid being used.
At that time, I still thought there was no solution to the question mentioned above, but as I continue to learn, I find that many times I can find different ways from a different angle.