replace() and replaceAll()
replace() swaps every occurrence of literal text; replaceAll() and replaceFirst() treat the first argument as a regular expression.
public String replaceAll(String regex, String replacement)- Added in Java Java 1.4
Parameters
| Parameter | Type | Description |
|---|---|---|
$regex | String | The regular expression to match. For replace(), the literal target text instead. |
$replacement | String | The replacement. In replaceAll and replaceFirst, $n refers to a capture group and backslash escapes the next char. |
Return value
StringA new String with every match of regex replaced. replace(CharSequence, CharSequence) does the same for literal text.
Errors
PatternSyntaxException if regex is invalid; IllegalArgumentException or IndexOutOfBoundsException if replacement refers to a missing group or ends with a lone backslash; NullPointerException for null arguments.
Changelog
- PHP Java 5replace(CharSequence target, CharSequence replacement) was added for literal text. replace(char, char) has existed since Java 1.0.
String has four replace methods and the names hide the most important difference.
| Method | First argument | Replaces |
|---|---|---|
replace(char, char) | a char | every occurrence |
replace(CharSequence, CharSequence) | literal text | every occurrence |
replaceAll(String, String) | a regular expression | every match |
replaceFirst(String, String) | a regular expression | the first match |
So replace already replaces all occurrences. The "All" in replaceAll is about regex, not about how many. All of them return a new String; the original never changes, so assign the result.
The regex traps
In replaceAll, characters such as . | $ ( ) [ ] * + ? and backslash have special meaning. "a.b".replaceAll(".", "/") gives ///, because . matches any char. If you want literal text, use replace, or wrap the pattern in Pattern.quote.
The replacement is special too. $1 inserts capture group 1 and a backslash escapes the next char. Inserting a price such as $25.00 or a Windows path with replaceAll fails or corrupts the text. Wrap user data in Matcher.quoteReplacement, or use replace when no pattern is needed.
Performance
replaceAll compiles its pattern on every call. That is fine for a one-off, but in a request path that runs thousands of times a second, hold a static final Pattern and call pattern.matcher(s).replaceAll(...). replace with literal text does not use regex at all and is the fastest choice.
Not a security tool
Stripping <script> or quotes with replace is not escaping. Attackers nest or encode payloads to survive one pass. Use a proper HTML encoder when writing HTML, and a PreparedStatement for SQL.
Example
When to use it
- Building URL slugs for blog posts or product pages from their titles.
- Masking email addresses and tokens in log lines before they are written.
- Collapsing repeated whitespace in a search query before it reaches the database.
Frequently asked questions
What is the difference between replace and replaceAll in Java?
Both replace every occurrence. replace treats its arguments as literal text; replaceAll treats the first as a regular expression and the second as a replacement pattern where $ and backslash are special.
Why does replaceAll(".", ...) replace every character?
In a regular expression . matches any char. Escape it as "\\.", use Pattern.quote("."), or use replace(".", ...) for literal text.
How do I replace only the first occurrence?
Use replaceFirst(regex, replacement). For literal text, wrap the target in Pattern.quote and the replacement in Matcher.quoteReplacement.
More examples
A dollar sign in the replacement
replaceAll reads $2 in the replacement as a reference to capture group 2, which does not exist. replace treats the value literally, and Matcher.quoteReplacement escapes it when you do need a regex.
void main() {
String template = "Total: AMOUNT";
String amount = "$25.00";
IO.println(template.replace("AMOUNT", amount));
try {
IO.println(template.replaceAll("AMOUNT", amount));
} catch (IndexOutOfBoundsException e) {
IO.println("replaceAll failed: " + e.getMessage());
}
IO.println(template.replaceAll("AMOUNT", Matcher.quoteReplacement(amount)));
}
// Output:
// Total: $25.00
// replaceAll failed: No group 2
// Total: $25.00Rewriting with capture groups
Groups let the replacement keep part of each match: the domain stays and the local part is hidden. Masking personal data this way before logging is a common requirement under data protection rules.
void main() {
String log = "[email protected] ip=10.0.0.7 [email protected]";
IO.println(log.replaceAll("([\\w.]+)@([\\w.]+)", "***@$2"));
String date = "2026-10-13";
IO.println(date.replaceAll("(\\d{4})-(\\d{2})-(\\d{2})", "$3/$2/$1"));
}
// Output:
// user=***@example.com ip=10.0.0.7 user=***@test.org
// 13/10/2026A precompiled Pattern for a hot path
Compiling the patterns once avoids recompiling them on every request, which String.replaceAll would do. Each run of non-slug chars becomes one dash, then a leading or trailing dash is removed.
static final Pattern NON_SLUG = Pattern.compile("[^a-z0-9]+");
static final Pattern EDGE_DASH = Pattern.compile("^-|-$");
String slugify(String title) {
String lower = title.strip().toLowerCase(Locale.ROOT);
String dashed = NON_SLUG.matcher(lower).replaceAll("-");
return EDGE_DASH.matcher(dashed).replaceAll("");
}
void main() {
IO.println(slugify("Spring Boot 4.1: What's New?"));
IO.println(slugify(" --Hello, World!-- "));
}
// Output:
// spring-boot-4-1-what-s-new
// hello-world
Discussion