ASCII in Java
char, int Casting, and Character Class Methods
Java's char type is a 16-bit unsigned integer that holds a UTF-16 code unit. Casting char to int yields the numeric code point directly, and for the 128 ASCII positions this matches the standard table without conversion. The Character class provides methods like isLetter(), isDigit(), and getNumericValue() that work on these integer values. Most encoding pain in Java comes from the Charset layer, where the default encoding varied by platform until JDK 18 made UTF-8 the universal default.
1. char and int: Direct Casting
In Java, char is a 16-bit unsigned integer type that stores a UTF-16 code unit. Casting between char and int is implicit in one direction and explicit in the other:
char letter = 'A';
int code = letter; // implicit widening: 65
char back = (char) 65; // explicit narrowing: 'A'
System.out.println((int) 'A'); // 65
System.out.println((int) 'a'); // 97
System.out.println((int) '0'); // 48
System.out.println((char) 10); // newline
For ASCII characters (0 through 127), the cast value matches the decimal column in the ASCII table exactly. 65 is uppercase A, 97 is lowercase a, 48 is the digit 0. Arithmetic on chars works because Java treats them as numbers: 'A' + 1 evaluates to 66 (an int), and (char)('A' + 1) gives 'B'.
Watch out: char is unsigned, so it ranges from 0 to 65535. Casting a negative int to char wraps around: (char)(-1) gives 65535, not an error.
2. Character Class Methods
java.lang.Character provides static methods for classifying and converting characters by their code point values:
Character.isLetter('A') // true
Character.isDigit('5') // true
Character.isWhitespace(' ') // true
Character.isUpperCase('A') // true
Character.toLowerCase('A') // 'a'
Character.toUpperCase('z') // 'Z'
Character.getNumericValue('7') // 7
Character.getNumericValue('A') // 10 (hex digit)
These methods handle the full Unicode range, not just ASCII. Character.isLetter() returns true for Chinese characters, Arabic letters, and Cyrillic. To restrict checks to the ASCII range, combine with a range test:
public static boolean isAsciiLetter(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
public static boolean isAsciiPrintable(char c) {
return c >= 32 && c <= 126;
}
The 95 printable ASCII characters span positions 32 (space) through 126 (tilde). The letter positions follow the original 1963 arrangement.
3. String to ASCII Codes
Java strings are sequences of chars. Access individual code points with charAt() or iterate with chars() (returns an IntStream since Java 9):
String text = "Hello";
// charAt approach
for (int i = 0; i < text.length(); i++) {
System.out.printf("%c = %d%n", text.charAt(i), (int) text.charAt(i));
}
// IntStream approach (Java 9+)
int[] codes = text.chars().toArray();
// [72, 101, 108, 108, 111]
Both produce the same values you would see in the text to ASCII converter. Note that chars() returns UTF-16 code units. For characters outside the BMP (emojis, rare CJK), use codePoints() instead to get actual Unicode code points.
4. Byte Arrays and Charset
Converting between String and byte[] requires specifying a character encoding. The US-ASCII charset maps each character to a single byte in the range 0 through 127.
import java.nio.charset.StandardCharsets;
// String to bytes
byte[] bytes = "Hello".getBytes(StandardCharsets.US_ASCII);
// [72, 101, 108, 108, 111]
// Bytes to String
String text = new String(bytes, StandardCharsets.US_ASCII);
// "Hello"
Always use StandardCharsets.US_ASCII or StandardCharsets.UTF_8 instead of the string name "ASCII". Using the constant is type-safe and avoids the checked UnsupportedEncodingException that the string overload forces you to catch.
Before JDK 18, String.getBytes() without an argument used the platform default encoding. On Windows with a Japanese locale, that could be Shift_JIS. On Linux, usually UTF-8. JDK 18 (JEP 400) changed the default to UTF-8 everywhere. If your code must run on older JDKs, always specify the charset explicitly.
5. Building an ASCII Table
public class AsciiTable {
public static void main(String[] args) {
System.out.printf("%5s %4s %8s %s%n",
"Dec", "Hex", "Binary", "Char");
for (int i = 0; i < 128; i++) {
String ch = (i >= 32 && i <= 126)
? String.valueOf((char) i)
: String.format("(ctrl-%d)", i);
System.out.printf("%5d 0x%02X %s %s%n",
i, i,
String.format("%7s",
Integer.toBinaryString(i)).replace(' ', '0'),
ch);
}
}
}
This generates all 128 positions with decimal, hexadecimal, and binary columns. Compare the output against the ASCII chart view to verify. The base converter handles interactive conversions between number bases.
6. Common Pitfalls
char arithmetic returns int
Adding two chars or a char and an int produces an int, not a char. This compiles fine: int sum = 'A' + 'B'; (131). But char sum = 'A' + 'B'; fails with a compiler error because the result is wider than char. Cast explicitly: char sum = (char)('A' + 1);.
byte is signed
Java's byte type is signed, ranging from -128 to 127. ASCII values 0 through 127 fit, but the byte for value 127 is the maximum. If you process Extended ASCII or raw binary data with values 128 through 255, bytes become negative. Use Byte.toUnsignedInt(b) or b & 0xFF to get the correct unsigned value.
Default encoding surprises
Reading a file with new FileReader(path) (pre-JDK 18) uses the platform default encoding. On a developer's macOS machine, this is UTF-8. On the CI server running Windows, it could be cp1252. Everything reads fine in development and produces corrupted text in production. Always wrap with an explicit charset: new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8).
7. Practical Recipes
Strip non-ASCII from a string
public static String stripNonAscii(String input) {
return input.replaceAll("[^\\x00-\\x7F]", "");
}
Case conversion via bit manipulation
char lower = (char) ('A' | 32); // 'a'
char upper = (char) ('a' & ~32); // 'A'
This works because ASCII letters are arranged with exactly 32 between upper and lower pairs. Bit 5 (decimal value 32) is the only difference. Setting it with OR gives lowercase. Clearing it with AND gives uppercase.
Validate ASCII input
public static boolean isAscii(String s) {
return s.chars().allMatch(c -> c >= 0 && c <= 127);
}
public static boolean isPrintableAscii(String s) {
return s.chars().allMatch(c -> c >= 32 && c <= 126);
}
Use isPrintableAscii() to reject control characters from user input in form fields, filenames, or API payloads. The control characters article explains which ones still have active meaning in modern systems.