ErrorFixHub
Java

Java substring() Method: Complete Guide with Examples & Pitfalls

Master Java substring() with this complete guide. Learn syntax, examples, common pitfalls like StringIndexOutOfBoundsException, and performance best practices.

JAVA

Ever gotten an unexpected StringIndexOutOfBoundsException and spent hours debugging? You're not alone. The Java substring() method is a powerful tool in the String class, but its index rules can be tricky. I've lost count of how many times I've seen production code break because of an off-by-one error or an unvalidated index from user input. This guide covers everything from basic syntax to advanced performance considerations, so you can use substring() with confidence.

Text 'Hello' on a textured stone surface, ideal for greeting concepts.

Understanding the Java String substring() Method Syntax

Before we dive into examples, let's get the fundamentals straight. The java string substring method is part of the String class and returns a new string that is a portion of the original. Because strings in Java are immutable, this method always returns a brand new String object—it never modifies the original.

The Two Overloaded Signatures: substring(int beginIndex) and substring(int beginIndex, int endIndex)

The method comes in two flavors:

public String substring(int beginIndex);
public String substring(int beginIndex, int endIndex);

The single-parameter version extracts everything from beginIndex to the end of the string. The two-parameter version extracts from beginIndex (inclusive) to endIndex (exclusive). That last part trips up more developers than I can count—the character at endIndex is not included in the result.

Here's a quick demonstration:

String text = "Hello, World!";

String first = text.substring(7);      // Returns "World!"
String second = text.substring(0, 5);  // Returns "Hello"

System.out.println(first);   // Output: World!
System.out.println(second);  // Output: Hello

Notice how substring(0, 5) gives us "Hello" even though the string has 13 characters. The character at index 5 (which is the comma) is excluded.

Key Parameters and Their Behavior: beginIndex and endIndex

Let's break down the rules clearly:

ConditionBehavior
beginIndex is inclusiveThe character at this index is included in the result
endIndex is exclusiveThe character at this index is not included
beginIndex == endIndexReturns an empty string ("")
endIndex omittedSubstring extends to the end of the original string
beginIndex is 0Substring starts from the beginning
One edge case worth remembering: if beginIndex equals endIndex, you get an empty string. This isn't an error—it's perfectly valid. I've used this behavior intentionally in parsing logic where I needed to handle empty segments gracefully.
A man processes gravel in search of diamonds in a water-filled pit, Sierra Leone.

Practical Java substring() Examples for Everyday Coding

Theory is fine, but let's get our hands dirty with some real code. These are the patterns I actually use in production.

Extracting a Substring from a Start Index to the End

The single-parameter version is perfect when you need everything after a certain point. Here's a classic example—extracting the domain from an email address:

public class SubstringExample {
    public static void main(String[] args) {
        String email = "developer@example.com";
        
        // Find the position of '@'
        int atIndex = email.indexOf('@');
        
        // Extract everything after '@'
        String domain = email.substring(atIndex + 1);
        
        System.out.println("Email: " + email);
        System.out.println("Domain: " + domain);
    }
}

Output:

Email: developer@example.com
Domain: example.com

Notice how I used atIndex + 1 to skip past the @ character itself. This is a pattern you'll use constantly—combining indexOf() to find a position and substring() to extract from there.

Extracting a Substring Between Two Indices

When you know exactly where your substring starts and ends, the two-parameter version is your friend. Let's extract "World" from "Hello World!":

public class SubstringExample2 {
    public static void main(String[] args) {
        String message = "Hello World!";
        
        // Extract characters from index 6 (inclusive) to index 11 (exclusive)
        String word = message.substring(6, 11);
        
        System.out.println("Original: " + message);
        System.out.println("Extracted: " + word);
    }
}

Output:

Original: Hello World!
Extracted: World

Let me walk through the indices: H is at index 0, e at 1, l at 2, l at 3, o at 4, space at 5, W at 6, o at 7, r at 8, l at 9, d at 10, ! at 11. So substring(6, 11) grabs indices 6 through 10—"World". The ! at index 11 is excluded because endIndex is exclusive.

Real-World Use Case: Combining indexOf() and substring() to Extract Data

Here's where things get interesting. In real applications, you rarely know the exact indices ahead of time. You need to find them dynamically. Let's parse a value from a URL query string:

