为什么T variableName = variableName = value;
在Java中起作用?其中T
可以是任何类型,甚至可以是原始类型。
Integer v1 = v1 = 1;
有效。这是否意味着变量声明在语句中具有最高优先级?您能否解释其背后的技术性?
注意:整个问题已被重构/更新。下面的某些评论可能与上下文无关。
在@SSP的发布链接旁边,有一种方法可以查看JShell在幕后的行为。
切换常规调试信息(使用/help /debug
获取所有选项)
jshell> /debug
| Debugging on
现在执行您的语句并打印生成的类
jshell> String foo = foo
class $JShell$11 {
public static String foo;
public static Object do_it$() throws Throwable {
String foo_ =
foo;
return foo = foo_;
}
}
这解释了语句的jhell
和javac
的不同行为。
我将尝试回答这个问题。我应该确定,这个答案是基于对编程语言通常如何工作的一般知识而不是对Java规范的特定知识的猜测。
首先,我可以确认这种情况出现在编译的Java中,而不仅仅是JShell。
这是我想发生的事情:
这是否意味着变量声明在语句中具有最高优先级?
(可能还有-参见下面@ user207421的有用注释)的排序。
我们必须考虑编译器如何将此代码转换为可操作的机器代码。通常,语言使用stack管理local内存,每个方法/函数调用都会在堆栈的顶部分配新的内存区域(frame)。当函数退出时,将弹出堆栈,并释放该内存区域。为了高效运行,大多数语言都要求知道在编译时特定功能的堆栈帧需要多少内存。这样,在运行时构建和释放堆栈不需要(或很少)分支代码。
编译器如何计算每个堆栈帧需要多少内存?简而言之,该函数主体中声明的每个参数和局部变量都需要足够的空间。
因此,在运行时调用函数时,整个帧的足够内存分配在堆栈的顶部,每个变量和参数都有一个空间。这意味着在运行时,只要包含其功能的代码开始运行,便会有效地“声明”每个变量,因此该变量的任何assignement(即=
)在函数执行期间的任何时候都是内存安全的体。
因此T x = x = <some value>
非常安全。右边的分配首先发生,而左边的分配(实际上是无操作)发生在第二。出于所有实际目的,声明T x
被提升到整个函数的顶部,因此定义发生了[[even firster。]为什么感到困惑?
“在声明变量之前不能使用变量”]]之类的东西。但是实际上这是一个过分概括的内容,涵盖了两件事:未分配存储空间时无法读取或写入变量
第二个要求是强制执行逻辑一致性-如果尚未分配变量,则表示该变量的内存段可能无法包含有用的值,因此读取它将是逻辑错误。该规则由编译器在语义上强制执行。没有任何需要执行(特别是对于原语的)的理由,但它表示逻辑错误,或更糟糕的是,安全漏洞。但是,代码T x = x = <some value>
不会违反此规则。第一次x
是
read是在左侧分配期间发生的,这是在右侧分配之后发生的。像boolean x = x == true
这样的替代语句确实无法编译。这只会给我们带来更多需要考虑的事情。像这样的代码有什么问题:
x = 7;
int x;
从运行时的角度来看,这里没有什么错。但是,这使编译器的工作更加困难。为了执行类型检查,编译器必须先读取声明int x;
,才能确定x = 7;
是有效的语句。这是主要的
technical原因,我可以想到为什么语言规范将要禁止此类代码。有效地,我们是在说:
变量声明在运行时被提升到函数的顶部,但是必须在编译时对该变量进行任何引用之前发生。
还有另一个简单的原因,可能导致人们在首次分配变量后不希望声明该变量:这会使代码难以理解。出于同样的原因,人们可能会争辩说也应该禁止类似T x = x = <some value>
的语句,但显然,语言设计者要么不同意,要么没有想到。