我对我所看到的答案感到十分困惑 stackoverflow加上java文档
虽然上面链接中的docs和stack中的所有理论似乎都指出UTF-16是Java支持的本机字符集,但还有另一种理论认为它依赖于JVM / OS,例如:在this链接中,它说:
Java虚拟机的每个实例都有一个默认字符集,它可能是也可能不是标准字符集之一。默认字符集是在虚拟机启动期间确定的,通常取决于底层操作系统使用的语言环境和字符集。
然后在另一部分的同一链接中说
Java编程语言的本机字符编码是UTF-16。
我发现很难理解这些明显矛盾的陈述:
再次,现在,当我执行以下代码时:
package org.sheel.classes;
import java.nio.charset.Charset;
public class Test {
public static void main(String[] args) {
System.out.println(Charset.defaultCharset());
}
}
...在在线编辑器中,我看到了UTF-8。在我的本地系统中,我看到了windows-1252
最后,有一个JDK增强提案(JEP),讨论如何将默认值更改为UTF-8
这种混淆会有解释吗?
答案 0 :(得分:3)
String内部是一个char数组toCharArray()
,每个char都是一个utf-16代码点。将字符串转换为字节数组而不指定字符集getBytes()
时,将使用操作系统。
答案 1 :(得分:2)
String使用的内部编码与平台的默认字符集无关。它们完全相互独立。
在内部,String可以将其数据存储为任何内容。作为程序员,我们不与私有实现交互;我们只能使用公共方法。公共方法通常将String的数据返回为UTF-16(char
值),但有些(如codePoints() method)可以返回完整的UTF-32 int值。这些方法都没有指出String数据是如何在内部存储的,只是程序员可以检查该数据的形式。
因此,不是说String在内部将数据存储为UTF-16或任何其他编码,而是说String保存一系列Unicode代码点,并使它们以各种形式提供,最常见的是char值。
默认字符集是Java从底层系统获得的东西。
正如roberto指出的那样,当你使用某些(过时的)方法和构造函数时,默认的字符集很重要。将字符串转换为字节,或将字节转换为字符串,而不显式指定字符集,将使用默认字符集。同样,在不指定字符集的情况下创建InputStreamReader或OutputStreamWriter将使用默认字符集。
依赖默认字符集通常是不明智的,因为它会使您的代码在不同平台上的行为不同。此外,一些字符集可以表示所有已知字符,但是一些字符集只能表示整个Unicode指令集的一小部分。特别是,Windows通常有一个默认的字符集,它使用单个字节来表示每个字符(在美国版本的Windows中为windows-1252
),显然这对于数十万个可用字符来说空间不足。
如果您依赖默认字符集,则确实有可能丢失信息:
String s = "\u03c0\u22603"; // "π≠3"
byte[] bytes = s.getBytes();
for (byte b : bytes) {
System.out.printf("%02x ", b);
}
System.out.println();
在大多数系统上,这将打印:
cf 80 e2 89 a0 33
在Windows上,这可能会打印出来:
3f 3f 33
pi和不相等的字符没有在windows-1252字符集中表示,因此在Windows上,getBytes方法用问号(字节值3f)替换它们。
如果不涉及到或来自字节的转换,String对象永远不会丢失信息,因为无论它们如何在内部存储数据,String类都保证每个字符都将被保留。