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
1// File: StringCreationMethods.java
2
3public class StringCreationMethods {
4
5 public static void main(String[] args) {
6
7 // 1 — String literal (most common — goes to String Pool)
8 String s1 = "hello";
9
10 // 2 — new String from literal (heap object — avoid this)
11 String s2 = new String("hello");
12
13 // 3 — new String from char array
14 char[] chars = {'J', 'a', 'v', 'a'};
15 String s3 = new String(chars);
16
17 // 4 — new String from char array, offset and count
18 String s4 = new String(chars, 0, 2); // "Ja"
19
20 // 5 — new String from byte array (using platform default charset)
21 byte[] bytes = {72, 101, 108, 108, 111}; // ASCII: H e l l o
22 String s5 = new String(bytes);
23
24 // 6 — new String from byte array with explicit charset
25 String s6 = new String(bytes, java.nio.charset.StandardCharsets.UTF_8);
26
27 // 7 — new String from StringBuilder or StringBuffer
28 StringBuilder sb = new StringBuilder("Dynamic");
29 String s7 = new String(sb);
30
31 // 8 — String.valueOf() — converts any type
32 String s8 = String.valueOf(42); // "42"
33 String s9 = String.valueOf(3.14); // "3.14"
34 String s10 = String.valueOf(true); // "true"
35 String s11 = String.valueOf('A'); // "A"
36 String s12 = String.valueOf((Object)null); // "null" — no NPE
37
38 // 9 — Concatenation — both compile-time and runtime
39 String s13 = "Hello" + ", " + "World"; // compile-time constant
40
41 System.out.println("s1 = " + s1);
42 System.out.println("s3 = " + s3);
43 System.out.println("s4 = " + s4);
44 System.out.println("s5 = " + s5);
45 System.out.println("s7 = " + s7);
46 System.out.println("s8 = " + s8);
47 System.out.println("s12 = " + s12);
48 System.out.println("s13 = " + s13);
49
50 System.out.println();
51
52 // Pool vs Heap — == shows which share the same object
53 System.out.println("s1 == s2 : " + (s1 == s2)); // false — heap
54 System.out.println("s1 == 'hello' : " + (s1 == "hello")); // true — pool
55 System.out.println("s1.equals(s2) : " + s1.equals(s2)); // true — content
56 }
57}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
1// File: StringLengthCharAccess.java
2
3public class StringLengthCharAccess {
4
5 public static void main(String[] args) {
6
7 String text = "DevStackFlow";
8
9 // length() — number of chars (not bytes internally)
10 System.out.println("length() : " + text.length()); // 12
11
12 // charAt(index) — char at zero-based position
13 System.out.println("charAt(0) : " + text.charAt(0)); // D
14 System.out.println("charAt(11) : " + text.charAt(text.length() - 1)); // w
15
16 // codePointAt(index) — Unicode code point at position
17 System.out.println("codePointAt(0) : " + text.codePointAt(0)); // 68 (D)
18
19 // toCharArray() — copy of internal chars
20 char[] arr = text.toCharArray();
21 System.out.println("toCharArray()[0] : " + arr[0]); // D
22
23 // getBytes() — byte representation using default charset
24 byte[] bytes = text.getBytes();
25 System.out.println("getBytes length : " + bytes.length); // 12 (ASCII range)
26
27 // getBytes with explicit charset
28 byte[] utf8 = text.getBytes(java.nio.charset.StandardCharsets.UTF_8);
29 System.out.println("UTF-8 bytes len : " + utf8.length); // 12
30
31 // chars() — IntStream of char values (Java 8+)
32 System.out.print("chars() stream : ");
33 text.chars().limit(5).forEach(c -> System.out.print((char)c + " "));
34 System.out.println();
35
36 // isEmpty() and isBlank()
37 System.out.println();
38 System.out.println("''.isEmpty() : " + "".isEmpty()); // true
39 System.out.println("' '.isEmpty() : " + " ".isEmpty()); // false
40 System.out.println("' '.isBlank() : " + " ".isBlank()); // true
41 }
42}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
1// File: StringSearchDemo.java
2
3public class StringSearchDemo {
4
5 public static void main(String[] args) {
6
7 String text = "Java programming is fun. Java is everywhere.";
8
9 // indexOf — first occurrence of char or substring
10 System.out.println("indexOf('J') : " + text.indexOf('J')); // 0
11 System.out.println("indexOf('Java') : " + text.indexOf("Java")); // 0
12 System.out.println("indexOf('Java', 5) : " + text.indexOf("Java", 5)); // 25 — from pos 5
13 System.out.println("indexOf('Python') : " + text.indexOf("Python")); // -1 not found
14
15 // lastIndexOf — last occurrence
16 System.out.println("lastIndexOf('Java') : " + text.lastIndexOf("Java")); // 25
17 System.out.println("lastIndexOf('.') : " + text.lastIndexOf('.')); // 43
18
19 // contains — whether substring exists
20 System.out.println("contains('fun') : " + text.contains("fun")); // true
21 System.out.println("contains('boring') : " + text.contains("boring")); // false
22
23 // startsWith and endsWith
24 System.out.println("startsWith('Java') : " + text.startsWith("Java")); // true
25 System.out.println("endsWith('.') : " + text.endsWith(".")); // true
26 System.out.println("startsWith('is', 20) : " + text.startsWith("is", 20)); // true — at offset 20
27
28 // matches — tests entire string against a regex
29 String phone = "9876543210";
30 System.out.println("phone matches \\d{10} : " + phone.matches("\\d{10}")); // true
31 System.out.println("phone matches \\d{9} : " + phone.matches("\\d{9}")); // false
32
33 System.out.println();
34
35 // regionMatches — compare a region of two strings
36 String s1 = "Hello World";
37 String s2 = "world is great";
38 // regionMatches(ignoreCase, thisOffset, other, otherOffset, length)
39 boolean match = s1.regionMatches(true, 6, s2, 0, 5);
40 System.out.println("regionMatches (ignoreCase): " + match); // true — "World" == "world"
41 }
42}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
1// File: StringExtractionDemo.java
2
3public class StringExtractionDemo {
4
5 public static void main(String[] args) {
6
7 String src = "Order: ORD-2024-001 | Customer: Priya Sharma | Amount: Rs.4999";
8
9 // substring(start) — from start to end
10 System.out.println("substring(7) : " + src.substring(7));
11
12 // substring(start, end) — start inclusive, end exclusive
13 System.out.println("substring(7,19) : " + src.substring(7, 19)); // ORD-2024-001
14
15 // Dynamic extraction — find by marker and slice
16 int custStart = src.indexOf("Customer: ") + 10;
17 int custEnd = src.indexOf(" |", custStart);
18 System.out.println("Customer name : " + src.substring(custStart,
19 custEnd == -1 ? src.length() : custEnd));
20
21 System.out.println();
22
23 // split — breaks at regex matches
24 String csv = "Priya,28,Bengaluru,Developer,85000";
25 String[] parts = csv.split(",");
26 for (int i = 0; i < parts.length; i++) {
27 System.out.println("parts[" + i + "] : " + parts[i]);
28 }
29
30 System.out.println();
31
32 // split with limit
33 String sentence = "one:two:three:four:five";
34 String[] limited = sentence.split(":", 3);
35 System.out.println("split with limit=3 : " + java.util.Arrays.toString(limited));
36
37 // split preserving trailing empty — limit = -1
38 String trailing = "A,B,,C,";
39 System.out.println("Normal split : " + java.util.Arrays.toString(trailing.split(",")));
40 System.out.println("Limit=-1 split : " + java.util.Arrays.toString(trailing.split(",", -1)));
41 }
42}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.
1// File: StringTransformDemo.java
2
3public class StringTransformDemo {
4
5 public static void main(String[] args) {
6
7 String raw = " Hello, DevStackFlow! ";
8
9 // Case conversion
10 System.out.println("toUpperCase() : " + raw.trim().toUpperCase());
11 System.out.println("toLowerCase() : " + raw.trim().toLowerCase());
12
13 // Whitespace removal
14 System.out.println("trim() : '" + raw.trim() + "'");
15 System.out.println("strip() : '" + raw.strip() + "'");
16 System.out.println("stripLeading() : '" + raw.stripLeading() + "'");
17 System.out.println("stripTrailing() : '" + raw.stripTrailing() + "'");
18
19 System.out.println();
20
21 // Replace — literal and regex
22 String text = " too many spaces between words ";
23 System.out.println("replace space : '" + text.trim().replace(" ", "-") + "'");
24 System.out.println("replaceAll \\s+ : '" + text.trim().replaceAll("\\s+", " ") + "'");
25 System.out.println("replaceFirst : '" + text.trim().replaceFirst("\\s+", "_") + "'");
26
27 System.out.println();
28
29 // concat — appends a String (similar to +)
30 String part1 = "Hello";
31 String part2 = " World";
32 System.out.println("concat() : " + part1.concat(part2));
33
34 // repeat — Java 11+
35 System.out.println("'-'.repeat(20) : " + "-".repeat(20));
36 System.out.println("'ab'.repeat(4) : " + "ab".repeat(4));
37
38 System.out.println();
39
40 // indent — Java 12+ — adds leading whitespace to each line
41 String multiline = "Line 1\nLine 2\nLine 3";
42 System.out.println("Original:\n" + multiline);
43 System.out.println("indent(4):\n" + multiline.indent(4));
44
45 // strip + lines — Java 11+ — splits on line terminators
46 String block = " first \n second \n third ";
47 System.out.println("lines().stripped:");
48 block.lines()
49 .map(String::strip)
50 .forEach(System.out::println);
51 }
52}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
1// File: StringComparisonFull.java
2
3import java.util.Objects;
4
5public class StringComparisonFull {
6
7 public static void main(String[] args) {
8
9 String a = "Apple";
10 String b = "banana";
11 String c = "Apple";
12 String d = null;
13
14 // equals — content, case-sensitive
15 System.out.println("equals() : " + a.equals(c)); // true
16 System.out.println("equals() : " + a.equals(b)); // false
17
18 // equalsIgnoreCase — content, case-insensitive
19 System.out.println("equalsIgnoreCase() : " + "APPLE".equalsIgnoreCase(a)); // true
20
21 // compareTo — lexicographic order
22 // Returns: negative if a < b, 0 if equal, positive if a > b
23 System.out.println("compareTo(b) : " + a.compareTo(b)); // negative (A < b)
24 System.out.println("compareTo(c) : " + a.compareTo(c)); // 0 — equal
25 System.out.println("compareToIgnoreCase(b) : " + a.compareToIgnoreCase("apple")); // 0
26
27 System.out.println();
28
29 // Objects.equals — null-safe comparison
30 System.out.println("Objects.equals(a,c) : " + Objects.equals(a, c)); // true
31 System.out.println("Objects.equals(a,null) : " + Objects.equals(a, d)); // false
32 System.out.println("Objects.equals(null,null): " + Objects.equals(d, d)); // true
33
34 System.out.println();
35
36 // contentEquals — compare with CharSequence (StringBuilder, StringBuffer)
37 StringBuilder sb = new StringBuilder("Apple");
38 System.out.println("contentEquals(sb) : " + a.contentEquals(sb)); // true
39 System.out.println("contentEquals('Apple') : " + a.contentEquals("Apple")); // true
40 }
41}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
1// File: StringJoiningDemo.java
2
3import java.util.List;
4
5public class StringJoiningDemo {
6
7 public static void main(String[] args) {
8
9 // String.join — static, joins with delimiter
10 System.out.println(String.join(", ", "Mumbai", "Delhi", "Bengaluru", "Pune"));
11 System.out.println(String.join(" | ", new String[]{"Java", "SQL", "Spring"}));
12
13 // join with Iterable
14 List<String> names = List.of("Priya", "Rohan", "Sneha");
15 System.out.println(String.join(", ", names));
16
17 System.out.println();
18
19 // String.format — structured assembly
20 String msg = String.format("Dear %s, order %s of Rs.%.2f is %s.",
21 "Priya", "ORD-001", 1299.50, "PLACED");
22 System.out.println(msg);
23
24 System.out.println();
25
26 // concat vs +
27 String base = "Hello";
28 String joined = base.concat(", World").concat("!");
29 System.out.println("concat chain : " + joined);
30
31 System.out.println();
32
33 // Collectors.joining — stream to joined String
34 String result = names.stream()
35 .map(String::toUpperCase)
36 .collect(java.util.stream.Collectors.joining(", ", "[", "]"));
37 System.out.println("stream joining : " + result);
38 }
39}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
1// File: StringRegexDemo.java
2
3public class StringRegexDemo {
4
5 public static void main(String[] args) {
6
7 // matches — entire string must match the pattern
8 String email = "priya.sharma@meesho.com";
9 String phone = "9876543210";
10 String pincode = "560034";
11
12 System.out.println("=== matches() — whole string ===");
13 System.out.println("email valid : " + email.matches("[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}"));
14 System.out.println("phone valid : " + phone.matches("[6-9]\\d{9}"));
15 System.out.println("pin valid : " + pincode.matches("\\d{6}"));
16
17 System.out.println();
18
19 // replaceAll — replace all regex matches
20 System.out.println("=== replaceAll() ===");
21 String messy = " Too many spaces between words ";
22 System.out.println("collapse spaces : " + messy.trim().replaceAll("\\s+", " "));
23
24 String rawPhone = "+91 (98765) 43-210";
25 System.out.println("digits only : " + rawPhone.replaceAll("[^0-9]", ""));
26
27 String html = "<b>Hello</b> <i>World</i>";
28 System.out.println("strip tags : " + html.replaceAll("<[^>]+>", ""));
29
30 System.out.println();
31
32 // replaceFirst — only first match
33 System.out.println("=== replaceFirst() ===");
34 String txt = "error: disk full. error: memory low. error: timeout.";
35 System.out.println(txt.replaceFirst("error:", "CRITICAL:"));
36
37 System.out.println();
38
39 // split with regex
40 System.out.println("=== split() ===");
41 String log = "2024-01-15 10:30:00 INFO Server started on port 8080";
42 String[] tokens = log.split("\\s+", 4); // split on whitespace, max 4 parts
43 System.out.println("Date : " + tokens[0]);
44 System.out.println("Time : " + tokens[1]);
45 System.out.println("Level : " + tokens[2]);
46 System.out.println("Message : " + tokens[3]);
47 }
48}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()
1// File: StringAdvancedDemo.java
2
3public class StringAdvancedDemo {
4
5 public static void main(String[] args) {
6
7 // intern() — returns pooled canonical form
8 String heap = new String("developer");
9 String pooled = heap.intern();
10 String literal = "developer";
11
12 System.out.println("heap == literal : " + (heap == literal)); // false
13 System.out.println("pooled == literal : " + (pooled == literal)); // true
14 System.out.println("heap.equals(pooled): " + heap.equals(pooled)); // true
15
16 System.out.println();
17
18 // hashCode() — content-based, cached, same for equal strings
19 String s1 = "hello";
20 String s2 = new String("hello");
21 System.out.println("s1.hashCode() : " + s1.hashCode()); // same
22 System.out.println("s2.hashCode() : " + s2.hashCode()); // same
23 System.out.println("hash == hash : " + (s1.hashCode() == s2.hashCode())); // true
24 System.out.println("identity hash s1 : " + System.identityHashCode(s1)); // different
25 System.out.println("identity hash s2 : " + System.identityHashCode(s2)); // from s1
26
27 System.out.println();
28
29 // contentEquals — compare String content with CharSequence
30 String str = "hello";
31 StringBuilder sb = new StringBuilder("hello");
32 StringBuffer sbf = new StringBuffer("hello");
33
34 System.out.println("contentEquals(SB) : " + str.contentEquals(sb)); // true
35 System.out.println("contentEquals(SBF): " + str.contentEquals(sbf)); // true
36
37 System.out.println();
38
39 // String.valueOf vs toString for null safety
40 Object obj = null;
41 System.out.println("String.valueOf(null) : " + String.valueOf(obj)); // "null" — safe
42 // obj.toString() would throw NullPointerException
43 }
44}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() |
Real-World Example — User Data Validation and Profile Formatter
The Business Problem
A user onboarding service at a company like Razorpay or CRED collects profile data from multiple sources — mobile app, web form, admin import. The raw data contains inconsistent casing, extra whitespace, unformatted phone numbers, and invalid email addresses. The service uses the String API to validate, clean, and format every field before saving to the database.
1// File: UserProfile.java
2
3public class UserProfile {
4 public final String name;
5 public final String email;
6 public final String phone;
7 public final String city;
8 public final String panNumber;
9
10 public UserProfile(String name, String email,
11 String phone, String city, String panNumber) {
12 this.name = name;
13 this.email = email;
14 this.phone = phone;
15 this.city = city;
16 this.panNumber = panNumber;
17 }
18
19 @Override
20 public String toString() {
21 return String.format(
22 "Name: %-20s | Email: %-30s | Phone: %-12s | City: %-12s | PAN: %s",
23 name, email, phone, city, panNumber);
24 }
25}1// File: ProfileService.java
2
3public class ProfileService {
4
5 // Format name — title case, trim, normalise spaces
6 public static String formatName(String raw) {
7 if (raw == null || raw.isBlank()) return null;
8 String[] words = raw.strip().toLowerCase().split("\\s+");
9 StringBuilder result = new StringBuilder();
10 for (String word : words) {
11 if (!word.isEmpty()) {
12 result.append(Character.toUpperCase(word.charAt(0)))
13 .append(word.substring(1))
14 .append(" ");
15 }
16 }
17 return result.toString().stripTrailing();
18 }
19
20 // Validate and normalise email
21 public static String validateEmail(String raw) {
22 if (raw == null || raw.isBlank()) return null;
23 String clean = raw.strip().toLowerCase();
24 // Basic format check — contains @ with content on both sides, dot in domain
25 if (!clean.matches("[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}")) {
26 return null;
27 }
28 return clean;
29 }
30
31 // Normalise phone — strip non-digits, remove country code, validate length
32 public static String formatPhone(String raw) {
33 if (raw == null || raw.isBlank()) return null;
34 String digits = raw.replaceAll("[^0-9]", "");
35 if (digits.length() == 12 && digits.startsWith("91")) {
36 digits = digits.substring(2);
37 }
38 if (digits.length() != 10) return null;
39 // Format as XXXXX-XXXXX
40 return digits.substring(0, 5) + "-" + digits.substring(5);
41 }
42
43 // Format city — title case, standardise known variants
44 public static String formatCity(String raw) {
45 if (raw == null || raw.isBlank()) return null;
46 String city = formatName(raw);
47 if (city == null) return null;
48 // Standardise known aliases
49 if ("Bangalore".equalsIgnoreCase(city) || "Bengaluru".equalsIgnoreCase(city)) return "Bengaluru";
50 if ("Bombay".equalsIgnoreCase(city) || "Mumbai".equalsIgnoreCase(city)) return "Mumbai";
51 if ("Madras".equalsIgnoreCase(city) || "Chennai".equalsIgnoreCase(city)) return "Chennai";
52 return city;
53 }
54
55 // Validate PAN — 10 chars, pattern AAAAA9999A
56 public static String validatePan(String raw) {
57 if (raw == null || raw.isBlank()) return null;
58 String pan = raw.strip().toUpperCase().replaceAll("\\s", "");
59 return pan.matches("[A-Z]{5}[0-9]{4}[A-Z]") ? pan : null;
60 }
61
62 // Process a full profile
63 public static UserProfile process(String name, String email,
64 String phone, String city, String pan) {
65 return new UserProfile(
66 formatName(name),
67 validateEmail(email),
68 formatPhone(phone),
69 formatCity(city),
70 validatePan(pan));
71 }
72}1// File: ProfileServiceDemo.java
2
3public class ProfileServiceDemo {
4
5 public static void main(String[] args) {
6
7 System.out.println("╔══════════════════════════════════════════╗");
8 System.out.println("║ USER PROFILE PROCESSING SERVICE ║");
9 System.out.println("╚══════════════════════════════════════════╝\n");
10
11 String[][] rawProfiles = {
12 {" PRIYA SHARMA ", "PRIYA@RAZORPAY.COM", "+91-98765-43210", " BENGALURU ", "ABCDE1234F"},
13 {"rohan mehta", "rohan@cred.club", "98765 43211", "Mumbai", "FGHIJ5678K"},
14 {"SNEHA RAO", "sneha.invalid", "9123456789", "MADRAS", "KLMNO9012L"},
15 {"", "karan@flipkart.com", "12345", "Delhi", "abcde1234f"},
16 {"Ananya Krishnan", "ANANYA@MEESHO.IN", "91 87654 32109", "Bangalore", "PQRST3456M"},
17 };
18
19 System.out.printf("%-3s %-20s %-30s %-12s %-12s %-10s%n",
20 "#", "Name", "Email", "Phone", "City", "PAN");
21 System.out.println("-".repeat(95));
22
23 for (int i = 0; i < rawProfiles.length; i++) {
24 String[] r = rawProfiles[i];
25 UserProfile profile = ProfileService.process(r[0], r[1], r[2], r[3], r[4]);
26
27 System.out.printf("%-3d %-20s %-30s %-12s %-12s %-10s%n",
28 i + 1,
29 orMissing(profile.name),
30 orMissing(profile.email),
31 orMissing(profile.phone),
32 orMissing(profile.city),
33 orMissing(profile.panNumber));
34 }
35
36 System.out.println();
37 System.out.println("Profiles with all fields valid: " +
38 countValid(rawProfiles));
39 }
40
41 static String orMissing(String val) {
42 return val != null ? val : "MISSING";
43 }
44
45 static int countValid(String[][] raw) {
46 int count = 0;
47 for (String[] r : raw) {
48 UserProfile p = ProfileService.process(r[0], r[1], r[2], r[3], r[4]);
49 if (p.name != null && p.email != null && p.phone != null
50 && p.city != null && p.panNumber != null) {
51 count++;
52 }
53 }
54 return count;
55 }
56}Output:
╔══════════════════════════════════════════╗
║ USER PROFILE PROCESSING SERVICE ║
╚══════════════════════════════════════════╝
# Name Email Phone City PAN
-----------------------------------------------------------------------------------------------
1 Priya Sharma priya@razorpay.com 98765-43210 Bengaluru ABCDE1234F
2 Rohan Mehta rohan@cred.club 98765-43211 Mumbai FGHIJ5678K
3 Sneha Rao MISSING 91234-56789 Chennai KLMNO9012L
4 MISSING karan@flipkart.com MISSING Delhi ABCDE1234F
5 Ananya Krishnan ananya@meesho.in 87654-32109 Bengaluru PQRST3456M
Profiles with all fields valid: 2
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
1String value = getFromDatabase(); // might return null
2String upper = value.toUpperCase(); // NullPointerException if null
3
4// Fix — null check first
5if (value != null) {
6 upper = value.toUpperCase();
7}
8
9// Or use Objects for comparison, String.valueOf for conversion
10String safe = String.valueOf(value).toUpperCase(); // "NULL" if null — check intended behaviourMistake 2 — split(".") to Split by Dot
1String version = "1.8.0.302";
2String[] parts = version.split("."); // dot = any char in regex — returns empty array
3System.out.println(parts.length); // 0
4
5// Fix — escape the dot
6String[] correct = version.split("\\.");
7System.out.println(correct.length); // 4Mistake 3 — Using startsWith/endsWith Without Null Guard
1String filename = getFileName(); // might be null
2if (filename.endsWith(".pdf")) { // NullPointerException
3
4// Fix
5if (filename != null && filename.endsWith(".pdf")) {
6 // safe
7}Mistake 4 — Comparing with == After Runtime Operations
1String input = getUserInput(); // always a heap String
2String target = "admin";
3
4if (input == target) { // false — different objects even if content matches
5 allowAccess();
6}
7
8// Fix — always use equals()
9if (target.equals(input)) { // content comparison — correct
10 allowAccess();
11}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
| Topic | Link |
|---|---|
| How String immutability and the final class design work in detail | Java Immutable Strings → |
| How String Pool memory management works with intern and literals | Java String Pool → |
| How StringBuilder provides mutable alternatives to String operations | Java StringBuilder → |
| How String formatting with format specifiers works | Java String Formatting → |
| How equals() and hashCode() contract affects String in collections | Java equals() and hashCode() → |