java 文档里有写 一个 char 由两个自己组成 但一个 utf8 汉字由三个字节组成
为什么这样赋值没事?

char a = '我';

问了一下 deepseek ,它说:UTF-8 编码的汉字在 Java 中会自动转换为 UTF-16 编码,因此赋值时不会出现问题。

#1 AI

char 是 utf-16 编码

魔怔

输入法输入的汉字是三个字节 编译器自动把汉字转成 utf16 进行存储了

哥们你魔怔了吧,人家是把 ai 的提炼了一遍,一眼就能看清说啥,而不是 ai 的长篇大论,你这也举报?

一个 char 不够那就两个啊,code point 是 int 类型,超出 BMP 的字符不能用单个 char 表示,要用两个 char 组合

有文革那味了 人人自危

神奇的是 把变量用 FileWriter.write 方法 写入到文件 还是 3 个字节

可能因为默认编码 utf-8

FileWriter 默认 iso-8859-1 编码 单字节

java17 filewriter 默认 encoding utf8

你把语言里类型的字长跟存储时编码的存储方案搞混了。在 Java 语言里,你把一个中文的字符串取一下长度看是不是 1 ,跟 char 能不能对上,结论就出来了。

没搞混哦 你的回答比较低级
我如果这都不知道 就不会问底层的字节了
这帖的精华是 编译器把输入的 utf8 汉字的三个字节 主动隐式地换成了 2 个字节 utf16 了

总之就是,Java 语言用两个字节存储一个 char ,而一个汉字在 UTF-8 编码后有三个字节长,但是它仍然是一个 char ,在 Java 语言中占用两个字节;一个英文字母在 UTF-8 编码后是一个字节长,但它也是一个 char ,在 Java 语言中占用两个字节。

有没搞混无所谓了,你自己判断就好。

那你看看这个中文汉字 '𠮷' 还能赋值么

2 字节上限只有 65536 个字符,但 unicode 已经超过 10 万个字符了。

你怕是有什么大病吧?

“一个 char 由两个字节组成 但一个 utf8 汉字由三个字节组成”,这句话本身没有问题,但代码里的 char 变量和这句话里的 char 不是一回事.。

字符的本质就是一个整数,比如“我”的编码是 25105 ,几乎所有程序在运行时都会直接存储 25105 。

编译器同理,无论源码里面是什么,用什么编码,最后都会统一解析出 25105 这个数值。

java 的 char 类型本质是一个 16 位整数。char a = '我'; 本质等价于 short a = 25105 。显然没有问题。

所以这个问题其实和“Unicode”、“UTF-8”没有任何关系、更不要去扯什么文件编码,那只会越扯越糊涂。

根据 java 核心技术卷说的,char 类型采用 utf16 编码规则,char 描述 utf16 编码规则中的一个代码单元,一些中文用 utf16 编码规则的时候一部分是占用 2 字节-一个代码单元,一部分是 4 字节,2 个代码单元

因为 char 是 16 位无符号整数,用来表示 UTF-16 码位。而 UTF-16 本身是 2 字节或者 4 字节的变长编码,“我”是在 BMP 里的,所以只需要 2 字节即可表示。如果你从扩展 B 区找一个汉字,就会发现它是不能被赋值给 char 的。

补充: char 类型不是采用 utf16 编码规则,而是描述了 UTF-16 编码中的一个代码单元

你才是菜鸟 不知道编辑器当前 utf8 编码下 输入一个汉字会插入三个字节 在源代码保存的就是三个字节 只是编译器转成了 utf16 两个字节

你其实没有理解精髓

对 一般 utf16 是 4 个字节 我还在奇怪 为什么 java unicode 两个字节

该字能在 java17 赋值给 char 但只能通过位移得到 2 个有用字节 如果 String.valueOf(a).getBytes("UTF-16") 则得不到有用东西
必须赋值给 String 才能处理

这个字的四个字节在此码表网站显示不出来 www.toolhelper.cn/Encoding/UTF16
D842 DFB7

在 java16 及以上 可以把这种超出两字节的汉字 赋值给 char 但得不到正确 bytes

该字通过 string.getBytes("UTF-8") 得到 4 个字节
其实它在 utf8 下
www.mytju.com/classCode/tools/encode_utf8.asp

是 6 个字节

对应 utf8 编码 fa a0 ae b7 能在编辑器中正常显示
可能网站 mytju 给出的 utf8 不准确

这种涉及具体设计的东西,为什么不直接看文档呢: docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/Character.html#unicode

太长不看:

char 数据类型基于 Unicode 规范,该规范将字符( characters )定义为固定宽度的 16 位实体。从 U+0000 到 U+FFFF 的字符集有时被称为基本多语言平面 (Basic Multilingual Plane ,BMP)。码位大于 U+FFFF 的字符称为补充字符( supplementary characters )。UTF-16 编码这些补充字符的方式是,利用一对 16 位整数(称为「代用码位」), 第一个来自高代用值范围(\uD800-\uDBFF ),第二个来自低代用值范围(\uDC00-\uDFFF )。

因此,一个 char 值代表基本多语言平面中的一个码位,包括 UTF-16 编码使用的代用码位。为了表示那些在 UTF-16 中需要多码位编码的补充字符们(如部分汉字、符号等),将用 int 类型来代表一个完整 Unicode 码位。

因此,那些接受 char 类型的字符串工具函数,将无法处理补充字符;而接受 int 类型的那些,就可以处理所有字符。

