Java String Basics: Exploring Immutability, Memory Structure, and StringBuilder

Om NikhargeOm Nikharge
5 min read

String Initialization in Java

  •     public class StringInitializationExamples {
            public static void main(String[] args) {
                // Using string literal
                String str1 = "Hello, World!";
    
                // Using new keyword
                String str2 = new String("Hello, World!");
    
                // Using StringBuilder
                StringBuilder sb = new StringBuilder();
                sb.append("Hello").append(", ").append("World!");
            }
        }
    

    The above code snippet demonstrates different ways of initializing strings in Java. It is crucial to understand how each initialization is represented in memory, as this knowledge can help write efficient Java code.

    Using String Literals

      String str1 = "Hello, World!";
      String str2 = "Hello, World!";
      String str3 = str1;
      str3 = str2.substring(7);
    

    When a string is created using a string literal, the JVM stores it in a memory area called the String Constant Pool (SCP). The SCP is a dedicated space in heap memory where string values defined in the program are stored.

    Strings in Java are represented as character arrays in the SCP, but it is important to note that strings in the SCP are stored as full-fledged String objects, not just raw character arrays.

    Behavior of String Literals in Memory

      String str1 = "Hello, World!";
      String str2 = "Hello, World!";
      String str3 = str1;
      str1 = str1.substring(7);
    

    The JVM checks the SCP for the string "Hello, World!":

    • If it is not already present, a new String object is created in the SCP.

    • str1 references this object.

    • When str2 is assigned the same value, it refers to the same object in the SCP.

    • str3 also references str1, meaning all three variables point to the same string in SCP.

However, when str1 = str1.substring(7); is executed:

  • The JVM checks if "World!" exists in the SCP.

  • If not, it creates a new String object.

  • str1 now references this new object instead of modifying the original.

This property is called String Immutability—once a string object is created, its value cannot be changed. Any operation that modifies a string creates a new String object instead of altering the original.

Key Points About String Literals

  • The SCP does not store raw character arrays directly; it stores full-fledged String objects.

  • Methods like .length() and .toUpperCase() remain accessible since SCP stores complete String objects.

Using the new Keyword

    String str1 = new String("hello"); // Creates a new String object on the heap
    String str2 = new String("hello"); // Creates a new String object on the heap
    str2 = str2.substring(1); // Creates a new String object on the heap

When a string is created using new String("hello"), a new String object is allocated outside the SCP, in heap memory. This object is independent of any existing strings in the SCP.

Even if another string with the same value exists in memory, Java will create a new object in the heap. This is the key difference between using literals and new String(). However, immutability still applies—any modification results in a new object being created instead of altering the original.

Why Are Strings Immutable?

  • Thread Safety: Strings are widely used in Java (e.g., file paths, user input, network communication). Making them immutable ensures safe sharing across threads and components.

  • Memory Efficiency: If strings were mutable, the JVM could not optimize memory usage through the String Constant Pool, which allows multiple references to the same object instead of creating duplicates.

Example Demonstrating Immutability

    public class StringsInJava {
        private static void mod(String str) {
            str += " append";
            System.out.println("Modified str: " + str);
        }
        public static void main(String[] args) {
            String str1 = "Hello World";
            String str2 = new String("Hello World");
            System.out.println("str2 before modification by mod(): " + str2);
            mod(str1);
            System.out.println("str2 after modification by mod(): " + str2);
        }
    }
Output:
    str2 before modification by mod(): Hello World
    Modified str: Hello World append
    str2 after modification by mod(): Hello World

Here, str2 remains unchanged even after passing it to the mod() method. Like other primitive types, Strings also used for passing values to different methods in the code, but even though their object reference is being shared though variables, their immutability prevents modification of the original value.

Using StringBuilder

    StringBuilder sb = new StringBuilder("Hello");
    sb.append(" World");  // Modifies the same object instead of creating a new one
    System.out.println(sb);  // Output: Hello World

Until now, we've seen that strings in Java are immutable. However, sometimes you need mutable strings, especially when frequent modifications occur.

Why Use StringBuilder?

  • Efficient Memory Usage: When modifying a StringBuilder, it updates the same object instead of creating a new one like String.

  • Performance Optimization: Useful in scenarios where a string undergoes multiple changes, such as within loops.

When you create a StringBuilder object:

  • It is stored in heap memory, not in the SCP.

  • It maintains an internal char[] array that resizes dynamically.

  • Unlike String, modifications do not create a new object.

Key Takeaways

  1. Comparing Strings:

    • Strings created using literals can be compared with == because they refer to the same object in SCP.

    • Strings created using new String() should be compared using .equals() because they are separate objects, even if their values are identical.

  2. String Immutability:

    • Ensures thread safety and prevents unintended modifications.

    • Optimizes memory usage by enabling JVM to share string literals efficiently.

  3. Use StringBuilder for Mutability:

    • When frequent string modifications are needed (e.g., loops), StringBuilder is preferred over String to save memory and enhance performance.

By understanding how Java handles strings in memory, you can write optimized, efficient, and memory-conscious code!

0
Subscribe to my newsletter

Read articles from Om Nikharge directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Om Nikharge
Om Nikharge

I'm a software engineer from Maharashtra, India, currently learning and working in Full-Stack Development. My primary tech stack includes Java, Spring Boot, and React. I enjoy exploring various aspects of software engineering and technology in general.