Regular expressions in Java
Two classes do the work, and three String methods hide them for simple cases.
The String shortcuts
For one-off use:
"2026-08-28".matches("\\d{4}-\\d{2}-\\d{2}"); // true
text.replaceAll("\\s+", " "); // collapse whitespace
text.split(",\\s*"); // comma, optional space
All three take a regex. replaceFirst exists too. String.replace — no “All” — takes literal text, not a pattern, which is the one to reach for when you do not want regex behaviour.
Pattern and Matcher
When you need the matched text, or the same pattern more than once:
Pattern p = Pattern.compile("(\\w+)@(\\w+\\.\\w+)");
Matcher m = p.matcher(input);
while (m.find()) {
System.out.println("user: " + m.group(1));
System.out.println("host: " + m.group(2));
}
Pattern is the compiled expression, Matcher is one attempt against one input. Patterns are immutable and thread-safe; matchers are neither, so create a matcher per use.
matches, find, lookingAt
The distinction that causes most confusion:
matches()— the entire input must matchfind()— searches for the next match anywhere, and can be called repeatedlylookingAt()— must match at the start, need not reach the end
String.matches is whole-string, which is why a pattern that works in an online tester often “fails” in Java: the tester was doing find.
Groups
Parentheses capture. Group 0 is the whole match; the rest are numbered by opening parenthesis, left to right. Named groups are clearer when there are several:
Pattern p = Pattern.compile("(?<year>\\d{4})-(?<month>\\d{2})");
if (m.matches()) {
String year = m.group("year");
}
Calling group() before a successful find() or matches() throws IllegalStateException — the matcher has no match to report on.
Compile once
private static final Pattern EMAIL = Pattern.compile("...");
String.matches and replaceAll recompile every call. Inside a loop over a large input that dominates the cost. Hoisting the pattern to a static final field is the standard fix and costs nothing.
Flags
Pattern.compile("hello", Pattern.CASE_INSENSITIVE);
Pattern.compile("^\\d+$", Pattern.MULTILINE); // ^ and $ per line
Pattern.compile("a.b", Pattern.DOTALL); // . matches newline
Catastrophic backtracking
Nested quantifiers over overlapping alternatives — (a+)+b is the textbook shape — can take exponential time on input that nearly matches. If a regex ever runs against untrusted input, keep it simple, and prefer a parser for anything structured.