Java Tutorial
🔍

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

Java
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

Java
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

Java
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

Java
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.

Java
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

Java
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

Java
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

Java
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()

Java
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

CategoryMethods
Length / Accesslength(), charAt(i), codePointAt(i), isEmpty(), isBlank()
SearchingindexOf(s), indexOf(s,from), lastIndexOf(s), contains(s), startsWith(s), endsWith(s), matches(regex), regionMatches(...)
Extractionsubstring(s), substring(s,e), split(regex), split(regex,limit), toCharArray(), getBytes(), chars(), lines()
CasetoUpperCase(), toLowerCase(), toUpperCase(Locale), toLowerCase(Locale)
Whitespacetrim(), strip(), stripLeading(), stripTrailing(), indent(n)
Replacereplace(c,c), replace(s,s), replaceAll(regex,s), replaceFirst(regex,s)
Combineconcat(s), String.join(sep,...), String.format(...), formatted(...), repeat(n)
Compareequals(o), equalsIgnoreCase(s), compareTo(s), compareToIgnoreCase(s), contentEquals(cs)
ConvertString.valueOf(x), toString(), intern(), hashCode()
Modern Javastrip(), 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.

Java
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}
Java
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}
Java
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

Java
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 behaviour

Mistake 2 — split(".") to Split by Dot

Java
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); // 4

Mistake 3 — Using startsWith/endsWith Without Null Guard

Java
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

Java
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 CharSequenceStringBuilder 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

TopicLink
How String immutability and the final class design work in detailJava Immutable Strings →
How String Pool memory management works with intern and literalsJava String Pool →
How StringBuilder provides mutable alternatives to String operationsJava StringBuilder →
How String formatting with format specifiers worksJava String Formatting →
How equals() and hashCode() contract affects String in collectionsJava equals() and hashCode() →
Java String Class | DevStackFlow