Understanding Strings in Java
Explore how Java represents and stores strings using the String Pool and heap memory. Understand string immutability, concatenation methods, proper formatting techniques, and the correct way to compare strings using .equals() instead of ==. This lesson helps you master efficient and error-free text handling in Java applications.
Text processing is fundamental to almost every application we build. Whether we are logging a username, processing a payment ID, or displaying a chat message, we rely on text. In Java, text is represented by the String class. While strings may look like simple primitive values, they are actually complex objects with unique behaviors regarding memory and modification.
In this lesson, we will explore how Java stores strings efficiently, why they cannot be changed once created, and how to manipulate them effectively.
The String object and creation
Unlike int or boolean, a String is a reference type, meaning it is an object. However, because strings are so common, Java provides a special shorthand syntax for creating them.
There are two ways to create a string:
String literal: We enclose text in double quotes (
"Hello"). Java optimizes this by storing the string in a special memory area called the String Pool. If we use the same literal twice, Java reuses the existing instance to save memory.newkeyword: We can explicitly create a new object usingnew String("Hello"). This forces Java to create a distinct object on the heap, bypassing the pool’s optimization. We rarely do this in practice, but understanding the difference is vital for debugging.
Lines 5–6: We create two string variables using literals. Because the text is identical,
s1ands2point to the exact same memory address in the String Pool.Line 10: We use the
newkeyword. This forces the JVM to create a brand new object in standard heap memory, even though the content ("Java") is the same.Lines 13–14: We print the result of reference comparisons.
s1 == s2is true because they share the same address.s1 == s3is false becauses3is a completely separate object.
This diagram illustrates how literals share references in the String Pool, whereas objects created with new occupy distinct memory locations on the heap.
Understanding immutability
One of the most defining characteristics of the String class is immutability. Once a String object is created, its internal state (the sequence of characters) cannot be changed.
When we appear to modify a string, such as by adding text to it, Java does not change the original object. Instead, it creates a new String object containing the result and updates our variable to point to this new object. The old object remains in memory (until garbage collected) or stays in the String Pool.
Line 3: We create a string
greetingwith the value"Hello".Line 6: We call
.concat(). This creates a new string"Hello, World"in memory, but we do not assign it to anything. The originalgreetingvariable still points to"Hello".Line 7: We print
greeting, confirming it has not changed.Line 10: We call
.concat()again, but this time we assign the result back togreeting. The variable now points to the new string object.
Concatenation and composition
The most common string operation is combining text, known as concatenation. We typically use the + operator. Java handles + smartly: if we add a non-string value (like a number) to a string, Java automatically converts the value to a string before combining them.
While + is convenient, remember that every concatenation creates a new string object. For combining many strings in a loop, this is inefficient (we will discuss StringBuilder for those cases later).
Line 8: We build a single string
profile. Java evaluates this left-to-right. It combinesfirstName, a space, andlastName. When it encountersyear(an integer), it converts1815into"1815"and appends it to the string.Line 10: We print the final formatted result.
Formatting strings
While the + operator works well for simple combinations, it becomes messy and hard to read when we have multiple variables or need specific formatting (like controlling decimal places).
Java provides String.format() and the modern .formatted() method (introduced in Java 15) to solve this. These allow us to use a template with placeholders like %s (for strings), %d (for integers), and %.2f (for floating-point numbers).
Line 8: We use
String.formatwith a template string.%sis replaced byproduct.%.2froundspriceto two decimal places (1000.00).%dis replaced bystock.Line 11: We use the newer
.formatted()instance method, which achieves the same result but reads more naturally (left-to-right).Lines 13–14: Both outputs are identical:
"Item: Laptop | Price: $1000.00 | Stock: 5".
Comparing strings correctly
This is the most common bug for beginners: comparing strings using ==.
==compares references: It checks if two variables point to the exact same memory address..equals()compares content: It checks if two strings contain the same sequence of characters.
Because strings can exist in the pool or on the heap, two strings can contain identical text but live at different addresses. Always use .equals() to check if text matches.
Line 3:
literalis created in the String Pool.Line 4:
inputis forced onto the heap usingnew. They have different memory addresses.Line 7:
literal == inputreturnsfalsebecause the addresses differ. Access is incorrectly denied.Line 13:
literal.equals(input)returnstruebecause the characters "password" match exactly.
We now understand that strings in Java are immutable objects that require careful handling. Every time we modify a string, we create a new one. We also learned how to use formatting templates to make our strings readable and the golden rule of string comparison: never use == for content checking, always use .equals().