克隆对象的值更改时,原始对象也会更改

我正在尝试使用克隆,但当克隆的对象的值发生更改时,原始对象也会发生更改。 正如你可以看到KalaGameState不使用任何对象,所以浅拷贝应该工作。

 /**
     *  This class represents the current state of a Kala game, including 
     *  which player's turn it is along with the state of the board; i.e. the 
      * numbers of stones in each side pit, and each player's 'kala'). 
     */
    public class KalaGameState implements Cloneable
{
    // your code goes here
 private int turn;

    private int[] sidePit;
       private boolean game;


       public Object clone() {
           try {
               return super.clone();
           }
           catch (CloneNotSupportedException e) {
               // This should never happen
               throw new InternalError(e.toString());
           }
       }

    /**
     * Constructs a new GameState with a specified number of stones in each 
     * player's side pits.
     * @param startingStones the number of starting stones in each side pit.
     * @throws InvalidStartingStonesException if startingStones not in the range 1-10.
     */
    public KalaGameState(int startingStones) throws InvalidStartingStonesException
    {
    game=true;
     turn=0;
       sidePit=new int[14];
      for (int i=0; i <= 13 ; i++)
      {
       sidePit[i] = startingStones;

       }
      sidePit[6] =0;
      sidePit[13] =0;
     // your code goes here
    }

    /**
     * Returns the ID of the player whose turn it is.  
     * @return A value of 0 = Player A, 1 = Player B.
     */
    public int getTurn()
    {
       return turn; // your code goes here
    }

    /**
     * Returns the current kala for a specified player.
     * @param playerNum A value of 0 for Player A, 1 for Player B.
     * @throws IllegalPlayerNumException if the playerNum parameter 
     * is not 0 or 1.  
     */     
    public int getKala(int playerNum) throws IllegalPlayerNumException
    {
     if(playerNum!=0 || playerNum!=1)
      throw new IllegalPlayerNumException(playerNum);

     if(playerNum==0)
        return sidePit[6];
     else
      return
      sidePit[13];
     // your code goes here
    }

    /**
     * Returns the current number of stones in the specified pit for 
     * the player whose turn it is.
     * @param sidePitNum the side pit being queried in the range 1-6.
     * @throws IllegalSidePitNumException if the sidePitNum parameter.
     * is not in the range 1-6.
     */ 
    public int getNumStones(int sidePitNum) throws IllegalSidePitNumException
    {
     if(turn==0)
     {
     if(sidePitNum>6 )
      throw new IllegalSidePitNumException(sidePitNum);
     }
     else
      if(sidePitNum>12)
       throw new IllegalSidePitNumException(sidePitNum);


     if(turn==0)
      return sidePit[sidePitNum];
    else
     return sidePit[sidePitNum+6];

     // your code goes here
    }

    /**
     * Returns the current number of stones in the specified pit for a specified player.
     * @param playerNum the player whose kala is sought. (0 = Player A, 1 = Player B).  
     * @param sidePitNum the side pit being queried (in the range 1-6).
     * @throws IllegalPlayerNumException if the playerNum parameter is not 0 or 1.  
     * @throws IllegalSidePitNumException if the sidePitNum parameter is not in the 
     * range 1-6.
     */

    public int getNumStones(int playerNum, int sidePitNum) throws IllegalPlayerNumException, 
                                                                  IllegalSidePitNumException
    {
     /*if(playerNum>2)
      throw new IllegalPlayerNumException(playerNum);
     if(turn==0)
     {
     if(sidePitNum>6 )
      throw new IllegalSidePitNumException(sidePitNum);
     }
     else
      if(sidePitNum>12)
       throw new IllegalSidePitNumException(sidePitNum);
     */

     if(playerNum==0)
       return sidePit[sidePitNum];
     else if(playerNum==1)
      return sidePit[sidePitNum+7];
     else
      return sidePit[sidePitNum];

    }

    /**
     * Returns the current score for a specified player - the player's 
     * kala plus the number of stones in each of their side pits.
     * @param playerNum the player whose kala is sought. (0 = Player A, 1 = Player B).  
     * @throws IllegalPlayerNumException if the playerNum parameter is not 0 or 1.  
     */ 
    public int getScore(int playerNum) throws IllegalPlayerNumException
    {
     if(playerNum>1)
      throw new IllegalPlayerNumException(playerNum);
   int score=0;
     if(playerNum==0)
     {
      for(int i=0;i<=5;i++)
       score=score+sidePit[i];
     score=score+sidePit[6];
     }
     else
     {
      for(int i=7;i<=12;i++)
       score=score+sidePit[i];
     score=score+sidePit[13];
     }

     // your code goes here
     return score;
    }

