关于Java中的拷贝问题
在实验二中就因为Java中的拷贝机制踩了个坑,到实验2结束都不知道是哪里的问题,知道做到实验三才想起来到底是咋回事,故做此文。
import java.util.ArrayList;
import java.util.List;
public class foo {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Integer> l1 = new ArrayList<Integer>();
l1.add(3);
List<Integer> l2 = l1;
l2.set(0, 0);
System.out.println(l1.get(0)+" "+l2.get(0));
}
}
输出结果:0 0
直接复制,实际上是复制的引用,两者指向同一个对象,当l2中的成员变量进行变化时,l1的成员变量也跟着变化(这就是我Lab2中出现的bug之一)。可以考虑下面的方法
import java.util.ArrayList;
import java.util.List;
public class foo {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Integer> l1 = new ArrayList<Integer>();
l1.add(3);
List<Integer> l2 = new ArrayList<Integer>();
l2.addAll(l1);
l2.set(0, 0);
System.out.println(l1.get(0)+" "+l2.get(0));
}
}
输出结果:3 0
浅拷贝:Object.clone():Creates and returns a copy of this object. The precise meaning of “copy” may depend on the class of the object. The general intent is that, for any object x
, the expression:
x.clone() != x
will be true, and that the expression:
x.clone().getClass() == x.getClass()
will be true
, but these are not absolute requirements. While it is typically the case that:
x.clone().equals(x)
will be true
, this is not an absolute requirement.
代码:
package P3;
import java.util.*;
public class Person implements Cloneable{
private int age;
private String name;
private List<Person> friendsList = new LinkedList<>();
boolean visit = false;
public Person(String string,int age) {
this.name = string;
this.age = age;
}
void addFriend(Person p)
{
friendsList.add(p);
}
String getName()
{
return new String(name);
}
/**
* @return the friendsList
*/
public List<Person> getFriendsList() {
return friendsList;
}
/**
* @param friendsList the friendsList to set
*/
public void setFriendsList(List<Person> friendsList) {
this.friendsList = friendsList;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @param visit the visit to set
*/
public void setVisit(boolean visit) {
this.visit = visit;
}
int getAge(Person p)
{
int res = this.age;
return res;
}
List<Person> getFriends()
{
return friendsList;
}
public Object Clone()
{
try {
return (Person)super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
}
package P3;
import java.util.LinkedList;
import java.util.List;
public class test {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Person> l1 = new LinkedList<Person>();
List<Person> l2 = new LinkedList<Person>();
Person p1 = new Person("A",12);
Person p = new Person("C",9);
Person l = new Person("D",8);
l1.add(p);
l2.add(l);
p1.setFriendsList(l1);
Person p2 = (Person) p1.Clone();
System.out.println(p1.getName()+" "+p1.getAge());
System.out.println(p1.getFriendsList().size());
System.out.println(p2.getName()+" "+p2.getAge());
System.out.println(p2.getFriendsList().size());
p2.setAge(10);
p2.setName("B");
p2.addFriend(l);
System.out.println(p1.getName()+" "+p1.getAge());
System.out.println(p1.getFriendsList().size());
System.out.println(p2.getName()+" "+p2.getAge());
System.out.println(p2.getFriendsList().size());
}
}
输出结果为:
A 12
1
A 12
1
A 12
2
B 10
2
p2作为由p1调用clone 得到的对象,当p2的name和age发生变化时都不会影响到p1相关属性的变化,但是当p2对其List操作时,p1的List也受到影响。由此可以得到clone主要操作为:创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段是值类型的,那么对该字段执行复制;如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象。对于基本类型,直接拷贝其值,对于实例对象,拷贝其地址医用,因此才会出现新对象与原有对象公用一个实例变量,对于String直接拷贝其地址引用,但是在修改时,生成一个新的字符串。
解决方法:
可以考虑在clone函数上做手脚,进行修改。但是可能会影响程序的性能。
public Object Clone()
{
Person p = null;
try {
p = (Person)super.clone();
p.setFriendsList(new LinkedList<Person>(p.getFriendsList()));
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
return p;
}
测试结果
A 12
1
A 12
1
A 12
1
B 10
2
深拷贝:创造一个新的对象,并且新的对象与母对象之间不存在共享引用。序列化
package P3;
import java.io.*;
import java.io.Serializable;
import java.util.*;
public class Person implements Serializable{
private static final long serialVersionUID = 2335521992258577660L;
private int age;
private String name;
private List<Person> friendsList = new LinkedList<>();
boolean visit = false;
public Person(String string,int age) {
this.name = string;
this.age = age;
}
void addFriend(Person p)
{
friendsList.add(p);
}
String getName()
{
return new String(name);
}
/**
* @return the friendsList
*/
public List<Person> getFriendsList() {
return friendsList;
}
/**
* @param friendsList the friendsList to set
*/
public void setFriendsList(List<Person> friendsList) {
this.friendsList = friendsList;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @param visit the visit to set
*/
public void setVisit(boolean visit) {
this.visit = visit;
}
int getAge(Person p)
{
int res = this.age;
return res;
}
List<Person> getFriends()
{
return friendsList;
}
public Object deepClone() throws Exception{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
package P3;
import java.util.LinkedList;
import java.util.List;
public class test {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
List<Person> l1 = new LinkedList<Person>();
List<Person> l2 = new LinkedList<Person>();
Person p1 = new Person("A",12);
Person p = new Person("C",9);
Person l = new Person("D",8);
l1.add(p);
l2.add(l);
p1.setFriendsList(l1);
Person p2 = (Person) p1.deepClone();
System.out.println(p1.getName()+" "+p1.getAge());
System.out.println(p1.getFriendsList().size());
System.out.println(p2.getName()+" "+p2.getAge());
System.out.println(p2.getFriendsList().size());
p2.setAge(10);
p2.setName("B");
p2.addFriend(l);
System.out.println(p1.getName()+" "+p1.getAge());
System.out.println(p1.getFriendsList().size());
System.out.println(p2.getName()+" "+p2.getAge());
System.out.println(p2.getFriendsList().size());
}
}
测试结果
A 12
1
A 12
1
A 12
1
B 10
2