public class QueryStringParser {
    public static void main(String[] args) {
        String url = "https://api.example.com/users?id=12345&name=John&age=30";
        
        // Extract the value of the 'name' parameter
        String paramName = "name=";
        int startIndex = url.indexOf(paramName);
        
        if (startIndex != -1) {
            startIndex += paramName.length();  // Move past "name="
            int endIndex = url.indexOf('&', startIndex);
            
            // If there's no '&' after, the value extends to the end
            if (endIndex == -1) {
                endIndex = url.length();
            }
            
            String name = url.substring(startIndex, endIndex);
            System.out.println("Name: " + name);
        }
    }
}

Output:

Name: John

This pattern—find the start, find the end, extract the middle—is the backbone of simple text parsing. I've used variations of this code in countless projects, from parsing HTTP headers to extracting configuration values.

Avoiding the StringIndexOutOfBoundsException: Common Pitfalls and Solutions

If there's one thing I wish every Java developer knew, it's how to avoid this exception. It's the most common runtime error I see with substring().

When Does Java Throw StringIndexOutOfBoundsException?

The java substring indexoutofboundsexception occurs under three conditions:

  1. Negative beginIndex: You can't start before the beginning of the string.
  2. endIndex greater than string length: You can't go past the end.
  3. beginIndex greater than endIndex: The start can't come after the end.

Here's code that triggers each condition:

public class ExceptionExample {
    public static void main(String[] args) {
        String text = "Java";
        
        // Condition 1: Negative beginIndex
        // String result1 = text.substring(-1);  // Throws StringIndexOutOfBoundsException
        
        // Condition 2: endIndex > string length
        // String result2 = text.substring(0, 5);  // Throws StringIndexOutOfBoundsException
        
        // Condition 3: beginIndex > endIndex
        // String result3 = text.substring(3, 2);  // Throws StringIndexOutOfBoundsException
    }
}

I've commented out the problematic lines, but trust me—uncomment them and you'll see the exception. The error message in modern Java is actually quite helpful: StringIndexOutOfBoundsException: begin 3, end 2, length 4. It tells you exactly what went wrong.

Best Practices for Safe Substring Extraction

So how do we write code that doesn't blow up? Here are the strategies I recommend:

1. Validate indices before calling substring():

public static String safeSubstring(String text, int beginIndex, int endIndex) {
    if (text == null) {
        return null;
    }
    
    // Clamp indices to valid ranges
    int safeBegin = Math.max(0, beginIndex);
    int safeEnd = Math.min(text.length(), endIndex);
    
    // Ensure beginIndex <= endIndex
    if (safeBegin > safeEnd) {
        return "";
    }
    
    return text.substring(safeBegin, safeEnd);
}

2. Use StringUtils.substring() from Apache Commons Lang:

If you're already using Apache Commons Lang in your project, StringUtils.substring() handles all these edge cases gracefully:

import org.apache.commons.lang3.StringUtils;

String result = StringUtils.substring("Hello World", -5, 20);
// Returns "Hello World" - no exception thrown

The library version handles null strings, clamps out-of-bounds indices, and swaps reversed indices. It's a solid choice if you don't want to roll your own validation.

3. Always check indexOf() results:

When using indexOf() to find positions, always check if it returned -1 (not found) before using the result in substring():

int start = text.indexOf("start");
if (start != -1) {
    String extracted = text.substring(start);
} else {
    // Handle the "not found" case
}

Java substring() vs. subSequence() vs. split(): Choosing the Right Tool

One of the most common questions I get is when to use substring() versus its alternatives. Each has its place.

substring() vs. subSequence(): What's the Difference?

The subSequence() method returns a CharSequence, while substring() returns a String. In practice, the object returned by subSequence() is often the same String object, but the API contract is different:

String text = "Hello World";

String sub1 = text.substring(0, 5);        // Returns a String
CharSequence sub2 = text.subSequence(0, 5); // Returns a CharSequence

System.out.println(sub1.getClass());  // class java.lang.String
System.out.println(sub2.getClass());  // class java.lang.String (in practice)

My recommendation? Use substring() when you need a String result. It's more direct and avoids potential type conversion issues. The only time I use subSequence() is when I'm working with an API that specifically requires a CharSequence.

substring() vs. split(): When to Use Which for Parsing

This is a performance question as much as a functionality question. The split() method uses regular expressions, making it powerful but potentially slow:

// Using split() - flexible but slower
String[] parts = "user:john:30".split(":");
String name = parts[1];

// Using indexOf() + substring() - faster for simple delimiters
String data = "user:john:30";
int firstColon = data.indexOf(':');
int secondColon = data.indexOf(':', firstColon + 1);
String name = data.substring(firstColon + 1, secondColon);

