Skip to content
Java strings 6 min read

Strings

In Java, a String is a sequence of characters — and it is one of the most heavily used types in the entire language. Unlike primitive types, String is a full-blown class in java.lang, which means it comes packed with dozens of useful methods right out of the box.

What Is a String?

You can create a String in two ways: with a string literal or with the new keyword.

String greeting = "Hello, World!";          // string literal (preferred)
String name     = new String("Jasmine");    // explicit object creation

Both give you a String object, but they behave differently under the hood — the literal version takes advantage of the String Pool, while new String(...) always allocates a fresh object on the heap.

Tip: Prefer string literals. They are faster, use less memory, and are the idiomatic Java style.

Strings Are Objects

Because String extends Object, every string inherits methods like equals(), hashCode(), and toString(). It also implements CharSequence, Comparable<String>, and Serializable.

String s = "java";
System.out.println(s.length());         // 4
System.out.println(s.toUpperCase());    // JAVA
System.out.println(s.charAt(0));        // j

Output:

4
JAVA
j

String Immutability

One of the most important facts about Java strings is that they are immutable: once created, the character data inside a String object can never change. Every operation that “modifies” a string actually returns a brand-new String object.

String original = "hello";
String modified = original.toUpperCase();

System.out.println(original);   // hello  (unchanged)
System.out.println(modified);   // HELLO  (new object)

This design enables thread safety, caching (the String Pool), and safe use of strings as HashMap keys. See Why String is Immutable for the full explanation.

Common String Operations

Comparing Strings

Never use == to compare string content — use .equals() or .equalsIgnoreCase().

String a = "Java";
String b = "java";

System.out.println(a.equals(b));             // false
System.out.println(a.equalsIgnoreCase(b));   // true

== checks reference equality (same object in memory), not value equality. This is a classic beginner trap — learn more in String Comparison.

Warning: "Java" == new String("Java") evaluates to false even though the characters are identical, because the two references point to different heap objects.

Concatenation

You can join strings with + or String.concat():

String first = "Hello";
String second = "World";
String result = first + ", " + second + "!";
System.out.println(result);   // Hello, World!

Under the hood, the Java compiler converts + chains into StringBuilder operations, so a handful of concatenations in one expression is perfectly fine. Avoid + inside tight loops though — see String Concatenation for the details.

Substring

Extract a portion of a string with substring():

String text = "DevCraftly";
System.out.println(text.substring(3));      // Craftly
System.out.println(text.substring(3, 8));   // Craft

substring(start) goes to the end; substring(start, end) is exclusive of end. Details are covered in Substring.

Checking and Searching

String s = "  Learn Java  ";
System.out.println(s.trim());              // "Learn Java"
System.out.println(s.contains("Java"));   // true
System.out.println(s.startsWith("  Le")); // true
System.out.println(s.indexOf("Java"));    // 7
System.out.println(s.isEmpty());          // false
System.out.println("".isBlank());         // true  (Java 11+)

A full reference of every built-in method lives in String Methods.

Under the Hood

The String Pool

The JVM maintains a special region of the heap (the String constant pool) to deduplicate literal strings. When you write "hello" twice in source code, both variables point to the exact same object.

String x = "hello";
String y = "hello";
System.out.println(x == y);   // true — same pooled object

You can manually move a heap string into the pool with intern(). Read the full story in String Pool & intern().

Memory Layout

Internally, a String wraps a byte[] (since Java 9’s Compact Strings optimization — earlier versions used char[]). If all characters fit in Latin-1, Java stores one byte per character, cutting memory use roughly in half for ASCII-heavy workloads.

hashCode() Caching

String.hashCode() is computed lazily on first call and then cached in a private field. Because strings are immutable, the hash can never change, making them ideal keys for HashMap and HashSet.

Mutable Alternatives: StringBuilder and StringBuffer

When you need to build a string piece by piece — especially in a loop — reach for StringBuilder:

StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 5; i++) {
    sb.append(i);
    if (i < 5) sb.append(", ");
}
System.out.println(sb.toString());   // 1, 2, 3, 4, 5

StringBuilder is fast but not thread-safe. If multiple threads share the same builder, use StringBuffer instead (same API, synchronized). The trade-off is explained in StringBuffer vs StringBuilder.

ClassMutableThread-safeUse when
StringNoYes (immutable)Fixed text, keys, constants
StringBuilderYesNoSingle-threaded string building
StringBufferYesYesMulti-threaded string building

Useful Formatting

Since Java 15, String.formatted() is a convenient alternative to String.format():

String msg = "Hello, %s! You are %d years old.".formatted("Ali", 30);
System.out.println(msg);
// Hello, Ali! You are 30 years old.

For multi-line strings, Text Blocks (Java 15+) are even cleaner:

String json = """
        {
            "name": "Java",
            "version": 21
        }
        """;

In This Section

  • Why String is Immutable — Understand the design decision that makes strings thread-safe and pool-able by default.
  • String Pool & intern() — How the JVM deduplicates string literals and how intern() lets you opt heap strings into the pool.
  • String Comparison — When to use ==, .equals(), and compareTo() — and why mixing them up causes subtle bugs.
  • String Concatenation — The + operator, concat(), and when to switch to StringBuilder for performance.
  • Substring — Slicing strings with substring(), and how Java 7u6 changed the implementation to avoid memory leaks.
  • String Methods — A practical reference for every important method on java.lang.String.
  • StringBuffer — The original thread-safe, mutable string class and when to reach for it.
  • StringBuilder — The faster, non-synchronized mutable string builder you should use in single-threaded code.
  • String vs StringBuffer — A head-to-head comparison covering mutability, performance, and use cases.
  • StringBuffer vs StringBuilder — Synchronization cost, benchmarks, and the golden rule for choosing between the two.
  • Create an Immutable Class — Learn the pattern Java itself uses for String so you can apply it to your own value objects.
  • toString() Method — How every object can describe itself as a string, and best practices for overriding toString().
  • StringTokenizer — A legacy class for splitting strings by delimiter, with comparisons to modern split() and Scanner.
  • Reverse a String — Multiple approaches from a simple StringBuilder flip to character-array tricks.
  • Wrapper Classes — Converting between strings and numeric types (Integer.parseInt(), String.valueOf(), etc.) relies on wrapper class methods.
  • Regular Expressions — Java’s Pattern and Matcher classes let you search and transform strings with powerful regex patterns.
  • Text Blocks — Java 15’s multi-line string syntax that eliminates manual \n and escaping.
  • StringBuilder — The go-to class for efficient, mutable string construction in single-threaded code.
  • Collections Framework — Strings are frequently stored in List, Set, and Map — understanding both topics together is essential.
  • Stream API — Java 8 streams pair beautifully with strings for filtering, mapping, and joining collections of text.
Last updated June 13, 2026
Was this helpful?