Course topics

By WebNest Studio Engineering Team

Java - Core Tutorial

Java String

A String in Java represents a sequence of characters, and it is one of the most frequently used types in any Java program. Unlike primitive types, String is a class defined in the java.lang package, so every String you create is actually an object with methods you can call on it.

The single most important fact about Java Strings is that they are immutable — once a String object is created, its character content can never be changed. Any operation that appears to "modify" a String, such as concatenation or replacing characters, actually creates and returns a brand-new String object, leaving the original untouched. This design choice is deliberate, and it shapes how Strings behave in memory, in collections, and across threads.

Why it matters

Strings are the most used type in almost any Java program: names, messages, keys, JSON, SQL, file paths. They also behave differently from every other object you meet early on, because they can never be changed after creation. Until that is clear, code such as s.toUpperCase(); on a line by itself looks as if it should work, and it silently does nothing.

Creating Strings: Literal vs. new String()

There are two distinct ways to create a String, and they behave differently in memory. A string literal, written as String s = "hello";, is placed in a special memory region called the String Constant Pool (part of the heap since Java 7). A String created with new String("hello") always allocates a fresh object on the heap, outside the pool, even if an identical literal already exists there.

  • String s1 = "hello"; — reuses a pooled object if "hello" already exists in the pool.
  • String s2 = new String("hello"); — always creates a new object on the heap, separate from the pool.
  • Calling s2.intern() returns the pooled reference, effectively opting the object into the pool.

Watch: Why Java Strings Cannot Be Changed

A quick video on what really happens when you "change" a String: a new object is created and the original stays the same.

Watch on YouTube

What Happens When You "Change" a String

When code seems to change a String, the object itself is never touched. A statement like a = a + " 17"; builds a brand-new String and then points the variable a at it; any other variable still holding the old reference keeps seeing the old text. The same is true for every String method — toUpperCase(), replace(), trim() and concat() all return a new String, and calling one without assigning the result changes nothing.

The String class enforces this in three ways: the class is declared final, so no subclass can add methods that modify it; the array holding its characters is private final, so no outside code can reach it; and no method ever writes into that array after construction.

Watch: Why Java Strings Are Immutable

A quick video on the reasons behind String immutability: the string pool, security, thread safety and cached hash codes.

Watch on YouTube

Why Strings Are Immutable

Java's designers made String immutable for several concrete reasons, not just as an arbitrary restriction. Because Strings are used everywhere — as class names in class loading, as keys in HashMap, as file paths, and as network resource identifiers — allowing them to change after creation would open the door to serious bugs and security holes. Each reason below depends on the same guarantee: once a String has been checked, stored or shared, its text stays exactly as it was.

  • String pool sharing — multiple references can safely share the same object because no one can modify it out from under another reference.
  • Security — sensitive values like file paths, usernames, and class names cannot be altered after being validated and passed to a security-sensitive method.
  • Thread safety — an immutable object can be freely shared between threads without synchronization, since no thread can ever see it in a half-changed state.
  • Hashcode caching — since the content never changes, String caches its hashCode() the first time it is computed, making repeated use as a HashMap key fast.

Strings and the char[] Underneath

Internally, a String stores its characters (historically in a char[], and since Java 9 often in a more compact byte[] with an encoding flag, via the "Compact Strings" feature). This internal array is private and final, and String never exposes a mutable reference to it, which is exactly what preserves immutability from the outside.

Examples

Immutability in action — concatenation creates a new object

Java
public class StringImmutabilityDemo {
    public static void main(String[] args) {
        String original = "Java";
        String changed = original.concat(" Rocks");

        System.out.println("original: " + original);
        System.out.println("changed: " + changed);
        System.out.println("same object? " + (original == changed));
    }
}
Output
original: Java
changed: Java Rocks
same object? false

Reassigning a variable is not changing the String

