Java String Class
Java String Class
java.lang.String is the class every Java developer uses from day one — but most developers never look inside it. The String class is not just a container for characters. It is a carefully engineered immutable type with a compact internal representation, a pool mechanism for memory efficiency, a rich API of over sixty methods, and a set of behaviours around equality, hashing, and comparison that influence how it works in collections, security, and concurrent systems.
This article is a complete reference for the String class — from how it stores characters internally in Java 9+, through every important method category, to patterns that appear in interviews at every level.
The String Class — Internal Structure
Before Java 9, String stored characters in a char[] — each character taking 2 bytes (UTF-16). Java 9 introduced Compact Strings: if all characters in a string fit within Latin-1 (ISO-8859-1, the first 256 Unicode code points), the string is stored in a byte[] using 1 byte per character — half the memory. If any character is outside Latin-1, a byte[] with UTF-16 encoding is used (2 bytes per character).
String internal structure (Java 9+):
public final class String {
private final byte[] value; ← character data (1 or 2 bytes per char)
private final byte coder; ← LATIN1 = 0, UTF16 = 1
private int hash; ← cached hashCode (0 = not computed yet)
private boolean hashIsZero; ← true only if hash genuinely computed to 0
}
Example: "hello" (all Latin-1)
value = [104, 101, 108, 108, 111] ← h, e, l, l, o as bytes
coder = 0 (LATIN1) ← 5 bytes used
Example: "नमस्ते" (Devanagari — outside Latin-1)
value = [0xE0,0xA4,... 12 bytes] ← UTF-16 encoding
coder = 1 (UTF16) ← 2 bytes per character
Before Java 9:
value = char[]{104, 101, 108, 108, 111} ← always 2 bytes per char (10 bytes for "hello")
This compact representation reduces memory for typical English-language applications by roughly 50% — a significant improvement for systems that hold millions of strings in memory.
String Creation — All Ways
// File: StringCreationMethods.java
public class StringCreationMethods {
public static void main(String[] args) {
// 1 — String literal (most common — goes to String Pool)
String s1 = "hello";
// 2 — new String from literal (heap object — avoid this)
String s2 = new String("hello");
// 3 — new String from char array
char[] chars = {'J', 'a', 'v', 'a'};
String s3 = new String(chars);
// 4 — new String from char array, offset and count
String s4 = new String(chars, 0, 2); // "Ja"
// 5 — new String from byte array (using platform default charset)
byte[] bytes = {72, 101, 108, 108, 111}; // ASCII: H e l l o
String s5 = new String(bytes);
// 6 — new String from byte array with explicit charset
String s6 = new String(bytes, java.nio.charset.StandardCharsets.UTF_8);
// 7 — new String from StringBuilder or StringBuffer
StringBuilder sb = new StringBuilder("Dynamic");
String s7 = new String(sb);
// 8 — String.valueOf() — converts any type
String s8 = String.valueOf(42); // "42"
String s9 = String.valueOf(3.14); // "3.14"
String s10 = String.valueOf(true); // "true"
String s11 = String.valueOf('A'); // "A"
String s12 = String.valueOf((Object)null); // "null" — no NPE
// 9 — Concatenation — both compile-time and runtime
String s13 = "Hello" + ", " + "World"; // compile-time constant
System.out.println("s1 = " + s1);
System.out.println("s3 = " + s3);
System.out.println("s4 = " + s4);
System.out.println("s5 = " + s5);
System.out.println("s7 = " + s7);
System.out.println("s8 = " + s8);
System.out.println("s12 = " + s12);
System.out.println("s13 = " + s13);
System.out.println();
// Pool vs Heap — == shows which share the same object
System.out.println("s1 == s2 : " + (s1 == s2)); // false — heap
System.out.println("s1 == 'hello' : " + (s1 == "hello")); // true — pool
System.out.println("s1.equals(s2) : " + s1.equals(s2)); // true — content
}
}Output:
s1 = hello
s3 = Java
s4 = Ja
s5 = Hello
s7 = Dynamic
s8 = 42
s12 = null
s13 = Hello, World
s1 == s2 : false
s1 == 'hello' : true
s1.equals(s2) : true
String Length and Character Access
// File: StringLengthCharAccess.java
public class StringLengthCharAccess {
public static void main(String[] args) {
String text = "DevStackFlow";
// length() — number of chars (not bytes internally)
System.out.println("length() : " + text.length()); // 12
// charAt(index) — char at zero-based position
System.out.println("charAt(0) : " + text.charAt(0)); // D
System.out.println("charAt(11) : " + text.charAt(text.length() - 1)); // w
// codePointAt(index) — Unicode code point at position
System.out.println("codePointAt(0) : " + text.codePointAt(0)); // 68 (D)
// toCharArray() — copy of internal chars
char[] arr = text.toCharArray();
System.out.println("toCharArray()[0] : " + arr[0]); // D
// getBytes() — byte representation using default charset
byte[] bytes = text.getBytes();
System.out.println("getBytes length : " + bytes.length); // 12 (ASCII range)
// getBytes with explicit charset
byte[] utf8 = text.getBytes(java.nio.charset.StandardCharsets.UTF_8);
System.out.println("UTF-8 bytes len : " + utf8.length); // 12
// chars() — IntStream of char values (Java 8+)
System.out.print("chars() stream : ");
text.chars().limit(5).forEach(c -> System.out.print((char)c + " "));
System.out.println();
// isEmpty() and isBlank()
System.out.println();
System.out.println("''.isEmpty() : " + "".isEmpty()); // true
System.out.println("' '.isEmpty() : " + " ".isEmpty()); // false
System.out.println("' '.isBlank() : " + " ".isBlank()); // true
}
}Output:
length() : 12
charAt(0) : D
charAt(11) : w
codePointAt(0) : 68
toCharArray()[0] : D
getBytes length : 12
UTF-8 bytes len : 12
chars() stream : D e v S t
''.isEmpty() : true
' '.isEmpty() : false
' '.isBlank() : true
Searching Within a String
// File: StringSearchDemo.java
public class StringSearchDemo {
public static void main(String[] args) {
String text = "Java programming is fun. Java is everywhere.";
// indexOf — first occurrence of char or substring
System.out.println("indexOf('J') : " + text.indexOf('J')); // 0
System.out.println("indexOf('Java') : " + text.indexOf("Java")); // 0
System.out.println("indexOf('Java', 5) : " + text.indexOf("Java", 5)); // 25 — from pos 5
System.out.println("indexOf('Python') : " + text.indexOf("Python")); // -1 not found
// lastIndexOf — last occurrence
System.out.println("lastIndexOf('Java') : " + text.lastIndexOf("Java")); // 25
System.out.println("lastIndexOf('.') : " + text.lastIndexOf('.')); // 43
// contains — whether substring exists
System.out.println("contains('fun') : " + text.contains("fun")); // true
System.out.println("contains('boring') : " + text.contains("boring")); // false
// startsWith and endsWith
System.out.println("startsWith('Java') : " + text.startsWith("Java")); // true
System.out.println("endsWith('.') : " + text.endsWith(".")); // true
System.out.println("startsWith('is', 20) : " + text.startsWith("is", 20)); // true — at offset 20
// matches — tests entire string against a regex
String phone = "9876543210";
System.out.println("phone matches \\d{10} : " + phone.matches("\\d{10}")); // true
System.out.println("phone matches \\d{9} : " + phone.matches("\\d{9}")); // false
System.out.println();
// regionMatches — compare a region of two strings
String s1 = "Hello World";
String s2 = "world is great";
// regionMatches(ignoreCase, thisOffset, other, otherOffset, length)
boolean match = s1.regionMatches(true, 6, s2, 0, 5);
System.out.println("regionMatches (ignoreCase): " + match); // true — "World" == "world"
}
}Output:
indexOf('J') : 0
indexOf('Java') : 0
indexOf('Java', 5) : 25
indexOf('Python') : -1
lastIndexOf('Java') : 25
lastIndexOf('.') : 43
contains('fun') : true
contains('boring') : false
startsWith('Java') : true
endsWith('.') : true
startsWith('is', 20) : true
phone matches \d{10} : true
phone matches \d{9} : false
regionMatches (ignoreCase): true
Extracting Substrings
// File: StringExtractionDemo.java
public class StringExtractionDemo {
public static void main(String[] args) {
String src = "Order: ORD-2024-001 | Customer: Priya Sharma | Amount: Rs.4999";
// substring(start) — from start to end
System.out.println("substring(7) : " + src.substring(7));
// substring(start, end) — start inclusive, end exclusive
System.out.println("substring(7,19) : " + src.substring(7, 19)); // ORD-2024-001
// Dynamic extraction — find by marker and slice
int custStart = src.indexOf("Customer: ") + 10;
int custEnd = src.indexOf(" |", custStart);
System.out.println("Customer name : " + src.substring(custStart,
custEnd == -1 ? src.length() : custEnd));
System.out.println();
// split — breaks at regex matches
String csv = "Priya,28,Bengaluru,Developer,85000";
String[] parts = csv.split(",");
for (int i = 0; i < parts.length; i++) {
System.out.println("parts[" + i + "] : " + parts[i]);
}
System.out.println();
// split with limit
String sentence = "one:two:three:four:five";
String[] limited = sentence.split(":", 3);
System.out.println("split with limit=3 : " + java.util.Arrays.toString(limited));
// split preserving trailing empty — limit = -1
String trailing = "A,B,,C,";
System.out.println("Normal split : " + java.util.Arrays.toString(trailing.split(",")));
System.out.println("Limit=-1 split : " + java.util.Arrays.toString(trailing.split(",", -1)));
}
}Output:
substring(7) : ORD-2024-001 | Customer: Priya Sharma | Amount: Rs.4999
substring(7,19) : ORD-2024-001
Customer name : Priya Sharma
parts[0] : Priya
parts[1] : 28
parts[2] : Bengaluru
parts[3] : Developer
parts[4] : 85000
split with limit=3 : [one, two, three:four:five]
Normal split : [A, B, , C]
Limit=-1 split : [A, B, , C, ]
Modifying and Transforming Strings
Every method returns a new String — none modifies the original.
// File: StringTransformDemo.java
public class StringTransformDemo {
public static void main(String[] args) {
String raw = " Hello, DevStackFlow! ";
// Case conversion
System.out.println("toUpperCase() : " + raw.trim().toUpperCase());
System.out.println("toLowerCase() : " + raw.trim().toLowerCase());
// Whitespace removal
System.out.println("trim() : '" + raw.trim() + "'");
System.out.println("strip() : '" + raw.strip() + "'");
System.out.println("stripLeading() : '" + raw.stripLeading() + "'");
System.out.println("stripTrailing() : '" + raw.stripTrailing() + "'");
System.out.println();
// Replace — literal and regex
String text = " too many spaces between words ";
System.out.println("replace space : '" + text.trim().replace(" ", "-") + "'");
System.out.println("replaceAll \\s+ : '" + text.trim().replaceAll("\\s+", " ") + "'");
System.out.println("replaceFirst : '" + text.trim().replaceFirst("\\s+", "_") + "'");
System.out.println();
// concat — appends a String (similar to +)
String part1 = "Hello";
String part2 = " World";
System.out.println("concat() : " + part1.concat(part2));
// repeat — Java 11+
System.out.println("'-'.repeat(20) : " + "-".repeat(20));
System.out.println("'ab'.repeat(4) : " + "ab".repeat(4));
System.out.println();
// indent — Java 12+ — adds leading whitespace to each line
String multiline = "Line 1\nLine 2\nLine 3";
System.out.println("Original:\n" + multiline);
System.out.println("indent(4):\n" + multiline.indent(4));
// strip + lines — Java 11+ — splits on line terminators
String block = " first \n second \n third ";
System.out.println("lines().stripped:");
block.lines()
.map(String::strip)
.forEach(System.out::println);
}
}Output:
toUpperCase() : HELLO, DEVSTACKFLOW!
toLowerCase() : hello, devstackflow!
trim() : 'Hello, DevStackFlow!'
strip() : 'Hello, DevStackFlow!'
stripLeading() : 'Hello, DevStackFlow! '
stripTrailing() : ' Hello, DevStackFlow!'
replace space : 'too---many---spaces---between---words'
replaceAll \s+ : 'too many spaces between words'
replaceFirst : 'too_many spaces between words'
concat() : Hello World
'-'.repeat(20) : --------------------
'ab'.repeat(4) : abababab
Original:
Line 1
Line 2
Line 3
indent(4):
Line 1
Line 2
Line 3
lines().stripped:
first
second
third
Comparing Strings
// File: StringComparisonFull.java
import java.util.Objects;
public class StringComparisonFull {
public static void main(String[] args) {
String a = "Apple";
String b = "banana";
String c = "Apple";
String d = null;
// equals — content, case-sensitive
System.out.println("equals() : " + a.equals(c)); // true
System.out.println("equals() : " + a.equals(b)); // false
// equalsIgnoreCase — content, case-insensitive
System.out.println("equalsIgnoreCase() : " + "APPLE".equalsIgnoreCase(a)); // true
// compareTo — lexicographic order
// Returns: negative if a < b, 0 if equal, positive if a > b
System.out.println("compareTo(b) : " + a.compareTo(b)); // negative (A < b)
System.out.println("compareTo(c) : " + a.compareTo(c)); // 0 — equal
System.out.println("compareToIgnoreCase(b) : " + a.compareToIgnoreCase("apple")); // 0
System.out.println();
// Objects.equals — null-safe comparison
System.out.println("Objects.equals(a,c) : " + Objects.equals(a, c)); // true
System.out.println("Objects.equals(a,null) : " + Objects.equals(a, d)); // false
System.out.println("Objects.equals(null,null): " + Objects.equals(d, d)); // true
System.out.println();
// contentEquals — compare with CharSequence (StringBuilder, StringBuffer)
StringBuilder sb = new StringBuilder("Apple");
System.out.println("contentEquals(sb) : " + a.contentEquals(sb)); // true
System.out.println("contentEquals('Apple') : " + a.contentEquals("Apple")); // true
}
}Output:
equals() : true
equals() : false
equalsIgnoreCase() : true
compareTo(b) : -31
compareTo(c) : 0
compareToIgnoreCase(b) : 0
Objects.equals(a,c) : true
Objects.equals(a,null) : false
Objects.equals(null,null): true
contentEquals(sb) : true
contentEquals('Apple') : true
String Joining and Combining
// File: StringJoiningDemo.java
import java.util.List;
public class StringJoiningDemo {
public static void main(String[] args) {
// String.join — static, joins with delimiter
System.out.println(String.join(", ", "Mumbai", "Delhi", "Bengaluru", "Pune"));
System.out.println(String.join(" | ", new String[]{"Java", "SQL", "Spring"}));
// join with Iterable
List<String> names = List.of("Priya", "Rohan", "Sneha");
System.out.println(String.join(", ", names));
System.out.println();
// String.format — structured assembly
String msg = String.format("Dear %s, order %s of Rs.%.2f is %s.",
"Priya", "ORD-001", 1299.50, "PLACED");
System.out.println(msg);
System.out.println();
// concat vs +
String base = "Hello";
String joined = base.concat(", World").concat("!");
System.out.println("concat chain : " + joined);
System.out.println();
// Collectors.joining — stream to joined String
String result = names.stream()
.map(String::toUpperCase)
.collect(java.util.stream.Collectors.joining(", ", "[", "]"));
System.out.println("stream joining : " + result);
}
}Output:
Mumbai, Delhi, Bengaluru, Pune
Java | SQL | Spring
Priya, Rohan, Sneha
Dear Priya, order ORD-001 of Rs.1299.50 is PLACED.
concat chain : Hello, World!
stream joining : [PRIYA, ROHAN, SNEHA]
String and Regular Expressions
// File: StringRegexDemo.java
public class StringRegexDemo {
public static void main(String[] args) {
// matches — entire string must match the pattern
String email = "priya.sharma@meesho.com";
String phone = "9876543210";
String pincode = "560034";
System.out.println("=== matches() — whole string ===");
System.out.println("email valid : " + email.matches("[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}"));
System.out.println("phone valid : " + phone.matches("[6-9]\\d{9}"));
System.out.println("pin valid : " + pincode.matches("\\d{6}"));
System.out.println();
// replaceAll — replace all regex matches
System.out.println("=== replaceAll() ===");
String messy = " Too many spaces between words ";
System.out.println("collapse spaces : " + messy.trim().replaceAll("\\s+", " "));
String rawPhone = "+91 (98765) 43-210";
System.out.println("digits only : " + rawPhone.replaceAll("[^0-9]", ""));
String html = "<b>Hello</b> <i>World</i>";
System.out.println("strip tags : " + html.replaceAll("<[^>]+>", ""));
System.out.println();
// replaceFirst — only first match
System.out.println("=== replaceFirst() ===");
String txt = "error: disk full. error: memory low. error: timeout.";
System.out.println(txt.replaceFirst("error:", "CRITICAL:"));
System.out.println();
// split with regex
System.out.println("=== split() ===");
String log = "2024-01-15 10:30:00 INFO Server started on port 8080";
String[] tokens = log.split("\\s+", 4); // split on whitespace, max 4 parts
System.out.println("Date : " + tokens[0]);
System.out.println("Time : " + tokens[1]);
System.out.println("Level : " + tokens[2]);
System.out.println("Message : " + tokens[3]);
}
}Output:
=== matches() ===
email valid : true
phone valid : true
pin valid : true
=== replaceAll() ===
collapse spaces : Too many spaces between words
digits only : 919876543210
strip tags : Hello World
=== replaceFirst() ===
CRITICAL: disk full. error: memory low. error: timeout.
=== split() ===
Date : 2024-01-15
Time : 10:30:00
Level : INFO
Message : Server started on port 8080
intern(), hashCode(), and contentEquals()
// File: StringAdvancedDemo.java
public class StringAdvancedDemo {
public static void main(String[] args) {
// intern() — returns pooled canonical form
String heap = new String("developer");
String pooled = heap.intern();
String literal = "developer";
System.out.println("heap == literal : " + (heap == literal)); // false
System.out.println("pooled == literal : " + (pooled == literal)); // true
System.out.println("heap.equals(pooled): " + heap.equals(pooled)); // true
System.out.println();
// hashCode() — content-based, cached, same for equal strings
String s1 = "hello";
String s2 = new String("hello");
System.out.println("s1.hashCode() : " + s1.hashCode()); // same
System.out.println("s2.hashCode() : " + s2.hashCode()); // same
System.out.println("hash == hash : " + (s1.hashCode() == s2.hashCode())); // true
System.out.println("identity hash s1 : " + System.identityHashCode(s1)); // different
System.out.println("identity hash s2 : " + System.identityHashCode(s2)); // from s1
System.out.println();
// contentEquals — compare String content with CharSequence
String str = "hello";
StringBuilder sb = new StringBuilder("hello");
StringBuffer sbf = new StringBuffer("hello");
System.out.println("contentEquals(SB) : " + str.contentEquals(sb)); // true
System.out.println("contentEquals(SBF): " + str.contentEquals(sbf)); // true
System.out.println();
// String.valueOf vs toString for null safety
Object obj = null;
System.out.println("String.valueOf(null) : " + String.valueOf(obj)); // "null" — safe
// obj.toString() would throw NullPointerException
}
}Output:
heap == literal : false
pooled == literal : true
heap.equals(pooled): true
s1.hashCode() : 99162322
s2.hashCode() : 99162322
hash == hash : true
identity hash s1 : 1173230247
identity hash s2 : 856419764
contentEquals(SB) : true
contentEquals(SBF): true
String.valueOf(null) : null
String — Full API Quick Reference
| Category | Methods |
|---|---|
| Length / Access | length(), charAt(i), codePointAt(i), isEmpty(), isBlank() |
| Searching | indexOf(s), indexOf(s,from), lastIndexOf(s), contains(s), startsWith(s), endsWith(s), matches(regex), regionMatches(...) |
| Extraction | substring(s), substring(s,e), split(regex), split(regex,limit), toCharArray(), getBytes(), chars(), lines() |
| Case | toUpperCase(), toLowerCase(), toUpperCase(Locale), toLowerCase(Locale) |
| Whitespace | trim(), strip(), stripLeading(), stripTrailing(), indent(n) |
| Replace | replace(c,c), replace(s,s), replaceAll(regex,s), replaceFirst(regex,s) |
| Combine | concat(s), String.join(sep,...), String.format(...), formatted(...), repeat(n) |
| Compare | equals(o), equalsIgnoreCase(s), compareTo(s), compareToIgnoreCase(s), contentEquals(cs) |
| Convert | String.valueOf(x), toString(), intern(), hashCode() |
| Modern Java | strip(), stripLeading(), stripTrailing(), isBlank(), lines(), repeat(), indent(), formatted(), translateEscapes() |
Basic Example
public class Main {
public static void main(String[] args) {
String text = " Java Programming ";
String trimmed = text.trim();
System.out.println("Trimmed : '" + trimmed + "'");
System.out.println("Length : " + trimmed.length());
System.out.println("Upper : " + trimmed.toUpperCase());
System.out.println("Contains : " + trimmed.contains("Program"));
System.out.println("Replaced : " + trimmed.replace("Java", "Python"));
}
}Output
Trimmed : 'Java Programming' Length : 17 Upper : JAVA PROGRAMMING Contains : true Replaced : Python Programming
How it works:
- ›
trim()returns a newStringwith the leading and trailing spaces removed —textitself is unchanged. - ›
length()counts the characters intrimmed, which is17once the padding is gone. - ›
toUpperCase()andreplace()each build and return a brand newStringfromtrimmed's content. - ›
contains()scans the characters and returnstruebecause"Program"appears inside"Java Programming".
Best Practices
Use Objects.equals(a, b) for null-safe comparison when both sides might be null. String.equals() throws NullPointerException when called on a null reference. "constant".equals(var) handles a null var safely but not a null constant. Objects.equals(a, b) handles null on both sides — the cleanest option when either value might be absent.
Prefer strip() over trim() for user input in Java 11+. strip() recognises Unicode whitespace including non-breaking spaces, while trim() only removes characters with ASCII code ≤ 32. User input from web forms or mobile apps may contain Unicode whitespace that trim() silently leaves behind.
Use String.valueOf() instead of calling toString() directly on objects that might be null. String.valueOf(obj) returns "null" safely when obj is null. obj.toString() crashes. For any value that might be null — database fields, optional configuration, user-supplied data — String.valueOf() is the safer conversion.
Use lines() instead of split("\\n") for multiline string processing in Java 11+. lines() correctly handles all line terminators — \n, \r\n, and \r — while split("\\n") only handles Unix-style. It also returns a lazy Stream<String> which integrates naturally with stream operations.
Common Mistakes
Mistake 1 — Calling String Methods on a Null Reference
String value = getFromDatabase(); // might return null
String upper = value.toUpperCase(); // NullPointerException if null
// Fix — null check first
if (value != null) {
upper = value.toUpperCase();
}
// Or use Objects for comparison, String.valueOf for conversion
String safe = String.valueOf(value).toUpperCase(); // "NULL" if null — check intended behaviourMistake 2 — split(".") to Split by Dot
String version = "1.8.0.302";
String[] parts = version.split("."); // dot = any char in regex — returns empty array
System.out.println(parts.length); // 0
// Fix — escape the dot
String[] correct = version.split("\\.");
System.out.println(correct.length); // 4Mistake 3 — Using startsWith/endsWith Without Null Guard
String filename = getFileName(); // might be null
if (filename.endsWith(".pdf")) { // NullPointerException
// Fix
if (filename != null && filename.endsWith(".pdf")) {
// safe
}Mistake 4 — Comparing with == After Runtime Operations
String input = getUserInput(); // always a heap String
String target = "admin";
if (input == target) { // false — different objects even if content matches
allowAccess();
}
// Fix — always use equals()
if (target.equals(input)) { // content comparison — correct
allowAccess();
}Interview Questions
Q1. What is the internal structure of a String in Java 9 and later?
In Java 9+, String uses a byte[] (not char[]) internally via the Compact Strings optimisation. A coder field indicates the encoding: 0 for LATIN1 (one byte per character) and 1 for UTF16 (two bytes per character). If all characters in the string fit within Latin-1, Java stores them as one byte each, halving memory usage for typical English text. Before Java 9, every character occupied two bytes in a char[] regardless of its value. The hash field caches the result of hashCode() after first computation — safe because the content can never change.
Q2. What is the difference between equals(), ==, compareTo(), and contentEquals() for Strings?
== compares object references — only true if both variables point to the exact same object. equals() compares character content, case-sensitively. equalsIgnoreCase() does the same ignoring case. compareTo() returns a negative integer if the calling string is lexicographically less than the argument, zero if equal, positive if greater — used for sorting. contentEquals(CharSequence) compares the String's content with any CharSequence — StringBuilder or StringBuffer — returning true when the characters match.
Q3. What does String.intern() do and when is it useful?
intern() returns the canonical pooled representation of a String. If the pool already contains an equal String, that reference is returned; otherwise, the String is added to the pool and its reference returned. It is useful in memory-intensive systems that process millions of repeated strings — log levels, status codes, enum-like values — where deduplication into the pool dramatically reduces heap usage. In typical application code, intern() is unnecessary and adds synchronisation overhead. Always use equals() for comparison regardless of whether intern() was called.
Q4. What is the difference between trim() and strip() in Java?
trim() removes leading and trailing characters whose Unicode value is ≤ 32 — the ASCII whitespace range. strip() (Java 11+) removes leading and trailing whitespace as defined by Character.isWhitespace(), which includes Unicode whitespace characters beyond the ASCII range — non-breaking spaces, ideographic spaces, and others. For user input from international sources or web forms, strip() is more correct. stripLeading() and stripTrailing() remove only from one side.
Q5. Why is String declared final in Java?
To prevent subclassing. Without final, a developer could write a MutableString extends String that overrides methods to allow modification — breaking every assumption that libraries, the JVM, and frameworks make about String immutability. Making String final ensures that every String reference refers to exactly java.lang.String — no subtypes, no surprises. This is essential for the String Pool to work safely, for HashMap keys to be reliable, and for security-critical operations to remain unbreakable.
Q6. What exception does String.format() throw on type mismatch and how do you handle it?
java.util.IllegalFormatConversionException is thrown when an argument's type does not match the format specifier — for example, passing a double to %d. java.util.MissingFormatArgumentException is thrown when there are fewer arguments than format specifiers. Both are runtime exceptions — not caught at compile time. Handle them by ensuring argument types match specifiers, or use try-catch around format calls that accept user-defined templates. For production code, unit test all format strings with representative data.
FAQs
Is String serialisable in Java?
Yes. java.lang.String implements java.io.Serializable. A String field in a serialisable class is serialised normally. This means Strings can be written to streams, sent over networks, and stored in files as part of serialised objects without any extra configuration.
What is the String.chars() method and when do you use it?
chars() returns an IntStream of the Unicode code points (char values) of each character. It enables stream-style processing of individual characters: str.chars().filter(c -> c == 'a').count() counts the occurrences of 'a'. The stream yields int values — cast to char for display. It is useful when you want to apply stream operations — filter, map, distinct, sorted — to the characters of a string.
What does String.lines() do differently from split("\\n")?
lines() (Java 11+) handles all common line terminators — \n, \r\n, and \r — while split("\\n") only recognises Unix \n. lines() returns a lazy Stream<String> that integrates with stream operations. split("\\n") returns an eager String[]. For processing multiline text from files or APIs that might use Windows-style \r\n line endings, lines() is more robust.
Can a String contain null characters?
Yes. String can contain the null character '\0' (Unicode code point 0). It is a valid character. new String(new char[]{'\0', 'A'}) creates a two-character String containing a null byte followed by 'A'. length() returns 2. However, null characters in Strings cause problems with C-based systems, native libraries, and certain display contexts — handle them explicitly if your data might contain them.
What is translateEscapes() in Java 15?
translateEscapes() processes escape sequences in a String — turning the literal two characters \n into an actual newline character, \t into a tab, and so on. Before Java 15, you had to use replace("\\n", "\n") manually. This method is particularly useful when reading escape sequences from configuration files or user input that should be treated as actual escape characters.
Summary
java.lang.String is the most complete and most used class in the Java standard library. Its internal compact representation (byte array in Java 9+) reduces memory use for typical text. Its immutability enables pool sharing, thread safety, stable hash codes, and security. Its API covers searching, extraction, transformation, comparison, formatting, regex, streams, and conversion — over sixty methods addressing virtually every text-processing need.
The methods that appear in nearly every real Java project: equals(), equalsIgnoreCase(), trim() / strip(), split(), replace() / replaceAll(), substring(), indexOf(), contains(), startsWith(), endsWith(), toUpperCase(), toLowerCase(), isEmpty() / isBlank(), String.format(), String.valueOf(), String.join(), intern(), compareTo(), and matches().
For interviews, be ready to explain the compact strings optimisation, describe the difference between every comparison method, explain why String is final, demonstrate intern() and its use case, and distinguish trim() from strip().
What to Read Next
Learn the four core ideas behind object-oriented programming.
Practice Programs
Put this topic into practice — each program has full code, output and a step-by-step explanation you can run online.
Compares two strings and reports their lexicographic order.
Determines whether one string appears inside another.
Replaces every occurrence of a word with another word.