Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
从编译到运行时,Java字符串编码是如何工作的_Java_String_Character Encoding - Fatal编程技术网

从编译到运行时,Java字符串编码是如何工作的

从编译到运行时,Java字符串编码是如何工作的,java,string,character-encoding,Java,String,Character Encoding,我最近意识到我并不完全理解Java的字符串编码过程 考虑以下代码: public class Main { public static void main(String[] args) { System.out.println(java.nio.charset.Charset.defaultCharset().name()); System.out.println("ack char: ^"); /* where ^ = 0x06, the ack

我最近意识到我并不完全理解Java的字符串编码过程

考虑以下代码:

public class Main
{
    public static void main(String[] args)
    {
        System.out.println(java.nio.charset.Charset.defaultCharset().name());
        System.out.println("ack char: ^"); /* where ^ = 0x06, the ack char */
    }
}
由于控制字符为,因此我选择
ack
char进行测试

现在我用不同的文件编码、UTF-8和。两者编译为完全相同的内容,每个字节由
md5sum
验证

然后我运行程序:

$ java Main | hexdump -C
00000000  55 54 46 2d 38 0a 61 63  6b 20 63 68 61 72 3a 20  |UTF-8.ack char: |
00000010  06 0a                                             |..|
00000012

$ java -Dfile.encoding=iso-8859-1 Main | hexdump -C
00000000  49 53 4f 2d 38 38 35 39  2d 31 0a 61 63 6b 20 63  |ISO-8859-1.ack c|
00000010  68 61 72 3a 20 06 0a                              |har: ..|
00000017

$ java -Dfile.encoding=windows-1252 Main | hexdump -C
00000000  77 69 6e 64 6f 77 73 2d  31 32 35 32 0a 61 63 6b  |windows-1252.ack|
00000010  20 63 68 61 72 3a 20 06  0a                       | char: ..|
00000019
无论使用哪种编码,它都能正确输出
0x06

好的,它仍然输出相同的
0x06
,这将被windows-1252代码页解释为可打印的[ACK]字符

这就引出了几个问题:

  • 编译的Java文件的代码页/字符集是否与编译它的系统的默认字符集相同?这两者总是同义的吗
  • 编译后的表示形式似乎并不依赖于编译时字符集,是否确实如此
  • 这是否意味着,如果Java文件中的字符串不使用当前字符集/区域设置的标准字符,则在运行时可能会对其进行不同的解释
  • 关于Java中的字符串和字符编码,我还应该了解什么

  • 如果使用不同的编码进行编译,这些编码只会影响源文件。如果源代码中没有任何特殊字符,则生成的字节码不会有任何差异

    对于运行时,使用操作系统的默认字符集。这独立于用于编译的字符集。

    Erm基于,并且两种编码中的ACK控制字符完全相同。您指出的链接的区别在于,DOS/Windows实际上是如何为Windows-1252中的大多数控制字符(如Heart/Club/Spade/Diamond字符和similey)提供符号的,而ISO-8859则没有

  • 源文件可以采用任何编码
  • 您需要告诉编译器源文件的编码(例如,
    javac-encoding…
    );否则,假定采用平台编码
  • 在类文件二进制文件中,字符串文本存储为(修改的)UTF-8,但除非您使用字节码,否则这并不重要(请参阅)
  • Java中的字符串始终是UTF-16(请参阅)
  • System.out
    将字符串从UTF-16转换为系统编码中的字节,然后再写入标准输出
  • 注:

    关于Java中字符串编码的“要知道什么”摘要:

    • 内存中的
      String
      实例是一个16位“代码单元”序列,Java将其处理为
      char
      值。从概念上讲,这些代码单元编码一系列“代码点”,其中代码点是“根据Unicode标准归属于给定字符的数字”。代码点的范围从0到100多万,尽管到目前为止只定义了10万左右。从0到65535的代码点被编码到单个代码单元中,而其他代码点使用两个代码单元。这个过程称为UTF-16(又名UCS-2)。有一些微妙之处(一些代码点无效,例如65535,在前65536中有2048个代码点的范围,精确地保留用于编码其他代码点)
    • 代码页等不会影响Java在RAM中存储字符串的方式。这就是为什么“Unicode”以“Uni”开头。只要您不使用字符串执行I/O,您就处在Unicode的世界中,每个人都使用相同的字符到代码点的映射
    • 字符集在将字符串编码为字节或从字节解码字符串时起作用。除非明确指定,否则Java将使用一个默认字符集,该字符集取决于用户“locale”,这是一个模糊的集合概念,表示是什么让日本的计算机说日语。当您使用
      System.out.println()
      打印字符串时,JVM会将字符串转换为适合这些字符所在位置的内容,这通常意味着使用字符集将其转换为字节,这取决于当前语言环境(或JVM对当前语言环境的猜测)
    • 一个Java应用程序是Java编译器。Java编译器需要解释源文件的内容,这些源文件在系统级别上只是一堆字节。Java编译器然后为其选择一个默认字符集,并根据当前语言环境进行选择,就像Java一样,因为Java编译器本身是用Java编写的。Java编译器(
      javac
      )接受命令行标志(
      -encoding
      ),该标志可用于覆盖该默认选择
    • Java编译器生成与语言环境无关的类文件。不管Java编译器用来解释源文件的字符集是什么,字符串文字最终都会以UTF-8编码的方式出现在这些类文件中。运行Java编译器的系统上的区域设置会影响源代码的解释方式,但一旦Java编译器理解字符串包含代码点编号6,那么这个代码点就是类文件中的代码点,而不是其他代码点。请注意,代码点0到127在UTF-8、CP-1252和ISO-8859-1中具有相同的编码,因此您获得的结果也就不足为奇了
    • 即使如此,
      String
      实例也不依赖于任何类型的编码,只要它们保留在RAM中,您可能希望对字符串执行的某些操作就依赖于语言环境。这不是编码的问题;但语言环境也定义了一种“语言”,因此大写和小写的概念取决于所使用的语言。通常的嫌疑犯是调用
      “unicode”.toUpperCase()
      :这会产生
      “unicode”
      ,除非当前语言环境是土耳其语,在这种情况下,您会得到
      “UNİcode”
      (“
      I
      ”有一个点)。这里的基本假设是