在 java 中,为什么 FileInputStream 的方法 read() 不会抛出“不兼容的类型:可能的有损转换”?
In java, how come method read() from FileInputStream works does not throw " incompatible types: possible lossy conversion"?
我目前正在经历 Java I/O tutorial 并且很难理解 FileInputStream class 的 read() 方法。我知道根据文档,read() 方法从流中读取 "byte" 数据,returns 表示字节(0 到 256 之间)的整数,如果到达文件末尾则为 -1。
Byte in java 的范围在 -128 到 127 之间,所以,当我编辑 xanadu.txt 并添加 ASCI 符号“ƒ”(其十进制值为 131)时,怎么会这样呢? java 不会通过抛出值 131 超出字节定义的范围(-128 和 127)的错误来抱怨?当我尝试使用文字对此进行测试时,我得到了两个不同的结果。
以下作品:
byte b = 120;
int c = b;
System.out.println((char)c);
Output: x
但这不起作用(即使添加到 xanadu.txt 时它起作用):
byte b = 131;
int c = b;
System.out.println((char)c);
Output: error: incompatible types: possible lossy conversion from int to byte
byte b = 131;
我尝试使用字节显式转换:(这怎么可能?)
byte b = (byte)131;
int c = b;
System.out.println((char)c);
Output: テ
关于 I/O 流,我完全是新手,请有人帮助我理解它。
编辑:事实证明我缺乏关于类型转换概念的知识,特别是在理解 "Widening" 和 "Narrowing" 之间的区别方面。阅读更多关于这些概念的信息有助于我理解为什么显式(也称为缩小)转换有效。
请允许我解释一下:请看第三个代码块,我在其中将文字“131”显式转换为字节类型。如果我们要将文字 131 转换为 32 位有符号 2 的补码整数的二进制形式,我们将得到 00000000 00000000 00000000 10000011,这是 32 位或 4 个字节。回想一下 Java 数据类型 'byte' 只能容纳 8 位有符号 2 的补码整数,因此,131 超出范围,因此我们得到错误 "possible lossy conversion from int to byte"。但是,当我们明确地将它转换为字节时,我们是 'chopping off' 或正确的术语是 'narrowing' 二进制到 8 位整数。因此,当我们这样做时,生成的二进制文件为 10000011,十进制值为 -125。由于 -125 在 -128 和 127 的范围内,byte 可以毫无问题地接受和存储它。现在,当我尝试描述 int c 中字节的值时,会发生隐式或 "widening" 转换,其中 8 位二进制形式的 -125 10000011 被转换为等效的 -125 二进制形式的 32 位 11111111 11111111 11111111 10000011。最后,system.out 试图输出 (char)c 的值,这是另一个显式或 "narrowing" 转换,它试图从 32 位有符号缩小到 16 位无符号。转换完成后,我们得到二进制形式的 11111111 10000011。现在,当这个二进制文件被 java 转换成字符形式时,它 returns テ。
我可以总结说它有助于将所有内容转换为二进制形式并从那里开始。但请确保您了解编码和 2's complement
byte b = 131; // this is 8 bits type, but >8 bits value
int c = b; // this is 32 bits type
System.out.println((char)c); // this is 16 bits type
Output: error: incompatible types: possible lossy conversion from int to byte
byte b = 131;
131的二补编码为:
2^7+2^1+2^0
^^^
sign bit
131 将不适合 signed 字节,而当从字节转换为 int 时 two complement representation that is used for signed types. The highest bit=sign bit is set which gets extended 中没有溢出。
Java 编译器注意到 131 不适合导致错误的字节。
我不知道你从哪里得到值 131,但据我所知,LATIN SMALL LETTER F WITH HOOK (ƒ) 不在原始 ASCII 字符集中,而是在扩展 ASCII 中,与十进制值 159。参见 here。它也以 UTF-16 编码(Java char
的编码方式)为十六进制 192(十进制值 402)。
首先,确保您的文本文件采用扩展 ASCII 编码,而不是 UTF-8(最有可能的编码)。然后你可以使用FileInputStream
到read
文件,你会得到159
.
请注意,159
超出了 Java byte
类型的范围。这很好,因为 read
return 是 int
。但是,如果文本文件以 UTF-8 编码,则 ƒ 以 2 个字节编码,因此 read
将一次读取一个字节。
您的第二个代码块不起作用,因为如您所说,byte
从 -128 变为 127,因此 131 显然不合适。
您的第三个代码块将 131 强制为一个字节,这会导致溢出,并且值 "wraps back around" 变为 -125。 b
和 c
都是 -125。当您将其转换为 char
时,它变为 65411,因为此转换涉及先将整数填充为 16 位,然后将其视为无符号整数。
当您使用 FileInputStream.read
而不是自己进行这些转换时这一切都有效的原因是因为 read
实际上 return 是 int
,而不是 byte
。只是int
它returns永远在-1~255范围内。这就是为什么我们说“read
returns一个字节”,但它的实际return 类型是 int
.
我目前正在经历 Java I/O tutorial 并且很难理解 FileInputStream class 的 read() 方法。我知道根据文档,read() 方法从流中读取 "byte" 数据,returns 表示字节(0 到 256 之间)的整数,如果到达文件末尾则为 -1。
Byte in java 的范围在 -128 到 127 之间,所以,当我编辑 xanadu.txt 并添加 ASCI 符号“ƒ”(其十进制值为 131)时,怎么会这样呢? java 不会通过抛出值 131 超出字节定义的范围(-128 和 127)的错误来抱怨?当我尝试使用文字对此进行测试时,我得到了两个不同的结果。
以下作品:
byte b = 120;
int c = b;
System.out.println((char)c);
Output: x
但这不起作用(即使添加到 xanadu.txt 时它起作用):
byte b = 131;
int c = b;
System.out.println((char)c);
Output: error: incompatible types: possible lossy conversion from int to byte
byte b = 131;
我尝试使用字节显式转换:(这怎么可能?)
byte b = (byte)131;
int c = b;
System.out.println((char)c);
Output: テ
关于 I/O 流,我完全是新手,请有人帮助我理解它。
编辑:事实证明我缺乏关于类型转换概念的知识,特别是在理解 "Widening" 和 "Narrowing" 之间的区别方面。阅读更多关于这些概念的信息有助于我理解为什么显式(也称为缩小)转换有效。
请允许我解释一下:请看第三个代码块,我在其中将文字“131”显式转换为字节类型。如果我们要将文字 131 转换为 32 位有符号 2 的补码整数的二进制形式,我们将得到 00000000 00000000 00000000 10000011,这是 32 位或 4 个字节。回想一下 Java 数据类型 'byte' 只能容纳 8 位有符号 2 的补码整数,因此,131 超出范围,因此我们得到错误 "possible lossy conversion from int to byte"。但是,当我们明确地将它转换为字节时,我们是 'chopping off' 或正确的术语是 'narrowing' 二进制到 8 位整数。因此,当我们这样做时,生成的二进制文件为 10000011,十进制值为 -125。由于 -125 在 -128 和 127 的范围内,byte 可以毫无问题地接受和存储它。现在,当我尝试描述 int c 中字节的值时,会发生隐式或 "widening" 转换,其中 8 位二进制形式的 -125 10000011 被转换为等效的 -125 二进制形式的 32 位 11111111 11111111 11111111 10000011。最后,system.out 试图输出 (char)c 的值,这是另一个显式或 "narrowing" 转换,它试图从 32 位有符号缩小到 16 位无符号。转换完成后,我们得到二进制形式的 11111111 10000011。现在,当这个二进制文件被 java 转换成字符形式时,它 returns テ。
我可以总结说它有助于将所有内容转换为二进制形式并从那里开始。但请确保您了解编码和 2's complement
byte b = 131; // this is 8 bits type, but >8 bits value
int c = b; // this is 32 bits type
System.out.println((char)c); // this is 16 bits type
Output: error: incompatible types: possible lossy conversion from int to byte
byte b = 131;
131的二补编码为:
2^7+2^1+2^0
^^^
sign bit
131 将不适合 signed 字节,而当从字节转换为 int 时 two complement representation that is used for signed types. The highest bit=sign bit is set which gets extended 中没有溢出。
Java 编译器注意到 131 不适合导致错误的字节。
我不知道你从哪里得到值 131,但据我所知,LATIN SMALL LETTER F WITH HOOK (ƒ) 不在原始 ASCII 字符集中,而是在扩展 ASCII 中,与十进制值 159。参见 here。它也以 UTF-16 编码(Java char
的编码方式)为十六进制 192(十进制值 402)。
首先,确保您的文本文件采用扩展 ASCII 编码,而不是 UTF-8(最有可能的编码)。然后你可以使用FileInputStream
到read
文件,你会得到159
.
请注意,159
超出了 Java byte
类型的范围。这很好,因为 read
return 是 int
。但是,如果文本文件以 UTF-8 编码,则 ƒ 以 2 个字节编码,因此 read
将一次读取一个字节。
您的第二个代码块不起作用,因为如您所说,byte
从 -128 变为 127,因此 131 显然不合适。
您的第三个代码块将 131 强制为一个字节,这会导致溢出,并且值 "wraps back around" 变为 -125。 b
和 c
都是 -125。当您将其转换为 char
时,它变为 65411,因为此转换涉及先将整数填充为 16 位,然后将其视为无符号整数。
当您使用 FileInputStream.read
而不是自己进行这些转换时这一切都有效的原因是因为 read
实际上 return 是 int
,而不是 byte
。只是int
它returns永远在-1~255范围内。这就是为什么我们说“read
returns一个字节”,但它的实际return 类型是 int
.