Skip to content

Lesson 01 · Text — Strings & Text Blocks

Objectives

After this lesson you will be able to:

  • Explain why String is immutable and what the string pool does.
  • Predict == vs .equals() results for literals, new String, and concatenations.
  • Use the high-yield String methods correctly — especially 0-based indexing and end-exclusive substring.
  • Choose StringBuilder over repeated concatenation, and avoid its equals trap.
  • Write text blocks and reason about incidental whitespace, \, and \s.

String is immutable

A String never changes after it's created. Every "modifying" method returns a new String and leaves the original untouched.

java
String s = "java";
s.toUpperCase();        // result thrown away!
System.out.println(s);  // java   (unchanged)
s = s.toUpperCase();    // reassign to keep the new String
System.out.println(s);  // JAVA

Immutability is why String is safe to share and cache — which leads directly to the pool.

The string pool & == vs equals

String literals are interned: the JVM keeps one shared copy in the string pool, so equal literals are the same object. new String(...) always creates a distinct object.

java
String a = "hi";
String b = "hi";
String c = new String("hi");

a == b           // true   — same pooled object
a == c           // false  — c is a new object
a.equals(c)      // true   — same characters
  • == compares references (identity).
  • .equals() compares contents — almost always what you want for text.

Exam trap

A compile-time constant concatenation is folded and pooled, but concatenation involving a variable builds a new String at runtime:

java
String x = "hi";
String y = "h" + "i";          // constant → pooled
String p = "h";
String z = p + "i";            // variable → new object at runtime
x == y                          // true
x == z                          // false
x == z.intern()                 // true  — intern() returns the pooled copy

High-yield String methods

Indexing is 0-based, and ranges are end-exclusive (begin inclusive, end exclusive).

java
String s = "hello";
s.length();             // 5     (method, not a field)
s.charAt(0);            // 'h'
s.indexOf('l');         // 2     (-1 if not found)
s.substring(1, 3);      // "el"  (index 1,2 — NOT 3)
s.substring(2);         // "llo"
s.replace('l', 'L');    // "heLLo" (all occurrences; original unchanged)
s.contains("ell");      // true
s.toUpperCase();        // "HELLO"
"  hi  ".strip();       // "hi"  (Unicode-aware, Java 11)
"ab".repeat(3);         // "ababab"  (Java 11)
" ".isBlank();          // true  (Java 11)

Gotcha

strip() (Java 11+) is Unicode-aware; the older trim() only removes characters <= U+0020. Prefer strip(). Also note replace replaces all occurrences and takes plain text, while replaceAll takes a regex.

What prints? — substring bounds
java
String s = "hello";
System.out.println(s.substring(1, 3));
System.out.println(s.substring(5));      // begin == length is allowed
System.out.println(s.substring(1, 6));   // end > length

Answer: el, then an empty line (""), then a StringIndexOutOfBoundsException. substring(5) on a length-5 string is the empty string (valid); end = 6 is out of range and throws.

Concatenation vs StringBuilder

+ on strings is convenient but each operation creates a new String. Building a string in a loop with + is O(n²); use a StringBuilder (mutable, no new object per step).

java
// Avoid: a new String every iteration
String out = "";
for (int i = 0; i < n; i++) out += i;

// Prefer:
var sb = new StringBuilder();
for (int i = 0; i < n; i++) sb.append(i);
String result = sb.toString();

StringBuilder methods mutate in place and return this, so calls chain:

java
new StringBuilder("abc").append("d").insert(0, ">").reverse().toString(); // "dcba>"

Exam trap

StringBuilder does not override equals — it uses object identity. Two builders with identical contents are not equal:

java
new StringBuilder("a").equals(new StringBuilder("a"));  // false
"a".contentEquals(new StringBuilder("a"));              // true  — compare via String

Text blocks (Java 15+)

A text block is a multi-line string literal delimited by """. It removes the noise of \n and escaped quotes — ideal for JSON, SQL, and HTML.

java
String json = """
    {
      "name": "Ada",
      "lang": "Java"
    }""";

Rules that show up on the exam:

  • The opening """ must be followed by a line terminator — """hi""" does not compile.
  • Incidental (leading) whitespace is stripped based on the least-indented line and the position of the closing """. Move the closing delimiter left to keep more indentation.
  • A trailing space is normally stripped; \s (a space escape) preserves it, and a line-ending \ is a line continuation (joins with the next line, no newline inserted).
java
String s = """
    one \
    two""";          // "one two"   (\ joins the lines)

SDET note

Text blocks make embedded test fixtures (expected JSON/HTML, multi-line CSV) far more readable than escaped one-liners. But remember the value is still a plain String — trailing-newline and indentation rules bite when you compare against an exact expected value. Assert on the exact string and let a failing test show you the real whitespace.

Key Takeaways

  • String is immutable; "modifying" methods return a new String. Reassign to keep the result.
  • Literals are pooled → equal literals are ==. new String is a fresh object. Compare text with .equals(), not ==. Constant concatenation is pooled; variable concatenation isn't.
  • Indexing is 0-based; substring(begin, end) is end-exclusive; indexOf returns -1 when absent. strip() beats trim(); replace is literal, replaceAll is regex.
  • Use StringBuilder for repeated building; its methods chain, but it has no value equals (use contentEquals / compare as String).
  • Text blocks ("""): opening delimiter needs a newline; incidental whitespace is stripped relative to the closing """; \ continues a line and \s keeps a trailing space.

Lesson Quiz

Lesson Quiz · Text — Strings & Text Blocks0 / 10
  1. What does this print?

    String s = "java";
    s.toUpperCase();
    System.out.println(s);
    • AJAVA
    • Bjava
    • CCompile error
    • DAn exception
  2. Given String a = "hi"; String c = new String("hi"); which are TRUE? (select all) (select all that apply)

    • Aa == "hi"
    • Ba == c
    • Ca.equals(c)
    • Dc == "hi"
  3. What is "hello".substring(1, 3)?

    • A"ell"
    • B"el"
    • C"hel"
    • D"ello"
  4. What happens? System.out.println("hello".substring(1, 6));

    • APrints "ello"
    • BPrints "hello"
    • CStringIndexOutOfBoundsException
    • DPrints "ello "
  5. Which expression is TRUE?

    String x = "hi";
    String p = "h";
    String z = p + "i";
    • Ax == z
    • Bx == "h" + "i"
    • Cz == "hi"
    • Dx == p
  6. What does this evaluate to? new StringBuilder("a").equals(new StringBuilder("a"))

    • Atrue
    • Bfalse
    • CCompile error
    • DDepends on the JVM
  7. Which String methods are Unicode-aware / added in Java 11? (select all) (select all that apply)

    • Astrip()
    • Btrim()
    • CisBlank()
    • Drepeat(int)
  8. Which text block is INVALID (won't compile)?

    • A"""\n hi\n """
    • BA block whose opening """ is on its own followed by a newline
    • C"""hi""" on a single line
    • DA block using \s to keep a trailing space
  9. What is the value of this text block (using \\ line continuation)?

    String s = """
        one \
        two""";
    • A"one \n two"
    • B"one two"
    • C"one two"
    • D"onetwo"
  10. Best choice for building a large string inside a loop?

    • ARepeated += on a String
    • BStringBuilder.append
    • CString.concat in a loop
    • DCreating a new String each iteration

Next: Numeric Values — wrappers, Math & BigDecimal. Run the matching code in labs/src/main/java/com/jse21/m01_values/Text.java.