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 tofalseeven 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.
| Class | Mutable | Thread-safe | Use when |
|---|---|---|---|
String | No | Yes (immutable) | Fixed text, keys, constants |
StringBuilder | Yes | No | Single-threaded string building |
StringBuffer | Yes | Yes | Multi-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(), andcompareTo()— and why mixing them up causes subtle bugs. - String Concatenation — The
+operator,concat(), and when to switch toStringBuilderfor 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
Stringso 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()andScanner. - Reverse a String — Multiple approaches from a simple
StringBuilderflip to character-array tricks.
Related Topics
- Wrapper Classes — Converting between strings and numeric types (
Integer.parseInt(),String.valueOf(), etc.) relies on wrapper class methods. - Regular Expressions — Java’s
PatternandMatcherclasses let you search and transform strings with powerful regex patterns. - Text Blocks — Java 15’s multi-line string syntax that eliminates manual
\nand escaping. - StringBuilder — The go-to class for efficient, mutable string construction in single-threaded code.
- Collections Framework — Strings are frequently stored in
List,Set, andMap— understanding both topics together is essential. - Stream API — Java 8 streams pair beautifully with strings for filtering, mapping, and joining collections of text.