Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/329.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/svg/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java:假定对象引用或复制_Java_Reference - Fatal编程技术网

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应该复制传递到其构造函数中的点(如图所示),还是只引用它们?如果假定其参考值,是否可以保证只要边界框存在,这些点对象就会存在

两个问题:

  • 复制还是不复制? 这取决于,如果该点将被共享——被其他线程使用,那么制作一个副本更好;如果不共享,则仅使用引用可能会获得微小的性能增益

  • 是否保证只要边界框存在,这些点对象就会存在? 是的,它是由JVM保证的。由于边界框引用了这些点,因此不会对它们进行垃圾收集

  • 如果假定其参考值,是否保证 只要边界框存在,点对象就会存在

    是的,点对象将继续存在,直到不再使用为止。这就是JVM垃圾收集为您所做的

    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;
        }
    }