如果一个方法有一个局部变量i
:
int i = 10;
然后我分配一个新值:
i = 11;
这会分配一个新的内存位置吗?还是只是替换原始值?
这是否意味着原语是不可变的?
这会分配一个新的内存位置吗?还是只是替换原始值?
Java 并没有真正保证变量将对应于内存位置。例如,您的方法可能会以i
存储在寄存器中的方式进行优化——或者甚至可能根本不存储,如果编译器可以看到您从未真正使用过它的值,或者如果它可以跟踪代码并直接使用适当的值。
但把它放在一边。. . 如果我们在这里抽象为局部变量表示调用堆栈上的内存位置,那么i = 11
将简单地修改该内存位置的值。它不需要使用新的内存位置,因为变量i
是唯一引用旧位置的东西。
这是否意味着原语是不可变的?
是与否:是的,原语是不可变的,但不,这不是因为上述原因。
当我们说某些东西是可变的时,我们的意思是它可以被改变:改变但仍然具有相同的身份。例如,当你长出头发时,你正在改变自己:你仍然是你,但你的一个属性是不同的。
在原语的情况下,它们的所有属性完全由它们的身份决定;1
always 意味着1
,无论如何,并且1 + 1
总是2
。你无法改变这一点。
如果给定int
变量具有 value 1
,您可以将其更改为具有该值2
,但这是身份的完全改变:它不再具有与以前相同的值。这就像改变me
指向别人而不是指向我:它实际上并没有改变我,它只是改变了me
。
当然,对于对象,您通常可以同时做到以下两点:
StringBuilder sb = new StringBuilder("foo");
sb.append("bar"); // mutate the object identified by sb
sb = new StringBuilder(); // change sb to identify a different object
sb = null; // change sb not to identify any object at all
通俗地说,这两者都将被描述为“变化sb
”,因为人们将使用“ sb
”来指代变量(包含引用)和它所指的对象(当它指代一个时)。这种松散是可以的,只要你记住重要的区别。
Immutable
意味着每次和对象的值发生变化时,都会在堆栈上为它创建一个新的引用。对于原始类型,您不能谈论不可变性,只有包装类是不可变的。Javacopy_by_value
不通过引用使用。
如果您传递原始变量或引用变量,这没有区别,您总是传递变量中位的副本。因此,对于原始变量,您传递的是表示值的位的副本,如果您传递的是对象引用变量,则传递的是表示对对象的引用的位的副本。
例如,如果你传递一个值为 3 的 int 变量,你传递的是代表 3 的位的副本。
一旦声明了一个原语its primitive type can never change
,尽管它的值可以改变。
让我们更进一步,在其中添加另一个变量 j。
int i = 10;
int j = i;
i = 11
在 java 中,为 i 和 j 的值分配了 8 个字节的内存(i 为 4 个字节,j 为 4 个字节)。i 的值被传递给 j,现在 j 和 i 具有相同的值但不同的内存地址。现在 i 的值更改为 11,这意味着对于相同的内存地址 i 的值从 10 更改为 11,但 j 的值位于不同的内存位置,因此它保持为 10。
在对象的情况下,值(或引用)本身就是一个地址(或堆地址),因此如果有人更改它,它也会反映给其他人。例如在对象中:-
Person p1 = new Person();
Person p2 = p1;
因此,要么 p1 进行更改,要么 p2 进行更改,两者都会被更改。无论是 Java、Python 还是 Javascript,都是一样的。在原始的情况下,它是实际值,但在对象的情况下,它是实际对象的地址——这就是诀窍。
这不是一个完整的答案,但它是一种证明原始类型值不变性的方法。
如果原始值(文字)是可变的,则以下代码可以正常工作:
int i = 10; // assigned i the literal value of 10
5 = i; // reassign the value of 5 to equal 10
System.out.println(5); // prints 10
当然,这不是真的。
整数值,例如 5、10 和 11 已经存储在内存中。当您设置一个等于其中之一的变量时:它会更改内存插槽中的i
值。
您可以通过以下代码的字节码在此处看到这一点:
public void test(){
int i = 10;
i = 11;
i = 10;
}
字节码:
// access flags 0x1
public test()V
L0
LINENUMBER 26 L0
BIPUSH 10 // retrieve literal value 10
ISTORE 1 // store it in value at stack 1: i
L1
LINENUMBER 27 L1
BIPUSH 11 // same, but for literal value 11
ISTORE 1
L2
LINENUMBER 28 L2
BIPUSH 10 // repeat of first set. Still references the same literal 10.
ISTORE 1
L3
LINENUMBER 29 L3
RETURN
L4
LOCALVARIABLE this LTest; L0 L4 0
LOCALVARIABLE i I L1 L4 1
MAXSTACK = 1
MAXLOCALS = 2
正如您在字节码中看到的(希望如此),它引用了文字值(例如:10),然后将其存储在变量的插槽中i
。当您更改 的值时i
,您只是更改了存储在该插槽中的值。价值观本身并没有改变,它们的位置是。
是的,它们是不可变的。它们是完全不变的。
这里有一个很好的解释。它适用于 Go,但在 Java 中也是如此。或 C 系列中的任何其他语言。
原始文字和final
原始变量是不可变的。不是final
原始变量是可变的。
任何原始变量的身份都是该变量的名称,很明显,这样的身份是不可更改的。