charAt()
Reads the single char at a zero-based index, and throws StringIndexOutOfBoundsException when the index is outside the String.
public char charAt(int index)- Added in Java Java 1.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$index | int | Zero-based position, from 0 to length() - 1. |
Return value
charThe char value at the given index. The first char is at index 0.
Errors
IndexOutOfBoundsException (thrown as StringIndexOutOfBoundsException) if index is negative or not less than length().
charAt(i) returns the char at position i. Indexes start at 0, so the first char is s.charAt(0) and the last is s.charAt(s.length() - 1). Anything outside that range throws StringIndexOutOfBoundsException, which is a subclass of IndexOutOfBoundsException.
The empty String trap
s.charAt(0) on an empty String throws. Input from a query parameter, a CSV cell or a header can be empty, so check isEmpty() before reading the first char, or use startsWith, which simply returns false.
char is a number
A char is an unsigned 16-bit integer. That is useful: c - '0' turns the digit '7' into the int 7, and c >= 'a' && c <= 'z' tests for a lowercase ASCII letter. It also surprises people: c + 1 is an int, so printing it shows a number, and you need a cast to get a char back. Likewise "" + c1 + c2 joins two chars, but c1 + c2 adds them.
One char is not always one character
An emoji is stored as two chars, a high and a low surrogate. charAt returns just one half, which is not a printable character on its own. If the text can contain any Unicode, use codePointAt(i) and step forward with Character.charCount(cp), or use codePoints().
When to use it
Reach for charAt in small hand-written parsers and validators: checking a token alphabet, computing a check digit, or scanning a path for separators. It is fast, does not allocate, and the JIT compiler handles it well in a tight loop. For searching, indexOf is clearer; for anything pattern-shaped, a precompiled regular expression is easier to read. Avoid Character.isLetterOrDigit when you mean ASCII only, because it accepts letters from every script.
Example
When to use it
- Validating that a request ID header uses only letters, digits and hyphens.
- Checking the check digit of a card or account number before calling a payment API.
- Reading the first char of a sort parameter to tell '-createdAt' (descending) from 'createdAt'.
Frequently asked questions
How do I get the last character of a String in Java?
Use s.charAt(s.length() - 1), after checking that s is not empty. On an empty String that index is -1 and the call throws StringIndexOutOfBoundsException.
How do I convert a digit char to an int?
Subtract '0': '7' - '0' is 7. Character.getNumericValue(c) also works and handles digits from other scripts, while Character.digit(c, 16) parses hexadecimal digits.
More examples
Checking a token alphabet
Explicit ASCII ranges reject the accented letter, which Character.isLetterOrDigit would accept. That difference matters for request IDs or keys that end up in URLs, file names or log searches.
boolean isToken(String s) {
if (s.isEmpty()) return false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
boolean ok = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9') || c == '-';
if (!ok) return false;
}
return true;
}
void main() {
for (String s : List.of("req-7f3a", "req 7f3a", "", "abc\u00e9")) {
IO.println("\"" + s + "\" " + isToken(s));
}
}
// Output:
// "req-7f3a" true
// "req 7f3a" false
// "" false
// "abcé" falseComputing a Luhn check digit
Subtracting '0' turns a digit char into its numeric value without parsing. Card numbers and many reference numbers use this check, so a payment form can reject a typo before calling the provider.
boolean luhn(String digits) {
int sum = 0;
boolean twice = false;
for (int i = digits.length() - 1; i >= 0; i--) {
int d = digits.charAt(i) - '0'; // '7' - '0' is 7
if (twice) {
d *= 2;
if (d > 9) d -= 9;
}
sum += d;
twice = !twice;
}
return sum % 10 == 0;
}
void main() {
IO.println(luhn("79927398713"));
IO.println(luhn("79927398710"));
}
// Output:
// true
// falsecharAt and characters outside the BMP
charAt(0) returns only the high surrogate of the emoji, while codePointAt(0) combines both halves into the real code point. The exclamation mark sits at index 2 because the emoji occupies two chars.
void main() {
String s = "\uD83D\uDE00!";
char first = s.charAt(0);
IO.println(Character.isHighSurrogate(first));
IO.println(Integer.toHexString(first));
int cp = s.codePointAt(0);
IO.println(Integer.toHexString(cp));
IO.println(Character.toString(cp));
IO.println(s.charAt(2)); // the emoji used indexes 0 and 1
}
// Output:
// true
// d83d
// 1f600
// 😀
// !
Discussion