【问题标题】:Are there any real life uses for the Java _signed_ byte primitive type?Java _signed_ byte 原始类型有什么实际用途吗?
【发布时间】:2011-10-17 01:44:32
【问题描述】:

由于某些莫名其妙的原因,byte 原始类型是用 Java 签名的。这意味着有效值是 -128..127,而不是通常的 0..255 范围,表示一个字节中的 8 个有效位(没有符号位)。

这意味着所有字节操作代码通常都会进行整数计算并最终屏蔽掉最后 8 位。

我想知道是否存在任何 Java byte 原始类型非常适合的现实生活场景,或者这只是一个完全无用的设计决策?


编辑:唯一的实际用例是本地代码的单字节占位符。换句话说,不能在 Java 代码中作为字节进行操作。


编辑:我现在看到一个内部紧密循环需要除以 7(数字 0..32)的地方,因此可以使用字节作为数据类型来完成查找表,因此可以保持较低的内存使用量L1 缓存使用情况。这不是指有符号/无符号,而是实际使用的情况。

【问题讨论】:

  • 我经常编写操作字节数组的代码。这是很常见的事情。
  • @Chris Lively - 我不同意。 Java 大多是好的设计决策,也有一些坏苹果
  • 签名的不仅仅是byte。除了char,我相信所有原始整数数据类型(intshortlongbyte)都是有符号的。没有可以应用于其中任何一个的无符号修饰符。在一致性的意义上,这是有道理的。我认为真正的问题是缺少无符号整数数据类型是否有意义。
  • @Chris,“Write Once Run Everywhere”——足以让我们忍受大多数坏东西。他们是对的。
  • @Thorbjørn Ravn Andersen:嗯……我的 Java 朋友实际上说的有点不同:“一次编写,到处调试”;)一切都很有趣。无论哪种方式,为一个好问题 +1。

标签: java types byte


【解决方案1】:

Josh Bloch 最近mentioned in a presentation 认为这是语言中的错误之一。

我认为这背后的原因是 java 没有无符号数字类型,byte 应该符合该规则。 (注:char 无符号,但不代表数字)

至于具体问题:我想不出任何例子。而且即使有例子,它们也会比 0..255 的例子少,并且可以使用掩码(而不是大多数)来实现

【讨论】:

  • 我知道他们更少。我很好奇它们是否存在。
  • 他早在 Neal Gafter 的“Java Puzzlers”(2005 年)中就提到了这一点。来自谜题 24:“语言设计者的教训是字节值的符号扩展是错误和混乱的常见来源。”
  • Gosling:"Quiz any C developer about unsigned, and pretty soon you discover that almost no C developers actually understand ... unsigned arithmetic",所以他们决定将其排除在外。我认为他们的主要错误是认为byte 的用例与int 的用例基本相同。
  • @j-g-faustus:C 对无符号类型的问题都源于这样一个事实,即相同大小的有符号类型和无符号类型之间的操作都强制为无符号。小于int 的无符号类型不会造成任何问题,因为没有小于int 的有符号类型可以与之交互。至于更大的类型,恕我直言,应该有三个:32 位自然数、32 位抽象代数环和 64 位抽象代数环。对数字的操作要么产生算术正确的结果,要么抛出溢出异常,...
  • ...和操作——除了比较——数字和环之间应该隐式地将数字转换为环,但环不应该转换回数字。像 hashCode 这样使用整数包装行为的东西应该使用 32 位环类型进行计算,然后使用合适的方法将环类型转换回 int,将环类型转换为有符号整数。
【解决方案2】:

byte, short, char 类型大多无用,除非在数组中使用以节省空间。

JavaJVM 都没有真正支持它们。几乎所有对它们的操作都会先将它们提升为intlong。我们甚至不能写类似的东西

short a=1, b=2;
a = a + b;  // illegal
a = a << 1; // illegal

那么为什么还要费心在byte, short, char 类型上定义操作呢?

他们所做的只是偷偷扩大转换范围,这会让程序员大吃一惊。

