Skip to main content

9.2 Mastering the String Class

note

This guide is based on Java 21. For the latest specifications, refer to the official Java Documentation.

One of the most commonly handled data types in Java programming is text — the String. The String class, included in the java.lang package, provides a powerful set of features for storing and manipulating text.

1. String Immutability

In Java, the frequently used String is not a simple data type but rather a class (object) that holds a sequence of characters. There is one critical property you must always remember when working with the String class: once a String is created, its contents can never be changed in memory (immutability).

public class StringImmutability {
public static void main(String[] args) {
String a = "Hello";
String b = a; // a and b point to the same object

a = a + " World"; // A new String object "Hello World" is created!

System.out.println(a); // Hello World (new object)
System.out.println(b); // Hello (original object, unchanged!)

// String Pool: identical literals share the same object
String s1 = "Java";
String s2 = "Java";
String s3 = new String("Java"); // forces creation of a new object

System.out.println(s1 == s2); // true (same pool object)
System.out.println(s1 == s3); // false (different object)
System.out.println(s1.equals(s3)); // true (same content)
}
}
String Comparison

== compares addresses (references), so never use it to compare string content. Always use equals() to compare string content.

String Pool and intern()

public class StringPoolDemo {
public static void main(String[] args) {
String s1 = new String("Java"); // creates a new object on the heap
String s2 = s1.intern(); // registers in String Pool (reuses if already present)
String s3 = "Java"; // retrieved from String Pool

System.out.println(s2 == s3); // true (both are the same pool object)
System.out.println(s1 == s3); // false (s1 is a separate object on the heap)
}
}

2. Creating Strings and Basic Comparison

public class StringCreation {
public static void main(String[] args) {
// Various creation methods
String s1 = "Hello, Java!";
String s2 = String.valueOf(42); // number → string
String s3 = String.valueOf(true); // boolean → string
String s4 = String.valueOf(3.14); // double → string
char[] chars = {'J', 'a', 'v', 'a'};
String s5 = new String(chars); // char array → string

System.out.println(s1); // Hello, Java!
System.out.println(s2); // 42
System.out.println(s3); // true
System.out.println(s4); // 3.14
System.out.println(s5); // Java

// Comparison methods
String a = "Hello";
String b = "hello";
System.out.println(a.equals(b)); // false (case-sensitive)
System.out.println(a.equalsIgnoreCase(b)); // true (case-insensitive)
System.out.println(a.compareTo(b)); // negative (H comes before h)
System.out.println(a.compareToIgnoreCase(b)); // 0 (equal)
}
}

3. Complete String Method Reference

Length and Character Access

public class StringLengthChar {
public static void main(String[] args) {
String s = "Hello, Java!";

// length(): string length
System.out.println(s.length()); // 12

// charAt(index): character at a specific position
System.out.println(s.charAt(0)); // H
System.out.println(s.charAt(7)); // J

// isEmpty(): whether the string is empty (length() == 0)
System.out.println("".isEmpty()); // true
System.out.println(" ".isEmpty()); // false (spaces are not empty)

// isBlank(): whether the string contains only whitespace (Java 11+)
System.out.println(" ".isBlank()); // true
System.out.println(" a".isBlank()); // false

// toCharArray(): convert to char array
char[] arr = s.toCharArray();
System.out.println(arr[0]); // H
}
}

Searching and Checking

public class StringSearch {
public static void main(String[] args) {
String s = "Hello, Java Programming!";

// contains(): checks if the string contains a substring
System.out.println(s.contains("Java")); // true
System.out.println(s.contains("Python")); // false

// startsWith() / endsWith(): checks beginning/end
System.out.println(s.startsWith("Hello")); // true
System.out.println(s.endsWith("!")); // true

// indexOf(): position of first occurrence (returns -1 if not found)
System.out.println(s.indexOf("a")); // 8 (the 'a' in Java)
System.out.println(s.indexOf("z")); // -1 (not found)

// lastIndexOf(): position of last occurrence
System.out.println(s.lastIndexOf("a")); // 20 (the 'a' in Programming)

// indexOf(str, fromIndex): search starting from a specific position
System.out.println(s.indexOf("a", 10)); // 14 (first 'a' in Programming)
}
}

Extracting and Splitting

public class StringExtract {
public static void main(String[] args) {
String s = "Hello, Java Programming!";

// substring(start): from start index to end
System.out.println(s.substring(7)); // Java Programming!

// substring(start, end): from start to end-1
System.out.println(s.substring(7, 11)); // Java

// split(regex): split by delimiter
String csv = "apple,banana,cherry,date";
String[] fruits = csv.split(",");
for (String f : fruits) {
System.out.print(f + " "); // apple banana cherry date
}
System.out.println();

// split(regex, limit): split into at most limit pieces
String[] parts = csv.split(",", 2);
System.out.println(parts[0]); // apple
System.out.println(parts[1]); // banana,cherry,date

// Split using regex
String sentence = "one two three"; // multiple spaces
String[] words = sentence.split("\\s+"); // one or more whitespace characters
System.out.println(words.length); // 3
}
}

