Skip to content
Java strings 5 min read

Substring

Extracting a portion of a string is one of the most common operations in Java, and the substring() method is the primary tool for the job. It lets you “slice” out any section of a String by specifying character positions, and it returns a brand-new String containing only those characters.

The Two Overloads

String.substring() comes in two flavors:

MethodWhat It Returns
substring(int beginIndex)Characters from beginIndex to the end of the string
substring(int beginIndex, int endIndex)Characters from beginIndex up to, but not including, endIndex

Both are zero-indexed — the first character is at index 0.

String text = "Hello, Java!";

// From index 7 to end
String s1 = text.substring(7);
System.out.println(s1);  // Java!

// From index 0 (inclusive) to 5 (exclusive)
String s2 = text.substring(0, 5);
System.out.println(s2);  // Hello

Output:

Java!
Hello

Tip: Think of the indexes as pointing between characters, like a cursor position. Index 0 is before the first character, index 1 is after the first character, and so on. substring(2, 5) captures what’s between cursor 2 and cursor 5.

Understanding the Index Rules

The golden rule for substring(beginIndex, endIndex):

  • beginIndexinclusive (the character at this position is included)
  • endIndexexclusive (the character at this position is NOT included)
  • The resulting string length is always endIndex - beginIndex
String word = "submarine";
//             012345678

System.out.println(word.substring(0, 3));  // sub  (length 3)
System.out.println(word.substring(3, 6));  // mar  (length 3)
System.out.println(word.substring(6));     // ine  (length 3)

Output:

sub
mar
ine

Edge Cases and Special Behaviors

Extracting the Whole String

Calling substring(0) or substring(0, text.length()) returns a string equal to the original. In Java 7+, it actually returns the same object reference as an optimization in some implementations.

String s = "hello";
String full = s.substring(0);
System.out.println(full.equals(s));  // true

Empty String

If beginIndex == endIndex, you get an empty string — not null, not an error.

String s = "hello";
String empty = s.substring(3, 3);
System.out.println(empty.isEmpty());  // true
System.out.println(empty.length());   // 0

Last N Characters

A handy pattern for extracting the tail of a string:

String filename = "report_2024.pdf";
String extension = filename.substring(filename.length() - 3);
System.out.println(extension);  // pdf

StringIndexOutOfBoundsException

If your indexes are out of range, Java throws StringIndexOutOfBoundsException (a subclass of IndexOutOfBoundsException). This is a common runtime bug, so always validate indexes before using them.

Common causes:

MistakeExampleResult
beginIndex is negatives.substring(-1)Exception
endIndex > s.length()s.substring(0, 99) on a 5-char stringException
beginIndex > endIndexs.substring(5, 2)Exception
beginIndex > s.length()s.substring(10) on a 5-char stringException
String s = "Java";

try {
    String bad = s.substring(0, 10); // endIndex > length
} catch (StringIndexOutOfBoundsException e) {
    System.out.println("Caught: " + e.getMessage());
}

Output:

Caught: Range [0, 10) out of bounds for length 4

Warning: Always guard dynamic index calculations with bounds checks or wrap in a try-catch when the input is not guaranteed. A simple if (end <= s.length()) check can save you from a crash in production.

Practical Patterns

Removing a Prefix or Suffix

String url = "https://example.com";

// Remove "https://"
if (url.startsWith("https://")) {
    String host = url.substring("https://".length());
    System.out.println(host);  // example.com
}

Extracting Between Two Delimiters

String data = "name=Alice;age=30";

int start = data.indexOf('=') + 1;
int end   = data.indexOf(';');
String name = data.substring(start, end);
System.out.println(name);  // Alice

Combining with indexOf

indexOf() and substring() are natural partners. You find the position of a separator, then slice around it.

String email = "[email protected]";
int atSign = email.indexOf('@');

String localPart  = email.substring(0, atSign);        // user
String domainPart = email.substring(atSign + 1);        // example.com

System.out.println("User:   " + localPart);
System.out.println("Domain: " + domainPart);

Output:

User:   user
Domain: example.com

Tip: Java 11 added stripLeading(), stripTrailing(), and strip() for whitespace trimming. For more general extraction needs, consider Regular Expressions with Matcher.group().

substring() vs Other Slicing Approaches

ApproachUse When
substring(begin, end)You know exact character positions
split(regex)You want to split on a delimiter
charAt(i)You need a single character
String.format() / printfYou’re building a new string from parts
Pattern / MatcherComplex pattern-based extraction

Under the Hood

Java 6: Shared Backing Array (The Old Way)

In Java 6 and earlier, substring() did not copy characters. Instead, it returned a new String object that shared the original’s internal char[] array, using offset and count fields to mark the slice. This was fast to create but caused memory leaks: a tiny substring held a reference to the entire original array, preventing it from being garbage collected.

Java 7u6+: Full Copy (The Current Behavior)

Starting with Java 7 update 6 (and continuing through Java 21), substring() always creates a new, independent copy of the relevant characters. The trade-off:

  • No memory leak — the original string can be collected if unreferenced
  • Slightly more allocation cost for large strings, but far safer for long-lived code

CharSequence and Interoperability

String implements CharSequence, so substring() results plug directly into APIs that accept CharSequence — like StringBuilder.append(), Pattern.matcher(), and more. You rarely need to wrap the result.

Performance Tip for Heavy Slicing

If you’re extracting many substrings from a large string (e.g., parsing a log file line by line), using a BufferedReader with direct line reads is more efficient than repeated substring() calls on a giant string. For in-memory parsing, consider StringTokenizer or split() for delimiter-based work, or a Scanner for token-based reading (see Scanner).

Quick Reference

String s = "abcdefgh";
//          01234567

s.substring(2);       // "cdefgh"   — from index 2 to end
s.substring(2, 5);    // "cde"      — index 2 inclusive to 5 exclusive
s.substring(0, 1);    // "a"        — just the first character
s.substring(7, 8);    // "h"        — just the last character
s.substring(0);       // "abcdefgh" — full copy
s.substring(4, 4);    // ""         — empty string
  • String Methods — full reference for all built-in String methods including indexOf, replace, and trim
  • Strings — start here for a complete overview of how Strings work in Java
  • String Comparison — how to compare strings correctly with equals() and compareTo()
  • Regular Expressions — powerful pattern-based extraction when substring() isn’t enough
  • StringBuffer and StringBuilder — mutable alternatives when you need to build or modify strings efficiently
  • StringTokenizer — a lightweight alternative for splitting strings by delimiter
Last updated June 13, 2026
Was this helpful?