【问题标题】:Why does ByteArrayOutputStream use int?为什么 ByteArrayOutputStream 使用 int?
【发布时间】:2014-01-29 18:37:04
【问题描述】:

也许有人可以帮助我理解,因为我觉得我遗漏了一些可能会影响我的程序运行方式的东西。

我正在使用 ByteArrayOutputStream。除非我错过了一些重要的东西,否则这个类的重点是创建一个 byte[] 数组以供其他用途。

但是,BAOS 上的“普通”写入函数采用 int 而不是字节 (ByteArrayOutputStream.write)。

根据this(Primitive Data Types)页面,在Java中,int是32位数据类型,byte是8位数据类型。

如果我写这段代码...

int i = 32;
byte b = i;

我收到一条警告,提示可能有损转换需要对此进行更改...

int i = 32;
byte b = (byte)i;

我真的对 write(int) 感到困惑......

【问题讨论】:

  • 请注意,是OutputStream.write 决定了这一点,而不是真正的ByteArrayOutputStream
  • 是的,ByteArrayOutputStream 几乎不能这样做,但是它的父类可以这样做。
  • 使用byte 数据类型可能是个坏主意,byte[] 的形式除外。 short 也是如此,恕我直言。将 byte 和 short 视为必要的邪恶,这只对让您读写奇怪的二进制数据格式有好处。不要使用字节/短变量、方法参数或返回类型。 (不,你不会以任何实质性的方式节省空间(除了在极少数情况下),而且你的代码实际上可能会更慢。)
  • 感谢大家的讨论。我很好奇强制转换许多其他函数的动机和避免确实是有道理的,即使它在我的大脑中微不足道。我正在使用 BAOS 为我自己的内部虚拟机生成自定义字节码,所以我还想确认我没有写一些天生就坏掉的东西。

标签: java


【解决方案1】:

为了方便0x7F 以上的无符号字节,发生这种情况。 int 将被默默地缩小以写入。事实上,代码does that 带有(byte) 演员表。

正如英戈所说:

一个可能的原因可能是要写入的字节通常是某些操作的结果,该操作自动将其操作数转换为 int[,例如] 某些位操作。因此,代码中会充斥着字节转换,这对理解没有任何帮助。

【讨论】:

  • 目前还不清楚为什么它不能是 byte 参数 - 如果调用者愿意,他们可以很容易地进行转换......
  • @JonSkeet Java API 中的许多设计决策很难找到其背后的合理原因。事实是该方法接受一个 int 并在其中进行显式转换。
  • 这就是 what 的事实——但问题是 why。我同意在这里很难找到原因 - 但这只是意味着我们不知道所提问题的答案......
  • 一个可能的原因可能是要写入的字节通常是自动将其操作数转换为int 的某些操作的结果。像一些位操作。因此,代码中会充斥着byte 的强制转换,这对理解没有任何帮助。可以说,如果 Java 在今天被发明出来,最好只在 [] 后面允许字节和短,并保持所有方法签名都没有字节和短。
  • @Ingo:我会说确实使代码更清晰,因为它很明显你只写了一个字节而不是整个整数。如果您的计算结果为int,并且您调用write(value),那么四分之三的潜在数据将被删除并不是很明显......
【解决方案2】:

ByteArrayOutputStream 只是覆盖了OutputStream 中声明的抽象方法。所以真正的问题是为什么OutputStream.write(int) 是这样声明的,而它的既定目标是向流中写入一个字节。流的实现在这里无关紧要。

您的直觉是正确的 - 在我看来,这是设计的缺陷。是的,它会丢失数据,正如文档中明确指出的那样:

要写入的字节是参数 b 的低八位。 b 的高 24 位被忽略。

将其设为write(byte) 会更明智(在我看来)。唯一的缺点是你不能在没有强制转换的情况下使用文字值调用它:

// Write a single byte 0. Works with current code, wouldn't work if the parameter
// were byte.
stream.write(0);

这看起来不错,但不是 - 因为文字 0 的类型是 int,它不能隐式转换为 byte。您必须使用:

// Ugly, but would have been okay with write(byte).
stream.write((byte) 0);

对我来说,按原样设计 API 的理由还不够充分,但这就是我们所拥有的 - 并且自 Java 1.0 以来一直如此。不幸的是,如果没有整个地方的重大变化,现在就无法修复它。

【讨论】:

  • Java 中的字节是有符号的,因此如果该方法将byte 作为参数,我们就无法执行stream.write( 0xFF ) 并获得预期的全部字节。现在为什么 java 不支持无符号字节…
  • @JoshuaTaylor Java 实际上支持将整数类型解释为无符号整数类型,在某种程度上甚至支持无符号长整数类型。不明确支持这些东西是一个非常明智的决定。真正需要无符号(这非常罕见)的人将不得不研究一下位和移位运算符。那又怎样?
  • @JoshuaTaylor:你可以做到stream.write((byte) 0xff),那么正确的事情就会发生。
  • @Ingo:不,我会说让byte 签名是一个非常糟糕的主意。未签名几乎总是更有用。 (C# 同时有bytesbyte,我不记得我上次使用sbyte 是什么时候了。)另见oracle.com/technetwork/articles/javase/…
  • @Ingo:问题在于将byte 作为一个数字类型开始。以我的经验,它通常用于没有真正作为普通数字处理的值 - 就像char 值不是一样。 (是的,您可以对它们执行相同的操作,但它们通常不代表某种量级。)char 是无符号的,那么为什么 byte 不应该呢?
【解决方案3】:

这主要是因为Java 虚拟机模型的堆栈讨厌byte,但喜欢int堆栈使用32 位插槽,与int 的大小匹配。 p>

但是您会注意到 java *确实* 喜欢 byte[] 引用。但那是因为数组的内容存储在堆中(而不是堆栈)。每当一个特定的byte 被寻址并移动到堆栈(bipushsipush 操作码)时,它们就会立即转换为整数。

但有时 java 实际上使用 257(!) 值。当 InputStream#read() 返回 256 个值,但当它没有内容时,它将返回 -1 值。或者,也可以抛出 EOFException(就像其他一些方法一样),但 java 中的异常很慢。

即使您不需要 -1 的值来替换 OutputStream#write 它是连贯的,并且可以减少强制转换。但是,是的,它也具有误导性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-06
    • 2016-10-31
    • 1970-01-01
    • 1970-01-01
    • 2010-09-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多