Back to Primitive Types & Wrappers
Q9Question 9 of 11

What is the char type and how does Java handle Unicode?

Key Concepts
  • char is 16-bit unsigned (0 to 65535) holding one UTF-16 code unit, not one visible character
  • BMP characters (letters, digits, ₹ symbol) fit in a single char
  • Supplementary characters (emoji, rare scripts) need a surrogate pair: two chars
  • String.length() counts code units, not visible characters – use codePointCount() for true count
  • charAt() can return half a surrogate – use codePointAt() for correct Unicode handling
  • char participates in arithmetic as a number: 'A' + 1 = int 66, cast back to char for 'B'

Definition

char is a 16-bit unsigned type that holds one UTF-16 code unit. Most characters fit in one char, but emoji and rare scripts need two chars (a surrogate pair).

Three facts about char:

  • 16-bit unsigned. Range is 0 to 65,535. It holds one UTF-16 code unit, not necessarily one visible character.
  • BMP characters fit in one char. English letters, digits, currency symbols like ₹, Chinese/Japanese/Korean characters all fit.
  • Supplementary characters need two chars. Emoji like 😀 and rare scripts have code points above 65,535 and are encoded as a surrogate pair (high surrogate + low surrogate).

Think of char as a fixed-size box that holds one postcard. Most postcards (common letters and symbols) fit in one box. But large panoramic postcards (emoji) are split across two boxes. If you count boxes, you get the wrong number of postcards. You need to count actual postcards by checking whether two consecutive boxes form one panoramic pair.

Understanding the Concepts

What is a char?

A char in Java is a 16-bit unsigned integer type. It holds one UTF-16 code unit, which is a number from 0 to 65,535.

JAVA
char letter = 'A';           // code point 65
char digit = '5';            // code point 53
char rupee = '₹';            // code point 8377 (U+20B9)
char unicode = '\u0041';     // Unicode escape: also 'A'

System.out.println((int) letter);  // 65
System.out.println((int) rupee);   // 8377

Because char is numeric, it can participate in arithmetic:

JAVA
char a = 'A';
int next = a + 1;              // 65 + 1 = 66 (int)
char b = (char)(a + 1);        // cast back to char: 'B'
System.out.println(b);          // B

What is UTF-16 and why does it matter?

Unicode assigns a number (code point) to every character. The first 65,536 code points (U+0000 to U+FFFF) are called the Basic Multilingual Plane (BMP). These fit in a single 16-bit char.

Characters with code points above U+FFFF (supplementary characters) do not fit in one char. UTF-16 encodes them as a surrogate pair: two chars, a high surrogate (0xD800–0xDBFF) followed by a low surrogate (0xDC00–0xDFFF).

JAVA
String emoji = "😀";  // U+1F600, code point 128512
System.out.println(emoji.length());         // 2 (two code units!)
System.out.println(emoji.codePointCount(0, emoji.length())); // 1 (one character)
System.out.println(emoji.codePointAt(0));   // 128512

char high = emoji.charAt(0);  // 0xD83D (high surrogate)
char low = emoji.charAt(1);   // 0xDE00 (low surrogate)
System.out.println(Character.isHighSurrogate(high));  // true
System.out.println(Character.toCodePoint(high, low)); // 128512

Why does String.length() give the wrong count?

length() counts code units (chars), not visible characters. For strings with emoji or supplementary characters, the count is wrong.

JAVA
String name = "Rajesh";     // 6 BMP chars
String withEmoji = "Hi 😀"; // 3 BMP chars + 1 emoji (2 code units)

System.out.println(name.length());      // 6 (correct)
System.out.println(withEmoji.length()); // 5 (WRONG: visible is 4)
System.out.println(withEmoji.codePointCount(0, withEmoji.length())); // 4 (correct)

In banking, customer names from some regions may contain supplementary characters. Always use codePointCount() when the true character count matters.

How do you iterate over characters correctly?

JAVA
// WRONG: charAt() can return half a surrogate
for (int i = 0; i < str.length(); i++) {
    char c = str.charAt(i);  // may be half an emoji
}

