深浅拷贝
Object类clone()方法
深浅拷贝概念(重要)
深浅拷贝实现方式
我们首先来学习一下Object类中的clone()方法
//我们可以看到,clone()方法为本地方法,且使用该方法可能会抛出CloneNotSupportedException异常
protected native Object clone() throws CloneNotSupportedException;
接下来我们探寻一下CloneNotSupportedException异常出现的原因:
class Teacher{//自定义teacher类,其中包含两个属性及构造方法 getter setter方法
private String name;
private String job;
public Teacher(String name, String job) {
this.name = name;
this.job = job;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", job='" + job + '\'' +
'}';
}
}
//自定义Student类,与Teacher类不同的是Student类中除了两个普通属性外还包括Teacher属性
class Student{
private String name;
private Integer age;
private Teacher teacher;
public Student(String name, Integer age, Teacher teacher) {
this.name = name;
this.age = age;
this.teacher = teacher;
}
//我们知道clone()方法是Object类中方法,所有类均默认继承Object类,Student类中clone()方法实际上是覆写Object类中clone()方法,因我们希望拷贝Student类故返回类型为Student
public Student clone() throws CloneNotSupportedException {
return (Student) super.clone();//直接调用Object类中clone()方法,但注意向下转型强转
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
public class Test{
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher=new Teacher("小芳","Java teacher");
Student student=new Student("小花",20,teacher);
Student studentClone=student.clone();
//获得拷贝Student中属性,查看是否拷贝成功
System.out.println(studentClone.getName());
System.out.println(studentClone.getAge());
System.out.println(studentClone.getTeacher());
System.out.println(teacher==studentClone.getTeacher());//判断拷贝对象中teacher对象是否与之前地址相同
}
}
//Exception in thread "main" java.lang.CloneNotSupportedException: Student
//at java.lang.Object.clone(Native Method)
//at Student.clone(Test.java:45)
//at Test.main(Test.java:75)
通过以上代码我们可以看到,在程序运行阶段抛出异常,实际上通过查看源码可知存在Cloneable接口,该接口内无任何方法,但并不是所有类均可进行拷贝,若想拷贝则必须实现Clonable接口(标志接口)否则就会抛出CloneNotSupportedException异常。
//Cloneable源码
* @see java.lang.CloneNotSupportedException
* @see java.lang.Object#clone()
* @since JDK1.0
*/
public interface Cloneable {
}
故上文中我们要对Student类进行拷贝,Student类就应该实现Cloneable接口。对上文代码进行修改:
class Teacher{
private String name;
private String job;
public Teacher(String name, String job) {
this.name = name;
this.job = job;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", job='" + job + '\'' +
'}';
}
}
class Student implements Cloneable{
private String name;
private Integer age;
private Teacher teacher;
public Student(String name, Integer age, Teacher teacher) {
this.name = name;
this.age = age;
this.teacher = teacher;
}
public Student clone() throws CloneNotSupportedException {
return (Student) super.clone();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
public class Test{
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher=new Teacher("小芳","Java teacher");
Student student=new Student("小花",20,teacher);
Student studentClone=student.clone();
System.out.println(studentClone.getName());
System.out.println(studentClone.getAge());
System.out.println(studentClone.getTeacher());
System.out.println(teacher==studentClone.getTeacher());
}
}
//小花
//20
//Teacher{name='小芳', job='Java teacher'}
//true
接下来简单学习一下深浅拷贝的概念(熟悉)
浅拷贝–对象值拷贝(基本类型内容拷贝,引用类型地址拷贝(即栈内存拷贝对象指向原对象))
对于浅拷贝而言,拷贝出来的对象仍然保留原对象的所有引用。
浅拷贝问题:只要任意一个拷贝对象(或原对象)中的引用发生改变,所有对象均会收到影响。
深拷贝:拷贝出来的对象产生了所有引用的新对象
深拷贝特点:修改任意一个对象,不会对其他对象产生影响。
深浅拷贝的实现方式:
浅拷贝
如上文代码实际上就为浅拷贝,因拷贝的新对象中teacher类与旧对象地址相同。这就说明无论拷贝多少,其地址均指向同一块内存。
//改变teacher对象地址
class Teacher{
private String name;
private String job;
public Teacher(String name, String job) {
this.name = name;
this.job = job;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", job='" + job + '\'' +
'}';
}
}
class Student implements Cloneable{
private String name;
private Integer age;
private Teacher teacher;
public Student(String name, Integer age, Teacher teacher) {
this.name = name;
this.age = age;
this.teacher = teacher;
}
public Student clone() throws CloneNotSupportedException {
return (Student) super.clone();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
public class Test{
public static void main(String[] args) throws CloneNotSupportedException {
Teacher teacher=new Teacher("小芳","Java teacher");
Teacher teacher1=new Teacher("小红","Python teacher");
Student student=new Student("小花",20,teacher);
teacher=teacher1;//引用传递
Student studentClone=student.clone();
System.out.println(studentClone.getName());
System.out.println(studentClone.getAge());
System.out.println(studentClone.getTeacher());
System.out.println(teacher==studentClone.getTeacher());
}
}
//小花
//20
//Teacher{name='小芳', job='Java teacher'}
//false
深拷贝
(1)利用递归思想实现深拷贝。也就是说不论是要拷贝的类还是作为该类属性的第三方自定义类均需实现Cloneable接口,类内部覆写Object类的clone()方法。
(2)序列化实现深拷贝(常用)–不再实现Cloneable接口,但需实现Serializable(序列化标志接口),只需在要拷贝的类中定义方法进行数据拷贝即可(注意此时该方法名称不可为clone,因此时并不是覆写Object类中clone()方法而是通过内存进行对象拷贝)
//递归思想实现深拷贝
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import java.io.*;
class Person implements Cloneable{
private String name;
public Person(String name) {
this.name = name;
}
public Person clone(){
Person person=null;
try {
person=(Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return person;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Teacher implements Cloneable{
private String name;
private String job;
private Person person;
public Teacher(String name, String job, Person person) {
this.name = name;
this.job = job;
this.person = person;
}
public Teacher clone(){
Teacher teacher=null;
try {
teacher= (Teacher) super.clone();
teacher.person=this.person.clone();//调用Person类的clone()方法
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return teacher;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", job='" + job + '\'' +
", person=" + person +
'}';
}
}
class Student implements Cloneable {
private String name;
private Integer age;
private Teacher teacher;
public Student(String name, Integer age, Teacher teacher) {
this.name = name;
this.age = age;
this.teacher = teacher;
}
public Student clone(){
Student student=null;
try {
student= (Student) super.clone();
student.teacher=this.teacher.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
public class Test{
public static void main(String[] args) throws IOException, ClassNotFoundException {
Person person=new Person("花花");
Teacher teacher=new Teacher("小芳","Java teacher",person);
Student student=new Student("小花",20,teacher);
Student studentClone=student.clone();
System.out.println(studentClone.getName());
System.out.println(studentClone.getAge());
System.out.println(studentClone.getTeacher());
System.out.println(teacher==studentClone.getTeacher());
}
}
//小花
//20
//Teacher{name='小芳', job='Java teacher', person=Person{name='花花'}}
//false
//序列化实现深拷贝
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import java.io.*;
class Teacher implements Serializable{
private String name;
private String job;
public Teacher(String name, String job) {
this.name = name;
this.job = job;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", job='" + job + '\'' +
'}';
}
}
class Student implements Serializable {
private String name;
private Integer age;
private Teacher teacher;
public Student(String name, Integer age, Teacher teacher) {
this.name = name;
this.age = age;
this.teacher = teacher;
}
public Student cloneObject() throws IOException, ClassNotFoundException {
//利用内存进行拷贝操作
ByteOutputStream bos=new ByteOutputStream();//获得内存输出流
ObjectOutputStream oos=new ObjectOutputStream(bos);//序列化
oos.writeObject(this);//向内存中写入数据
ByteArrayInputStream bis=new ByteArrayInputStream(bos.getBytes());//获得内存输入流
ObjectInputStream ois=new ObjectInputStream(bis);//反序列化
return (Student) ois.readObject();//从内存中读取数据
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
}
public class Test{
public static void main(String[] args) throws IOException, ClassNotFoundException {
Teacher teacher=new Teacher("小芳","Java teacher");
Student student=new Student("小花",20,teacher);
Student studentClone=student.cloneObject();
System.out.println(studentClone.getName());
System.out.println(studentClone.getAge());
System.out.println(studentClone.getTeacher());
System.out.println(teacher==studentClone.getTeacher());
}
}
//小花
//20
//Teacher{name='小芳', job='Java teacher'}
//false