在java中,无法使 用类直接赋值,即使用Login loginB=loginA;这样的赋值方式,虽然编译起来是没有错的,但往往与实际用法出现叉错.本意是new 一个新类出来,将LoginA的值赋给LoginB,但是,这时候,你会发现,如果你修改了LoginB的值,loginA的值也会随之而改变,这是因为什么呢?
其实这个原因很简单,因为java不同C语言类的有指针,java是通过直接指向内存的引用而调用对象的
如图:
由图可以看出,对像LoginB仍然指向LoginA的堆空间
为了解决这种情况,我使用如下的例子:
先定义User类
public class User {
private int id;
private String userName;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
private int id;
private String userName;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
再定义Login类
public class Login implements Cloneable {
private int id;
private String loginIp;
private User loginUser;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLoginIp() {
return loginIp;
}
public void setLoginIp(String loginIp) {
this.loginIp = loginIp;
}
public User getLoginUser() {
return loginUser;
}
public void setLoginUser(User loginUser) {
this.loginUser = loginUser;
}
public Object clone() {
Login login=null;
try {
login = (Login) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return login;
}
}
public class Login implements Cloneable {
private int id;
private String loginIp;
private User loginUser;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLoginIp() {
return loginIp;
}
public void setLoginIp(String loginIp) {
this.loginIp = loginIp;
}
public User getLoginUser() {
return loginUser;
}
public void setLoginUser(User loginUser) {
this.loginUser = loginUser;
}
public Object clone() {
Login login=null;
try {
login = (Login) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return login;
}
}
测试一:
public class Test {
public static void main(String[] args) {
Login loginA=new Login();
loginA.setId(1000);
User user = new User();
user.setId(2000);
loginA.setLoginUser(user);
Login loginB=loginA;
loginB.setId(1050);
loginB.getLoginUser().setId(2050);
System.out.println("loginA id:"+loginA.getId());
System.out.println("user id:"+user.getId());
}
}
public static void main(String[] args) {
Login loginA=new Login();
loginA.setId(1000);
User user = new User();
user.setId(2000);
loginA.setLoginUser(user);
Login loginB=loginA;
loginB.setId(1050);
loginB.getLoginUser().setId(2050);
System.out.println("loginA id:"+loginA.getId());
System.out.println("user id:"+user.getId());
}
}
输出结果:loginA id:1050
user id:2050
user id:2050
测试二:将Test类中的Login loginB=loginA;
改成:Login loginB=(Login) loginA.clone();
输出结果:loginA id:1000
user id:2050
user id:2050
由这两个测试,可以看出,使用clone()赋值,与直接赋值的区别,在这里,可以看出Login中的User类的值,无论是使用clone()赋值还是 使用直接赋值,它里面的值都会随着LoginB的改变而改变,但改变LoginB其它内容的时候,LoginA中却不会产生变化,这种情况叫做浅度克隆
浅度克隆只会将基础数据类型克隆出来,而深层的对像则不会被克隆出来;
注:使用浅度克隆的类除了要重写Object类中的Clone()方法,还必须要实现 Cloneable接口,才可以合法地对该类实例进行按字段复制。
为了解决类中类不能被克隆的情况:所以产生了深度克隆
将创建Login类更改成如下:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Login implements Serializable {
private int id;
private String loginIp;
private User loginUser;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLoginIp() {
return loginIp;
}
public void setLoginIp(String loginIp) {
this.loginIp = loginIp;
}
public User getLoginUser() {
return loginUser;
}
public void setLoginUser(User loginUser) {
this.loginUser = loginUser;
}
public Login clone(Login login) {
Login newLogin=null;
ByteArrayInputStream bin = null;
ByteArrayOutputStream bout = null;
try {
//把对象对到内存中去
bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(login);
oos.close();
//把对象从内存中读出来
ByteArrayInputStream bais = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
newLogin = (Login) ois.readObject();
ois.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
private int id;
private String loginIp;
private User loginUser;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLoginIp() {
return loginIp;
}
public void setLoginIp(String loginIp) {
this.loginIp = loginIp;
}
public User getLoginUser() {
return loginUser;
}
public void setLoginUser(User loginUser) {
this.loginUser = loginUser;
}
public Login clone(Login login) {
Login newLogin=null;
ByteArrayInputStream bin = null;
ByteArrayOutputStream bout = null;
try {
//把对象对到内存中去
bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(login);
oos.close();
//把对象从内存中读出来
ByteArrayInputStream bais = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
newLogin = (Login) ois.readObject();
ois.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return newLogin;
}
}
}
}
同时User类也要实现Serializable 接口,目的是标识可序列化
测试三:将Test类中的:Login loginB=(Login) loginA.clone();
改为: Login loginB=new Login().clone(loginA);
测试结果:loginA id:1000
user id:2000
user id:2000
由此可以看出,loginB类中的任何值改变了,原赋值中的类的值是不会跟着改变的
内存变化: