我的主类中有一个Game.java类的对象游戏。我需要创建一个游戏对象的副本(game_copy),这样当我对game_copy的棋盘进行更改时,游戏板就不会改变。我把这个game_copy对象改为:
Game game_copy = new Game() ;
game_copy = game.clone() ;
但是当我对game_copy的董事会进行更改时,游戏板会因为仍然共享参考而改变。我该如何解决这个问题? 以下是我正在使用的Game和rplayer类:
class Game implements Cloneable{
int n;
ArrayList<ArrayList<String>> board;
rplayer [] players;
int a ;
public Game(int n){
this.n=n;
players = new rplayer[2] ;
players[0]=new rplayer(this.a);
players[1]=new rplayer(this.a);
board= new ArrayList<ArrayList<String>>();
for(int p= 0 ; p < n*n ; p++){
ArrayList<String> r = new ArrayList<String>() ;
board.add(r) ;
}
}
public Game clone(){
Game gm ;
try {
gm = (Game) super.clone();
}
catch (CloneNotSupportedException e) {
System.out.println (" Cloning not allowed. " );
return null;
}
return gm
}
}
class rplayer{
int a;
public rplayer(int a){
this.a=a;
}
}
这是我在尝试使用.clone()方法之前尝试过的,但制作复制构造函数的想法也不起作用。这两个对象总是相关的。
public Game(Game g){
this.n=g.n;
this.players = new rplayer[2] ;
rplayer[] temp_players = new rplayer[2] ;
temp_players = g.players;
this.players = temp_players ;
this.board= new ArrayList<ArrayList<String>>();
ArrayList<ArrayList<String>> temp_board = new ArrayList<ArrayList<String>>() ;
for(int p= 0 ; p < n*n ; p++){
ArrayList<String> r = new ArrayList<String>() ;
board.add(r) ;
temp_board.add(r) ;
}
temp_board = g.board ;
board= temp_board;
}
答案 0 :(得分:1)
根据Javadocs:
否则,此方法创建此对象的类的新实例,使用此对象的相应字段的内容初始化其所有字段,就像通过赋值一样;这些字段的内容本身不会被克隆。因此,此方法执行此对象的“浅拷贝”,而不是“深拷贝”操作。
换句话说,创建了一个新对象,但对象内部的引用仍然指向相同的成员变量。您需要复制对象内的内容以及对象本身。
https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html
答案 1 :(得分:0)
我发现通过使用Serializable
接口标记对象然后在内存中序列化对象来复制对象的深层副本。由于您的Game对象引用了另一个对象,并且该对象也引用了其他对象,因此最好通过序列化来克隆该对象。这将确保您获得完全不同的对象。随着对象变得复杂,Java克隆将无法实现此目的。
这里是如何序列化内存中对象的示例代码。
class Game implements Serializable {
........
........
........
public Game makeClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(outputStream);
out.writeObject(this);
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
ObjectInputStream in = new ObjectInputStream(inputStream);
Game copied = (Game) in.readObject();
return copied;
}
}
答案 2 :(得分:0)
通常不建议实现Cloneable
接口。约书亚布洛赫Effective Java (2nd Edition)的一些摘录:
明智地覆盖
clone
。 Cloneable接口旨在作为 mixin接口,用于通告允许克隆的对象。不幸的是,它没有达到这个目的。它的主要缺陷是缺少clone
方法,并且Object
的克隆方法受到保护。
Josh接着解释了Cloneable接口和clone
方法的许多缺点。他建议作为一种替代方案是你制作一个复制构造函数&#34;将一个对象的状态复制到新副本中,防御性地复制需要保护的内容。在您的情况下,复制构造函数可能看起来像这样......
public Game(Game original) {
if (null == original)
throw new IllegalArgumentException("Game argument must not be null for copying");
// int primitives can be copied by value harmlessly.
this.n = original.n;
this.a = original.a;
// Collections require special protection, however, to ensure that
// manipulating one game's state doesn't manipulate the other.
ArrayList<ArrayList<String>> boardCopy = new ArrayList<>();
for (ArrayList<String> subList : original.board) {
// Fortunately, Strings are immutable, so we don't need to
// manually copy them. And ArrayList has a copy constructor
// of its own, so that's handy!
ArrayList<String> subCopy = new ArrayList<>(subList);
boardCopy.add(subCopy);
}
this.board = boardCopy;
this.players = new rplayer[original.players.length];
for (int i = 0; i < original.players.length; i++) {
rplayer player = original.players[i];
// If--and ONLY if--the rplayer class is immutable, this
// is safe; otherwise you need to copy each player on your
// own!
this.players[i] = player;
}
}
这里的想法是你要彻底消除远处的任何怪异动作&#34 ;;也就是说,确保改变Game
A的状态不能间接改变Game
B的状态。如果某些东西是不可变的(字符串,包裹的原语,你自己的不可变类) ,您可以重复使用它们之间的引用。否则,您需要将这些对象的状态复制到您的副本将使用的全新实例。