Why substring() End Index Is Exclusive
Fast recall
Quick revision
Core answer
substring(start, end) uses a half-open range: start is included and end is excluded, so the result length is end - start. This supports empty ranges and adjacent slices without overlap: s.substring(0, k) and s.substring(k) divide the string. Both indexes must satisfy 0 <= start <= end <= length().Whether you know the convention and can quickly calculate correct indices without off-by-one errors.
Using the last character's index as end — e.g. substring(0, 4) on 'hello' gives 'hell' not 'hello'. To include index 4 you need substring(0, 5).
The exclusive-end convention comes from Dijkstra's 1982 paper 'Why numbering should start at zero' — he showed that half-open intervals [start, end) give the nicest arithmetic: no ±1 adjustments, no edge cases at boundaries.
Interview-ready guide
Interview answer
The character at the end index isn't included — substring(start, end) gives you everything from start up to end minus one. So "hello".substring(1, 3) is "el", just positions 1 and 2.
The reason this is worth internalizing is the arithmetic it buys you. Because the interval is half-open, end - start is exactly the length of what you get back — if I want five characters starting at index 2, that's substring(2, 7), no off-by-one fiddling. Adjacent slices also tile perfectly: substring(0, 5) and substring(5, 10) share no character and leave no gap. Java uses this same [start, end) convention everywhere — Arrays.copyOfRange(), List.subList() — so once it clicks, it transfers.
The mistake everyone makes once is "hello".substring(0, 4) expecting the whole word and getting "hell". To include the last character you go one past its index — substring(0, 5) — or just substring(0, s.length()). It feels odd that you can pass length() as an index, but that's the half-open design, and it's the same reasoning Dijkstra gave for why numbering should start at zero. When I want the rest of a string, I always reach for s.length() as the end.
Why substring() End Index Is Exclusive
Direct Answer
substring(start, end) uses a half-open range: start is included and end is excluded, so the result length is end - start. This supports empty ranges and adjacent slices without overlap: s.substring(0, k) and s.substring(k) divide the string. Both indexes must satisfy 0 <= start <= end <= length().
Code Example
public class SubstringDemo {
public static void main(String[] args) {
String s = "hello";
// 01234 — index positions
System.out.println(s.substring(1, 3)); // el — indices 1 and 2 only
System.out.println(s.substring(0, 5)); // hello — all 5 chars
System.out.println(s.substring(2)); // llo — from index 2 to end
// Length formula: end - start = result length
System.out.println(s.substring(1, 4).length()); // 3 (= 4 - 1)
// Common mistake
System.out.println(s.substring(0, 4)); // hell — NOT hello!
// To get 'hello': substring(0, 5) or substring(0, s.length())
// Practical use: extract domain from email
String email = "alice@example.com";
int at = email.indexOf('@'); // 5
String domain = email.substring(at + 1); // example.com
System.out.println(domain);
// Extract filename without extension
String file = "report.pdf";
int dot = file.lastIndexOf('.');
String name = file.substring(0, dot); // report
System.out.println(name);
}
}
Common Mistakes & Pitfalls
- Using the last character's index as
end—"hello".substring(0, 4)returns"hell", not"hello". To include index 4 you needend = 5, one past the last character. - Forgetting
endcan equallength()—substring(0, s.length())is perfectly legal and returns the whole string.s.length()is the one index you can pass that doesn't point at a real character. - Passing
end > length()orstart < 0— both throwStringIndexOutOfBoundsException.endmay equal the length but never exceed it. - Swapping the arguments —
start > endthrows the same exception even when both numbers are individually in range. - Assuming
substring()is free — current JDK implementations copy the selected content; this storage detail is not part of the String API contract, so on a hot path that slices a huge string repeatedly you pay a real copy cost each call (older JDKs shared the parent array and leaked it instead).
Check your understanding
Answer these without looking back at the article.
- What is the difference between substring(start) and substring(start, end)?
- What happens if you pass an out-of-bounds index to substring()?
- How do you extract a domain from an email string using substring and indexOf?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.