Java:假定对象引用或复制
以两类为例:Java:假定对象引用或复制,java,reference,Java,Reference,以两类为例: public class Point { public int x; public int y; public Point(int xVal, int yVal) { x = xVal; y = yVal; } public Point(Point pt) { x = pt.x; y = pt.y; } } public class BoundingBox {
public class Point {
public int x;
public int y;
public Point(int xVal, int yVal) {
x = xVal;
y = yVal;
}
public Point(Point pt) {
x = pt.x;
y = pt.y;
}
}
public class BoundingBox {
public Point topLeft;
public Point bottomRight;
public BoundingBox(Point setTopLeft, Point setBottomRight) {
topLeft = new Point(setTopLeft);
bottomRight = new Point(setBottomRight);
}
}
BoundingBox应该复制传递到其构造函数中的点(如图所示),还是只引用它们?如果假定其参考值,是否可以保证只要边界框存在,这些点对象就会存在 两个问题:
Point topLeft = new Point(1, 2);
Point bottomRight = new Point(3, 4);
BoundingBox box = new BoundingBox(topLeft, bottomRight);
topLeft.x = 5; // Oops, this just changed box.topLeft.x
一般来说,避免“gotchas”是一种很好的做法——代码的工作方式出乎意料。即使你现在还记得你的代码有哪些缺陷,但当你第一次忘记这些缺陷时,你会非常困惑。你应该始终在你的类中创建存储的可变对象的防御性副本。你应该总是防御性地编写程序,假设每个使用你的类的人都会破坏它 事实证明,尽管类本身可能是不可变的,但并不意味着其中的对象也是不可变的。您需要为您在类中使用的可变对象创建防御副本 下面是一个例子:
public class MyClass {
private Point foo;
private Point bar;
public MyClass(Point foo, Point bar) {
this.foo = foo;
this.bar = bar;
}
public Point foo() {
return foo;
}
public Point bar() {
return bar;
}
. . .
//Seems harmless enough?
//Lets destroy it
Point foo = new Point(1 ,2);
Point bar = new Point(3 ,4);
MyClass mc = new MyClass(foo, bar);
bar.x = 99; //<-- changes internal of mc!
对于问题的第二部分,只要
BoundingBox
存在,其中包含的对象也应该存在。JVM不会垃圾收集这些数据,直到对它们的所有引用不再存在。这两种方法都不对。这完全取决于应用程序的具体情况。如果边界框需要依赖于不从其下方更改的点,则必须在构造函数中进行复制。但是,您还需要确保只在任何getter方法中发布对象的副本(例如,如果您最终拥有getTopLeft(),getBottomRight())更一般地说,这是一个在设计对象模型时是否使用组合还是聚合的问题。聚合并不意味着所有权。换句话说,聚合对象可以存在于其父对象的范围之外,就像您只存储传递给构造函数的相同点引用的情况一样对象组合时,子对象的寿命将与父对象的寿命相同。这是通过复制点,然后小心不要传递存储点的引用(仅复制)来实现的。这样,一旦父对象消失,子对象也会消失。如果可以使点对象不可变,则只引用点就可以了。可以通过将x,y字段声明为最终字段来实现,如下所示:
public class Point {
public final int x;
public final int y;
public Point(int xVal, int yVal) {
x = xVal;
y = yVal;
}
public Point(Point pt) {
x = pt.x;
y = pt.y;
}
}
将引用保留在可变对象上意味着如果其他类更改了topLeft、topRight引用,BoundingBox实例可能只会更改其维度,从而导致很难调试的bug
此外,Java引用统计对象,直到所有引用都被释放,它们才会被销毁。因此,只要有对它们的引用,边界框中的点就可以了。我认为只有基元类型将按值传递。其他类型将按引用传递。我知道,但我需要知道是否复制该值这些对象中的(将其克隆到另一个新对象),或者保留它们的引用并继续使用它们,尽管这与这个问题非常相关,关于“按引用传递”和“按值传递”的争论真的很无聊,但我想说的是,java中没有“按引用传递”这样的东西。看看:是的,我喜欢将其视为不可变的引用。对象将继续存在就其本身而言,不依赖于变量。谢谢。这是我需要知道的,因为我的对象正在多线程应用程序中使用。我想它可能会保留某种垃圾收集的引用计数,但我不确定。并且创建点对象的线程可能会停止存在。对象的生存期不受限制创建它的线程。一个对象是否被垃圾收集,不仅取决于它是否被引用。哇,我不敢相信我没有想到明显的…谢天谢地,你提出了这一点。确实非常好。另一方面,你可以使点不可变(通过将x和y设为“final”),这将避免像Java的“String”类那样出现这种情况。这也是非常有用的。我从来没有想到过这一点。我的大脑似乎想到了所有事情,除了显而易见的事情,尽管我更像是一个C程序员(函数式语言)所创建的对象将不会在别处进行更改,因此它们很可能是不可变的。
public class Point {
public final int x;
public final int y;
public Point(int xVal, int yVal) {
x = xVal;
y = yVal;
}
public Point(Point pt) {
x = pt.x;
y = pt.y;
}
}