java为什么复制数组会减一_如果从数组中复制了Java,为什么Java需要对最终变量进行显式强制转换?...

从以下代码开始…

byte foo = 1;

byte fooFoo = foo + foo;

当我尝试编译此代码时,会得到以下错误…

Error:(5, 27) java: incompatible types: possible lossy conversion from int to byte

…但如果foo是最终的…

final byte foo = 1;

final byte fooFoo = foo + foo;

文件将成功编译。

继续执行以下代码…

final byte[] fooArray = new byte[1];

fooArray[0] = 1;

final byte foo = fooArray[0];

fooArray[0] = 127;

System.out.println("foo is:" + foo);

…将打印

foo is: 1

…这很好。该值将被复制到最终变量,并且不能再更改。使用数组中的值不会更改EDOCX1的值(如预期的那样…)。

为什么以下内容需要演员表?

final byte[] fooArray = new byte[1];

fooArray[0] = 1;

final byte foo = fooArray[0];

final byte fooFoo = foo + foo;

这与这个问题中的第二个例子有什么不同?为什么编译器会给出以下错误?

Error:(5, 27) java: incompatible types: possible lossy conversion from int to byte

怎么会这样?

只需注意第二个示例,即从数组初始化foo,并测试它是否不变。即使foo不是final,它的值也不会改变。赋值(在本例中是初始化)复制发生时的值(1),即该值。无论最终结果是不是,fooArray[0]到127的后续变化不会自动传播到foo。

JLS(§5.2)对常量表达式的赋值转换有特殊的规则:

In addition, if the expression is a constant expression (§15.28) of type byte, short, char, or int:

A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.

如果我们遵循上面的链接,我们可以在常量表达式的定义中看到:

Literals of primitive type and literals of type String

The additive operators + and -

Simple names (§6.5.6.1) that refer to constant variables (§4.12.4).

如果我们遵循上面的第二个链接,我们会看到

A variable of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28), is called a constant variable.

因此,如果foo是一个常量变量,那么foo + foo只能分配给fooFoo。要将其应用于您的案例:

byte foo = 1;没有定义常量变量,因为它不是final变量。

final byte foo = 1;确实定义了一个常量变量,因为它是final并用常量表达式(原始文本)初始化。

final byte foo = fooArray[0];没有定义常量变量,因为它没有用常量表达式初始化。

注意,fooFoo本身是否是final并不重要。

但最后一个问题呢:"这怎么可能发生?"(此处=从int到byte的可能有损转换)

@实际上,在你的例子中,这是不可能发生的。但是编译器不知道这一点;规范不允许它进行这种推断。

值1非常适合一个字节;1+1也是如此;当变量是最终变量时,编译器可以进行常量折叠。(换句话说:编译器在执行+operation时不使用foo;而是使用"raw"1值)

但是当变量不是最终变量时,所有关于转换和升级的有趣规则都会出现(请参见这里;您想阅读第5.12节关于扩大原始转换的内容)。

第二部分:使一个数组成为最终数组仍然允许您更改它的任何字段;再次如此;不可能持续折叠;因此"加宽"操作再次开始。

这确实是编译器在与final一起使用时在持续折叠中所做的,正如我们从字节代码中看到的那样:

byte f = 1;

// because compiler still use variable 'f', so `f + f` will

// be promoted to int, so we need cast

byte ff = (byte) (f + f);

final byte s = 3;

// here compiler will directly compute the result and it know

// 3 + 3 = 6 is a byte, so no need cast

byte ss = s + s;

//----------------------

L0

LINENUMBER 12 L0

ICONST_1 // set variable to 1

ISTORE 1 // store variable 'f'

L1

LINENUMBER 13 L1

ILOAD 1 // use variable 'f'

ILOAD 1

IADD

I2B

ISTORE 2 // store 'ff'

L2

LINENUMBER 14 L2

ICONST_3 // set variable to 3

ISTORE 3 // store 's'

L3

LINENUMBER 15 L3

BIPUSH 6 // compiler just compute the result '6' and set directly

ISTORE 4 // store 'ss'

如果您将最后一个字节更改为127,它还会抱怨:

final byte s = 127;

byte ss = s + s;

在这种情况下,编译器计算结果并知道它超出了限制,因此它仍然会抱怨它们不兼容。

更多:

还有一个关于用绳子不断折叠的问题:

我只有手机可以接,所以我跳过了字节码部分。很高兴你自愿这么做——)

同时也说明了其动机:当编译到字节码时,为了存储和计算,byte变量被扩大到int变量,除非右边的操作数是final常量,在这种情况下,计算可以在编译时静态进行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值