值傳遞與對象拷貝 值傳遞和引用傳遞 調用一個有參函數的時候,會把實際參數傳遞給形式參數。但是,在程式語言中,這個傳遞過程中傳遞的兩種情況,即值傳遞和引用傳遞 兩者區別 1. 值傳遞(pass by value)是指在調用函數時將實際參數複製一份傳遞到函數中,這樣在函數中如果對參數進行修改,將不會影響 ...
值傳遞與對象拷貝
值傳遞和引用傳遞
調用一個有參函數的時候,會把實際參數傳遞給形式參數。但是,在程式語言中,這個傳遞過程中傳遞的兩種情況,即值傳遞和引用傳遞
兩者區別
- 值傳遞(pass by value)是指在調用函數時將實際參數複製一份傳遞到函數中,這樣在函數中如果對參數進行修改,將不會影響到實際參數
- 引用傳遞(pass by reference)是指在調用函數時將實際參數的地址直接傳遞到函數中,那麼在函數中對參數所進行的修改,將影響到實際參數
java中的值傳遞
java中只存在值傳遞,不存在引用傳遞
對於基本數據類型
public class ParamTest { public static void main(String[] args) { ParamTest pt = new ParamTest(); int i = 10; pt.pass(i); System.out.println("print in main , i is " + i); } public void pass(int j) { j = 20; System.out.println("print in pass , j is " + j); } } /* 輸出 print in pass , j is 20 print in main , i is 10 */
由此可見,對於基本數據類型,是在棧中重新建立一個引用 j,使其值等於傳遞的參數值 i,改變 j 並不影響 i
對於引用數據類型
示例一
public static void main(String[] args) { ParamTest pt = new ParamTest(); User hello = new User(); hello.setName("Hello"); hello.setGender("Male"); pt.pass(hello); System.out.println("print in main , user is " + hello); } public void pass(User user) { user.setName("HelloChange"); System.out.println("print in pass , user is " + user); } /* 輸出 print in pass , user is User{name='HelloChange', gender='Male'} print in main , user is User{name='HelloChange', gender='Male'} */
示例二
public static void main(String[] args) { ParamTest pt = new ParamTest(); User hello = new User(); hello.setName("Hello"); hello.setGender("Male"); pt.pass(hello); System.out.println("print in main , user is " + hello); } public void pass(User user) { user = new User(); user.setName("HelloChange"); user.setGender("Male"); System.out.println("print in pass , user is " + user); } /* 輸出 print in pass , user is User{name='HelloChange', gender='Male'} print in main , user is User{name='Hello', gender='Male'} */
示例一中,調用 User 類方法改變了記憶體地址上對象的屬性,但這並不是對傳入參數本身的改變,實際上傳入 hello 之後,在棧中建立 user 引用,且指向地址與 hello 相同,即指向同一對象,對對象的改變並不是對 hello 這個引用的改變,其值一直在棧中指向對象空間地址
示例二中,傳入 hello 之後,在棧中建立 user 引用並於 hello 等值,但通過 new 對象,使 user 引用指向了新的對象,即 user 在棧中的值改變了,而 hello 的值一直指向原對象並不受影響
對象拷貝
含義
有時侯我們需要獲得一個新對象,和已經存在的對象完全相同,但又必須相互獨立,如果使用簡單的賦值方式,實際上指向的還是同一記憶體地址上的對象,操作一個引用可能會影響另一個,這就需要對象拷貝來獲取一個完全相同的新對象
5種方式
setter/getter
new 新對象後用 set/get 方法設置屬性
淺克隆
- 被覆制的類需要實現Clonenable介面(不實現的話在調用 clone 方法會拋出 CloneNotSupportedException 異常), 該介面為標記介面(不含任何方法)
- 覆蓋clone( ) 方法,訪問修飾符設為 public。方法中調用 super.clone( ) 方法得到需要的複製對象
class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } class Student implements Cloneable { private int number; private Address addr; public Address getAddr() { return addr; } public void setAddr(Address addr) { this.addr = addr; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } @Override public Object clone() { Student stu = null; try { stu = (Student) super.clone(); //淺複製 } catch (CloneNotSupportedException e) { e.printStackTrace(); } return stu; } } public class Test { public static void main(String args[]) { Address addr = new Address(); addr.setAddress("杭州市"); Student stu1 = new Student(); stu1.setNumber(123); stu1.setAddr(addr); Student stu2 = (Student) stu1.clone(); System.out.println("學生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAddress()); System.out.println("學生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAddress()); stu2.setNumber(124); addr.setAddress("西湖區"); System.out.println("學生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAddress()); System.out.println("學生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAddress()); } } /* 輸出 學生1:123,地址:杭州市 學生2:123,地址:杭州市 學生1:123,地址:西湖區 學生2:124,地址:西湖區 */
通過淺克隆獲得新對象,其基本數據類型成員變數得到了複製,修改後不影響原對象
若變數為引用數據類型,則只複製地址引用,還是指向相同地址,修改時會相互影響
若變數為 String 類型,則拷貝其地址引用。但是在修改時,它會從字元串池中重新生成一個新的字元串,原有字元串對象保持不變
深克隆
- 淺複製只是複製了addr 變數的引用,並沒有真正的開闢另一塊空間,將值複製後再將引用返回給新對象
- 為了達到真正的複製對象,而不是純粹引用複製。我們需要將 Address 類可複製化,並且修改 clone 方法,代碼如下:
class Address implements Cloneable { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public Object clone()//Address 類可複製化 { Address addr = null; try { addr = (Address) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return addr; } } class Student implements Cloneable { private int number; private Address addr; public Address getAddr() { return addr; } public void setAddr(Address addr) { this.addr = addr; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } @Override public Object clone() { Student stu = null; try { stu = (Student) super.clone(); //淺複製 } catch (CloneNotSupportedException e) { e.printStackTrace(); } stu.addr = (Address) addr.clone(); //引用數據類型變數深複製 return stu; } } public class Test { public static void main(String args[]) { Address addr = new Address(); addr.setAddress("杭州市"); Student stu1 = new Student(); stu1.setNumber(123); stu1.setAddr(addr); Student stu2 = (Student) stu1.clone(); System.out.println("學生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAddress()); System.out.println("學生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAddress()); stu2.setNumber(124); addr.setAddress("西湖區"); System.out.println("學生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAddress()); System.out.println("學生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAddress()); } } /* 輸出 學生1:123,地址:杭州市 學生2:123,地址:杭州市 學生1:123,地址:西湖區 學生2:124,地址:杭州市 */
工具類
org.apache.commons 組件 BeanUtils 和 PropertyUtils,靜態方法 copyProperties(Object o1,Object o2)
序列化
序列化就是將對象寫到流的過程,寫到流中的對象是原有對象的一個拷貝,而原對象仍然存在於記憶體中。通過序列化實現的拷貝不僅可以複製對象本身,而且可以複製其引用的成員對象,因此通過序列化將對象寫到一個流中,再從流里將其讀出來,可以實現深克隆。需要註意的是能夠實現序列化的對象其類必須實現Serializable介面,否則無法實現序列化操作
class Professor implements Serializable { String name; int age; Professor(String name, int age) { this.name = name; this.age = age; } } class Student implements Serializable { String name;//常量對象 int age; Professor p;//引用數據類型 Student(String name, int age, Professor p) { this.name = name; this.age = age; this.p = p; } //深克隆 public Object deepClone() throws IOException, ClassNotFoundException { //將對象寫到流里 ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oo = new ObjectOutputStream(bo); oo.writeObject(this); //從流里讀出來 ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi = new ObjectInputStream(bi); return (oi.readObject()); } } public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException { Professor p = new Professor("wangwu", 50); Student s1 = new Student("zhangsan", 18, p); Student s2 = (Student) s1.deepClone(); s2.p.name = "lisi"; s2.p.age = 30; System.out.println("name=" + s1.p.name + "," + "age=" + s1.p.age); //學生1的教授不改變 } }