Transforming and Modifying

public class StringTransform {
public static void main(String[] args) {
String s = " Hello, Java! ";

// trim(): remove leading and trailing whitespace (ASCII whitespace only)
System.out.println(s.trim()); // "Hello, Java!"

// strip(): remove leading and trailing whitespace (includes Unicode, Java 11+)
System.out.println(s.strip()); // "Hello, Java!"
System.out.println(s.stripLeading()); // "Hello, Java! " (leading only)
System.out.println(s.stripTrailing()); // " Hello, Java!" (trailing only)

// toUpperCase() / toLowerCase()
System.out.println("hello".toUpperCase()); // HELLO
System.out.println("JAVA".toLowerCase()); // java

// replace(old, new): replace all matching substrings
String str = "banana";
System.out.println(str.replace("a", "o")); // bonono

// replaceAll(regex, replacement): replace using regex
String phone = "010-1234-5678";
System.out.println(phone.replaceAll("-", "")); // 01012345678
System.out.println(phone.replaceAll("[0-9]", "*")); // ***-****-****

// replaceFirst(regex, replacement): replace only the first match
System.out.println("aaa".replaceFirst("a", "b")); // baa

// repeat(n): repeat the string n times (Java 11+)
System.out.println("ha".repeat(3)); // hahaha
}
}

Joining and Formatting

public class StringFormat {
public static void main(String[] args) {
// concat(): concatenate strings (same as + but does not allow null)
String s = "Hello".concat(", World!");
System.out.println(s); // Hello, World!

// String.join(): join with a delimiter
String joined = String.join("-", "2026", "03", "23");
System.out.println(joined); // 2026-03-23

String.join(", ", "apple", "banana", "cherry"); // apple, banana, cherry

// String.format(): C-style printf formatting (Java 1.5+)
String name = "Alice";
int age = 30;
double score = 95.5;
String formatted = String.format("Name: %s, Age: %d, Score: %.1f", name, age, score);
System.out.println(formatted); // Name: Alice, Age: 30, Score: 95.5

// formatted(): instance method version (Java 15+)
String result = "Name: %s, Age: %d".formatted(name, age);
System.out.println(result); // Name: Alice, Age: 30

// Common format specifiers
System.out.printf("%d%n", 42); // integer
System.out.printf("%05d%n", 42); // 5 digits, zero-padded: 00042
System.out.printf("%.2f%n", 3.14159); // 2 decimal places: 3.14
System.out.printf("%-10s|%n", "left"); // left-aligned, 10 chars: left |
System.out.printf("%10s|%n", "right"); // right-aligned, 10 chars: right|
}
}

4. String Type Conversion

String to Other Types

public class StringParsing {
public static void main(String[] args) {
String intStr = "123";
String doubleStr = "3.14";
String boolStr = "true";
String longStr = "9876543210";

// String → primitive
int i = Integer.parseInt(intStr); // 123
double d = Double.parseDouble(doubleStr); // 3.14
boolean b = Boolean.parseBoolean(boolStr); // true
long l = Long.parseLong(longStr); // 9876543210

// String → wrapper object (Integer.valueOf uses cache)
Integer iObj = Integer.valueOf(intStr); // Integer object
Double dObj = Double.valueOf(doubleStr); // Double object

System.out.printf("int: %d, double: %.2f, boolean: %b, long: %d%n", i, d, b, l);

// Radix conversion
int binary = Integer.parseInt("1010", 2); // binary "1010" → 10
int hex = Integer.parseInt("FF", 16); // hex "FF" → 255
System.out.println("1010(2) = " + binary); // 10
System.out.println("FF(16) = " + hex); // 255
}
}

Other Types to String

public class ToStringConversion {
public static void main(String[] args) {
int i = 42;
double d = 3.14;
boolean b = true;
char c = 'A';

// String.valueOf(): safest approach (null → "null")
String s1 = String.valueOf(i); // "42"
String s2 = String.valueOf(d); // "3.14"
String s3 = String.valueOf(b); // "true"
String s4 = String.valueOf(c); // "A"

// toString() method (throws NullPointerException if null!)
String s5 = Integer.toString(i); // "42"
String s6 = Integer.toString(255, 16); // "ff" (converts to hex)
String s7 = Integer.toBinaryString(10); // "1010" (binary)
String s8 = Integer.toHexString(255); // "ff"
String s9 = Integer.toOctalString(8); // "10" (octal)

// + operator (concatenate with empty string)
String s10 = "" + i; // "42"

System.out.println(s1 + ", " + s6 + ", " + s7 + ", " + s9);
// 42, ff, 1010, 10
}
}

5. Regular Expressions

