优先用 charAt 获取最后字符,效率高且语义清晰,但需判空并注意不支持补充字符;substring 返回 String 且兼容字符串操作,但同样无法正确处理 emoji;真正健壮方案需基于 codePointCount 和 offsetByCodePoints。

用 charAt 获取字符串最后一位字符
直接通过索引访问,效率高、语义清晰,适合只需要单个字符的场景。注意索引从 0 开始,最后一位是 str.length() - 1。
常见错误:对空字符串调用会抛 StringIndexOutOfBoundsException。
- 务必先判空:
if (str != null && !str.isEmpty()) - 返回类型是
char,不是String;若后续要拼接或比较字符串,得转成String.valueOf(str.charAt(...)) - 不支持 Unicode 补充字符(如 emoji),遇到代理对(surrogate pair)可能取到高位或低位代理,而非完整字符
用 substring 截取最后一位作为子串
substring 返回的是 String 类型,天然兼容字符串操作,且能正确处理补充字符(只要 JDK ≥ 7 并使用 codePointCount/offsetByCodePoints 配合,但单纯 substring(str.length()-1) 仍按 UTF-16 索引)。
常见错误:对长度为 0 或 1 的字符串未做边界检查,substring(-1) 或 substring(1) 抛异常。
立即学习“Java免费学习笔记(深入)”;
- 安全写法:
str.length() >= 1 ? str.substring(str.length() - 1) : "" - 如果字符串可能为
null,需前置判断,否则NullPointerException - 性能略低于
charAt(涉及新字符串对象创建),但日常使用差异可忽略
Unicode 补充字符(如 ?、??)怎么办
Java 字符串底层是 UTF-16,一个 emoji 可能占两个 char(即一个代理对)。此时 charAt(str.length()-1) 只拿到后半个代理,显示为 ;而 substring(str.length()-1) 同样只截一半,结果无效。
- 正确做法是先用
str.codePointCount(0, str.length())获取真实字符数 - 再用
str.offsetByCodePoints(0, lastCodePointIndex)找到最后一个码点起始位置 - 最后用
str.substring(startPos, startPos + charCount)截取(charCount通常是 1 或 2) - 更简单:JDK 9+ 可用
str.codePoints().reduce((a, b) -> b).orElse(-1)获取最后码点,再转字符串,但无法还原原字符宽度
选哪个?看你要的是 char 还是 String,以及是否要支持 emoji
纯 ASCII 场景下,charAt 更轻量;需要字符串操作或兼容性优先,选 substring。但真正要健壮支持 Unicode,两者都得升级逻辑——不能只依赖 .length() 和末位索引。
最容易被忽略的点:开发时用英文测试一切正常,上线后用户输入中文、emoji、阿拉伯文,charAt 和 substring 都可能静默出错,不是崩溃,而是取错字。











