Java原型克隆未按预期工作?
sprotype.javaJava原型克隆未按预期工作?,java,design-patterns,prototype,Java,Design Patterns,Prototype,sprotype.java public abstract class SwimmersPrototype implements Cloneable { public SwimmersPrototype clone() throws CloneNotSupportedException{ return (SwimmersPrototype)super.clone(); } } 在调用sort之后,更改原始类的游泳者列表,复制对象,也将更改 这里我只是在一个表中显
public abstract class SwimmersPrototype implements Cloneable {
public SwimmersPrototype clone() throws CloneNotSupportedException{
return (SwimmersPrototype)super.clone();
}
}
在调用sort之后,更改原始类的游泳者列表,复制对象,也将更改
这里我只是在一个表中显示原始对象的游泳者列表,我可以根据表中的属性进行排序,但每当单击默认排序按钮时,我都希望按照游泳者之前插入的默认顺序列出游泳者?但应用排序也会更改克隆对象的游泳者列表?克隆方法的默认版本会创建对象的浅拷贝。对象的浅层副本将具有原始对象所有字段的精确副本。如果原始对象将对其他对象的任何引用作为字段,则仅将这些对象的引用复制到克隆对象中,而不会创建这些对象的副本。这意味着通过克隆对象对这些对象所做的任何更改都将反映在原始对象中,反之亦然。浅复制不是与原始对象100%不相交。浅复制不是100%独立于原始对象
克隆方法的默认版本创建对象的浅层副本。对象的浅层副本将具有原始对象所有字段的精确副本。如果原始对象将对其他对象的任何引用作为字段,则仅将这些对象的引用复制到克隆对象中,而不会创建这些对象的副本。这意味着通过克隆对象对这些对象所做的任何更改都将反映在原始对象中,反之亦然。浅复制不是与原始对象100%不相交。浅复制不是100%独立于原始对象
类对象的默认克隆方法仅执行浅层克隆。如前所述,您需要自己实施深度克隆:
按照约定,此方法返回的对象应为
独立于正在克隆的此对象。要做到这一点,
独立性,可能需要修改的一个或多个字段
对象在返回之前由super.clone返回。通常情况下
意味着复制任何组成内部深度的可变对象
要克隆的对象的结构并替换对的引用
这些对象包含对副本的引用
这意味着您的原始源代码只执行一个浅拷贝:
public class Swim extends javax.swing.JFrame {
Swimmers swimmers;
Swimmers swimmersCopy;
/**
* Creates new form Swim
*/
public Swim() {
initComponents();
swimmers = new Swimmers();
fillSwimmers();
fillTable(swimmers.getSwimmers());
jTableListener();
try {
swimmersCopy = (Swimmers)swimmers.clone();
} catch (CloneNotSupportedException ex) {
Logger.getLogger(Swim.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
然而,正如您在评论中所说的,您不希望复制游泳者,而是实施一个写时复制策略。首先,您应该意识到,在多线程应用程序中,如果游泳者对象有可能被多个线程并发使用,那么要使写时拷贝完全正确是非常困难的。如果在您的情况下这不是一个问题,您只需对排序方法进行以下更改:
在这里,我们制作了一份游泳运动员名单的副本,以确保我们不会对可能共享的名单进行排序。通过制作一个副本,我们知道这个对象是唯一一个持有对我们要修改的列表的引用的对象
即使对象从未被克隆过,上述修改也会不必要地增加列表副本的开销。为了避免这种开销,您可以添加一个引用计数字段,在克隆方法中增加该字段,并考虑在克隆对象不再使用时减少该字段。如果引用计数大于1,则只需复制列表
顺便说一句,作为一般规则,在使用可克隆接口之前,所有Java开发人员都应该阅读Josh Bloch在其书中对此所说的内容。类对象的默认克隆方法只进行浅层克隆。如前所述,您需要自己实施深度克隆:
按照约定,此方法返回的对象应为
独立于正在克隆的此对象。要做到这一点,
独立性,可能需要修改的一个或多个字段
对象在返回之前由super.clone返回。通常情况下
意味着复制任何组成内部深度的可变对象
要克隆的对象的结构并替换对的引用
这些对象包含对副本的引用
这意味着您的原始源代码只执行一个浅拷贝:
public class Swim extends javax.swing.JFrame {
Swimmers swimmers;
Swimmers swimmersCopy;
/**
* Creates new form Swim
*/
public Swim() {
initComponents();
swimmers = new Swimmers();
fillSwimmers();
fillTable(swimmers.getSwimmers());
jTableListener();
try {
swimmersCopy = (Swimmers)swimmers.clone();
} catch (CloneNotSupportedException ex) {
Logger.getLogger(Swim.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
然而,正如您在评论中所说的,您不希望复制游泳者,而是实施一个写时复制策略。首先,您应该意识到,在多线程应用程序中,如果游泳者对象有可能被多个线程并发使用,那么要使写时拷贝完全正确是非常困难的。如果在您的情况下这不是一个问题,您只需对排序方法进行以下更改:
在这里,我们制作了一份游泳运动员名单的副本,以确保我们不会对可能共享的名单进行排序。通过制作一个副本,我们知道这个对象是唯一一个持有对我们要修改的列表的引用的对象
上面的修改将不必要地增加列表副本的开销,即使对象没有
r已经被克隆。为了避免这种开销,您可以添加一个引用计数字段,在克隆方法中增加该字段,并考虑在克隆对象不再使用时减少该字段。如果引用计数大于1,则只需复制列表
顺便说一句,作为一般规则,在使用可克隆接口之前,所有Java开发人员都应该阅读Josh Bloch在书中对其所说的内容。这背后的原因是克隆方法不会复制整个对象:创建的副本将共享同一列表。 使用克隆方法是一种危险的操作方式。例如,您可能更喜欢使用复制构造函数:
public List<Swimmer> sort() {
swimmers = new ArrayList<>(swimmers); // Copy on write
return sortStrategy.sort(swimmers);
}
请注意,在本例中,游泳者的复制构造函数调用ArrayList的复制构造函数。这背后的原因是克隆方法不会复制整个对象:创建的副本将在此处共享相同的列表。 使用克隆方法是一种危险的操作方式。例如,您可能更喜欢使用复制构造函数:
public List<Swimmer> sort() {
swimmers = new ArrayList<>(swimmers); // Copy on write
return sortStrategy.sort(swimmers);
}
请注意,在本例中,游泳者的复制构造函数调用ArrayList的复制构造函数。开始学习:旁注:构建ArrayList时不要使用原始类型。Java7+的正确语法是swimbers=newArrayList;此外,sortStrategy将为null,并在调用sort时抛出一个NPE,但我假设您没有展示更多代码,与此问题无关。开始学习:旁注:在构建ArrayList时不要使用原始类型。Java7+的正确语法是swimbers=newArrayList;另外,sortStrategy将为null,并在调用sort时抛出一个NPE,但我假设您没有显示更多的代码,与此问题无关。谢谢,但我不想修改克隆对象上的任何属性。@HenokTesfaye如果您不修改克隆对象上的任何属性,然后你的克隆对象将引用与原始对象相同的列表。我认为这就像写时复制。为了利用内存空间。@HenokTesfaye如果您的sortStrategy.sort方法按其显示的位置对列表进行排序,那么您应该执行类似return-swimbers=sortStrategy.sortnew-arraylistswimbers的操作;以确保原始列表保持不变。如果可以,我需要一个解释?谢谢,但我不想修改克隆对象上的任何属性。@HenokTesfaye好的,如果不修改克隆对象上的任何属性,那么克隆对象将引用与原始对象相同的列表。我认为这类似于写时复制。为了利用内存空间。@HenokTesfaye如果您的sortStrategy.sort方法按其显示的位置对列表进行排序,那么您应该执行类似return-swimbers=sortStrategy.sortnew-arraylistswimbers的操作;以确保原始列表保持不变。如果可以,我需要一个解释?您应该删除该词;这就是它的工作原理。不要在构建ArrayList时使用原始类型。@DavidConrad谢谢!你应该删除这个词;这就是它的工作原理。不要在构建ArrayList时使用原始类型。@DavidConrad谢谢!
public List<Swimmer> sort() {
swimmers = new ArrayList<>(swimmers); // Copy on write
return sortStrategy.sort(swimmers);
}
public Swimmers(Swimmers s) {
this.swimmers = new ArrayList<Swimmer>(s.swimmers);
}