Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Java has no standard-library method specified as an exact equivalent of JavaScript’s encodeURIComponent(). For matching output, encode the input’s UTF-8 bytes, leave only JavaScript’s permitted characters unescaped, and percent-encode the rest. URLEncoder is for form encoding, where spaces become +, so it is not a drop-in replacement.
A Java implementation that matches JavaScript
This method matches encodeURIComponent() for valid Java strings: it uses UTF-8, uppercase hexadecimal escapes, and the same unescaped character set. It also rejects unpaired UTF-16 surrogates, which JavaScript reports as a URIError rather than silently replacing.
import java.nio.charset.StandardCharsets;
public final class JavaScriptUriEncoding {
private static final char[] HEX = "0123456789ABCDEF".toCharArray();
private JavaScriptUriEncoding() {
}
public static String encodeURIComponent(String input) {
if (input == null) {
throw new NullPointerException("input");
}
validateUtf16(input);
byte[] bytes = input.getBytes(StandardCharsets.UTF_8);
StringBuilder result = new StringBuilder(bytes.length);
for (byte value : bytes) {
int b = value & 0xFF;
if (isSafe(b)) {
result.append((char) b);
} else {
result.append('%')
.append(HEX[b >>> 4])
.append(HEX[b & 0x0F]);
}
}
return result.toString();
}
private static boolean isSafe(int b) {
return (b >= 'A' && b <= 'Z')
|| (b >= 'a' && b <= 'z')
|| (b >= '0' && b <= '9')
|| b == '-' || b == '_' || b == '.'
|| b == '!' || b == '~' || b == '*'
|| b == ''' || b == '(' || b == ')';
}
private static void validateUtf16(String input) {
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (Character.isHighSurrogate(c)) {
if (i + 1 >= input.length()
|| !Character.isLowSurrogate(input.charAt(i + 1))) {
throw new IllegalArgumentException(
"Input contains a lone high surrogate at index " + i);
}
i++; // Consume the matching low surrogate.
} else if (Character.isLowSurrogate(c)) {
throw new IllegalArgumentException(
"Input contains a lone low surrogate at index " + i);
}
}
}
}
The allowlist is A-Z, a-z, 0-9, and - _ . ! ~ * ' ( ). Every other character is first represented as UTF-8 bytes, then each byte is written as % followed by two uppercase hexadecimal digits. This is the character set and encoding behavior documented for JavaScript’s encodeURIComponent().
For example:
String encoded = JavaScriptUriEncoding.encodeURIComponent("A B&日本語/?.!~*'()");
System.out.println(encoded);
// A%20B%26%E6%97%A5%E6%9C%AC%E8%AA%9E%2F%3F.!~*'()
Spaces become %20; delimiters such as &, =, /, ?, and # are escaped. Japanese characters are encoded as their UTF-8 bytes. The punctuation !~*'() remains unescaped.
#1 Best Overall
Why URLEncoder differs
URLEncoder implements application/x-www-form-urlencoded, the format used by HTML form data and some HTTP APIs. It is not a generic URI-component encoder. In form encoding, a space is represented by +.
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
String value = "a b+c&d";
System.out.println(URLEncoder.encode(value, StandardCharsets.UTF_8));
// a+b%2Bc%26d
JavaScript produces a%20b%2Bc%26d for the same input. The plus sign in the original value is escaped in both results; the difference is how a space is represented.
Rank #2
Use URLEncoder.encode(value, StandardCharsets.UTF_8) when the receiver expects form encoding. The Charset overload is available since Java 10. For JavaScript-compatible component output, use the explicit encoder above. Replacing + with %20 after form encoding may suffice for controlled, well-formed values, but it does not address malformed-surrogate behavior and disguises the distinction between two formats.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Unicode and malformed UTF-16
Java and JavaScript strings use UTF-16 code units. A character outside the Basic Multilingual Plane, such as 😀, is represented by a valid surrogate pair. Its UTF-8 bytes are F0 9F 98 80, so its encoded form is %F0%9F%98%80.
An unpaired high or low surrogate is different: JavaScript’s encoder throws for it. Java’s ordinary getBytes(UTF_8) conversion can replace malformed input, so validating the string before conversion is necessary for this behavior. The implementation throws IllegalArgumentException for these inputs; Java does not have JavaScript’s URIError type. See MDN’s explanation of malformed URI sequences.
This method accepts a Java String; it does not reproduce JavaScript’s dynamic argument coercion. JavaScript stringifies values such as numbers, booleans, null, and undefined before encoding. A Java method with a String parameter does not do that, and this implementation deliberately rejects null. If an application needs coercion, define it explicitly at the call site rather than assuming the encoder provides it.
Rank #4
Encode component values, not query syntax
encodeURIComponent() is intended for a component value, not for a complete URL. Encode each query value separately, then assemble the delimiters:
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →String query = "name="
+ JavaScriptUriEncoding.encodeURIComponent("Jack & Jill")
+ "&city="
+ JavaScriptUriEncoding.encodeURIComponent("Boston");
System.out.println(query);
// name=Jack%20%26%20Jill&city=Boston
The ampersand inside the name becomes %26, while the ampersand separating parameters stays literal. Encoding the complete query as one value would escape its separators; leaving a value unencoded can let characters such as & or = alter how it is parsed.
Best Value
For building a complete URI, use a URI or framework builder that understands its components and verify the escaping rules for the specific builder and component. A path segment, query value, and whole URI do not necessarily use the same rules. java.net.URI models and parses URI structure; it is not a drop-in encodeURIComponent() function.
Do not confuse JavaScript output with stricter RFC 3986 escaping
JavaScript leaves ! ' ( ) * unescaped. Some applications instead require a stricter RFC 3986 component representation that escapes those five characters as %21, %27, %28, %29, and %2A. That is a different output target; do not change those characters if compatibility with JavaScript is the requirement. MDN describes this distinction in its reference.
Test the cases that reveal mismatches
A useful parity test covers spaces, reserved characters, multibyte text, emoji, the safe punctuation, and malformed surrogate inputs—not just plain ASCII.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
class JavaScriptUriEncodingTest {
@Test
void matchesJavaScriptForMixedInput() {
assertEquals(
"A%20B%26%E6%97%A5%E6%9C%AC%E8%AA%9E%2F%3F.!~*'()",
JavaScriptUriEncoding.encodeURIComponent("A B&日本語/?.!~*'()"));
}
@Test
void encodesPlusAndEmoji() {
assertEquals("%2B", JavaScriptUriEncoding.encodeURIComponent("+"));
assertEquals("%F0%9F%98%80", JavaScriptUriEncoding.encodeURIComponent("😀"));
}
@Test
void rejectsUnpairedSurrogates() {
assertThrows(IllegalArgumentException.class,
() -> JavaScriptUriEncoding.encodeURIComponent("uD800"));
assertThrows(IllegalArgumentException.class,
() -> JavaScriptUriEncoding.encodeURIComponent("uDFFF"));
}
}
Additional expected results include hello world → hello%20world, a&b=c → a%26b%3Dc, /path?x=1#top → %2Fpath%3Fx%3D1%23top, é → %C3%A9, and !~*'() → !~*'().
Decoding is a separate choice
JavaScript’s decodeURIComponent("+") returns a literal plus. Java’s URLDecoder is for form-encoded data and turns + into a space, so it is not an exact counterpart. It pairs with URLEncoder, not with JavaScript’s component encoder. If you need exact decoding parity, use a decoder designed and tested for that requirement rather than substituting URLDecoder.
Quick Recap
Choose by the required wire format
| Requirement | Use |
|---|---|
Match JavaScript encodeURIComponent() |
The explicit UTF-8 component encoder above |
| Encode HTML form data | URLEncoder with UTF-8 |
| Decode form data | URLDecoder with UTF-8 |
| Assemble a complete URI | A URI or framework builder, checked for the relevant component’s rules |
| Strict RFC 3986 component output | An encoder intentionally implementing that target, not JavaScript compatibility |
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

