java中char固定占2字节,对应unicode bmp码点;string底层自java 9起按内容优化存储(latin-1用byte[],否则用char[]);getbytes()是编码转换而非读内存,new string(byte[], charset)是解码而非转编码。

Java中char在内存里占2个字节
Java的char类型固定占用2个字节(16位),这是由语言规范决定的。它直接对应Unicode基本多文种平面(BMP)中的一个码点,例如 'A' 是 U+0041,'中' 是 U+4E2D,都可用单个char表示。
注意:这不是“UTF-16编码后的字节数”,而是char本身的存储大小。JVM内部用UTF-16表示String,但char变量本身就是一个16位无符号整数,取值范围是 0 到 65535(即 \u0000 到 \uFFFF)。
对于超出BMP的字符(如部分emoji、古汉字),Java用两个char(代理对)表示,共占4字节内存,但这是String层面的逻辑,单个char变量仍为2字节。
String在内存中以UTF-16方式存储
从Java 9开始,String底层存储做了优化:如果字符串只含Latin-1字符(ASCII范围),会用byte[]以ISO-8859-1方式紧凑存储;否则回退到char[](即UTF-16)。但语义上,String始终代表Unicode字符序列,开发者无需关心底层是byte还是char数组。
你可以用以下方式验证实际内存布局:
- toCharArray() 返回char[],每个元素占2字节(BMP字符)
- String.length() 返回char数量,不是Unicode码点数(遇到代理对会返回2)
- codePointCount(0, str.length()) 才是真实Unicode字符数
getBytes()不是“获取内存字节”,而是编码转换
String.getBytes()不等于读取内存原始字节。它把String中已解码的Unicode字符,按指定编码规则重新编码成字节序列。例如:
-
"中".getBytes(StandardCharsets.UTF_8)→ 3字节:[0xE4, 0xB8, 0xAD] -
"中".getBytes(StandardCharsets.GBK)→ 2字节:[0xD6, 0xD0] -
"中".getBytes(StandardCharsets.UTF_16BE)→ 2字节:[0x4E, 0x2D](去掉BOM的大端UTF-16)
省略参数时使用系统默认编码(System.getProperty("file.encoding")),极易引发跨环境乱码,务必显式指定。
new String(byte[], charset) 是解码,不是“转编码”
这个构造方法的作用是:把按charset编码的字节流,还原成内存中的Unicode字符串。关键点:
- byte[]里存的是某种编码的原始字节(比如从文件或网络读来的GBK字节)
- charset参数告诉JVM:“这些字节是用这个编码写的,请按它反向解析”
- 结果String在内存中永远是Unicode(UTF-16形式),与输入编码无关
常见错误写法:new String(str.getBytes("GBK"), "UTF-8") —— 这相当于用UTF-8去解码GBK字节,必然乱码。正确流程应是:字节流 → 按原始编码解码为String → 再按目标编码重新编码为字节。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











