现在我们有这样一个需求。在后台输入一批学生的信息以后,封装成一个列表(List ),传送给前台打印出来。
这是第一版。
/**
* 这是最开始的Student 类
*/
public class Student1 {
private String name;
private int age;
private String teacher;
private String major;
// 各种setter 和getter
}
/**
* 第一个场景类:按照一开始的想法做的一个场景 ,现在我们是要把一批学生给记录到一个list里面,然后在页面打印出来
*/
public class Client1 {
public static void main(String[] args) {
List<Student1> list = new ArrayList<Student1>();// 建立存储学生信息的列表
Student1 student = new Student1();// 定义一个学生,接下来写好一样的信息
student.setAge(21);
student.setMajor("计算机");
student.setTeacher("黄大牛");
for (int i = 0; i < 10; i++) {
student.setName(RandomGenerator.getRandomLetters());
list.add(student);// 添加到列表中
}
System.out.println("将在页面上" + list.size() + "条记录,内容如下");
// 将列表返回给界面显示
for (Student1 _student : list) {
System.out.println("名字为 " + _student.getName() + " 的 "
+ _student.getMajor() + " 学院学生已经打印到界面");
}
}
}
结果如下:
将在页面上10条记录,内容如下
名字为 YdIkJf 的 计算机 学院学生已经打印到界面
名字为 YdIkJf 的 计算机 学院学生已经打印到界面
名字为 YdIkJf 的 计算机 学院学生已经打印到界面
名字为 YdIkJf 的 计算机 学院学生已经打印到界面
名字为 YdIkJf 的 计算机 学院学生已经打印到界面
名字为 YdIkJf 的 计算机 学院学生已经打印到界面
名字为 YdIkJf 的 计算机 学院学生已经打印到界面
名字为 YdIkJf 的 计算机 学院学生已经打印到界面
名字为 YdIkJf 的 计算机 学院学生已经打印到界面
名字为 YdIkJf 的 计算机 学院学生已经打印到界面
很明显,在list 里面用add() 方法是记下引用,而这个实际上的对象一直在变,所以所有的引用实际上都是指向同一个对象,结果当然是这样子。
得说题目要求就是返回一个列表,所以想在第一个循环里面打印的想法取消掉。那么只能这么改了。
for (int i = 0; i < 10; i++) {
Student1 student = new Student1();// 定义一个学生,接下来写好一样的信息
student.setAge(21);
student.setMajor("计算机");
student.setTeacher("黄大牛");
student.setName(RandomGenerator.getRandomLetters());
list.add(student);// 添加到列表中
}
的确能达到要求了,但是实际上造成了很多重复性动作,如果循环次数多,对象就成爆炸式增长。在这个情形下有了原型模式。事实上在开发中会经常有这样的情况,一个类需要做多次的数据库存储,但是每条记录又只是差那么一丁点,这时候就可以用原型模式了。
clone() 方法是基类Object 的一个方法,利用这个方法实现对象的复制。
修改后的代码如下:
/**
* 这是修改以后的Student类
*/
public class Student2 implements Cloneable {
private String name;
private int age;
private String teacher;
private String major;
public Student2 clone() {
Student2 student = null;
try {
student = (Student2) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
//各种setter()和getter()
}
public static void main(String[] args) {
List<Student2> list = new ArrayList<Student2>();// 建立存储学生信息的列表
Student2 student = new Student2();// 定义一个学生,接下来写好一样的信息
student.setAge(21);
student.setMajor("计算机");
student.setTeacher("黄大牛");
for (int i = 0; i < 10; i++) {// 以下是各个学生不同的地方
Student2 _student = student.clone();
_student.setName(RandomGenerator.getRandomLetters());
list.add(_student);// 添加到列表中
}
System.out.println("将在页面上" + list.size() + "条记录,内容如下");
// 将列表返回给界面显示
for (Student2 _student : list) {
System.out.println("名字为 " + _student.getName() + " 的 "
+ _student.getMajor() + " 学院学生已经打印到界面");
}
}
这就是原型模式(Prototype Pattern )了。事实上这非常容易理解,也没什么难度,那么它有什么好处呢。貌似这也是对象的大量生成? 有什么好处?
1. 原型模式是在内存中做对象的复制,性能上比实例化一个对象要好很多;
2. 不希望初始化的时候要写构造方法的时候。
什么场景需要用到呢?
需要在频繁new 一个对象的时候,可以用到;一个对象需要提供给很多对象访问而且各个调用者都可能需要修改它的值的时候;……
浅拷贝和深拷贝的区别?
Java 在执行clone() 方法的时候,只是在内存中分配跟主对象一模一样的内存块,那有什么是包含在这个内存块里面的呢。有基本数据类型(String 很特殊的被包括了)和引用,还有不可变对象(final )。但是引用的对象是不在的!这就出现了一个问题,对于被引用的东西,拷贝出来以后实际上被几个对象(包括原来的对象和拷贝出来的对象)共享着,这就不算真正意义上的拷贝了!因为实际生产中很多类不只是基本数据类型。这就是浅拷贝 。
那么我们应该怎么实现深拷贝 呢?其实很简单,在clone() 方法里面重新生成一个就是了。下面给出一段代码 :
浅拷贝 :跟我们之前的做法一样。
public class Student3 implements Cloneable {
private School school;
@Override
public Student3 clone() {
Student3 student = null;
try {
student = (Student3) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
}
深拷贝 :在clone() 方法中也要重新克隆一个。
@Override
public Student3 clone() {
Student3 student = null;
try {
student = (Student3) super.clone();
student.school = (School) school.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
事实上这个引用对象也必须实现clone() 方法。