在Java中:为什么有些流方法接受int而不是byte或char?

3
为什么有些写入字节/字符到流的方法要用int而不是byte/char?
有人告诉我,对于int而非char,因为Java中的char只有2个字节长度,这已经足以表示大多数常用字符符号,但对于某些字符符号(如中文等),字符被表示为超过2个字节,因此我们使用int。
这种解释有多接近真相?
编辑: 我使用“stream”一词来表示二进制和字符流(而不仅仅是二进制流)。
谢谢。

你应该具体说明这一点。特别是,流通常没有与char相关的方法,因为它们用于二进制数据;文本数据需要适当的编码,而流本身不应该知道。 - Jon Skeet
普通流只能处理二进制数据,即字节,而不能处理字符或整数。如果您想发送文本,我建议您使用带有所选编码的Writer,对于char、short、int等类型,您可以使用像DataOutputStream这样的适配器。 - Peter Lawrey
谢谢,但我不是在问如何将字符串写入文件! - Muhammad Hewedy
1
使用“流”这个词来表示二进制和字符流(不仅仅是二进制流),如果你使用这个词的话,那么你就用错了。你应该学会使用正确的语义,否则只会造成混乱。流只适用于字节,其他接口(Writer/Reader)处理字符和其他更高级别的抽象。 - user177800
6个回答

5
假设在此时,您具体谈论的是Reader.read()方法,那么您所转述的“某人”的说法实际上是不正确的。确实有一些Unicode代码点的值大于65535,因此不能表示为单个Java char。但是,Reader API实际上产生一系列Java char值(或-1),而不是一系列Unicode代码点。这在javadoc中明确说明。
如果您输入的内容包含一个(适当编码的) Unicode 码点,大于 65535,那么您需要调用两次 `read()` 方法才能看到它。您将得到一个 UTF-16 代理对;即两个 Java `char` 值,它们 `一起` 表示该码点。事实上,这符合 Java String、StringBuilder 和 StringBuffer 类的工作方式;它们都使用基于 UTF-16 的表示形式...带有嵌入的代理对。
`Reader.read()` 返回 `int` 而不是 `char` 的真正原因是允许它返回 `-1`,以表示没有更多的字符可读取。同样的逻辑解释了为什么 `InputStream.read()` 返回 `int` 而不是 `byte`。
假设Java的设计者们可能会规定read()方法抛出异常来表示“流结束”的条件。然而,这只是用另一种方式取代了一个潜在的错误源(未测试结果),而变成了另一个错误源(未处理异常)。此外,异常相对比较耗费资源,而流结束并不是真正意义上的意外/异常事件。总之,在我看来,当前的方法更好。
(另一个关于Reader API 16位特性的线索是read(char[]...)方法的签名。如果没有使用代理对,它如何处理大于65535的码点?)
编辑: DataOutputStream.writeChar(int)似乎有点奇怪。但是,javadoc清楚地说明了参数将作为2字节值写入。实际上,实现只将底部两个字节写入底层流中。
我认为没有一个好的理由来解释这个问题。无论如何,有一个与此相关的错误数据库条目(4957024),标记为“11-已关闭,不是缺陷”,并附有以下评论:

“这不是一个很好的设计或借口,但它太深入了,我们不能改变。”

这在某种程度上承认了这确实是一个缺陷,至少从设计角度来看。
但是,在我看来,这不值得大惊小怪。

1
不要忘记,Java的设计者和最初的目标受众都习惯于C语言,因此他们让read()尽可能地与fgetc()相似。 - Licky Lindsay
嗯...我不同意。如果他们想要完全相似,他们会将方法命名为getC或其他类似的名称。我确信设计师们受到了C库的启发,但有很多迹象表明他们并没有试图去模仿它们。 - Stephen C
你对read方法的返回类型为什么很模糊的解释。那么,能否请您解释一下为什么DataOutputStream#writeChar方法需要一个整数参数呢?http://java.sun.com/javase/6/docs/api/java/io/DataOutputStream.html#writeChar%28int%29 - Muhammad Hewedy
不要忘记,read()函数必须返回带外值和带内值。这才是真正的解释,而你对fgetc的猜测并不重要。 - user207421
我回答了关于DataOutputStream#writeChar的问题,请参见编辑,还有其他您期望的内容吗? - Stephen C

3
我不确定您具体指的是什么,但也许您在考虑 InputStream.read()?它返回一个整数而不是字节,因为返回值被重载以表示流的结尾,表示为 -1。由于有 257 种不同的可能返回值,一个字节是不足够的。
否则,也许您可以提供一些更具体的示例。

2

有几种可能的解释。

首先,正如一些人所指出的那样,这可能是因为read()必须返回一个int,因此让write()接受一个int可以被视为优雅的做法,以避免强制转换:

int read = in.read();
if ( read != -1 )
   out.write(read);
//vs
   out.write((byte)read);

第二,避免使用其他类型的转换可能会更好:
//write a char (big-endian)
char c;
out.write(c >> 8);
out.write(c);

//vs
out.write( (byte)(c >> 8) );
out.write( (byte)c );

0

最大可能的代码点是0x10FFFF,这无法适配一个char。然而,流方法是面向字节的,而写入器方法则是面向16位的。OutputStream.write(int)写入单个字节,Writer.write(int)仅查看低位16位。


0
在Java中,Streams用于原始字节。要写入字符,请将Stream包装在Writer中。
虽然Writer确实有write(int)(它写入16个低位比特;它是一个int,因为byte太小,short太小了,因为它是有符号的),但你应该使用write(char[])write(String)

0

可能是为了与返回整数的read()方法对称,没有什么严重的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接