Java
public class StringReferenceDemo {
    public static void main(String[] args) {
        String a = "Java";
        String b = a;          // b points to the same object as a

        a = a + " 17";         // creates a new String; a now points to it

        System.out.println("a: " + a);
        System.out.println("b: " + b);

        String name = "webnest";
        name.toUpperCase();    // result ignored - name is unchanged
        System.out.println("name: " + name);

        name = name.toUpperCase();  // capture the new String
        System.out.println("name: " + name);
    }
}
Output
a: Java 17
b: Java
name: webnest
name: WEBNEST

Why immutability keeps HashMap keys safe

Java
import java.util.HashMap;
import java.util.Map;

public class StringKeyDemo {
    public static void main(String[] args) {
        Map<String, Integer> stock = new HashMap<>();
        String key = "apple";
        stock.put(key, 10);

        key = key.toUpperCase();  // a new String; the key inside the map is untouched

        System.out.println("get(\"apple\"): " + stock.get("apple"));
        System.out.println("get(key): " + stock.get(key));
        System.out.println("key now: " + key);
    }
}
Output
get("apple"): 10
get(key): null
key now: APPLE

Common Mistakes

  • Believing that methods like concat() or replace() modify the original String in place — they always return a new String that must be captured in a variable.
  • Using new String("text") unnecessarily, which creates a wasteful duplicate object instead of reusing the pooled literal.
  • Assuming String is a primitive type because it can be created without "new" — String is always a reference type (an object).

Exercise

Count the vowels in a piece of text, treating upper and lower case the same.

Expected output: 5 for the text WebNest Studio.

Java · starter code
public class VowelCount {
    public static void main(String[] args) {
        String text = "WebNest Studio";
        int count = 0;

        // TODO: look at each character and count a, e, i, o and u in either case

        System.out.println(count);
    }
}
Show hints
  • Convert the text to lower case once, then read each character with charAt(i).
  • "aeiou".indexOf(ch) returns -1 when the character is not a vowel.
Show solution
Java · solution
public class VowelCount {
    public static void main(String[] args) {
        String text = "WebNest Studio";
        int count = 0;

        String lower = text.toLowerCase();
        for (int i = 0; i < lower.length(); i++) {
            if ("aeiou".indexOf(lower.charAt(i)) != -1) {
                count++;
            }
        }

        System.out.println(count); // 5
    }
}

Quick quiz

1. What does this print? String s = "hello"; s.toUpperCase(); System.out.println(s);

  • A. hello
  • B. HELLO
  • C. Hello
  • D. It does not compile
Show answer

A. toUpperCase() returns a new String and leaves the original untouched. The result was not assigned, so s is still "hello".

2. What does "Java".length() return?

  • A. 3
  • B. 4
  • C. 5
  • D. 8
Show answer

B. length() returns the number of chars in the String, which is 4.

3. How does String a = "hi"; differ from String b = new String("hi");?

  • A. There is no difference
  • B. b is faster to create
  • C. a refers to the pooled literal; b is a separate new object on the heap
  • D. a can be changed, b cannot
Show answer

C. A literal is taken from the string pool and shared. new String() always creates an additional object, even though the content is the same.

Interview questions

Is String a primitive type in Java?

Show answer

No. String is a class in java.lang, so a String variable holds a reference to an object. It only feels like a primitive because the language gives it special support: literals in double quotes, the + operator for concatenation, and the string pool.

What is the difference between creating a String with a literal and with new?

Show answer

A literal such as "hi" is placed in the string pool, and every identical literal in the program refers to that one object. new String("hi") forces a new object on the heap in addition to the pooled literal. That wastes memory and makes == comparisons false, so literals are preferred.

Key Points to Remember

  • Strings are immutable: once created, their character content never changes.
  • Reassigning a String variable points it at a new object; other references to the old String still see the old text.
  • String is a final class with a private final internal array (bytes since Java 9), so nothing can modify a String after it is built.
  • String literals live in the String Constant Pool; new String() always creates a separate heap object.
  • Immutability enables safe pool sharing, thread safety, security, and cached hashcodes.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering