Java原语是不可变的吗?

Java原语是不可变的吗?,java,primitive,Java,Primitive,如果一个方法有一个局部变量i: int i = 10; 然后我分配一个新值: i = 11; 这会分配一个新的内存位置吗?还是仅仅替换原始值 这是否意味着原语是不可变的?不可变的意味着每次和对象的值发生更改时,都会在堆栈上为其创建一个新的引用。 对于基本类型,您不能谈论不可变性,只有包装类是不可变的。 Java使用按值复制而不是引用 如果传递原语变量或引用变量,则没有区别 始终传递变量中位的副本。所以对于一个基本变量,你是 传递表示值的位的副本,如果传递对象引用变量,则传递表示对象引用的位的

如果一个方法有一个局部变量
i

int i = 10;
然后我分配一个新值:

i = 11;
这会分配一个新的内存位置吗?还是仅仅替换原始值


这是否意味着原语是不可变的?

不可变的意味着每次和对象的值发生更改时,都会在堆栈上为其创建一个新的引用。 对于基本类型,您不能谈论不可变性,只有包装类是不可变的。 Java使用
按值复制
而不是引用

如果传递原语变量或引用变量,则没有区别 始终传递变量中位的副本。所以对于一个基本变量,你是 传递表示值的位的副本,如果传递对象引用变量,则传递表示对象引用的位的副本

例如,如果传递值为3的int变量,则传递的是表示3的位的副本


一旦一个原语被声明,
它的原语类型永远不会改变,尽管它的值可以改变。

是的,它们是不可变的。它们是完全不变的

这里面有一个很好的解释。这是免费的,但在Java中也是一样的。或C族中的任何其他语言

这会分配一个新的内存位置吗?还是仅仅替换原始值

Java并没有真正保证变量将对应于内存位置;例如,您的方法可能会以这样的方式进行优化:
i
存储在寄存器中,或者甚至可能根本不存储,如果编译器可以看到您从未实际使用过它的值,或者如果它可以跟踪代码并直接使用适当的值

但抛开这一点不谈。如果我们将这里的抽象理解为局部变量表示调用堆栈上的内存位置,那么
i=11
将简单地修改该内存位置的值。它不需要使用新的内存位置,因为变量
i
是引用旧位置的唯一对象

这是否意味着原语是不可变的

是和否:是的,原语是不可变的,但不,这不是因为上面的原因

当我们说某些东西是可变的,我们的意思是它可以变异:在保持相同身份的同时改变。例如,当你长出你的头发时,你正在改变你自己:你仍然是你自己,但你的一个属性是不同的

在原语的情况下,它们的所有属性都完全由它们的身份决定
1
始终表示
1
,无论发生什么情况,
1+1
始终表示
2
。你不能改变这一点

如果给定的
int
变量的值为
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

一般来说,这两个变量都将被描述为“changing
sb
”,因为人们将使用“
sb
”来引用变量(包含引用)和它引用的对象(当它引用一个时)。这种松散性是好的,只要你记住重要的区别。

这不是一个完整的答案,但它是一种证明原语类型值不变性的方法

如果原语值(文字)是可变的,则以下代码可以正常工作:

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
的值时,您只是在更改存储在该插槽中的值。值本身没有改变,它们的位置是。

基本文本和
最终的
基本变量是不可变的。非
final
基本变量是可变的


任何基本变量的标识都是该变量的名称,很明显,这样的标识是不可更改的。

让我们更进一步,在其中添加另一个变量j

int i = 10;
int j = i;
i = 11
在java中,为i和j的值分配8字节的内存(4字节为i,4字节为j)。i的值被传递给j,现在j和i的值相同,但内存地址不同。 现在i的值被更改为11,这意味着对于相同的内存地址,i的值从10更改为11,但j的值位于不同的内存位置,因此它保持为10

对于对象,值(或引用)本身是一个地址(或堆地址),因此,如果一个对象更改了它,它也会反映给其他对象。例如,在对象中:-

Person p1 = new Person();
Person p2 = p1;


因此,p1进行更改或p2进行更改,两者都将更改。无论是Java、Python还是Javascript,都是一样的。对于原语,它是实际值,但对于对象,它是实际对象的地址——这就是诀窍。

它将替换原始值。Java原语不是对象<代码>整数(和其他基本包装类)是不可变的。@BrianRoach不,他没有。根据您的逻辑,字符串是可变的:
String str=“test”;str=“newStr”。为了回答OP的问题,它们实际上是不可变的。如果考虑<代码> I++</代码>,那就是:<代码> I= I+1 。您可以看到,它取
i
的值,添加一个值,然后将
i
重新分配给该n