Java 13 SE 规范不需要缓存装箱字节对象吗?

Is caching of boxed Byte objects not required by Java 13 SE spec?

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

If the value p being boxed is the result of evaluating a constant expression (§15.28) of type boolean, char, short, int, or long, and the result is true, false, a character in the range '\u0000' to '\u007f' inclusive, or an integer in the range -128 to 127 inclusive, then let a and b be the results of any two boxing conversions of p. It is always the case that a == b

我觉得奇怪的是,字节类型的值被从该措辞中遗漏了。

例如,在这样的代码中:

Byte b1=(byte)4;
Byte b2=(byte)4;
System.out.println(b1==b2);

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

在没有强制转换的情况下,它实际上以相同的方式工作:

Byte b1=4;

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

A narrowing primitive conversion followed by a boxing conversion may be used if the variable is of type Byte, Short, or Character, and the value of the constant expression is representable in the type byte, short, or char respectively.

因此表达式将被转换为字节,并且该字节类型的值将被装箱,因此无法保证该值是 interned。

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

你没看错。同一 5.1.7 部分的末尾(来自 https://docs.oracle.com/javase/specs/jls/se13/html/jls-5.html)说:

A boxing conversion may result in an OutOfMemoryError if a new instance of one of the wrapper classes (Boolean, Byte, Character, Short, Integer, Long, Float, or Double) needs to be allocated and insufficient storage is available.

Byte如果是预生成的话就不会出现了

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

Ideally, boxing a primitive value would always yield an identical reference. In practice, this may not be feasible using existing implementation techniques. The rule above is a pragmatic compromise, requiring that certain common values always be boxed into indistinguishable objects. The implementation may cache these, lazily or eagerly. For other values, the rule disallows any assumptions about the identity of the boxed values on the programmer's part. This allows (but does not require) sharing of some or all of these references.


不是 "proof",但也许值得一提:Integer 描述拳击承诺,13 and even 7

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

文本是一样的,尽管实现随着时间的推移发生了变化。

Byte 没有这样的语句,尽管它也被缓存了。 7, 13。缓存在两者中都有,但没有一个关于它的词(也没有关于装箱)。

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

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

注意JLS 6 counterpart的文字:

If the value p being boxed is true, false, a byte, a char in the range \u0000 to \u007f, or an int or short number between -128 and 127, then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.

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

但请注意long没有被提及。

然后,遇见JDK-7190924, 5.1.7: JLS does not mention caching of autoboxed longs

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

Alex Buckley 在他的第一条评论中批评 "byte is a type, not a value",没有考虑 "byte" 可能意味着 "all values in the byte range",但由于他还假设 "number" 最初意味着 "literal"(而不是 "numeric value"),他关注的是所有整数文字要么是 int 要么是 long。

他的初稿使用了术语 "integer literal" 并完全删除了类型。它的一个稍微修改的版本进入了 Java 8 JLS:

If the value p being boxed is an integer literal of type int between -128 and 127 inclusive (§3.10.1), or the boolean literal true or false (§3.10.3), or a character literal between '\u0000' and '\u007f' inclusive (§3.10.4), then let a and b be the results of any two boxing conversions of p. It is always the case that a == b.

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

所以这意味着

Byte b1 = 4;

由于整型字面量而评估为规范对象,其中 as

Byte b1 = (byte)4;

可能不是,因为 (byte)4 是常量表达式而不是文字。

多年后,在他的下一条评论中,他考虑 "constant expressions",确实可以输入,并重新表述短语,恢复类型,"boolean, char, short, int, or long",添加了 long,但忘记了"byte".

此结果短语是您引用的内容,自 Java 9.

以来已在规范中

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

不过,将缓存限制为编译时常量,当 JLS 6 在没有这种限制的情况下为范围内的所有值指定它时,已经是一个重大更改(这在实践中并不重要,只要它是通过 valueOf 实现的,它无法知道该值是否源自编译时常量。

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

...all byte values are cached

只要since Java 7.