Hacking the Immutable String class using Java Reflection

In this example, I will show you how you can modify the Immutable String Class which has private final fields just like we did in the previous tutorial [Hacking Immutable class using Java Reflection]. But before we can start, I would like to show you some insight into the String Class’ inner workings as this will aid in your understanding of what we are going to do. You will notice, that the String class is defined as final, so that it can no longer be subclassed or extended. The internal fields have also been defined as private and final, in our case the field used for storage for the String class is a field called value.

What we are going to do in this tutorial, is modify the String value in such a way as to change it’s underlying value but not modify it’s hashcode (in essence, fooling Java into thinking the String has not changed and fooling the equals operator.

Why is Java String made Immutable and Final

A common interview question that comes up constantly is “Why are Strings Immutable and Final?”.  While there are many good reasons to make String immutable, many of which have already been discussed in my other companion tutorial called Hacking Immutable class using Java Reflection. In this tutorial, we will delve deeper in the reasons behind making the String class Immutable and Final.

What are the advantages of String immutability?

String Intern Pool

By making the String class immutable the Java architects could perform special optimizations on it.  One technique they used was to create a String Intern Pool.   String Interning is a technique for storing only one copy of each distinct string value in a “Pool” or “Cache”.  Using this technique might necessitate a little extra time during String creation but it produces big payoffs in terms of processing time and space efficiency during reuse.   The concept of String interning dates back to Lisp, which used it for its symbols. String Intern Pools  are used by many other popular languages, including Python, Lua, Ruby, .NET languages like C#, Objective-C, Scheme and Smalltalk.  In Java, a String pool was created with a goal of being able to share any previously created strings.  However, in order to do that it had to be immutable.

Concurrency Safety

Since String objects are Immutable they are by default thread-safe. Therefore, when using Strings no additional synchronization or locking schemes are needed and String objects may be shared between threads without any contention. This features make coding and use of Strings objects in multithreaded applications much simpler and easier to use.

Performance

Since the String class is immutable, we know, in advance, that the contents of the String will not change during its lifetime. Because of this fact, we can put these String objects in a String Intern Pool for both speed and efficiency. Using String pool also speeds up string comparisons as the tedious work of comparing character by character is no longer required. With interning of String Java need only check the hashcode or the memory address of each of the strings and that will suffice.

Security

According to the creator of Java, James Gosling, one of the primary reasons for making String immutable was for Security. The published transcript from James Gosling on Java, May 2001 which was first published by JavaWorld in June 2001 states the following:

One of the things that forced Strings to be immutable was security. You have a file open method. You pass a String to it. And then it’s doing all kind of authentication checks before it gets around to doing the OS call. If you manage to do something that effectively mutated the String, after the security check and before the OS call, then boom, you’re in. But Strings are immutable, so that kind of attack doesn’t work. That precise example is what really demanded that Strings be immutable.

Immutable Classes

  • Define class as final
  • Make all fields private and final
  • Make the constructor private, and use public factory methods
  • Make sure all setter methods are made private or remove them altogether

String Class Java Code from Oracle

For the complete source code to the java.lang.String class please go to Java Source Code. I will only be showing a small snippet of the actual Java Source code for the String class below.

/*
 * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package java.lang;

import java.io.ObjectStreamClass;
import java.io.ObjectStreamField;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Formatter;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
 * The <code>String</code> class represents character strings. All
 * string literals in Java programs, such as <code>"abc"</code>, are
 * implemented as instances of this class.
 * <p>
 * Strings are constant; their values cannot be changed after they
 * are created. String buffers support mutable strings.
 * Because String objects are immutable they can be shared. For example:
 * <p><blockquote><pre>
 *     String str = "abc";
 * </pre></blockquote><p>
 * is equivalent to:
 * <p><blockquote><pre>
 *     char data[] = {'a', 'b', 'c'};
 *     String str = new String(data);
 * </pre></blockquote><p>
 * Here are some more examples of how strings can be used:
 * <p><blockquote><pre>
 *     System.out.println("abc");
 *     String cde = "cde";
 *     System.out.println("abc" + cde);
 *     String c = "abc".substring(2,3);
 *     String d = cde.substring(1, 2);
 * </pre></blockquote>
 * <p>
 * The class <code>String</code> includes methods for examining
 * individual characters of the sequence, for comparing strings, for
 * searching strings, for extracting substrings, and for creating a
 * copy of a string with all characters translated to uppercase or to
 * lowercase. Case mapping is based on the Unicode Standard version
 * specified by the {@link java.lang.Character Character} class.
 * <p>
 * The Java language provides special support for the string
 * concatenation operator (&nbsp;+&nbsp;), and for conversion of
 * other objects to strings. String concatenation is implemented
 * through the <code>StringBuilder</code>(or <code>StringBuffer</code>)
 * class and its <code>append</code> method.
 * String conversions are implemented through the method
 * <code>toString</code>, defined by <code>Object</code> and
 * inherited by all classes in Java. For additional information on
 * string concatenation and conversion, see Gosling, Joy, and Steele,
 * <i>The Java Language Specification</i>.
 *
 * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor
 * or method in this class will cause a {@link NullPointerException} to be
 * thrown.
 *
 * <p>A <code>String</code> represents a string in the UTF-16 format
 * in which <em>supplementary characters</em> are represented by <em>surrogate
 * pairs</em> (see the section <a href="Character.html#unicode">Unicode
 * Character Representations</a> in the <code>Character</code> class for
 * more information).
 * Index values refer to <code>char</code> code units, so a supplementary
 * character uses two positions in a <code>String</code>.
 * <p>The <code>String</code> class provides methods for dealing with
 * Unicode code points (i.e., characters), in addition to those for
 * dealing with Unicode code units (i.e., <code>char</code> values).
 *
 * @author  Lee Boynton
 * @author  Arthur van Hoff
 * @author  Martin Buchholz
 * @author  Ulf Zibis
 * @see     java.lang.Object#toString()
 * @see     java.lang.StringBuffer
 * @see     java.lang.StringBuilder
 * @see     java.nio.charset.Charset
 * @since   JDK1.0
 */

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence
{
    /** The value is used for character storage. */
    private final char value[];

    /** The offset is the first index of the storage that is used. */
    private final int offset;

    /** The count is the number of characters in the String. */
    private final int count;

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

    /**
     * Class String is special cased within the Serialization Stream Protocol.
     *
     * A String instance is written initially into an ObjectOutputStream in the
     * following format:
     * <pre>
     *      <code>TC_STRING</code> (utf String)
     * </pre>
     * The String is written by method <code>DataOutput.writeUTF</code>.
     * A new handle is generated to  refer to all future references to the
     * string instance within the stream.
     */
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];

    /**
     * Initializes a newly created {@code String} object so that it represents
     * an empty character sequence.  Note that use of this constructor is
     * unnecessary since Strings are immutable.
     */
    public String() {
        this.offset = 0;
        this.count = 0;
        this.value = new char[0];
    }

Full Program Listing

package com.avaldes.tutorials;

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

public class StringHacker {

  public static void main(String[] args) throws SecurityException, NoSuchFieldException,
                          IllegalArgumentException, IllegalAccessException {

    String myString = "Amaury Valdes";
    String string1  = "Jennifer Lawrence";
    String string2  = "Charlize Theron";

    System.out.println(string1 + ", HASHCODE: " + string1.hashCode());
    System.out.println(string2 + ", HASHCODE: " + string2.hashCode());

    System.out.println("\n---TEST FOR EQUALITY---");
        System.out.println(string1 + " == " + string2 + " | " + string1.equals(string2));

    // ------------------------------------------
    // MODIFY THE IMMUTABLE VALUE OF STRING
    // ------------------------------------------
        System.out.println("\n*** MODIFYING IMMUTABLE STRING ***");
    System.out.println(myString + ", HASHCODE: " + myString.hashCode());

    Class<?> myClass = myString.getClass();

    Field ms = myClass.getDeclaredField("value");
    ms.setAccessible(true);

    Object obj = ms.get(myString);
        char[] myChars = (char[]) obj;
        Arrays.fill(myChars, 0, myChars.length, 'X');

      // After Change -- Text has changed and notice hashcode has NOT CHANGED!!!!
        System.out.println("\n---IMMUTABLE OBJECT AFTER---");
        System.out.println(myString + ", HASHCODE: " + myString.hashCode());

        // Equality test is WRONG NOW!!!!
        System.out.println("\n---TEST FOR EQUALITY---");
        System.out.println(myString + " == Amaury Valdes | " + myString.equals("Amaury Valdes"));
  }
}

Output

Jennifer Lawrence, HASHCODE: -1596336554
Charlize Theron, HASHCODE: -329389982

---TEST FOR EQUALITY---
Jennifer Lawrence == Charlize Theron | false

*** MODIFYING IMMUTABLE STRING ***
Amaury Valdes, HASHCODE: 991548394

---IMMUTABLE OBJECT AFTER---
XXXXXXXXXXXXX, HASHCODE: 991548394

---TEST FOR EQUALITY---
XXXXXXXXXXXXX == Amaury Valdes | true

Java Reflection Examples

  • Class Example
    Simple example shows you how to dynamically load and instantiate a class, get class name, package name, and superclass name.
  • Modifier Example
    Showing you how to get information about classes, fields and methods using modifier method.
  • Method Example
    Providing full details of how to get all methods, and invoke methods using reflection.

Frequently Asked Questions on Java Reflection API (FAQs)

Please Share Us on Social Media

Facebooktwitterredditpinterestlinkedinmail

Leave a Reply

Your email address will not be published. Required fields are marked *