Java
UTF8
String Conversion
Byte Arrays
Programming Tips

How to convert Strings to and from UTF8 byte arrays in Java

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

To convert a Java String to a UTF-8 byte array, call str.getBytes(StandardCharsets.UTF_8). To convert back, use new String(bytes, StandardCharsets.UTF_8). Using the StandardCharsets constant instead of the string "UTF-8" avoids checked exceptions and is the recommended approach since Java 7. This article covers both the modern and legacy APIs, explains what happens under the hood during encoding and decoding, and shows patterns for handling edge cases in production code.

String to UTF-8 Byte Array

Modern Approach (Java 7+)

The cleanest way to encode a String into UTF-8 bytes uses StandardCharsets.UTF_8:

java
1import java.nio.charset.StandardCharsets;
2
3String text = "Hello, UTF-8 World!";
4byte[] bytes = text.getBytes(StandardCharsets.UTF_8);

This version has no checked exceptions. StandardCharsets.UTF_8 is a Charset object guaranteed to be available on every JVM, so UnsupportedEncodingException is impossible.

Legacy Approach (Pre-Java 7)

The older API takes a charset name as a string:

java
1try {
2    byte[] bytes = text.getBytes("UTF-8");
3} catch (java.io.UnsupportedEncodingException e) {
4    // This never actually happens for UTF-8,
5    // but the compiler requires handling it
6    throw new RuntimeException(e);
7}

The catch block is boilerplate that exists only to satisfy the compiler. Since UTF-8 is required by the Java specification, this exception cannot be thrown in practice. Prefer the StandardCharsets version to eliminate this noise.

UTF-8 Byte Array to String

Modern Approach

java
1import java.nio.charset.StandardCharsets;
2
3byte[] bytes = {72, 101, 108, 108, 111};
4String text = new String(bytes, StandardCharsets.UTF_8);
5// text = "Hello"

Legacy Approach

java
1try {
2    String text = new String(bytes, "UTF-8");
3} catch (java.io.UnsupportedEncodingException e) {
4    throw new RuntimeException(e);
5}

The same reasoning applies: prefer StandardCharsets.UTF_8 for cleaner code.

Partial Array Conversion

When you only need to decode a portion of the byte array:

java
// Decode bytes from index 2 to index 5 (exclusive)
String partial = new String(bytes, 2, 3, StandardCharsets.UTF_8);

This is useful when reading from buffers or protocols where the UTF-8 payload starts at an offset.

API Comparison

OperationModern APILegacy APIException Handling
String to bytesstr.getBytes(StandardCharsets.UTF_8)str.getBytes("UTF-8")None required vs UnsupportedEncodingException
Bytes to Stringnew String(bytes, StandardCharsets.UTF_8)new String(bytes, "UTF-8")None required vs UnsupportedEncodingException
Partial decodenew String(bytes, off, len, StandardCharsets.UTF_8)new String(bytes, off, len, "UTF-8")None required vs UnsupportedEncodingException

How UTF-8 Encoding Works Internally

Java Strings are internally stored as sequences of char values using UTF-16 encoding (or compact Latin-1 in Java 9+ with compact strings enabled). When you call getBytes(StandardCharsets.UTF_8), the JVM converts each Unicode code point from its internal representation to its UTF-8 byte sequence.

UTF-8 is a variable-width encoding. The number of bytes per character depends on the Unicode code point:

Code Point RangeUTF-8 BytesExample
U+0000 to U+007F1 byteASCII characters (A, z, 5)
U+0080 to U+07FF2 bytesLatin extensions, Greek, Cyrillic
U+0800 to U+FFFF3 bytesCJK characters, most symbols
U+10000 to U+10FFFF4 bytesEmoji, rare scripts

This means that "Hello".getBytes(StandardCharsets.UTF_8).length is 5 (one byte per ASCII character), but a string containing emoji or CJK characters will produce more bytes than characters:

java
1String emoji = "😀"; // Grinning face emoji
2byte[] emojiBytes = emoji.getBytes(StandardCharsets.UTF_8);
3System.out.println(emoji.length());        // 2 (UTF-16 surrogate pair)
4System.out.println(emojiBytes.length);     // 4 (UTF-8 encoding)
5
6String chinese = "你好"; // Chinese characters
7byte[] chineseBytes = chinese.getBytes(StandardCharsets.UTF_8);
8System.out.println(chinese.length());      // 2
9System.out.println(chineseBytes.length);   // 6 (3 bytes per character)

Using CharsetEncoder for Fine-Grained Control