    private int getSidePitArrayIndex(int sidePitNum) throws IllegalSidePitNumException
    {
     if(turn==0)
     {
     if(sidePitNum>6 )
      throw new IllegalSidePitNumException(sidePitNum);
     }
     else
      if(sidePitNum>12)
       throw new IllegalSidePitNumException(sidePitNum);


       if(turn==0)
       {
     return sidePitNum--;
       }
       else
       {

         return sidePitNum+6;

       }
    }

    public boolean gameOver()
    {
     int stone=0;

     if(turn==0)
     for(int i=0;i<=5;i++)
   stone=stone+getNumStones(i); 

     else
      for(int i=7;i<=12;i++)
       stone=stone+getNumStones(i-7); 

 if (stone==0)
  game=false;

 return game;
    }

    /**
     * Makes a move for the player whose turn it is.
     * @param sidePitNum the side pit being queried (should be in the range 1-6)
     * @throws IllegalSidePitNumException if the sidePitNum parameter is not in the range 1-6.
     * @throws IllegalMoveException if the side pit is empty and has no stones in it.
     */ 
    public void makeMove(int sidePitNum) throws IllegalSidePitNumException, IllegalMoveException
    {
     if(turn==0)
     {
     if(sidePitNum>6 )
      throw new IllegalSidePitNumException(sidePitNum);
     }
     else
      if(sidePitNum>12)
       throw new IllegalSidePitNumException(sidePitNum);
     /*
     if(turn==0)
     {
     if(sidePit[sidePitNum-1]==0)
      throw new IllegalMoveException(sidePitNum);
     }
     else
     { if(sidePit[sidePitNum-1+7]==0)
          throw new IllegalMoveException(sidePitNum);
     } 
      */

      sidePitNum--;
     int temp=sidePitNum;
     int pitNum=sidePitNum+1;
     int stones=getNumStones(turn,sidePitNum);

   if(turn==0)    
     sidePit[sidePitNum]=0;
   else
    {
    sidePitNum=sidePitNum+7;
    sidePit[sidePitNum]=0;
    pitNum=pitNum+7;
    }
  while(stones!=0)
  {
  if(turn==0)
  {
        sidePit[pitNum]=sidePit[pitNum]+1;
    stones--;
    pitNum++;
    if(pitNum==13)
     pitNum=0;
  } 

   else
   {

       sidePit[pitNum]=sidePit[pitNum]+1;
     stones--;
     pitNum++;
     if(pitNum==6)
      pitNum=7;
     else if(pitNum==14)
      pitNum=0;
    }

  }

  boolean res=anotherTurn(pitNum);
  if(!res){
   capture(pitNum,temp);
  if(turn==0)
   turn=1;
  else turn=0;}
   }

    private boolean anotherTurn(int pitNum)
    {pitNum--;
    boolean temp=false;

     if(turn==0)
    {if(pitNum==6)
       {turn=0;
       temp=true;
       }
    }      
      else
       if(pitNum==-1)
        {turn=1;
        temp=true;
        }
          return temp;
    }


    private void capture(int pitNum, int pit)
    {

     pitNum--; 
if(turn==0){
 if(sidePit[pitNum]==1 && pitNum<6)
  {
  if(pitNum==0)
  {
  sidePit[6]=sidePit[6]+sidePit[12]+1;
  sidePit[12]=0;

  }
  else if(pitNum==1)
  {
   sidePit[6]=sidePit[6]+sidePit[11]+1;
   sidePit[11]=0;

   }
  else if(pitNum==2)
  {
   sidePit[6]=sidePit[6]+sidePit[10]+1;
   sidePit[10]=0;
   }
  else if(pitNum==3)
  {
   sidePit[6]=sidePit[6]+sidePit[9]+1;
   sidePit[9]=0;
   }
  else if(pitNum==4)
  {
   sidePit[6]=sidePit[6]+sidePit[8]+1;
   sidePit[8]=0;
   }
  else if(pitNum==5)
  {
   sidePit[6]=sidePit[6]+sidePit[7]+1;
   sidePit[7]=0;
   }
  sidePit[pitNum]=0;
  }
}
  if(turn==1)
  { //pitNum=pitNum;
   if(sidePit[pitNum]==1 && pit+7>6)
    {
    if(pitNum==7)
    {
    sidePit[13]=sidePit[13]+sidePit[5]+1;
    sidePit[7]=0;
    }
    else if(pitNum==8)
    {
     sidePit[13]=sidePit[13]+sidePit[4]+1;
     sidePit[4]=0;
     }
    else if(pitNum==9)
    {
     sidePit[13]=sidePit[13]+sidePit[3]+1;
     sidePit[3]=0;
     }
    else if(pitNum==10)
    {
     sidePit[13]=sidePit[13]+sidePit[2]+1;
     sidePit[2]=0;
     }
    else if(pitNum==11)
    {
     sidePit[13]=sidePit[13]+sidePit[1]+1;
     sidePit[1]=0;
     }
    else if(pitNum==12)
    {
     sidePit[13]=sidePit[13]+sidePit[0]+1;
     sidePit[0]=0;
     }
    sidePit[pitNum]=0;
    }

  }

    }
}



