hello ,好久沒來了。
今天我來和大家分享一下有關引用變量的注意事項,一是加深一下自己的了解,二是對這塊不太了解的同學可以看看。
大神可飄過,有什麼不對或不足的地方請多多指教,謝謝。
假設場景:
有一個統計遊戲玩家資訊調查問卷系統,玩家填寫了調查問卷,會給玩家一些獎勵,當然目前這不是我們關注的部分。
我們需要記錄一下玩家的姓名,年齡,郵箱,以及玩家曾經玩過的遊戲有哪些。
既然要記錄玩家玩過的遊戲,必然要有Game類:
package indi.bruce.summary;
public class Game {
private int id; //随便填寫一個id
private String name; //總要有個遊戲名字吧
private String developer; //遊戲是誰開發的
private String type; //遊戲總要有個類型吧 ,例如:政策,體育,動作等等
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDeveloper() {
return developer;
}
public void setDeveloper(String developer) {
this.developer = developer;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
現在定義玩家類 Player:
package indi.bruce.summary;
import java.util.ArrayList;
import java.util.List;
public class Player {
private int id ; //玩家編号
private String name; //姓名
private int age; //年齡
private String email; //郵箱
private List<Game> gameList; //曾經玩過的遊戲,便于分析使用者行為
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public List<Game> getGameList() {
if(gameList == null){
return new ArrayList<Game>();
}
return gameList;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
正題開始:
假設場景1:玩家問卷資料已經存庫,玩家要修改自己的資訊,而系統需要記錄一下到底修改了哪些地方。
1)從資料庫讀取使用者的資料Player。
2)将原來的對象player複制出來一份給temp對象,玩家修改資訊在temp對象上修改(既然要記錄玩家修改了什麼,則需要原來的對象和現在的對象對比)。
3)兩個對象做一下對比,就可以做操作記錄了。
場景1測試代碼:
@Test
public void sceneOne(){
Player player = getPlayer(1110); //從資料庫擷取的對象
System.out.println("print player.getName() is:"+player.getName());
Player temp = player; //玩家修改資訊全部在副本temp上操作,這樣就可以對比到底玩家修改了什麼資訊,哈哈哈....
temp.setName("moon"); //玩家修改了名字
System.out.println("print temp.getName() is:" + temp.getName()); //哈哈...成功了,多簡單的事情
System.out.println("print player.getName() is:" + player.getName());
/*但是列印結果
* print player.getName() is:sky
* print temp.getName() is:moon
* print player.getName() is:moon
*
* 疑問:為什麼隻是修改temp屬性name的值,而player的name值也變了呢?
*/
}
//假設該方法是從資料庫擷取該使用者的資料對象,這不是我們目前關注的部分
public Player getPlayer(int id){
Player player = new Player();
player.setId(1110);
player.setName("sky");
player.setAge(20);
player.setEmail("[email protected]");
return player;
}
為什麼會出現上面的情況呢,來分析一下引用變量的工作原理:
1.代碼中"Player player = getPlayer(1110);"做了兩件事:
1)将玩家1110資料從資料庫資料庫讀出來(這部分假設從資料庫讀出來),并加載的堆記憶體中(player對象實際上隻存在堆記憶體中)。
2)建立一個引用變量player(其實就是指針)指向堆記憶體的player對象(也就是記錄堆記憶體中player的記憶體位址)
2.代碼"Player temp = player;"做了一件事:
建立引用變量temp,并且指向堆記憶體中的player對象。
總結:是以不管是針對棧中的引用變量temp,還是棧中的引用變量player操作,實際上都是操作堆記憶體中的player對象。
注意:解決場景1出現的問題,需要引入一個概念"深拷貝",概念請問度娘.
解決方案:1)首先Player類需要實作Cloneable接口:public class Player implements Cloneable
2)需要在Player中重寫Object的clone方法
public Object clone(){
Object obj = null;
try {
return super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
3)在調用的地方:Player player = getPlayer(1110); Player temp = (Player)player.clone();