Java 13 SE 规范不需要缓存盒装 Byte 对象吗?

lpe*_*tru 6 java boxing specifications java-13

阅读 JAVA 13 SE 规范,我在第 5 章第 5.1.7 节中找到。拳击转换以下保证:

如果被装箱的值 p 是对 boolean、char、short、int 或 long 类型的常量表达式(第 15.28 节)求值的结果,并且结果为 true、false,则为 '\u0000' 到 ' 范围内的字符\u007f'(含),或 -128 到 127(含)范围内的整数,然后让 a 和 b 是 p 的任意两次装箱转换的结果。a == b 总是这样

我觉得奇怪的是,字节类型的值被排除在该措辞之外。

例如,在如下代码中:

Byte b1=(byte)4;
Byte b2=(byte)4;
System.out.println(b1==b2);
Run Code Online (Sandbox Code Playgroud)

我们有一个byte类型的常量表达式,装箱后b1和b2的值可能是也可能不是同一个对象。

在没有演员的情况下,它实际上以相同的方式工作:

Byte b1=4;
Run Code Online (Sandbox Code Playgroud)

在这里,我们在赋值上下文中有一个 int 类型的常量表达式。所以,根据规范

如果变量的类型为 Byte、Short 或 Character,并且常量表达式的值可分别以 byte、short 或 char 类型表示,则可以使用缩窄原语转换后跟装箱转换。

因此表达式将被转换为字节,并且该字节类型的值将被装箱,因此不能保证该值是实习的。

我的问题是我对规范的解释是否正确,还是我遗漏了什么?我查看了规范是否需要使用 Byte.valueOf() 方法进行装箱(为此可以保证),但事实并非如此。

tev*_*dar 5

你理解正确。同一 5.1.7 部分的结尾(来自https://docs.oracle.com/javase/specs/jls/se13/html/jls-5.html)说:

如果需要分配包装类(Boolean、Byte、Character、Short、Integer、Long、Float 或 Double)之一的新实例并且没有足够的存储空间,则装箱转换可能会导致 OutOfMemoryError 。

Byte 如果预计它是预先生成的,则不会在那里。

另一件事,仍然来自同一段:

理想情况下,装箱一个原始值总是会产生一个相同的引用。实际上,使用现有的实现技术这可能是不可行的。上面的规则是一种务实的妥协,要求某些共同的价值观总是被装进无法区分的对象中。实现可能会延迟或急切地缓存这些。对于其他值,规则不允许程序员对装箱值的身份进行任何假设。这允许(但不要求)共享部分或全部这些引用。


不是“证明”,但也许值得一提:Integer描述拳击诺言,13甚至7

 * Cache to support the object identity semantics of autoboxing for values between
 * -128 and 127 (inclusive) as required by JLS.
Run Code Online (Sandbox Code Playgroud)

文本是相同的,即使实现随着时间的推移而改变。

Byte没有这样的语句,尽管它也被缓存了。713。缓存在两者中都存在,但没有一个字关于它(也没有关于拳击)。

  • 我认为,OutOfMemoryError 可能发生的事实是另一回事。尽管需要缓存布尔值,但也提到了布尔值。该值可能会被延迟创建,然后才被缓存。将第一个值添加到缓存时,创建第一个值时可能会发生 OutOfMemoryError。 (6认同)
  • 除了[其文档](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/lang/Byte. html#valueOf(byte)),它表示“*所有字节值都被缓存*” (3认同)

Hol*_*ger 5

TL;DR 这已在JDK 14 中修复,现在包括byte.

我认为这是一个规范错误,是多次重写的结果。

请注意JLS 6 对应的文本:

如果值p被装箱是truefalse,一byte,一个char在范围\ u0000的至\ u007f,或intshort数-128和127之间,然后让R1R2是任意两个装箱转换的结果页。r1 == r2总是如此。

在这里,byte明确提到被无条件地装箱到具有规范身份的对象。由于所有字节都在 -127..128 范围内,因此无需添加此类限制。

但请注意,long尚未提及。

然后,见JDK-7190924,5.1.7:JLS 没有提到自动装箱 longs 的缓存

在评论中,您可以看到它是如何发生的。

在他的第一条评论中,亚历克斯巴克利批评“字节是一种类型,而不是值”,没有考虑到“字节”可能意味着“字节范围内的所有值”,但由于他还假设“数字”最初意味着“文字” "(而不是例如“数值”),他关注所有整数文字要么是 int 要么是 long 的观点。

他的第一稿使用术语“整数文字”并完全删除了类型。它的一个稍微修改的版本使其成为Java 8 JLS

如果p被装箱的值是int介于-128127包含(第 3.10.1 节)之间的整数文字,或布尔文字truefalse(第 3.10.3 节),或介于'\u0000''\u007f'包含(第 3.10.4 节)之间的字符文字,则让ab是 的任意两个装箱转换的结果p。情况总是如此a == b

所以在 Java 8 中,类型根本不重要,但保证仅限于文字。

所以这意味着

Byte b1 = 4;
Run Code Online (Sandbox Code Playgroud)

由于整数文字,确实评估为规范对象,其中

Byte b1 = (byte)4;
Run Code Online (Sandbox Code Playgroud)

可能不是,因为它(byte)4是一个常量表达式,但不是一个文字。

多年后,在他的下一条评论中,他考虑了确实可以键入的“常量表达式”,并重新表述了该短语,将类型“boolean、char、short、int 或 long”带回来,添加了 long,但忘记了关于“字节”。

这个结果短语就是您引用的内容,自 Java 9 起就在规范中。

省略byte肯定不是故意的,因为没有合理的理由省略它,特别是当它之前存在时,所以从字面上看,这将是一个重大变化。

但是,将缓存限制为编译时常量,当 JLS 6 为范围内的所有值指定它时没有这样的限制,这已经是一个突破性的变化(这在实践中无关紧要,只要它是通过 实现的valueOf,它无法知道该值是否源自编译时常量)。

作为旁注,文档Byte.valueOf(byte)明确指出:

...所有字节值都被缓存

只要从 Java 7 开始

  • 更新。JLS 14 包含“byte”,这表明它是 JLS 13 中的无意遗漏。 (2认同)