For simple, fixed-position extraction, substring() is almost always faster. The split() method has to compile a regex pattern and allocate an array for the results. In performance-critical code—like parsing thousands of log lines per second—that overhead adds up.

However, split() shines when you need pattern-based parsing. If you're splitting on complex delimiters like "\\s+\\|\\s+", trying to do that with indexOf() and substring() would be a nightmare.

Performance and Memory: The Hidden Cost of substring() in Java 7+

Here's something that surprises many developers: the substring() method didn't always work the way it does today. Understanding the history helps you write better code.

How substring() Works Under the Hood: From Shared Character Arrays to Copying

In Java 6 and earlier, substring() shared the original string's character array. The new String object stored a reference to the same array, along with an offset and count to define the substring boundaries. This was fast—no copying needed—but it had a nasty side effect: memory leaks.

Imagine you had a massive string (say, a 10MB log file) and you extracted a tiny substring (like a 10-character ID). The original 10MB array would stay in memory as long as the tiny substring was referenced, because they shared the same underlying array. The garbage collector couldn't free the memory.

Java 7 changed this. Now, substring() creates a new character array and copies the relevant characters. This eliminates the memory leak but adds a small copy cost. For most applications, this is a worthwhile trade-off.

Best Practices for High-Performance String Manipulation

Given how substring() works in modern Java, here are my performance recommendations:

1. Avoid creating substrings in tight loops:

// Anti-pattern: Creating many substring objects
for (int i = 0; i < data.length(); i += 10) {
    String chunk = data.substring(i, i + 10);
    process(chunk);
}

// Better: Operate on the original string with indices
for (int i = 0; i < data.length(); i += 10) {
    process(data, i, i + 10);  // Pass indices instead of creating substrings
}

2. Use StringBuilder for repeated concatenation:

// Anti-pattern: Building a string with substring + concatenation
String result = "";
for (String part : parts) {
    result = result + part.substring(0, 2);  // Creates many intermediate strings
}

// Better: Use StringBuilder
StringBuilder builder = new StringBuilder();
for (String part : parts) {
    builder.append(part, 0, 2);
}
String result = builder.toString();

3. Consider charAt() and regionMatches() for inspection:

If you only need to check what a character is, don't create a substring:

// Anti-pattern: Creating a substring just to check a character
if (text.substring(0, 1).equals("A")) { ... }

// Better: Use charAt()
if (text.charAt(0) == 'A') { ... }

Frequently Asked Questions

Is the endIndex in Java substring() inclusive or exclusive?

The endIndex is exclusive. For example, "Hello World".substring(0, 5) returns "Hello"—the character at index 5 (which is a space) is not included. Think of it as "start at beginIndex and go up to, but not including, endIndex."

How to remove the last character from a string in Java using substring()?

Use substring(0, str.length() - 1):

String text = "Hello!";
String withoutLastChar = text.substring(0, text.length() - 1);
System.out.println(withoutLastChar);  // Output: Hello

Be careful with empty strings—"".substring(0, -1) will throw an exception. Always check str.length() > 0 first.

What is the difference between substring() and subSequence() in Java?

substring() returns a String, while subSequence() returns a CharSequence. In practice, subSequence() often returns the same String object, but the API contract is different. Use substring() when you need a String result—it's more direct and avoids potential type conversion issues.

How to extract a substring between two specific characters in Java?

Use indexOf() to find the positions, then substring() to extract:

String text = "Hello [world]!";
int start = text.indexOf('[') + 1;
int end = text.indexOf(']');
String extracted = text.substring(start, end);
System.out.println(extracted);  // Output: world

Conclusion

The Java substring() method is deceptively simple—two overloads, a few rules, and you're done. But as we've seen, the devil is in the details. The inclusive beginIndex and exclusive endIndex rule is the source of countless off-by-one errors. The StringIndexOutOfBoundsException lurks around every corner when you're not validating your indices. And the performance characteristics have changed significantly since Java 7.

Here's what I want you to remember:

  • Master the index rules: beginIndex is inclusive, endIndex is exclusive. When in doubt, write a quick test.
  • Always validate: Whether it's user input or the result of indexOf(), check your indices before calling substring().
  • Choose the right tool: substring() for fixed-position extraction, split() for pattern-based parsing, and StringBuilder for building strings.
  • Be mindful of performance: In modern Java, substring() copies data. Avoid creating unnecessary substrings in performance-critical code.

Now it's your turn. Try the examples, experiment with your own string manipulation scenarios, and see what you can build. If you have a specific use case or a tricky bug you'd like help with, leave a comment below—I'd love to hear about it.

Related Posts