这周对Shallow Clone 进行了一些挖掘
先查看一下Test源码:
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
List<Integer> list1 = new ArrayList<Integer>(list);
List<Integer> list2 = list.subList(0, list.size());
list2.add(3);
System.out.println("list == list1:" + list.equals(list1)+":::::::"+list);
System.out.println("list == list2:" + list.equals(list2)+":::::::"+list1);
System.out.println("list1 == list2:" + list1.equals(list2)+":::::::"+list2);
输出结果:
list == list1:false:::::::[1, 2, 3]
list == list2:true:::::::[1, 2]
list1 == list2:false:::::::[1, 2, 3]
结论:查看List的源码你会发现
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
list1虽然是浅拷贝,但元素类型是String,也就是说元素是深拷贝的。
而list2则是在list上面产生的一个试图view,所有的修改动作直接作用于原列表。
int[] clone = data.clone();
Arrays.sort(clone);
使用clone的时候,需新生成,然后在进行操作,否在直接Arrays.sort(data.clone());这样排序取出来的值是原始位置
Teachers[] teachers = new Teachers[2];
Teachers teachers1 = new Teachers("你好");
teachers[0] = teachers1;
Teachers teachers2 = new Teachers("407");
teachers[1] = teachers2;
Teachers[] copyOf = Arrays.copyOf(teachers, teachers.length);
copyOf[1].setName("40777");
Teachers[] newest = new Teachers[teachers.length];
for (int j = 0; j < teachers.length; j++) {
newest[j] = teachers[j];//避免浅拷贝粗糙方法,遍历赋值一个新的集合~这样才能不改变初始值
System.out.println(teachers[j]);
}
for (int j = 0; j < copyOf.length; j++) {
System.out.println(copyOf[j]);
}
newest[1].setName("公司");
for (int j = 0; j < newest.length; j++) {
System.out.println(newest[j]);
}
输出结果:
Teachers(name=你好)
Teachers(name=40777)
Teachers(name=你好)
Teachers(name=40777)
Teachers(name=你好)
Teachers(name=公司)
如果进行下面这种操作你会发现:
Teachers t = new Teachers("你好");
Teachers t1 = new Teachers("哈哈");
List<Teachers> list = new ArrayList<Teachers>();
list.add(t);
list.add(t1);
System.out.println(list+"..."+t+"..."+t1);
list.get(1).setName("好");//这次修改将会改变原有的对象属性值--哈哈
System.out.println(list+"..."+t+"..."+t1);
输出结果:
[Teachers(name=你好), Teachers(name=哈哈)]...Teachers(name=你好)...Teachers(name=哈哈)
[Teachers(name=你好), Teachers(name=好)]...Teachers(name=你好)...Teachers(name=好)
所以在此提醒各位:使用拷贝时需注意的坑~浅拷贝以及深拷贝的一些主要问题。
关于上周blog浅拷贝
1:subList场景应用:一个初始列表,需要删除中间某个范围内的值,除了遍历有没有"ont-lining",一行代码解决问题呢?
1)明确了subList方法的具体实现方式,所有的操作都是在原始列表上进行的,那我们就用subList先取出一个子列表,然后清空。因为subList返回的List是原始列表的一个视图,删除这个视图中的所有元素,最终就会反映到原始字符串上,那么一行代码即解决问题了。
list.subList(from,to).clear;
2:关于String:并不存在类似问题。
String str="AB";
String str1=new String(str);
String str2=str.substring(0,1)+"C"; System.out.println("str==str1?"+str.equals(str1)+":::::::"+str);//true...AB
System.out.println("str==str2?"+str.equals(str2)+":::::::"+str1+":::::::"+str2);//false...AB...AC
深浅拷贝==原型设计模式
原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。
原型模式的工作原理很简单:将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝自己来实现创建过程。由于在软件系统中我们经常会遇到需要创建多个相同或者相似对象的情况,因此原型模式在真实开发中的使用频率还是非常高的。原型模式是一种“另类”的创建型模式,创建克隆对象的工厂就是原型类自身,工厂方法由克隆方法来实现。
需要注意的是通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,通常对克隆所产生的对象进行修改对原型对象不会造成任何影响,每一个克隆对象都是相互独立的。通过不同的方式修改可以得到一系列相似但不完全相同的对象。
Object类提供一个clone()方法,可以将一个Java对象复制一份。因此在Java中可以直接使用Object提供的clone()方法来实现对象的克隆,Java语言中的原型模式实现很简单。
需要注意的是能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个Java类支持被复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个CloneNotSupportedException异常。
在Java语言中,通过覆盖Object类的clone()方法可以实现浅克隆。为了让大家更好地理解浅克隆和深克隆的区别
package com.spring.springboot;
/**
* @Author: Chao.Qin
* @Date: 2019/3/1 0001 17:13
* @Description:
*/
public class Grade implements Cloneable{
//为了简化设计和实现,假设一各班级中只有一个学生对象,实际情况中可以包含多个学生,可以通过List等集合对象来实现
private Students students;
private String name;
private String date;
private String content;
public void setName(String name) {
this.name = name;
}
public void setDate(String date) {
this.date = date;
}
public void setContent(String content) {
this.content = content;
}
public Students getStudents() {
return students;
}
public void setStudents(Students students) {
this.students = students;
}
public String getName() {
return (this.name);
}
public String getDate() {
return (this.date);
}
public String getContent() {
return (this.content);
}
//使用clone()方法实现浅克隆
public Grade clone() {
Object obj = null;
try {
obj = super.clone();
return (Grade)obj;
} catch (CloneNotSupportedException e) {
System.out.println("不支持复制!");
return null;
}
}
}
package com.spring.springboot;
/**
* @Author: Chao.Qin
* @Date: 2019/3/1 0001 17:15
* @Description:
*/
public class TestStudentAndGrade {
public static void main(String args[]) {
Grade grade = new Grade(); //创建原型对象
grade.setName("你好");
grade.setDate("第3周");
grade.setContent("这周工作很忙,每天加班!");
System.out.println("****动态****");
System.out.println("周次:" + grade.getDate());
System.out.println("姓名:" + grade.getName());
System.out.println("内容:" + grade.getContent());
System.out.println("--------------------------------");
Grade grade_1;
grade_1 = grade.clone(); //调用克隆方法创建克隆对象
grade_1.setDate("第4周");
System.out.println("****动态****");
System.out.println("周次:" + grade_1.getDate());
System.out.println("姓名:" + grade_1.getName());
System.out.println("内容:" + grade_1.getContent());
}
}
输出:
****动态****
周次:第3周
姓名:你好
内容:这周工作很忙,每天加班!
--------------------------------
****动态****
周次:第4周
姓名:你好
内容:这周工作很忙,每天加班!
考虑完再看接下来的代码:
浅拷贝:
package com.spring.springboot;
/**
* @Author: Chao.Qin
* @Date: 2019/3/1 0001 17:11
* @Description:
*/
public class Students implements Cloneable{
private String name; //姓名
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void download() {
System.out.println("学生姓名为" + name);
}
public Students clone() {
Object obj = null;
try {
obj = super.clone();
return (Students)obj;
} catch (CloneNotSupportedException e) {
System.out.println("不支持复制!");
return null;
}
}
@Override
public String toString() {
return "Students{" +
"name='" + name + '\'' +
'}';
}
}
package com.spring.springboot;
/**
* @Author: Chao.Qin
* @Date: 2019/3/1 0001 17:15
* @Description:
*/
public class TestStudentAndGrade {
public static void main(String args[]) {
Grade grade = new Grade(); //创建原型对象
grade.setName("你好");
grade.setDate("第3周");
grade.setContent("这周工作很忙,每天加班!");
System.out.println("****动态****");
System.out.println("周次:" + grade.getDate());
System.out.println("姓名:" + grade.getName());
System.out.println("内容:" + grade.getContent());
System.out.println("--------------------------------");
Grade grade_1;
grade_1 = grade.clone(); //调用克隆方法创建克隆对象
grade_1.setDate("第4周");
System.out.println("****动态****");
System.out.println("周次:" + grade_1.getDate());
System.out.println("姓名:" + grade_1.getName());
System.out.println("内容:" + grade_1.getContent());
Students students = new Students(); //创建学生对象
students.setName("你好");
grade.setStudents(students); //将附件添加到周报中
grade_1 = grade.clone(); //调用克隆方法创建克隆对象
grade_1.getStudents().setName("科技");
//比较班级
System.out.println("班级是否相同? " + (grade == grade_1));
//比较学生
System.out.println("学生是否相同? " + (grade.getStudents() == grade_1.getStudents()));
System.out.println("学生是否相同? " + grade.getStudents() + "===========" + grade_1.getStudents());
}
}
输出结果:
****动态****
周次:第3周
姓名:你好
内容:这周工作很忙,每天加班!
--------------------------------
****动态****
周次:第4周
姓名:你好
内容:这周工作很忙,每天加班!
班级是否相同? false
学生是否相同? true
学生是否相同? Students{name='科技'}===========Students{name='科技'}
很奇怪,为什么会这样。
疑问解答:由于使用的是浅克隆技术,因此班级对象复制成功,通过“==”比较原型对象和克隆对象的内存地址时输出false;但是比较学生对象的内存地址时输出true,说明它们在内存中是同一个对象。
深拷贝:
package com.spring.springboot;
import java.io.Serializable;
/**
* @Author: Chao.Qin
* @Date: 2019/3/1 0001 17:11
* @Description:
*/
public class Students implements Serializable {
private String name; //姓名
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void download() {
System.out.println("学生姓名为" + name);
}
public Students clone() {
Object obj = null;
try {
obj = super.clone();
return (Students)obj;
} catch (CloneNotSupportedException e) {
System.out.println("不支持复制!");
return null;
}
}
@Override
public String toString() {
return "Students{" +
"name='" + name + '\'' +
'}';
}
}
班级类Grade 不再使用Java自带的克隆机制,而是通过序列化来从头实现对象的深克隆,我们需要重新编写clone()方法,修改后的代码如下:
package com.spring.springboot;
import java.io.*;
/**
* @Author: Chao.Qin
* @Date: 2019/3/1 0001 17:13
* @Description:
*/
public class Grade implements Serializable {
//为了简化设计和实现,假设一各班级中只有一个学生对象,实际情况中可以包含多个学生,可以通过List等集合对象来实现
private Students students;
private String name;
private String date;
private String content;
public void setName(String name) {
this.name = name;
}
public void setDate(String date) {
this.date = date;
}
public void setContent(String content) {
this.content = content;
}
public Students getStudents() {
return students;
}
public void setStudents(Students students) {
this.students = students;
}
public String getName() {
return (this.name);
}
public String getDate() {
return (this.date);
}
public String getContent() {
return (this.content);
}
//使用clone()方法实现浅克隆
//public Grade clone() {
//
// Object obj = null;
//
// try {
//
// obj = super.clone();
//
// return (Grade)obj;
//
// } catch (CloneNotSupportedException e) {
//
// System.out.println("不支持复制!");
//
// return null;
//
// }
//
//}
//使用序列化技术实现深克隆
public Grade deepClone() throws IOException, ClassNotFoundException, OptionalDataException
{
//将对象写入流中
ByteArrayOutputStream bao=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bao);
oos.writeObject(this);
//将对象从流中取出
ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
return (Grade)ois.readObject();
}
}
package com.spring.springboot;
/**
* @Author: Chao.Qin
* @Date: 2019/3/1 0001 17:15
* @Description:
*/
public class TestStudentAndGrade {
public static void main(String args[]) {
Grade grade = new Grade(); //创建原型对象
try
{
grade_1 = grade.deepClone();//调用深克隆方法创建克隆对象
}
catch(Exception e)
{
System.err.println("克隆失败!");
}
//比较周报
System.out.println("周报是否相同? " + (grade == grade_1));
//比较附件
System.out.println("附件是否相同? " + (grade.getStudents() == grade_1.getStudents()));
}
}
输出:
周报是否相同? false
附件是否相同? false
从输出结果可以看出,由于使用了深克隆技术,学生对象也得以复制,因此用“==”比较原型对象的学生和克隆对象的学生时输出结果均为false。深克隆技术实现了原型对象和克隆对象的完全独立,对任意克隆对象的修改都不会给其他对象产生影响,是一种更为理想的克隆实现方式。
根据以上肯定大家都关注了对象借口序列化吧:扩展如下
Java语言提供的Cloneable接口和Serializable接口的代码非常简单,它们都是空接口,这种空接口也称为标识接口,标识接口中没有任何方法的定义,其作用是告诉JRE这些接口的实现类是否具有某个功能,如是否支持克隆、是否支持序列化等。
关于原型管理器的引入和实现下周在进行更深的拓展