Skip to content
Java fundamentals 4 min read

Strings

A String in Java is an object representing a sequence of characters. Strings are among the most-used types in any program, and Java gives them special treatment: literal syntax, a dedicated memory pool, and compiler support for concatenation. Crucially, Strings are immutable.

Creating Strings

You can create a String with a literal or with new. Literals are preferred and are interned in the String pool.

String a = "hello";              // literal, goes to the String pool
String b = new String("hello"); // forces a new heap object
char[] chars = {'h', 'i'};
String c = new String(chars);    // "hi"

Immutability

A String’s contents never change after creation. Methods that appear to modify a String actually return a new one, leaving the original untouched.

String s = "Java";
s.concat(" rocks");          // result discarded — s is unchanged
System.out.println(s);       // Java

String t = s.toUpperCase();  // new String returned
System.out.println(t);       // JAVA
System.out.println(s);       // Java (still)

Output:

Java
JAVA
Java

Immutability makes Strings thread-safe, cacheable, and safe to use as map keys. It is also why you must capture the return value of String methods.

The String Pool

String literals are stored in a special area of the heap called the String pool. Identical literals share the same object, so == (reference equality) returns true for them — but never rely on this for comparison.

String x = "cat";
String y = "cat";
String z = new String("cat");

System.out.println(x == y);          // true  — same pooled object
System.out.println(x == z);          // false — z is a distinct object
System.out.println(x.equals(z));     // true  — same characters
System.out.println(x == z.intern()); // true  — intern() returns pooled ref

Output:

true
false
true
true

Warning: Always compare String contents with .equals() (or .equalsIgnoreCase()), never ==. The == operator compares references and gives misleading results.

Common String Methods

MethodDescriptionExample → Result
length()number of characters"abc".length()3
charAt(i)char at index"abc".charAt(1)'b'
substring(a, b)slice [a, b)"hello".substring(1, 4)"ell"
indexOf(s)first index of s"banana".indexOf("na")2
contains(s)substring present?"hello".contains("ell")true
replace(a, b)replace all a with b"a-b-c".replace("-", "+")"a+b+c"
split(regex)split into array"a,b,c".split(",")[a, b, c]
trim() / strip()remove whitespace" hi ".strip()"hi"
toUpperCase()uppercase copy"hi".toUpperCase()"HI"
isBlank()empty or whitespace?" ".isBlank()true
repeat(n)repeat n times"ab".repeat(2)"abab"
format(...)printf-style formatString.format("%.2f", 3.1)"3.10"
String csv = "Ada,Linus,Grace";
String[] names = csv.split(",");
System.out.println(names.length + " names");
System.out.println(String.join(" | ", names));

Output:

3 names
Ada | Linus | Grace

StringBuilder vs StringBuffer

Because Strings are immutable, building one in a loop with + creates many throwaway objects. Use a mutable builder instead.

StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 3; i++) {
    sb.append("item").append(i).append(" ");
}
System.out.println(sb.toString().strip());  // item1 item2 item3

StringBuilder and StringBuffer share the same API. The difference: StringBuffer is synchronized (thread-safe) but slower; StringBuilder is not synchronized and faster.

TypeMutableThread-safeUse when
StringNoYesFixed text
StringBuilderYesNoSingle-threaded building (default choice)
StringBufferYesYesShared across threads (rare)

Tip: Default to StringBuilder. Reach for StringBuffer only when multiple threads append to the same instance — an uncommon scenario.

Text Blocks

Since Java 15, text blocks (""") write multiline strings without escaping, preserving formatting. Incidental indentation is stripped automatically.

String json = """
    {
        "name": "DevCraftly",
        "type": "platform"
    }
    """;
System.out.println(json);

Text blocks are ideal for embedded JSON, SQL, HTML, and any multiline literal.

Best Practices

  • Compare with .equals(); never use == for String content.
  • Use StringBuilder for repeated concatenation, especially in loops.
  • Prefer strip() over trim() for correct Unicode whitespace handling.
  • Use text blocks for multiline literals to avoid escape clutter.

Interview Questions

Q: Why are Strings immutable in Java? A: For thread safety, security (e.g., file paths and credentials can’t be altered after validation), safe hashing as map keys, and to enable the String pool to share instances. Any “modification” returns a new String.

Q: What is the difference between == and .equals() for Strings? A: == compares object references (memory addresses); .equals() compares character content. Pooled literals may share a reference, but you should always use .equals() to compare values.

Q: When would you use StringBuilder over String concatenation? A: When concatenating repeatedly, especially inside loops. Each + on Strings allocates a new object; StringBuilder mutates a single buffer, giving O(n) instead of O(n²) behavior.

Last updated June 1, 2026
Was this helpful?