【讨论】:

  • +1 但char 有时可以用作字符,而不是数字
  • 如果您需要对char 数据进行计算,最好使用int。幸运的是,在charint 之间进行转换比说byteint 更愉快
【解决方案3】:

令人惊讶的是,我上周第一次在 Java 中使用了byte,所以我确实有一个(尽管不寻常的)用例。我正在写一个native Java function,它可以让你在一个库中实现一个可以被Java调用的函数。 Java 类型需要转换为本地语言中的类型,在本例中为 C

该函数需要一个字节数组,但是(当时完全忘记了byte 类型)我让它接受了一个char[]。 Java 为 C 函数生成的签名给出了该参数的类型为jcharArray,它可以转换为一堆jchars,它们在jni.hunsigned short 中进行了类型定义。自然,这不是相同的大小——它是 2 个字节而不是 1 个字节。这会导致底层代码出现各种问题。使 Java 类型 byte[] 产生 jbyteArray,而 Linux 上的 jbyte 被类型定义为 signed char,这是正确的大小

【讨论】:

  • 您是在 Java 中操作它们,还是只是“保留空间”让 JNI 继续工作?
  • @Thor 只是传递给原生函数;该代码实际上类似于this.nativeFunction(new byte[] {0x04, ...});
【解决方案4】:

对我来说,带有 8 位签名样本的数字化声音(或任何其他信号)似乎是唯一合理的例子。当然,处理此类信号不需要有符号字节,并且可以争论 Java 字节是否“完全适合”。

我个人认为没有未签名是一个错误。不仅因为无符号字节/整数有更多用途,而且因为我更喜欢更强大的类型系统。如果能够指定负数无效并允许编译器检查和运行时异常异常,那就太好了。

【讨论】:

  • 是的,我认为ubyteushortuint 等变体在某些情况下可能很有用,例如位掩码或处理字节流。
【解决方案5】:

byte 广泛用于 Java Card 的小程序开发。因为卡片的资源有限,每一点内存都是宝贵的。顺便说一句,卡处理器在处理整数值方面存在限制。 int 类型支持是可选的,不支持 java.lang.String,因此所有整数运算和数据存储都由 byteshort 变量和数组完成。由于整数文字属于 int 类型,因此它们应该在整个代码中显式转换为 byteshort。与卡的通信通过 APDU 命令作为bytes 的数组传递给小程序,该数组应分解为bytes 以解码命令类、指令和参数。查看以下代码,您会看到 byteshort 类型对 Java Card 开发的重要性:

package somepackage.SomeApplet;

import javacard.framework.*;
import org.globalplatform.GPSystem;
import org.globalplatform.SecureChannel;

public class SomeApplet extends Applet {

    // Card status
    private final static byte ST_UNINITIALIZED     = (byte) 0x01;
    private final static byte ST_INITIALIZED       = (byte) 0x02;

    // Instructions & Classes
    private final static byte PROP_CLASS           = (byte) 0x80;     

    private final static byte INS_INIT_UPDATE      = (byte) 0x50;
    private final static byte INS_EXT_AUTH         = (byte) 0x82;

    private final static byte INS_PUT_DATA         = (byte) 0xDA;
    private final static byte INS_GET_RESPONSE     = (byte) 0xC0;
    private final static byte INS_GET_DATA         = (byte) 0xCA;


    private final static short SW_CARD_NOT_INITIALIZED       = (short) 0x9101;  
    private final static short SW_CARD_ALREADY_INITIALIZED   = (short) 0x9102;  

    private final static byte OFFSET_SENT = 0x00;
    private final static byte OFFSET_RECV = 0x01;
    private static short[] offset;

    private static byte[] fileBuffer;
    private static short fileSize = 0;

    public static void install(byte[] bArray, short bOffset, byte bLength) {
        new SomeApplet( bArray, bOffset, bLength);
    }

    public RECSApplet(byte[] bArray, short bOffset, byte bLength) {
        offset = JCSystem.makeTransientShortArray((short) 2, JCSystem.CLEAR_ON_RESET);
        fileBuffer = new byte[FILE_SIZE];

        byte aidLen = bArray[bOffset];
        if (aidLen== (byte)0){
            register();
        } else {
            register(bArray, (short)(bOffset+1), aidLen);
        }
    }