// CORRECT: iterate by code points
str.codePoints().forEach(cp -> {
    System.out.println(Character.toString(cp));
});

// CORRECT: manual code point iteration
for (int i = 0; i < str.length(); ) {
    int cp = str.codePointAt(i);
    System.out.println(Character.toString(cp));
    i += Character.charCount(cp);  // 1 for BMP, 2 for supplementary
}

What happens if you truncate a String by char index?

If you cut a string at a char index that falls between the two halves of a surrogate pair, you get a broken character.

JAVA
String text = "Account: 😀 Active";
String truncated = text.substring(0, 10);  // might split the emoji
System.out.println(truncated);  // "Account: ?" (broken surrogate)

// SAFE: use offsetByCodePoints
int safeEnd = text.offsetByCodePoints(0, 10);
String safeTruncated = text.substring(0, safeEnd);

In banking, this matters when truncating customer names or messages for display.

What Character utility methods exist?

JAVA
System.out.println(Character.isLetter('A'));       // true
System.out.println(Character.isDigit('5'));         // true
System.out.println(Character.isWhitespace(' '));    // true
System.out.println(Character.toUpperCase('a'));     // A
System.out.println(Character.toLowerCase('Z'));     // z
System.out.println(Character.isHighSurrogate('\uD83D')); // true
System.out.println(Character.charCount(128512));   // 2 (supplementary)
System.out.println(Character.charCount(65));       // 1 (BMP)

What are common mistakes?

  • Assuming length() counts visible characters. It counts code units. Use codePointCount() for true count.
  • Using charAt() for iteration. It returns one code unit, which may be half an emoji. Use codePoints() stream.
  • Truncating by char index. Can split surrogate pairs. Use offsetByCodePoints().
  • Comparing chars with ==. Works for BMP but not for supplementary (which are int code points). Use codePointAt() and compare ints.

What should a senior engineer know?

  • Java chose UTF-16 in 1995 when Unicode had fewer than 65,536 characters. Supplementary characters were added later, which is why Java's char does not hold all characters.
  • Kotlin and Swift use proper Unicode abstractions. Kotlin's String.length also counts code units, but String.codePointCount() and the codePoints stream handle it correctly.
  • Database VARCHAR length counts bytes or characters depending on the encoding. When storing emoji, make sure the column uses UTF-8 MB4 (MySQL) or nvarchar (SQL Server).
  • JSON and HTTP use UTF-8, not UTF-16. Java converts between UTF-16 (internal) and UTF-8 (network) automatically, but emoji in JSON may surprise parsers that expect ASCII.

Visual Overview

DIAGRAM
  Unicode Code Points:
    U+0000 to U+FFFF   → BMP (Basic Multilingual Plane)
                          → fits in one Java char
    U+10000 to U+10FFFF → Supplementary
                          → needs TWO Java chars (surrogate pair)

  Example: 😀 = U+1F600 = code point 128512
    char[0] = 0xD83D (high surrogate)
    char[1] = 0xDE00 (low surrogate)
    String.length() = 2, codePointCount() = 1

  char arithmetic:
    'A' = 65
    'A' + 1 = 66 (int, not char)
    (char)('A' + 1) = 'B'

Key Takeaway

A Java char is a 16-bit unsigned integer that holds one UTF-16 code unit, not one visible character. For the vast majority of text – English, Hindi, Chinese, currency symbols – each character fits in a single char. But characters with code points above 65,535 – most emoji, musical symbols, and rare scripts – need two chars called a surrogate pair.

This means String.length() counts code units, not visible characters. A string containing one emoji reports a length of 2. charAt() can return half a surrogate. Truncating by char index can split an emoji in half.

The correct approach is to use codePointCount() for the true character count, codePointAt() and codePoints() stream for iteration, and offsetByCodePoints() for safe truncation. In banking, this matters when handling customer names from scripts with supplementary characters, or when displaying emoji in notifications.

One sentence to remember: char holds one UTF-16 code unit, not one character – use codePoint methods for correct Unicode handling.

---

Share
Use ← → arrow keys to navigate