public class StringRegex {
public static void main(String[] args) {
// matches(): checks whether the entire string matches the pattern
String email = "user@example.com";
String emailPattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
System.out.println(email.matches(emailPattern)); // true

String badEmail = "not-an-email";
System.out.println(badEmail.matches(emailPattern)); // false

// replaceAll(): batch replace using regex
String dirty = " Hello World ";
String clean = dirty.trim().replaceAll("\\s+", " ");
System.out.println(clean); // Hello World

// Normalize phone number format
String phone1 = "010-1234-5678";
String phone2 = "01012345678";
String phone3 = "010.1234.5678";

String normalized = phone1.replaceAll("[^0-9]", ""); // keep digits only
System.out.println(normalized); // 01012345678

// split(): split using regex
String data = "one1two2three3four";
String[] parts = data.split("[0-9]"); // split by digits
for (String p : parts) System.out.print(p + " ");
// one two three four
System.out.println();

// Check if a string contains only digits
System.out.println("12345".matches("[0-9]+")); // true
System.out.println("123a5".matches("[0-9]+")); // false
}
}

6. Practical Example 1: Email Validation

public class EmailValidator {

private static final String EMAIL_PATTERN =
"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";

public static boolean isValidEmail(String email) {
if (email == null || email.isBlank()) {
return false;
}
return email.matches(EMAIL_PATTERN);
}

public static String maskEmail(String email) {
if (!isValidEmail(email)) return "Invalid email";
int atIndex = email.indexOf('@');
String local = email.substring(0, atIndex);
String domain = email.substring(atIndex);
if (local.length() <= 2) return local.charAt(0) + "**" + domain;
return local.substring(0, 2) + "*".repeat(local.length() - 2) + domain;
}

public static void main(String[] args) {
String[] emails = {
"user@example.com",
"john.doe@company.co.uk",
"invalid-email",
"no-at-sign.com",
"@domain.com",
""
};

System.out.println("=== Email Validation ===");
for (String email : emails) {
System.out.printf("%-30s → valid: %b, masked: %s%n",
email, isValidEmail(email), maskEmail(email));
}
}
}

Output:

=== Email Validation ===
user@example.com → valid: true, masked: us**@example.com
john.doe@company.co.uk → valid: true, masked: jo******@company.co.uk
invalid-email → valid: false, masked: Invalid email
no-at-sign.com → valid: false, masked: Invalid email
@domain.com → valid: false, masked: Invalid email
→ valid: false, masked: Invalid email

7. Practical Example 2: String Statistics Analysis

public class StringAnalyzer {

public static void analyze(String text) {
if (text == null || text.isEmpty()) {
System.out.println("No text to analyze.");
return;
}

System.out.println("=== String Statistics Analysis ===");
System.out.println("Original: " + text);
System.out.println("Total length: " + text.length());
System.out.println("Length without spaces: " + text.replace(" ", "").length());

// Word count
String[] words = text.trim().split("\\s+");
System.out.println("Word count: " + words.length);

// Count uppercase / lowercase / digits / spaces
int upper = 0, lower = 0, digit = 0, space = 0;
for (char c : text.toCharArray()) {
if (Character.isUpperCase(c)) upper++;
else if (Character.isLowerCase(c)) lower++;
else if (Character.isDigit(c)) digit++;
else if (c == ' ') space++;
}
System.out.printf("Uppercase: %d, Lowercase: %d, Digits: %d, Spaces: %d%n",
upper, lower, digit, space);

// Longest word
String longest = "";
for (String word : words) {
if (word.length() > longest.length()) longest = word;
}
System.out.println("Longest word: " + longest + " (" + longest.length() + " chars)");

// Palindrome check
String reversed = new StringBuilder(text).reverse().toString();
System.out.println("Reversed: " + reversed);
System.out.println("Palindrome: " + text.equalsIgnoreCase(reversed));
}

public static void main(String[] args) {
analyze("Hello Java Programming 2026");
System.out.println();
analyze("racecar");
}
}

Output:

=== String Statistics Analysis ===
Original: Hello Java Programming 2026
Total length: 27
Length without spaces: 24
Word count: 4
Uppercase: 3, Lowercase: 18, Digits: 4, Spaces: 3
Longest word: Programming (11 chars)
Reversed: 6202 gnimmargorP avaJ olleH
Palindrome: false

=== String Statistics Analysis ===
Original: racecar
Total length: 7
Length without spaces: 7
Word count: 1
Uppercase: 0, Lowercase: 7, Digits: 0, Spaces: 0
Longest word: racecar (7 chars)
Reversed: racecar
Palindrome: true
Pro Tip: Performance

Repeatedly concatenating String with + inside a loop creates a new object every time, degrading performance. Always use StringBuilder when assembling strings inside loops. Simple concatenation of constant strings is automatically optimized by the compiler.

In the next chapter we will learn about Wrapper Classes, which wrap primitive types as objects.