    public void process(APDU apdu) {
        if (selectingApplet()) {
            return;
        }
        byte[] buffer = apdu.getBuffer();
        short len = apdu.setIncomingAndReceive(); 

        byte cla = buffer[ISO7816.OFFSET_CLA];
        byte ins = buffer[ISO7816.OFFSET_INS];
        short lc = (short) (buffer[ISO7816.OFFSET_LC] & 0x00ff); 

        while (len < lc) {
            len += apdu.receiveBytes(len);
        }

        SecureChannel sc = GPSystem.getSecureChannel();
        if ((short)(cla & (short)0x80) == ISO7816.CLA_ISO7816) {
            switch (ins) {
                case INS_PUT_DATA:
                    putData(buffer, ISO7816.OFFSET_CDATA, offset[OFFSET_RECV], len);

                    if ((cla & 0x10) != 0x00) {
                        offset[OFFSET_RECV] += len;
                    } else {
                        fileSize = (short) (offset[OFFSET_RECV] + len);
                        offset[OFFSET_RECV] = 0;
                    }
                    return;

                case INS_GET_DATA:
                case INS_GET_RESPONSE:
                    sendData(apdu);
                    return;
                default:
                    ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
            }

        }
        else if ((byte) (cla & PROP_CLASS) == PROP_CLASS) {
            switch (ins) {
                case INS_INIT_UPDATE:
                case INS_EXT_AUTH:
                    apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, sc.processSecurity(apdu));
                    return;
                default:
                    ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
            }
        } else
            ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
    }

    // Some code omitted

}

【讨论】:

    【解决方案6】:

    我认为它是为了与short和int保持一致而签名的。

    至于它是否被大量使用,它使“字节数组”的概念成为一种构造而不是一种原语。

    这就是我所拥有的。 :)

    【讨论】:

    • @EJP 公平吗?这意味着 +8 净代表
    • @Louis Rhys 如果反对者费心解释反对票,我们可以讨论它。正如我所看到的那样,答案是完全正确的,当然是明智的,而且一开始就不值得投反对票。
    【解决方案7】:

    在单词大于 8 位的机器上,如果您希望将大量适合 8 位范围的值存储到单个数组中,这会有些用处,但通常使用它们不是一个好主意,因为byte 实际上比 int 更努力地耗尽内存。

    请记住,Java 是为非常小的消费设备(机顶盒)设计的。我希望如果在小型 8 位微处理器上以这种方式使用它,它会更有用,因为它完全适合字长,并且可以用于非常小规模的一般“数学”运算。

    我可以看到使它有符号的唯一原因是与 int 交互的无符号字节可能有点令人困惑——但我不相信它比有符号的字节更令人困惑!

    【讨论】:

      【解决方案8】:

      字节大小为 8 位。字节的大小有助于处理输入和输出,同时执行诸如写入文件或读取文件之类的功能。考虑一个场景,您想从键盘或任何文件中读取输入。如果您使用“字节”数据结构,您知道您一次接收一个字符,因为大小为 8 位。因此,每次收到输入流时,您就知道您实际上是一次收到一个字符。

      【讨论】:

        【解决方案9】:

        我在为 J2ME 编写软件和游戏时经常使用它。在大多数 J2ME 设备上,您的资源有限,因此将级别的映射存储在字节数组中比将其存储在 int 数组中占用的资源更少。

        【讨论】:

          【解决方案10】:

          我现在在 java 中使用字节来开发蓝牙 android 项目。

          【讨论】:

          • 你能解释一下你是如何使用它们的吗?没有任何解释或细节,这个答案不会增加太多。
          猜你喜欢
          • 2010-09-28
          • 2012-12-31
          • 1970-01-01
          • 2017-01-04
          • 1970-01-01
          • 2017-03-19
          • 2011-05-06
          • 2018-08-07
          • 2011-04-25
          相关资源
          最近更新 更多