Skip to main content

Section 5.1 Understanding Java Strings

Subsection 5.1.1 Overview and Objectives

In Java, String handling looks simple at first glance: just type "Hello World" and you’re done. But behind the scenes, strings are objects (not primitives), they’re immutable (you can’t actually change them in place), and they have special comparison rules. Understanding these fundamentals is crucial before applying the “Design Recipe” to real string-processing tasks.
By the end of this section, you’ll be able to:

Subsection 5.1.2 Strings as Objects, Not Primitives

In many languages, you might think of a string as a simple sequence of text stored directly in a variable. In Java, though, String variables actually hold references to String objects. This is different from primitives like int or boolean, which store numeric or true/false values directly.
Whenever you write:
String greeting = "Hello";
String greeting2 = new String("Hello");
Both lines create a String object internally. However:
  • Literal notation ("Hello") can reuse an existing copy if one already exists—Java often places these in a special "string pool."
  • new String("Hello") always creates a new object outside that pool, even if the text is the same.
In both cases, the text is "Hello", but greeting and greeting2 might point to different objects under the hood.
This distinction matters because:
  • A Java String variable (like greeting) doesn’t contain the text directly; it refers to where that text is stored.
  • It’s possible for multiple variables to share the exact same string object if they both refer to it. Other times, they might each refer to a different string object with the same text.
Understanding "reference vs. content" helps you avoid subtle bugs when comparing strings or wondering why "modifications" don’t behave as expected.
Here’s a rough ASCII diagram:
+------------------------+     +-----------------------------+
|  "String Pool" (temp)  |     |   General Object Storage    |
+------------------------+     +-----------------------------+
| "Hello" literal        | <-- |  new String("Hello") object | <- greeting2
+------------------------+     +-----------------------------+
     ^                              ^
     |                              |
  greeting                     (fresh object)
In this sketch:
  • String Pool is a place where the Java runtime may keep a single copy of each literal text (like "Hello") so it doesn’t duplicate them unnecessarily.
  • The new expression forces creation of a separate object in the general storage area (often called the heap). That’s why greeting2 references a distinct “Hello” object.

Subsection 5.1.3 Immutability: No In-Place Modification

Java Strings are immutable: once created, the sequence of characters in that string never changes. Instead of modifying the same object, methods like toUpperCase() or concat(...) create new String objects.
String word = "Hello";
word.toUpperCase();
System.out.println(word); // Prints "Hello", unchanged

word = word.toUpperCase();
System.out.println(word); // Prints "HELLO"
In the first call, the uppercase version of "Hello" is created, but we never stored it back into word. So word remains the original "Hello". Only on the second call do we assign word to the newly created string "HELLO".
This can be surprising if you’re coming from a language where "strings" can be changed in-place. In Java, always remember to capture the result of any "modifying" operation if you want to keep it.

Subsection 5.1.4 Comparing Strings: == vs. .equals(...)

Because String variables hold references to objects, using the == operator checks if two references point to the exact same string object. Meanwhile, .equals(...) compares the text content—i.e., do these objects have identical characters?
String a = "test";
String b = "test";
String c = new String("test");

System.out.println(a == b); // Possibly true if both share the same literal
System.out.println(a == c); // false, c is a distinct new String
System.out.println(a.equals(c)); // true, all have the same text "test"
We strongly recommend always using .equals(...) (or .equalsIgnoreCase(...) to ignore case) for string comparisons in Java. Relying on == can cause silent bugs if the compiler/runtime happens not to reuse the same literal object or if new was used.

Subsection 5.1.5 Optional: The String Pool

The string pool is an internal optimization in Java: whenever the compiler sees a literal like "test" in your code, it can store just one copy of that text in a special region. Multiple variables using that same literal may point to this single pooled copy, which sometimes makes == appear to work. However, this behavior is only guaranteed for identical string literals in the same program.
On the other hand, new String("test") always creates a new object that is not in the pool. Therefore, a == b can be false even if a and b have the same text. This is why .equals(...) is so important for correct content-based checks.

Subsection 5.1.6 Connecting to the Design Recipe

In the Design Recipe, the first step (Data Definition) asks: “What data am I dealing with, and how is it structured?” For Java strings:
  • Length: An integer count of the characters, accessible via str.length().
  • Characters: Each at indices [0..length()-1]. For instance, "Hello" has indices 0:’H’, 1:’e’, and so forth.
  • Immutability: Methods do not alter the string in-place— they produce new strings.
Keeping these points in mind prevents confusion when writing or analyzing code. For example, if you want to “make your text uppercase,” in Java you do something like:
text = text.toUpperCase();
rather than assuming you changed the original string. In upcoming sections, you’ll see how these fundamentals shape the way we search, slice, or combine strings while applying the rest of the Design Recipe (e.g., writing tests, refining method outlines, etc.).

Subsection 5.1.7 Check Your Understanding

Checkpoint 5.1.1. Check Immutability in Action.

Given the code:
String msg = "Hello";
msg.toUpperCase();
System.out.println(msg);
Question 1: What does the program actually print and why? Question 2: How do you adjust it so it prints HELLO?
Solution.
Answer:
  • It prints Hello because toUpperCase() creates a new string without reassigning msg.
  • Reassign msg with the method result: msg = msg.toUpperCase(). Then it prints HELLO.

Checkpoint 5.1.2. Multiple-Choice: Immutability Quiz.

Which statement best explains why Java String objects are considered immutable?
  • Because calling toUpperCase() changes the original String object in place.
  • No. toUpperCase() returns a new string; the original is unchanged.
  • Because once created, the character contents cannot be changed, and any “update” actually creates a new String object.
  • Correct. Java String never modifies the existing object.
  • Because String is a primitive type stored directly in each variable.
  • No. String is a reference type, not a primitive.
  • Because Java automatically uses intern() for every new String creation.
  • Interning is an optional optimization for string literals, not the reason for immutability.

Checkpoint 5.1.3. Fixing Immutability Mistakes.

In the code below, we want to print HELLO and WELCOME. Currently, it prints Hello and Welcome because the calls to toUpperCase() are not stored back into the variables. Fix the snippet so that it correctly prints uppercase versions of each.

Subsection 5.1.8 Conclusion

Java strings might look straightforward, yet they’re built upon distinct principles: they store characters in objects (not primitives), are immutable, and require .equals() for content comparison. Mastering these points is essential before tackling advanced string operations in the next sections. If you remember that String is about references, new objects, and in-place immutability, you’ll avoid common pitfalls when building real-world string-handling code.
Next, we’ll apply the Design Recipe in a step-by-step fashion to various string tasks—like slicing, searching, or replacing—knowing full well how Java String objects behave under the hood.
You have attempted of activities on this page.