Java编码详解:Unicode、UTF-16及JVM默认编码设置指南
关于编码的其他参考:

java 所采用的编码是 unicode 编码,具体为 utf-16 编码,且以双字节进行存储。
查看jvm的默认编码:
可以使用 System.out.println(Charset.defaultCharset()); 这一方式来查看 jvm 默认的字符集。我的 mac 系统下的默认字符集为 utf-8(java.nio.charset.CharsetICU[UTF-8])。
JVM的字符集编码取的是操作系统默认的字符集编码:
win xp 中文版中:一般是GBK。
在 win server 中文版里,通常是 GB18030(这只是一种猜测)。
Linux 中看本地语言环境配置文件怎么设置。
java字符串使用的是unicode编码。
Java 文件经过编译会形成 class 文件,该文件是按照 unicode 编码的。
这里的 Java 文件编码有很多种。不过,Java 编译器会自动把这些编码按照 Java 文件的编码格式正确读取,然后生成 class 文件。这里的 class 文件编码是 Unicode 编码,具体来说是 UTF-16 编码。
因此,在Java代码中定义一个字符串:
String s="汉字";
在编译前,java 文件可以使用各种编码。而在编译后形成 class 文件时,它们都以 Unicode 编码来表示,并且是一样的。
1.2、JVM中的编码
JVM 在加载 class 文件进行读取时,若使用 Unicode 编码方式,就能正确读取 class 文件。如此一来,原本定义的 String s="汉字",其在内存中的表现形式便是 Unicode 编码。
不同的系统和数据库经过多次编码后,若不理解其中的原理,就容易引发乱码。所以,在一个系统里,有必要将字符串的编码进行统一,简单来说,就是在对外方面保持统一。对于方法字符串参数和 IO 流,在中文系统中,GBK、GB13080、UTF-8、UTF-16 等都可被统一使用。但需选择具有更大字符集的编码,这样才能确保任何可能用到的字符都能正常显示,避免出现乱码问题。(假设对所有文件都用 ASCII 码)若如此,就无法实现双向转换了。
需要特别留意的是,UTF-8 并不能涵盖所有的中文字符集编码。所以,在某些特殊情形下,UTF-8 转换为 GB18030 有可能会出现乱码。
2、字符串编码的转换
JAVA 的字符串采用的是 unicode 编码。其他来源的字符不一定是 unicode 编码。当字符串输出到文件或其他地方时,会有不同字符集的需求。所以,字符串在与外界进行交互时,必须进行字符集转换。
在进行处理之前,需要明确“源”的编码,接着用指定的编码方式将其正确读取到内存中。若这是一个方法的参数,那就必须明确该字符串参数的编码,因为此参数有可能是由另一个日文系统传递过来的。当明确了字符串的编码之后,就能够按照要求对字符串进行正确处理,从而避免出现乱码。
在对字符串进行解码编码的时候,应该调用下面的方法:
获取以指定字符集编码的字节数组:getBytes(String charsetName)
构建一个字符串,其构造方式为:使用给定的字节数组(byte[] bytes)和字符集名称(String charsetName)来创建新的字符串。
如果上述字符集没有被指定,那么系统会依据默认字符集来进行转换。同时,如果来源或者目标不是默认字符集的话,就会出现乱码情况。
JVM 所使用的默认字符集是 Charset.defaultCharset()。
在 Java 中,进行字符字节转换操作时,如果没有提供字符集,就会使用默认的字符集。像在字符串与字节数组进行转换的时候,以及字节流与字符流进行转换的时候等等情况,都是如此。
在 WindowsXP 的中文版里,所使用的默认编码为 GBK。你可以进行如下操作来验证:运行一下。
publicclassTest {
或者:在 public static void main(String[] args) 这个结构里,其包含了主要的运行相关内容。
输出当前 JVM 的默认字符集,即 Charset.defaultCharset() 所返回的字符集。
当前JRE:1.6.0_16
当前JVM的默认字符集:GBK
举例:
1 String str = "中文";
2 // 获取JVM默认字符集
System.out.println("默认字符集:" + Charset.defaultCharset());
4
System.out.println("将字符串转换成 byte 数组。");
将字符串 str 转换为字节数组,得到的字节数组为 defaultByteArray ,即 defaultByteArray = str.getBytes() 。
使用“GBK”编码将字符串 str 转换为字节数组 gbkByteArray ,其字节数组的形式为 byte[] 。
创建一个名为 utfByteArray 的字节数组,该数组是通过将字符串 str 使用 "UTF-8" 编码方式进行编码得到的。
System.out.println("默认字节数组:");
将 defaultByteArray 转换为字符串形式,使用 Arrays.toString 方法,然后与 10 进行相加操作。
13
System.out.println("要将 byte 数组转换成字符串");
String 被创建为一个新的字符串,其内容来源于 defaultByteArray,即 String defaultStr = new String(defaultByteArray)。
创建一个字符串 gbkStr,它是通过使用 "GBK" 编码将 defaultByteArray 数组转换而来的。
创建一个名为 utfStr 的字符串,它是通过使用 "UTF-8" 编码将 defaultByteArray 数组转换而来的。
System.out.println("defaultStr 为:" + defaultStr);
System.out.println("gbkStr 为:" + gbkStr);
因为 utf-8 是变长编码,不存在与[-42, -48, -50, -60]相对应且使用 utf-8 字符集的字符串,所以会出现乱码现象。
System.out.println("utfStr 为:" + utfStr);
22
System.out.println("将字节流转化为字符流");
文件仅有“中文”这两个字,且文件是以“GBK”编码的,总共 4 个 byte。
创建了一个 BufferedReader 类型的 defaultReader,它是通过 new BufferedReader( 初始化的。
使用 new FileInputStream("src/encode.txt") 创建一个输入流,然后将其传递给 new InputStreamReader 来创建一个字符流读取器。
创建一个 BufferedReader 对象 gbkReader,这个 gbkReader 是通过用 InputStreamReader 来包装一个输入流而创建的,并且使用的字符编码是 gbk 。
创建一个新的 FileInputStream 对象,参数为 "src/encode.txt",字符编码为 "GBK"。
创建一个 BufferedReader 对象 utfReader,它是通过将一个 InputStreamReader 对象作为参数传递给 BufferedReader 的构造函数来创建的,这个 InputStreamReader 对象用于读取字节流并将其转换为字符流。
使用“src/encode.txt”创建一个新的 FileInputStream 对象,编码格式为“UTF-8”。
34
System.out.println("将字符流转化为字节流");
BufferedWriter 的 defaultWriter 是一个新创建的 BufferedWriter 对象。
创建一个新的 OutputStreamWriter 对象,该对象的参数为 System.out。
创建了一个 BufferedWriter 类型的 gbkWriter,它是通过将一个 OutputStreamWriter 与一个输出流进行包装而得到的,这个 OutputStreamWriter 又将字符编码设置为了 gbk 编码。
System.out 输出的内容为 GBK 。
创建了一个 BufferedWriter 类型的 utfWriter,它是通过将一个 OutputStreamWriter 包装在一个 BufferedWriter 中创建的,其中 OutputStreamWriter 的构造函数接收一个字节输出流作为参数。
System.out 打印出 "defaultWriter:";
defaultWriter 执行了写入操作,写入的内容是 str 。
这里不能使用 close() 方法,因为如果使用了,System.out 也会被关闭,那样后续就不会有输出了。
defaultWriter 执行了刷新操作。
gbkWriter 将字符串 str 进行写入操作。
48 gbkWriter.flush();
使用 utfWriter 写入字符串 str 。
51 utfWriter.flush();
Java
defaultCharset:GBK
##字符串转换成byte数组
defaultByteArray 包含的数值为 -42、-48、-50、-60
gbkByteArray 包含的元素为 -42、-48、-50、-60 。
utfByteArray 包含了 -28、-72、-83、-26、-106、-121 这些数值。 这些数值构成了 utfByteArray 的具体内容。 它由 -28 等这些特定的数值所组成。 这些特定的数值 -28 等共同构成了 utfByteArray。 utfByteArray 是由 -28 等一系列数值所形成的。 这些数值 -28 等一起构成了 utfByteArray 的部分。 它由 -28 等这样的数值组合而成。 这些数值 -28 等构成了 utfByteArray 的相关部分。
##byte数组转换成字符串
defaultStr:中文
gbkStr:中文
##字节流转化成字符流
defaultReader:中文
gbkReader:中文
##字符流转化成字节流
defaultWriter:中文
gbkReader:中文
utfReader:涓枃
文件名字的编码为 System.getProperty("sun.jnu.encoding") 。
今天在项目中遇到将文件通过 web 上传到文件系统的情况,发现中文文件名出现了乱码。接着,我们一步步排查了在 http 通讯过程中使用 utf-8 进行编解码的情况,但并未发现问题。同时,操作系统的默认字符集也是 utf-8。通过日志打印 System.getProperty("file.encoding"),其结果也是 utf-8 码。一时间,我们感到有些凌乱了。首先通过查资料得知,在使用 java 写文件时,文件名编码与 System.getProperty("sun.jnu.encoding")相关。接着打印此环境属性,发现输出是 ANSI 的一种编码。最终找到了问题的爆发点,通过在程序中设置 System.setProperty("sun.jnu.encoding","utf-8")从而解决了问题。
结论是通过问题解决得出的:sun.jnu.encoding 会对文件名的创建产生影响,而 file.encoding 会对文件内容产生影响。
在我们使用 Java 处理中文文件时,如果中文内容未出现乱码,但文件名出现了乱码,那我们就应该多考虑 sun.jnu.encoding 和 file.encoding 的区别。
Java程序的入口文件,也就是包含 main 函数的文件的编码是 System.getProperty("file.encoding")
该编码的默认值依据 Java 程序入口文件(即包含 main 函数的文件)的编码方式而定。具体情况可参考以下文章。同时,在运行 Java 程序时,能够通过 -Dfile.encoding="GBK" 来进行设定。倘若 -Dfile.encoding 指定的编码方式与 Java 程序入口文件的字符集不相符,就会引发乱码现象。可以在程序里通过 setProperty 方法直接进行设置。这种设置改变了“file.encoding”的值,然而看起来并没有什么作用。
System.out.println 输出的内容是 "file.encoding:" 。
获取系统属性“file.encoding”的值,然后将其与 3 进行某种操作(具体操作未明确提及)。
在一个不存在的目录当中去创建文件,然后去查看所产生的报错信息。
5 try {
创建一个新文件,该文件位于“directory/test.txt”路径下,通过 new File("directory/test.txt") 创建文件对象,然后调用 createNewFile() 方法来创建新文件。
如果发生 IOException 异常,则执行以下操作。
打印出异常的堆栈跟踪信息 e.printStackTrace()。
9 }
10
文件编码为 GBK,而不是 -Dfile.encoding="UTF-8"。
System.out 会打印出 "file.encoding:" 这个内容。
获取系统属性"file.encoding"的值并与 14 进行相加操作。
在一个不存在的目录里创建文件,然后查看所产生的报错信息。
16 try {
创建一个新的文件,文件路径为"directory/test.txt",通过 new File("directory/test.txt")创建文件对象,然后调用 createNewFile()方法来创建该文件。
通过调用 `e.printStackTrace()` 方法,可以将异常的相关信息打印到控制台或日志文件中,以便进行调试和分析。
20 }
Java
文件的编码为 GBK,并且 -Dfile.encoding 的值为 "GBK"。
file.encoding:GBK
Java 的 io 包中出现 IOException 异常,原因是系统无法找到指定的路径。
在 java.io.WinNTFileSystem 中创建独占文件(通过原生方法)。
在 java.io.File 类的 createNewFile 方法(位于 File.java 文件的 883 行处)。
在 Encoding 类的 main 方法中,位于 Encoding.java 文件的第 72 行。
关于“鏂囦欢缂栫爜鏄疓 BK,-Dfile.encoding="UTF-8"”的相关内容,目前未明确具体所指及相关背景信息,暂无法准确进行改写。若能提供更多关于此内容的信息,如其用途、相关场景等,将有助于更准确地进行改写。
file.encoding:UTF-8
java.io.IOException:无法获取指定的资源或发生了与输入/输出相关的错误。
at java.io.WinNTFileSystem.createFileExclusively(Native Method)
at java.io.File.createNewFile(File.java:883)
在 Encoding 类的 main 方法中,位于 Encoding.java 文件的第 83 行。
4、java对字符的处理
在 java 应用软件里,多处会涉及到字符集编码。有的地方需要进行恰当的设置,有的地方需要进行一定的处理。
3.1. getBytes(charset)
这是一个用于 java 字符串处理的标准函数。它的作用是把字符串所代表的字符依据 charset 进行编码,然后以字节的形式呈现出来。需要注意的是,在 java 内存中,字符串始终是按照 unicode 编码来存储的。正常情况下(即没有错误的时候),“中文”存储为“4e2d 6587”。若 charset 为“gbk”,它会被编码为“d6d0 cec4”,接着返回字节“d6 d0 ce c4”。若 charset 为“utf8”,则最终是“e4 b8 ad e6 96 87”。如果是“iso8859 - 1”这种情况,因为无法进行编码,所以最终会返回“3f 3f”,也就是两个问号。
3.2. new String(charset)
这是一个用于 Java 字符串处理的标准函数。它与上一个函数的作用相反。该函数会将字节数组按照指定的 charset 编码进行组合识别,然后将其转换为 unicode 进行存储。参考上述getBytes的例子可知,“gbk”与“utf8”都能得出正确结果“4e2d 6587”,然而“iso8859 - 1”最终却变成了“003f 003f”(两个问号)。
utf8 能够用来表示和编码所有的字符,所以新的字符串(通过将字符串 str 使用“utf8”编码后再用“utf8”解码得到)与原字符串 str 是相等的,也就是完全可逆的。
3.3. setCharacterEncoding()