稍微偏个题
其实你 c 语言里也可以这么写,而且有实际用处(不过一般不是中文,而是四个英文字母组成的字面量,类似 enum State { stop = 'stop' }这样的用法,然后就可以在内存里见到这个字面量了,简易调试的时候很有用(不过有字节序的问题,所以现在也不常用)

#29 手快发出去了。

再太长不看:char 就是 16 位整数,所以有的字符你无法赋值给 char 。int 则用于代表任意一个 Unicode 字符。Java 在 char[] 和 String 中储存字符串的方式是 UTF-16 编码。

你们不看字节码么?
这句代码经过编译器之后,就变成了 sipush 20320 ,管你是“你”还是什么,一律按照数字处理的,同理还有 boolean 只有 0 和 1 ,进行比较的时候其实就是判断等于 0 与否。本质都是一个数字,甚至观察 String 的本质,也是一堆 char ,一堆数字。

Java 9 之后 String 内部用 byte[],编码有 LATIN1 和 UTF-16 两种

#33 这我倒没了解过,有来源吗?我的断言是上面文档里的描述:

The Java platform uses the UTF-16 representation in char arrays and in the String and StringBuffer classes.

openjdk.org/jeps/254

大多数语言里面, char 都代表的是 single unicode scalar value, 而 utf8 只是编码规则, 长度是 1-4 bytes(问题中的 '我' 就会编码成 3 个 bytes), 覆盖了 BMP(基本多文种平面), 基本上够 99.99% 的各类用途, 而且 uft8 是兼容 ascii 且大小端无关的, uft16 以以上要考虑 ascii 兼容和大小端的问题

这个帖子的初忠是 当前编辑器编码 utf8 输入汉字'你'时 输入了三个字节 E4BDA0
java 编译器隐士地把 utf8 字符字面量转成 utf16 4F60 等于十进制 20320

大佬 能不能发个具体能利用这样 enum 调试 c 的例子 不是 c 高手

#2 你有病,有病要去治,不治迟早会出事。

OP 38# 编译器干的不就是这个,读取原始文件,然后进行语法识别和语义识别,判断到给本地变量 a 设置 char ,那就把等号后边的字符(以单引号包住的,前一步语法分析没有问题的)按照文件存储编码或者-encoding 选项进行识别,按数字处理,并根据不同的数字范围来使用不同的指令集,iconst_x bipush sipush ldc 等

为什么很多人说 char 是 utf-16 编码呢? char 存的是 unicode 不是 utf-8 或者 utf-16 。它能存 65536 个基本多文种平面( BMP )的字符,如果超过这个范围(生僻字)就需要两个 char 才能存得下。

char c = '\u0041';
System.out.println(c); // 输出:我

更正:
char wo = '\u6211';
System.out.println(wo); // 输出:我

超过这个范围就必须要用 String
没有两个 char 的表示法吧

我超市了一下,在两个字节能够表示的是可以直接这样赋值的,但超出了就会报错。

所以好像没啥问题,赋值中文确实可能出错,只是你的用例没到边界情况

 class Main {
 public static void main(String[] args) {
 // 创建一个包含超出基本多文种平面( BMP )字符的字符串
 char str = '𠜎';
 System.out.println("字符串: " + str);
 }
}

因为他本来就不是用的 utf8 ,java 用的就是 utf16 呀,这个和你代码文件的编码无关的,假设你使用 gbk2312 来保存你的代码,java 解析加载之后还是按他自己的规则走的。

你这样不行的 这种字符 赋值给 char 控制台输出乱码

#30

但这种写法按标准1会得到一个「实现定义」的值。

The value of an integer character constant containing more than
one character (e.g., 'ab'), or containing a character or escape
sequence that does not map to a single-byte execution character,
is implementation-defined.

考虑到可移植性,通常不建议使用。
除非你写的代码只应用于特定实现,且该实现对此有明确定义。

比如在 GCC2 中:

The compiler evaluates a multi-character character constant
a character at a time, shifting the previous value left by the
number of bits per target character, and then or-ing in the
bit-pattern of the new character truncated to the width of a
target character.

声明:本回答并非使用 LLM 生成。

44#
复制到 intelliJ 里直接就是两个\u ,在 uestudio 中可以显示“𠜎”,按照 UTF-8 保存,javac -encoding UTF8 编译报错。按照 UTF-16 保存,并且-encoding UTF16 ,同样。
45#
在编译阶段,可以指定代码来识别源代码文件格式。典型的问题就是 Windows 环境如果按照 utf-8 编辑文件,但是手工在 cmd 里编译的话,会按照 GBK 识别文件从而在中文字符上出问题。

你说的这是另外一件事。Windows 下,一个 Unicode 字符也是两个字节,你说是怎么做到的?当然是用更复杂的其它方法。

这种奇怪字符无法显示跟当前终端编码无关 gitbash 是 utf8 也无法显示 System.out.println("" + char )

除非这个 char 是 65535 里面的 那种生僻字符就无法显示

你如果能显示 是因为 jvm 实现由差别 因为 char 最多只能两个字节 那种生僻字符占 4 个字节

做不到的,10 万+字符携带的信息量,不可能编码进 2 字节中,Windows 一个 Unicode 字符也可能是 4 字节。UTF-16 对应的不是字符,可能是半个字符。


  1. ISO/IEC 9899:1999 §6.4.4.4/10
  2. gcc.gnu.org/onlinedocs/cpp/Implementation-defined-behavior.html