Java String Characters: charAt, chars(), toCharArray & the Character Class
A Java String is a sequence of char values, where each char is a 16-bit UTF-16 code unit. charAt(index) retrieves a single char; chars() returns an IntStream of char values for stream-based processing; toCharArray() gives you a mutable copy as a char[]. The Character class wraps char and provides static utility methods for classification (isDigit, isLetter, isWhitespace) and case conversion. This article covers all four plus the Unicode supplementary character subtlety that trips up many developers.
charAt(): Accessing a Single Character by Index
A StringcharcharAt(int index)char0length()StringIndexOutOfBoundsException
A char==int
String s = "Hello";
char first = s.charAt(0); // 'H'
char last = s.charAt(s.length() - 1); // 'o'
System.out.println(first); // H
System.out.println((int) first); // 72 — the Unicode code point for 'H'
// Iterate over characters with charAt
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
System.out.print(c + " "); // H e l l o
}
// Count vowels using charAt
long vowels = 0;
String text = "Hello World";
for (int i = 0; i < text.length(); i++) {
char c = Character.toLowerCase(text.charAt(i));
if ("aeiou".indexOf(c) >= 0) vowels++;
}
System.out.println("Vowels: " + vowels); // 3
chars() and codePoints(): Stream-Based Character Processing
chars()IntStreamcharintIntStreamCharStreamcharchars()filtermapdistinctcountsorted
codePoints()codePoints()chars()
String s = "Hello, World!";
// Count distinct letters (case-insensitive)
long distinctLetters = s.chars()
.map(Character::toLowerCase)
.filter(Character::isLetter)
.distinct()
.count();
System.out.println("Distinct letters: " + distinctLetters); // 8
// Count digits in a string
String mixed = "abc123def456";
long digitCount = mixed.chars()
.filter(Character::isDigit)
.count();
System.out.println("Digits: " + digitCount); // 6
// Reverse a string using chars()
String reversed = new StringBuilder(s).reverse().toString();
// Or with streams:
String rev2 = s.chars()
.collect(StringBuilder::new,
(sb, c) -> sb.insert(0, (char) c),
StringBuilder::append)
.toString();
// Frequency map of characters
Map<Character, Long> freq = "banana".chars()
.mapToObj(c -> (char) c)
.collect(java.util.stream.Collectors.groupingBy(
c -> c, java.util.stream.Collectors.counting()));
System.out.println(freq); // {a=3, b=1, n=2}
// codePoints() — correct for strings with supplementary characters (emoji)
String withEmoji = "Hello \uD83D\uDE00"; // 😀 is U+1F600
// 8 (surrogate pair = 2)
System.out.println("chars length: " + withEmoji.chars().count());
System.out.println("codePoints length: " + withEmoji.codePoints().count()); // 7 (emoji = 1)
codePoints()chars()chars()toCharArray(): Mutable Character Array
toCharArray()char[]StringtoCharArray()
The array is also the right way to handle sensitive data such as passwords: a char[]Arrays.fill(arr, '\0')String
String s = "hello";
char[] chars = s.toCharArray(); // {'h', 'e', 'l', 'l', 'o'}
// Sort characters alphabetically
java.util.Arrays.sort(chars);
System.out.println(new String(chars)); // "ehllo"
// Check if two strings are anagrams
boolean isAnagram(String a, String b) {
if (a.length() != b.length()) return false;
char[] ca = a.toLowerCase().toCharArray();
char[] cb = b.toLowerCase().toCharArray();
java.util.Arrays.sort(ca);
java.util.Arrays.sort(cb);
return java.util.Arrays.equals(ca, cb);
}
System.out.println(isAnagram("listen", "silent")); // true
System.out.println(isAnagram("hello", "world")); // false
// Build a string from a char array after modification
char[] letters = {'J', 'a', 'v', 'a'};
letters[0] = 'D';
String modified = new String(letters); // "Dava"
// Iterate a char[] — slightly faster than charAt() in tight loops
for (char c : "iterate me".toCharArray()) {
System.out.print(c);
}
The Character Class: Classification and Conversion
The CharactercharCharacter.isLetter('é')true'é'
// Classification methods
System.out.println(Character.isDigit('5')); // true
System.out.println(Character.isDigit('a')); // false
System.out.println(Character.isLetter('a')); // true
System.out.println(Character.isLetter('é')); // true (Unicode-aware)
System.out.println(Character.isLetterOrDigit('5')); // true
System.out.println(Character.isWhitespace(' ')); // true
System.out.println(Character.isWhitespace('\t')); // true
System.out.println(Character.isUpperCase('A')); // true
System.out.println(Character.isLowerCase('a')); // true
System.out.println(Character.isAlphabetic('a')); // true (broader than isLetter)
// Case conversion
System.out.println(Character.toUpperCase('a')); // A
System.out.println(Character.toLowerCase('Z')); // z
System.out.println(Character.toUpperCase('é')); // É (handles accented chars)
// Numeric value of a digit character
System.out.println(Character.getNumericValue('7')); // 7
System.out.println(Character.getNumericValue('a')); // 10 (hex digit)
System.out.println(Character.digit('F', 16)); // 15 (parse hex digit)
// Practical: remove all non-alphanumeric characters
String cleaned = "Hello, World! 123"
.chars()
.filter(Character::isLetterOrDigit)
.collect(StringBuilder::new,
(sb, c) -> sb.append((char) c),
StringBuilder::append)
.toString();
System.out.println(cleaned); // HelloWorld123
Note: Prefer Character.isWhitespace()' '\t\n\r\f