Java 为什么我的变量包含;2122“;而不是",;2322";?

Java 为什么我的变量包含;2122“;而不是",;2322";?,java,mutators,string-pool,Java,Mutators,String Pool,这是一道考试题。幸运的是我选择了正确的答案,但我仍然不明白为什么它是正确的 考虑一下这个计划: class D { protected C c; public D(C c) { this.c = new C(c); } public C getC() { return c; } public void setC(C c) { this.c = c; } } class C { protected String s; public C(S

这是一道考试题。幸运的是我选择了正确的答案,但我仍然不明白为什么它是正确的

考虑一下这个计划:

class D {
  protected C c;
  public D(C c) {
    this.c = new C(c);
  }
  public C getC() {
    return c;
  }
  public void setC(C c) {
    this.c = c;
  }
}
class C {
  protected String s;
  public C(String s) {
    this.s = s;
  }
  public C(C c) {
    this(c.s);
  }
  public String getS() {
    return s;
  }
  public void setS(String s) {
    this.s = s;
  }

  public static void main(String[] args) {
    C c1 = new C("1");
    C c2 = new C("2");
    D[] d = {
      new D(c1), new D(c1), new D(c2), new D(c2)
    };
    d[0] = d[3];
    c1.setS("3");
    String r = "";
    for (D i: d) {
      r += i.getC().getS();
    }
    System.out.println(r);
  }

}
它将打印
2122
。但是,我希望
2322
(运行代码时,我显然错了)。我的理由是:

在main方法的第三行中,
D
的四个实例被初始化。
D
的构造函数创建了
C
的一个新实例。
C
的实例有一个
String
变量,该变量指向内存中的某个点。现在,
d[1]
中对象的实例变量
c
,我们称之为
c3
,有一个实例变量(type
String
),我们称之为
s3
,指向与
String s1
相同的内存,变量为
c1

因此,当我们更改
s1
时,我希望
s3
的值也会更改,因为它指向内存中的同一点

另一方面,如果您更改
D
的构造函数,请参见下文,您将得到
2322
。我希望如此,因为现在
d[1]
中的变量
c3
直接指向
c1
的内存位置

public D(C c) {
  this.c = c;
}
到目前为止,我对解释的想法(可能是错误的):

  • 初始化实例变量
    s1
    /
    s3
    时,会生成新的
    String
    对象(到目前为止,我假设它们指向
    String
    池中的
    “1”
    ,因为
    C
    的构造函数使其看起来是这样的)
  • 更改
    s1
    时,它的指针将重定向到
    字符串
    池中的
    “3”
    。而不是池中的
    “1”
    变成
    “3”

  • 有人能解释这种行为吗?我的(错误的)推理中有哪些错误?

    这与
    字符串
    池无关。主要答覆:

    这是因为
    D
    基于
    C#C
    创建了一个新的
    C
    实例。这意味着
    D#c
    的实例与构造函数
    D
    中传递的参数
    c
    的实例不同,因此修改该实例不会影响
    D#c
    中的当前实例


    你对这一切都解释得很好

    以下是您正在测试的内容:

    class Surprise {
        String item;
        public Surprise(String item) {
            this.item = item;
        }
        //this is called copy constructor
        //because you receive an object from the same class
        //and copy the values of the fields into the current instance
        //this way you can have a "copy" of the object sent as parameter
        //and these two object references are not tied by any mean
        public Surprise(Surprise another) {
            //here you just copy the value of the object reference of another#item
            //into this#item
            this.item = another.item;
        }
    }
    
    class Box {
        Surprise surprise;
        public Box(Surprise surprise) {
            //here you create a totally new instance of Surprise
            //that is not tied to the parameter surprise by any mean
            this.surprise = new Surprise(surprise);
        }
    
        public static void main(String[] args) {
            Surprise surprise1 = new Surprise("1");
            Surprise surprise2 = new Surprise("2");
            Box[] boxes = {
                new Box(surprise1),
                new Box(surprise1),
                new Box(surprise2),
                new Box(surprise2)
            };
            boxes[0] = boxes[3];
            //you update surprise1 state
            //but the state of Box#surprise in the boxes that used surprise1
            //won't get affected because it is not the same object reference
            surprise1.item = "3";
            //print everything...
            System.out.println("Boxes full of surprises");
            //this code does the same as the printing above
            for (Box box : boxes) {
                System.out.print(box.surprise.item);
            }
            System.out.println();
        }
    }
    

    可能是@njzk2的重复项。它不是重复项。但这有助于理解这个程序的结果。@LuiggiMendoza
    因此,当我们更改s1时,我希望s3的值也会更改,因为它指向内存中的同一点。
    对我来说,OP被java中传递和引用变量的方式弄糊涂了。@njzk2是的。而提供这种联系作为唯一的答案可能不足以解释这种行为。这就是为什么我提供了一个答案来详细解释这一点。首先感谢你的回答。我知道按值传递/引用的一般概念。如果构造函数意外地生成一个新字符串,即:This.item=new String(另一个.item),而不是重复使用“other”中的同一个字符串,那么这个示例对我来说是有意义的。我可以理解为什么会有不同的惊喜,但我很难理解为什么会有不同的字符串。@flisk,因为您只复制字符串引用的值。因此,然后更新
    shopprise1#item
    ,只影响该变量。同样的情况也可能发生在
    int
    Integer
    甚至一个可变类上,因为您将
    shopprise1#item
    与一个全新的引用相关联。啊,那么基本上,shopprise1#item会改变它的指针吗?也可以在中间驻留,put System.out.println(d[1].getC().getS()==c1.s);d[0]=d[3]之后的行。在这里你可以看到,字符串仍然是一样的(就在更改之前),因为它打印为true。好的,很好,我想我现在完全理解了。我用StringBuffer替换了所有字符串,并用这种方式更改了c1:c1.setS(c1.s.replace(0,1,3));这个项目达到了我的预期。因为StringBuffer是可变的,所以当它的值更改时,它不需要创建新实例(即更改指针)。这样它就印上了“2322”。谢谢@请注意,您正在修改状态。如果您使用
    c1.s=newstringbuffer(“3”)然后将再次打印“2122”。同样,这是关于按值传递的。如果您使用
    new
    关键字,那么对象是否是不可变的并不重要。