When you need to handle malformed input or control error behavior, CharsetEncoder gives you more options than getBytes():

java
1import java.nio.ByteBuffer;
2import java.nio.CharBuffer;
3import java.nio.charset.CharsetEncoder;
4import java.nio.charset.CodingErrorAction;
5import java.nio.charset.StandardCharsets;
6
7CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder()
8    .onMalformedInput(CodingErrorAction.REPLACE)
9    .onUnmappableCharacter(CodingErrorAction.REPLACE);
10
11CharBuffer input = CharBuffer.wrap("Hello \uD800 World");  // Lone surrogate
12ByteBuffer output = encoder.encode(input);
13byte[] bytes = new byte[output.remaining()];
14output.get(bytes);

The CodingErrorAction options are:

  • REPORT: throw CharacterCodingException on invalid input
  • REPLACE: substitute the replacement character (U+FFFD)
  • IGNORE: silently drop invalid characters

The default behavior of getBytes() is REPLACE, which means malformed surrogate pairs are silently converted to the UTF-8 replacement character bytes without any warning.

Reading and Writing UTF-8 Files

For file I/O, the Files API in java.nio.file handles charset conversion directly:

java
1import java.nio.file.Files;
2import java.nio.file.Path;
3import java.nio.charset.StandardCharsets;
4
5// Write UTF-8
6Path path = Path.of("output.txt");
7Files.writeString(path, "Hello, World!", StandardCharsets.UTF_8);
8
9// Read UTF-8
10String content = Files.readString(path, StandardCharsets.UTF_8);
11
12// Read as byte array and convert manually
13byte[] rawBytes = Files.readAllBytes(path);
14String manual = new String(rawBytes, StandardCharsets.UTF_8);

For streaming large files, use BufferedReader and BufferedWriter with explicit charset:

java
1import java.io.*;
2import java.nio.charset.StandardCharsets;
3
4try (BufferedReader reader = new BufferedReader(
5        new InputStreamReader(new FileInputStream("input.txt"), StandardCharsets.UTF_8))) {
6    String line;
7    while ((line = reader.readLine()) != null) {
8        // Process each line
9    }
10}

Network and Serialization Patterns

When sending strings over the network or serializing to a byte protocol, always be explicit about encoding:

java
1import java.nio.charset.StandardCharsets;
2
3// Encoding for HTTP body
4String jsonBody = "{\"name\": \"test\"}";
5byte[] bodyBytes = jsonBody.getBytes(StandardCharsets.UTF_8);
6httpConnection.setRequestProperty("Content-Length", String.valueOf(bodyBytes.length));
7httpConnection.getOutputStream().write(bodyBytes);
8
9// Decoding from InputStream
10byte[] responseBytes = httpConnection.getInputStream().readAllBytes();
11String response = new String(responseBytes, StandardCharsets.UTF_8);

Note that Content-Length must be the byte length, not the string character length. For ASCII-only content these are the same, but for multibyte characters they differ.

Common Pitfalls

Using the platform's default charset by calling str.getBytes() without a charset argument is the most dangerous mistake. The default charset varies by operating system and JVM configuration. Code that works on a developer's macOS machine (default UTF-8) can produce corrupted data on a Windows server (default Windows-1252).

Confusing string length with byte length causes buffer overflows and truncation bugs. "Hello".length() returns 5, and its UTF-8 byte array is also 5 bytes. But a string with multibyte characters has a byte length larger than its character count.

Catching UnsupportedEncodingException and swallowing it silently hides a code smell. If you see this catch block, switch to StandardCharsets.UTF_8 and eliminate the exception entirely.

Assuming all byte arrays are valid UTF-8 leads to silent data corruption. When decoding bytes from an untrusted source, use CharsetDecoder with CodingErrorAction.REPORT to detect invalid sequences rather than silently replacing them.

Mixing charsets between encoding and decoding produces garbled text (mojibake). If you encode with UTF-8, you must decode with UTF-8. This is especially common in systems where one service writes files in one encoding and another reads them assuming a different one.

Summary

  • Use str.getBytes(StandardCharsets.UTF_8) to convert a String to UTF-8 bytes.
  • Use new String(bytes, StandardCharsets.UTF_8) to convert UTF-8 bytes back to a String.
  • Prefer StandardCharsets.UTF_8 over the string "UTF-8" to avoid unnecessary checked exceptions.
  • Remember that UTF-8 byte length is not always equal to string character length for non-ASCII text.
  • Use CharsetEncoder/CharsetDecoder when you need explicit control over malformed input handling.
  • Always specify a charset explicitly. Never rely on the platform default.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.