import java.io.BufferedReader;
import java.io.InputStreamReader;


public class RandomPlayer extends KalaPlayer{
 //KalaGameState state; 
 public int chooseMove(KalaGameState gs) throws NoMoveAvailableException
 {int[] moves;
  moves=getMoves(gs);
  try{
  for(int i=0;i<=5;i++)
   System.out.println(moves[i]);

  for(int i=0;i<=5;i++)
  {
   if(moves[i]==1)
   {

     KalaGameState state=(KalaGameState) gs.clone();

    state.makeMove(moves[i]);
     gs.getTurn();
    moves[i]=evalValue(state.getScore(0),state.getScore(1));
       }
  }

  }
  catch(IllegalMoveException e)
  {
   System.out.println(e);
   //chooseMove(state);
  }
  return 10;
 }

 private int evalValue(int score0,int score1)
 {
  int score=0;
  //int score0=0;
 // int score1=0;
  //getScore(0);
  //score1=state.getScore(1);

  //if((state.getTurn())==0)
  score=score1-score0;
  //else
   //score=score1-score0;
  System.out.println("score: "+score);
  return score;
 }
public int[] getMoves(KalaGameState gs)

{
  int[] moves=new int[6];

  for(int i=1;i<=6;i++)
  {
   if(gs.getNumStones(i)!=0)
    moves[i-1]=1;
   else moves[i-1]=0;
  }
return moves;  
}
}

你能解释一下哪里出了问题吗?

当我克隆KalaGameState

KalaGameState状态=(KalaGameState)gs.clone();

    state.makeMove(moves[i]);
     gs.getTurn();

当我调用state.makemove(...)时,它所做的更改也反映在gs实例中


你已经获得了所谓的对象的“浅拷贝”。 基本上所有的原语都被复制过来,包括对象引用(本例中的数组)。

你想要的是“深层复制”。 有几种方法可以解决这个问题,一种方法是使用克隆,另一种方法创建一个新的构造函数,它接收对象并将其全部复制(您必须创建一个新数组并将每个项目逐个复制到其中) , 例如)。

最简单的方法就是阅读这篇文章,从头开始(不一定是最好的)。


你需要clone() int[] sidePit (否则会在this和它的clone()之间共享。

KalaGameState clone = (KalaGameState) super.clone();
clone.sidePit = this.sidePit.clone();
return clone;

这应该可以解决您的问题,但是您也应该知道,Java中的克隆被认为是中断的。

关于clone()

  • 如何正确覆盖克隆方法?
  • 克隆()vs复制构造函数 - 在java中推荐
  • 行情

    如果您在我的书中阅读过关于克隆的内容,尤其是如果您在各行之间阅读的话,那么您会知道我认为克隆已经彻底崩溃了。

    来源:乔希布洛赫在设计 - 复制构造与克隆

    所有共享参考文献中的突变问题

  • java不可变对象的问题
  • 共享一个String[] ; String是不可变的; 数组不是

  • 问题是sidePit是对数组的引用。 对super.clone()的调用正在创建一个新的KalaGameState,但sidePit指向同一个数组。 super.clone()方法(和整个Clonable接口)只有在对象由基元和字符串(或任何其他不可变对象)组成时才起作用。 但是,对于您的课程,您需要一个如下所示的克隆:

    public KalaGameState clone() {
        KalaGameState result = new KalaGameState();
        result.sidePit = sidePit.clone();
        return result;
    } 
    
    链接地址: http://www.djcxy.com/p/40795.html

    上一篇: Original object is also changed when values of cloned object are changed

    下一篇: Java: recommended solution for deep cloning/copying an instance