String is the most used class in Java test code: locators, expected text, URLs, test data. This guide explains how strings are stored (the string pool), why they are immutable, when to use StringBuilder, and every String method you'll reach for.

Strings & String Pool

In Simple Terms

String Pool is like a whiteboard:

When you write 'Java' on the whiteboard, and your friend also writes 'Java', instead of two separate writings, everyone just points to the SAME 'Java' on the board. That's the String Pool — Java reuses the same string to save memory!

Creating Strings

// Method 1: String Literal → goes to String Pool
String s1 = "Hello";
String s2 = "Hello";  // s2 reuses s1's object in pool
System.out.println(s1 == s2);       // true (same object!)

// Method 2: new keyword → ALWAYS creates new Heap object
String s3 = new String("Hello");
String s4 = new String("Hello");
System.out.println(s3 == s4);       // false (different objects)
System.out.println(s3.equals(s4));  // true (same value)

// intern() → moves new String to pool / returns pool ref
String s5 = s3.intern();
System.out.println(s1 == s5);       // true (now in pool!)

Deep Dive: Why Strings are Immutable

Strings in Java are IMMUTABLE — once created, they cannot be changed. Here's why this is a deliberate design:

  • Security: String is used for class names, file paths, network URLs. If mutable, hackers could change them after validation.
  • String Pool works only because of immutability — if strings could change, sharing them would be dangerous.
  • Thread Safety: Immutable objects are inherently thread-safe, no synchronization needed.
  • HashCode caching: String caches its hashCode() result — only works because value never changes.
String s = "Hello";
s = s + " World";
// s does NOT modify the original!
// Creates NEW object: "Hello World" on Heap
// Original "Hello" is now eligible for GC

// Memory view:
// Pool: ["Hello"] ["Hello World"] [" World"]
// s now points to "Hello World"

StringBuilder vs StringBuffer vs String

FeatureString vs Builder
MutabilityString: Immutable | StringBuilder: Mutable
Thread SafetyString: Yes | StringBuilder: No | StringBuffer: Yes
PerformanceString: Slow for concatenation | Builder: Fast
When to useUse String for fixed values, Builder in loops
// BAD: Creates 1000 String objects in loop!
String result = "";
for (int i = 0; i < 1000; i++) {
    result += i;  // Each += creates a new String object
}

// GOOD: StringBuilder reuses same buffer
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);  // Modifies same internal char array
}
String result = sb.toString();  // Only 1 String object created!

Interview Questions

Why is String immutable in Java?

Security (strings used in classpath, URLs, DB connections), String Pool efficiency (sharing requires immutability), thread safety (no sync needed for immutable objects), and hashCode caching (computed once since value never changes).

What is String interning?

String.intern() checks if an equal string exists in the pool. If yes, returns the pool reference. If no, adds it to the pool and returns that reference. Useful to reduce memory when working with many equal strings.

Difference between == and .equals() for Strings?

== compares REFERENCES (memory addresses) — are they the same object? .equals() compares VALUES (character content) — do they contain the same text? Always use .equals() for string value comparison.

Advertisement

All String Methods — Complete Reference

String s = "Hello, World!";

// ── LENGTH & ACCESS ──
s.length()               // 13
s.charAt(0)              // 'H'
s.isEmpty()              // false
s.isBlank()              // false (Java 11: blank if only whitespace)

// ── SEARCH ──
s.indexOf('o')           // 4 (first occurrence)
s.indexOf('o', 5)        // 8 (from index 5)
s.lastIndexOf('o')       // 8
s.contains("World")      // true
s.startsWith("Hello")    // true
s.startsWith("World", 7) // true (from index 7)
s.endsWith("!")          // true
s.matches("Hello.*")     // true

// ── COMPARISON ──
s.equals("Hello, World!")          // true
s.equalsIgnoreCase("hello, world!") // true
s.compareTo("Hello, World!")       // 0
s.compareToIgnoreCase("abc")       // positive (H > a in ignore case)

// ── EXTRACTION ──
s.substring(7)           // "World!"
s.substring(7, 12)       // "World"
s.toCharArray()          // char[] {'H','e','l','l','o',...}
s.chars()                // IntStream of char values
s.codePointAt(0)         // 72 (Unicode code point for 'H')

// ── TRANSFORM ──
s.toLowerCase()          // "hello, world!"
s.toUpperCase()          // "HELLO, WORLD!"
s.trim()                 // remove leading/trailing whitespace (not unicode)
s.strip()                // Java 11: unicode-aware trim
s.stripLeading()         // Java 11: remove leading whitespace
s.stripTrailing()        // Java 11: remove trailing whitespace
s.replace('l', 'r')      // "Herro, Worrd!" (char replacement)
s.replace("World", "Java") // "Hello, Java!" (string replacement)
s.replaceAll("[aeiou]", "*") // regex replace
s.replaceFirst("\\d", "#")   // replace first match

// ── SPLIT & JOIN ──
"a,b,c".split(",")        // ["a", "b", "c"]
String.join("-", "a","b","c") // "a-b-c"
String.join(", ", List.of("x","y")) // "x, y"

// ── FORMATTING ──
String.format("%s is %d years old", "Alice", 30) // "Alice is 30 years old"
String.format("%.2f", 3.14159)  // "3.14"
String.format("%05d", 42)       // "00042" (zero-padded)
String.format("%-10s|", "hi")   // "hi        |" (left-aligned)
String.format("%,d", 1000000)   // "1,000,000" (comma separator)
// Java 15+ formatted() method:
"Value: %d".formatted(42)  // "Value: 42"

// ── JAVA 11+ METHODS ──
"  ".isBlank()            // true (only whitespace)
"line1\nline2".lines()    // Stream<String>: ["line1", "line2"]
"ha".repeat(3)            // "hahaha"
"  hello  ".strip()       // "hello"

// ── JAVA 12+ ──
"hello".indent(4)          // "    hello\n"
"<tag>content</tag>".transform(s -> s.toUpperCase()) // chain transform

// ── CONVERSION ──
String.valueOf(42)         // "42"
String.valueOf(true)       // "true"
String.valueOf('A')        // "A"
String.valueOf(3.14)       // "3.14"
Integer.parseInt("42")    // 42
Integer.valueOf("42")     // Integer object

// ── INTERN ──
String s1 = new String("hello").intern(); // puts in pool, returns pool ref
String s2 = "hello";
System.out.println(s1 == s2); // true after intern