一、单例模式
1、定义分析
所谓类的单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session对象。SessionFactory并不是轻量级的,一般情况下,一个项目通常只需要一个SessionFactory就够,此时就会使用到单例模式。
2、常见写法
饿汉式(静态常量)
public class SingletonTest01 {
public static void main(String[] args) {
System.out.println("饿汉式 静态变量法");
Singleton instance = Singleton.getInstance();
Singleton singleton = Singleton.getInstance();
System.out.println(instance == singleton);
System.out.println("instance.hashCode = " + instance.hashCode());
System.out.println("singleton.hashCode = " + singleton.hashCode());
}
}
//饿汉式(静态变量)
class Singleton{
//1.构造器私有化,外部无法new
private Singleton(){
}
//2.本类内部创建对象实例
private final static Singleton instance = new Singleton();
//3.提供一个公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instance;
}
}
步骤:
构造器私有化。
类的内部创建对象实例。
对外提供一个静态的公共方法。
分析:
写法简单,在类装载的时候就完成实例化,避免了线程同步问题。
没有达到懒加载的效果,如果从始至终从未使用过这个实例,则会造成内存的浪费。
可用,但可能造成内存浪费。
饿汉式(静态代码块)
public class SingletonTest02 {
public static void main(String[] args) {
System.out.println("饿汉式 静态代码块法");
Singleton instance = Singleton.getInstance();
Singleton singleton = Singleton.getInstance();
System.out.println(instance == singleton);
System.out.println("instance.hashCode = " + instance.hashCode());
System.out.println("singleton.hashCode = " + singleton.hashCode());
}
}
//饿汉式(静态代码块)
class Singleton{
//1.构造器私有化,外面无法new
private Singleton(){
}
//2.本类内部创建对象实例
private static Singleton instance;
static {
instance = new Singleton();
}
//3.对外提供一个公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instance;
}
}
分析:
与静态常量法类似,只不过将实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。
可用,但也会造成内存浪费。
懒汉式(线程不安全)
public class SingletonTest03 {
public static void main(String[] args) {
System.out.println("懒汉式 线程不安全");
Singleton instance = Singleton.getInstance();
Singleton singleton = Singleton.getInstance();
System.out.println(instance == singleton);
System.out.println("instance.hashCode = " + instance.hashCode());
System.out.println("singleton.hashCode = " + singleton.hashCode());
}
}
//懒汉式,线程不安全
class Singleton{
private static Singleton instance;
private Singleton(){
}
//提供一个静态的公有方法,当使用到该方法时,才去创建instance
//即懒汉式
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
分析:
起到了懒加载的效果,但是只能在单线程下使用。
如果在多线程下,一个线程进入了if(instance == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例,故而多线程环境下并未达到单例效果。
不可用,线程不安全。
懒汉式(线程安全,同步方法)
public class SingletonTest04 {
public static void main(String[] args) {
System.out.println("懒汉式 线程安全,同步方法");
Singleton instance = Singleton.getInstance();
Singleton singleton = Singleton.getInstance();
System.out.println(instance == singleton);
System.out.println("instance.hashCode = " + instance.hashCode());
System.out.println("singleton.hashCode = " + singleton.hashCode());
}
}
//懒汉式,线程安全,同步方法
class Singleton{
private static Singleton instance;
private Singleton(){}
//提供一个静态的公有方法,当使用到该方法时,才去创建instance
//即懒汉式
public static synchronized Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
分析:
解决了线程安全问题。
效率很低,每个线程在想获得实例的时候,执行getInstance()方法都要进行同步,其实该方法只需要一次实例化代码就够了,后面的线程想获取实例直接return即可,方法进行同步效率太低。
安全,但不推荐使用。
懒汉式(线程安全,同步代码块)
class Singleton{
private static Singleton instance;
private Singleton(){}
//提供一个静态的公有方法,当使用到该方法时,才去创建instance
//即懒汉式
public static Singleton getInstance(){
if (instance == null){
synchronized(Singleton.class){
instance = new Singleton();
}
}
return instance;
}
}
分析:
并没有解决线程安全问题,这种写法是错的,不可用。
双重检查
public class SingletonTest06 {
public static void main(String[] args) {
System.out.println("Double-Check 双重检查方式 推荐使用");
Singleton instance = Singleton.getInstance();
Singleton singleton = Singleton.getInstance();
System.out.println(instance == singleton);
System.out.println("instance.hashCode = " + instance.hashCode());
System.out.println("singleton.hashCode = " + singleton.hashCode());
}
}
//Double-Check
class Singleton{
private static volatile Singleton instance;
private Singleton(){}
//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题
//同时保证了效率,推荐使用
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
if (instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
分析:
Double-Check概念是多线程开发中常常使用到的,进行两次实例空检查,加上同步代码块,既保证了线程安全,又提高了执行效率,而且还是懒加载。
推荐使用这种方式。
静态内部类
public class SingletonTest07 {
public static void main(String[] args) {
System.out.println("静态内部类方式");
Singleton instance = Singleton.getInstance();
Singleton singleton = Singleton.getInstance();
System.out.println(instance == singleton);
System.out.println("instance.hashCode = " + instance.hashCode());
System.out.println("singleton.hashCode = " + singleton.hashCode());
}
}
//静态内部类实现单例,推荐使用
class Singleton{
//构造器私有化
private Singleton(){}
//静态内部类
private static class SingletonInnerClass{
private static Singleton INSTANCE = new Singleton();
}
//提供一个公有的静态方法
public static Singleton getInstance(){
return SingletonInnerClass.INSTANCE;
}
}
分析:
采用类装载机制来保证初始化实例时只有一个线程。
静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInnerClass类,从而完成Singleton的实例化。
类的静态属性只会在第一次加载类的时候初始化,故而,JVM帮助我们保证了现成的安全性,在类进行初始化时,别的线程是无法进入的。
避免了线程不安全,利用静态内部类特点实现延迟加载,效率高,推荐使用。
枚举
public class SingletonTest08 {
public static void main(String[] args) {
System.out.println("枚举类实现单例");
Singleton instance = Singleton.INSTANCE;
Singleton singleton = Singleton.INSTANCE;
System.out.println(instance == singleton);
System.out.println("instance.hashCode = " + instance.hashCode());
System.out.println("singleton.hashCode = " + singleton.hashCode());
}
}
//枚举
enum Singleton{
INSTANCE;
}
分析:
不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
3、JDK源码中的单例模式
4、单例模式注意事项和细节说明
单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new。
单例模式使用的场景:需要频繁进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)。
二、工厂模式
1、简单工厂模式
1.1 了解需求
看一个披萨店铺的项目,要便于披萨种类的扩展和维护:
披萨的种类很多,如GreekPizz、CheesePizz等;
披萨的制作又prepare、bake、cut、box;
完成披萨店订购功能;
1.2 使用传统方式来完成
public class Tools {
//写一个方法,可以获取客户希望订购的披萨种类
public String getType(){
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
}
catch (IOException e){
e.printStackTrace();
return "";
}
}
}
public abstract class Pizza {
public String name;
//准备原材料,不同的披萨不一样,因此做成抽象方法
public abstract void prepare();
public void bake(){
System.out.println(name + "Pizza is baking");
}
public void cut(){
System.out.println(name + "Pizza is cutting");
}
public void box(){
System.out.println(name + "Pizza is boxing");
}
public void setName(String name) {
this.name = name;
}
}
public class CheessPizza extends Pizza{
@Override
public void prepare() {
System.out.println("CheessPizza is preparing");
}
}
public class GreekPizza extends Pizza{
@Override
public void prepare() {
System.out.println("GreekPizza is preparing");
}
}
public class OrderPizza {
//构造器
public OrderPizza(){
Pizza pizza = null;
String orderType;//订购披萨的类型
do{
orderType = new Tools().getType();
if (orderType.equals("greek")){
pizza = new GreekPizza();
pizza.setName(" 希腊披萨 ");
}
else if(orderType.equals("cheese")){
pizza = new CheessPizza();
pizza.setName(" 奶酪披萨 ");
}
else {
break;
}
//输出Pizza制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}while (true);
}
}
分析优缺点:
优点是易于理解,简单易操作。
缺点是违反了设计模式的ocp原则,即没有对扩展开放,对修改关闭。我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码。
OrderPizza类既依赖于Pizza类,又依赖于其各个子类,新增加一个Pizza类时,订购Pizza的类都要修改。
改进思路分析:
分析:修改代码可以接受,但是如果我们在其它的地方也有创建Pizza的代码,就意味着也需要修改,而创建Pizza的代码,往往有多处。
思路:把创建Pizza对象封装到一个类中,这样我们有新的Pizza种类时,只需要修改该类即可,其它有创建Pizza对象的代码就不需要修改了,这就是简单工厂模式。
1.3 基本介绍
简单工厂模式属于创建型模式,是工厂模式的一种,是由一个工厂对象决定创建出哪一种产品类的实例,是工厂模式家族中最简单实用的模式。
定义一个创建对象的类,由这个类来封装实例化对象的行为。
在开发中,当我们用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式。
//简单工厂模式
public class SimpleFactory {
//根据orderType返回对应的Pizza对象
public Pizza createPizza(String orderType){
System.out.println("使用简单工厂模式");
Pizza pizza = null;
if (orderType.equals("greek")){
pizza = new GreekPizza();
pizza.setName(" 希腊披萨 ");
}
else if(orderType.equals("cheese")){
pizza = new CheessPizza();
pizza.setName(" 奶酪披萨 ");
}
else if (orderType.equals("pepper")){
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
}
return pizza;
}
}
public class OrderPizza {
//构造器
public OrderPizza(SimpleFactory simpleFactory){
setSimpleFactory(simpleFactory);
}
Pizza pizza = null;
public void setSimpleFactory(SimpleFactory simpleFactory){
do{
String orderType = new Tools().getType();
pizza = simpleFactory.createPizza(orderType);
//输出pizza信息
if (pizza != null){ //订购成功
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
else {
System.out.println("Pizza订购失败");
break;
}
}while (true);
}
}
1.4 静态工厂模式
public class StaticSimpleFactory {
public static Pizza createPizza(String orderType){
System.out.println("使用简单静态工厂模式");
Pizza pizza = null;
if (orderType.equals("greek")){
pizza = new GreekPizza();
pizza.setName(" 希腊披萨 ");
}
else if(orderType.equals("cheese")){
pizza = new CheessPizza();
pizza.setName(" 奶酪披萨 ");
}
else if (orderType.equals("pepper")){
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
}
return pizza;
}
}
public class OrderPizza {
//构造器
public OrderPizza(){
setSimpleFactory();
}
Pizza pizza = null;
public void setSimpleFactory(){
do{
String orderType = new Tools().getType();
pizza = StaticSimpleFactory.createPizza(orderType);
//输出pizza信息
if (pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
else {
System.out.println("Pizza订购失败");
break;
}
}while (true);
}
}
2、工厂方法模式
2.1 了解新需求
不同地区的客户可以点不同区域不同口味的披萨,比如北京的奶酪披萨、北京的胡椒披萨、伦敦的奶酪披萨、伦敦的胡椒披萨等。
2.2 思路分析
思路一:
使用简单工厂模式,创建不同的简单工厂类,比如BJPizzaSimpleFactory、LDPizzaSimpleFactory等,这样做的话,系统的可维护性、可扩展性不是很好。
思路二:
使用工厂方法模式,将披萨项目的实例化功能进行抽象,在不同地区的订购中具体实现。
2.3 基本介绍
工厂方法模式定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。
2.4 示例分析及代码
public abstract class Pizza {
public String name;
//准备原材料,不同的披萨不一样,因此做成抽象方法
public abstract void prepare();
public void bake(){
System.out.println(name + "Pizza is baking");
}
public void cut(){
System.out.println(name + "Pizza is cutting");
}
public void box(){
System.out.println(name + "Pizza is boxing");
}
public void setName(String name) {
this.name = name;
}
}
public class BJCheessPizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备 北京奶酪披萨 原材料……");
}
}
public class BJPepperPizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备 北京胡椒披萨 原材料……");
}
}
public class LDCheesePizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备 伦敦奶酪披萨 原材料……");
}
}
public class LDPepperPizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备 伦敦胡椒披萨 原材料……");
}
}
public abstract class OrderPizza {
public OrderPizza(){
Pizza pizza = null;
String orderType;//订购披萨的类型
do{
orderType = new Tools().getType();
pizza = createPizza(orderType);//抽象方法,由工厂子类完成
if(pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
}while (pizza != null);
}
public abstract Pizza createPizza(String orderType);
}
public class BJOrderPizza extends OrderPizza{
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if(orderType.equals("cheese")){
pizza = new BJCheessPizza();
pizza.name = "北京奶酪";
}
else if(orderType.equals("pepper")){
pizza = new BJPepperPizza();
pizza.name = "北京胡椒";
}
return pizza;
}
}
public class LDOrderPizza extends OrderPizza{
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if(orderType == "cheese"){
pizza = new LDCheesePizza();
pizza.name = "伦敦奶酪";
}
else if(orderType == "pepper"){
pizza = new LDPepperPizza();
pizza.name = "伦敦胡椒";
}
return pizza;
}
}
public class PizzaStore {
public static void main(String[] args) {
String area = "bj";
if(area == "bj")
new BJOrderPizza();
else if(area == "ld")
new LDOrderPizza();
}
}
三、抽象工厂模式
1、基本介绍
抽象工厂模式定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类。
抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。
从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。
将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。
2、类图
3、示例代码
public abstract class Pizza {
public String name;
//准备原材料,不同的披萨不一样,因此做成抽象方法
public abstract void prepare();
public void bake(){
System.out.println(name + "Pizza is baking");
}
public void cut(){
System.out.println(name + "Pizza is cutting");
}
public void box(){
System.out.println(name + "Pizza is boxing");
}
public void setName(String name) {
this.name = name;
}
}
public class BJCheesePizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备 北京Cheese 披萨原材料……");
}
}
public class BJPepperPizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备 北京胡椒 披萨原材料……");
}
}
public class LDCheesePizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备 伦敦Cheese 披萨原材料……");
}
}
public class LDPepperPizza extends Pizza{
@Override
public void prepare() {
System.out.println("准备 伦敦胡椒 披萨原材料……");
}
}
public interface AbsFactory {
public Pizza createPizza(String orderType);
}
public class BJFactory implements AbsFactory{
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if(orderType.equals("cheese")){
pizza = new BJCheesePizza();
pizza.name = "北京芝士";
}
else if (orderType.equals("pepper")){
pizza = new BJPepperPizza();
pizza.name = "北京胡椒";
}
return pizza;
}
}
public class LDFactory implements AbsFactory{
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese")){
pizza = new LDCheesePizza();
pizza.name = "伦敦芝士";
}
else if (orderType.equals("pepper")) {
pizza = new LDPepperPizza();
pizza.name = "伦敦胡椒";
}
return pizza;
}
}
public class OrderPizza {
//AbsFactory factory;
public OrderPizza(AbsFactory factory){
setFactory(factory);
}
private void setFactory(AbsFactory factory){
Pizza pizza = null;
//this.factory = factory;
do {
String orderType = new Tools().getType();
pizza = factory.createPizza(orderType);
if (pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
else {
System.out.println("订购失败……");
break;
}
}while (true);
}
}
public class PizzaStore {
public static void main(String[] args) {
AbsFactory factory = null;
String area = "bj";
if (area == "bj")
factory = new BJFactory();
else if (area == "ld")
factory = new LDFactory();
OrderPizza orderPizza = new OrderPizza(factory);
}
}
四、工厂模式小结
1、工厂模式的意义
将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和著项目的依赖关系的解耦。从而提高项目的扩展性和维护性。
2、三种工厂模式
简单工厂模式、工厂方法模式、抽象工厂模式
3、设计模式的依赖抽象原则
创建对象实例时,不要直接new 类,而是把这个new 类的动作放在一个工厂的方法中并返回,有的书上说,变量不要直接持有具体类的引用。
不要让类继承具体类,而是继承抽象类或者是实现接口。
不要覆盖基类中已经实现的方法。
五、原型模式
1、克隆羊问题
现有一只羊tom,姓名:tom,年龄1,颜色白色,编写程序创建和tom羊属性完全相同的4只羊。
2、传统方式解决克隆羊问题
思路分析(图解)
代码如下
public class Sheep {
private String name;
private int age;
private String color;
public Sheep(String name, int age, String color){
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String toString(){
return "name = " + this.getName() + " age = " + this.getAge() + " color = " + this.getColor();
}
}
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom",1,"白色");
Sheep sheep1 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
Sheep sheep2 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
Sheep sheep3 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
System.out.println(sheep.hashCode() + " --- " + sheep.toString());
System.out.println(sheep1.hashCode() + " --- " + sheep1.toString());
System.out.println(sheep2.hashCode() + " --- " + sheep2.toString());
System.out.println(sheep3.hashCode() + " --- " + sheep3.toString());
}
}
3、传统方式的优缺点
优点是比较好理解,简单易操作
在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活
改进的思路分析:
Java中object类是所有类的根类,object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力 ==> 原型模式
4、原型模式基本介绍
原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝他们自己来实施创建,即对象.clone()
形象的理解:孙大圣拔出猴毛,变出其他孙大圣
5、原型模式实例
代码实现
public class Sheep implements Cloneable {
private String name;
private int age;
private String color;
private String address = "鄂尔多斯";
public Sheep(String name, int age, String color){
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getAddress() {
return address;
}
public String toString(){
return "name = " + this.getName() + " age = " + this.getAge() + " color = " + this.getColor() + " address = " + this.getAddress();
}
//克隆该实例,使用默认的clone方法来完成
@Override
protected Object clone() {
Sheep sheep = null;
try{
sheep = (Sheep)super.clone();
}catch (Exception e){
System.out.println(e.getMessage());
}
return sheep;
}
}
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("tom",1,"白色");
Sheep sheep1 = (Sheep) sheep.clone();
Sheep sheep2 = (Sheep) sheep.clone();
Sheep sheep3 = (Sheep) sheep.clone();
System.out.println(sheep.hashCode() + " --- " + sheep.toString());
System.out.println(sheep1.hashCode() + " --- " + sheep1.toString());
System.out.println(sheep2.hashCode() + " --- " + sheep2.toString());
System.out.println(sheep3.hashCode() + " --- " + sheep3.toString());
}
}
6、深入讨论浅拷贝和深拷贝
浅拷贝
对于数据类型是基本数据类型及String类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。String属于Java中的字符串类型,也是一个引用类型,并不属于基本的数据类型。
对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
浅拷贝时使用默认的clone()方法来实现。
sheep = (Sheep) super.clone();
深拷贝
复制对象的所有基本数据类型的成员变量值
为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象科大的所有对象。即,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝
深拷贝实现方式一:重写clone方法来实现深拷贝
深拷贝实现方式二:通过对象序列化实现深拷贝(推荐)
public class DeepCloneableTarget implements Serializable, Cloneable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
//构造器
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
//因为该类的属性,都是String , 因此我们这里使用默认的clone完成即可
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class DeepProtoType implements Serializable, Cloneable{
public String name; //String 属性
public DeepCloneableTarget deepCloneableTarget;// 引用类型
public DeepProtoType() {
super();
}
//深拷贝 - 方式 1 使用clone 方法
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
//这里完成对基本数据类型(属性)和String的克隆
deep = super.clone();
//对引用类型的属性,进行单独处理
DeepProtoType deepProtoType = (DeepProtoType)deep;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone();
// TODO Auto-generated method stub
return deepProtoType;
}
//深拷贝 - 方式2 通过对象的序列化实现 (推荐)
public Object deepClone() {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this); //当前这个对象以对象流的方式输出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType copyObj = (DeepProtoType)ois.readObject();
return copyObj;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
} finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
System.out.println(e2.getMessage());
}
}
}
}
public class Client {
public static void main(String[] args) throws Exception {
DeepProtoType p = new DeepProtoType();
p.name = "张三";
p.deepCloneableTarget = new DeepCloneableTarget("大牛", "小牛");
//方式1 完成深拷贝
// DeepProtoType p2 = (DeepProtoType) p.clone();
// System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
// System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
//方式2 完成深拷贝
DeepProtoType p2 = (DeepProtoType) p.deepClone();
System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
}
}
7、总结
创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
不用重新初始化对象,而是动态地获得对象运行时的状态
如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
若成员变量无引用类型,浅拷贝clone即可;若引用类型的成员变量很少,可考虑递归实现clone,否则推荐序列化