设计模型《单例,贪婪模式和惰性模式》

面试题目

请使用UML类图画出原型模式的核心角色;

原型设计模式的深拷贝和浅拷贝是什么,并写出深拷贝的两种方式的原码(重写clone方法实现深拷贝,使用序列化来实现深拷贝);

单例模式哪些是线程安全的;

本章目标

单例模式;(单子模式)

原型模式;

本章内容

单例模式

概念:

在任何情况下,该类的实例对象只能有一个;
1.1饿汉方式两种
第一种:静态变量
class Single{
    //1.构造方法私有化,防止在类的外部可以创建该类
    private Single(){}
    //2.建立静态变量
    private final  static  Single instance = new Single();
    //3.建立对外共有的方法提供访问
    public static Single GetInstance(){
        return instance;
    }
}

调用:
public class SingleType1 {
    public static void main(String[] args) {
            Single s1 = Single.GetInstance();
            Single s2 = Single.GetInstance();
        	System.out.println(s1 == s2);

    }
}
第二种:静态代码块
//使用静态代码块
class Single{
    //1构造方法私有化,防止外部创建该类
    private Single(){}
    private static Single instance;
    //2.建立静态代码块
    static{
        instance = new Single();
    }
    //3.提供对外共有的模式
    public static Single GetInstance(){
        return instance;
    }
}

优缺点:

1.优点:这种写法比较简单,在类加载的时候就完成了实例化,避免了线程同步的问题,线程安全的模式;
2.缺点:在类装载的时候就完成实例化,没有达到Lazy Loading 的效果。如果从始至终从未使用过这个实例的话,则会造成内存的浪费;
3.这种方式基于classloader机制避免了多线程的同步问题,不过,instance在类装载的时候就实例化,在单例模式中大多数都是带哦用getInstance方法,但是导致类装载的原因有很多种,因为不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance就没有达到lazy loading的效果;
4.结论:这种单例模式可用,但是会造成内存资源的浪费;
1.2懒汉方式三种
第一种线程不安全
//使用懒汉,线程不安全
class Single{
    //创建静态变量
    private static Single instance;
    //方法私有化
    private Single(){}
    //在使用该实例的时候创建该对象的实例
    public static Single GetInstance(){
        if(null == instance){
                instance = new Single();
        }
        return instance;
    }
}

测试

public class TManyTh implements  Runnable {

    @Override
    public void run() {
        //获取单例
        Single s=Single.GetInstance();
        System.out.println(s);
    }


    public static void main(String[] args) {
        //
        for (int i = 0; i <30 ; i++) {
            TManyTh t=new TManyTh();
            Thread thread=new Thread(t);
            thread.start();
        }

    }
}

我们创建多个线程,发现并不能实现实例对象的唯一性;

这是因为如果两个线程都执行到 if(null == instance){的时候,如果其中有一个由于没有抢到时间片则不能向下执行,另一个线程抢到则检测没有对象,则又创建一个实例对象;

优点:实现了lazy loading 但是线程不安全,只能用在单线程中,不可用;

第二种线程安全,方法同步
//使用懒汉,线程安全
class Single{
    //创建静态变量
    private static Single instance;
    //方法私有化
    private Single(){}
    //在使用该实例的时候创建该对象的实例
    public synchronized static Single GetInstance(){
        if(null == instance){
                instance = new Single();
        }
        return instance;
    }
}

优缺点:

1.解决了线程不安全问题;

2.效率太低了,每个线程在想获类的实例的时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return 就行了,方法同步效率太低;

3.结论:在实际开发中,不推荐使用这种方式;

第三种同步代码块

class Single{
    //创建静态变量
    private static Single instance;
    //方法私有化
    private Single(){}
    //在使用该实例的时候创建该对象的实例
    public  static Single GetInstance(){
   
        if(null == instance){
        
            synchronized(Single.class) {
             
               
                instance = new Single();
                
            }
        }
        return instance;
    }
}

测试

public class TManyTh implements  Runnable {

    @Override
    public void run() {
        //获取单例
        Single s=Single.GetInstance();
        System.out.println(s);
    }


    public static void main(String[] args) {
        //
        for (int i = 0; i <30 ; i++) {
            TManyTh t=new TManyTh();
            Thread thread=new Thread(t);
            thread.start();
        }

    }
}
1.3双重检查
class Single{
    //创建静态变量
    private static Single instance;
    //方法私有化
    private Single(){}
    //在使用该实例的时候创建该对象的实例
    public  static Single GetInstance(){
     //先检查实例是否存在,如果不存在才进入下面的同步块  
        if(null == instance){
        // //同步块,线程安全地创建实例  
            synchronized(Single.class) {
            // //再次检查实例是否存在,如果不存在才真正地创建实例  
                  if(null == instance){
                      instance = new Single();
                  }
            }
        }
        return instance;
    }
}

优点:即实现了懒加载,又保证了线程安全;

这种实现方式可以实现既线程安全地创建实例,而又不会对性能造成太大的影响。它只是在第一次创建实例的时候同步,以后就不需要同步了,从而加快了运行速度。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-otwXjHbk-1588994372597)(E:\AAA\新乡\java高级特性\设计模式\assets/QQ截图20191216195425.png)]

1.4静态内部类
class Single{
    //方法私有化
    private Single(){}
    //静态内部类
    private static class SingleInstance{
        private static Single instance = new Single();
    }
    public static Single GetInstance(){
        return SingleInstance.instance;
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UifdaNzW-1588994372600)(E:\AAA\新乡\java高级特性\设计模式\assets/QQ截图20191216200324.png)]

1.5枚举
enum Single{
    instance;
}

测试

public class SingleType1 {
    public static void main(String[] args) {
        for (int i = 0; i <50 ; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Single s1 = Single.instance;
                    System.out.println(s1.hashCode());
                }
            }).start();
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kHsPE0Eo-1588994372602)(E:\AAA\新乡\java高级特性\设计模式\assets/QQ截图20191216200926.png)]

三、原型模式

思考:解决克隆狗狗的问题:

有一只狗狗,一岁了,黑白色,名字叫做tom,向克隆十只狗狗怎么做?

传统案例:


概念

原型模式,是用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节(实际上就是copy)。

使用原型模型解决格隆狗狗的问题:

package design.prototype;

public class Dog implements Cloneable{
    private String name;
    private int age;
    private String color;
    private String Address;
    private Dog friend;

    public Dog getFriend() {
        return friend;
    }

    public Dog(String name, int age, String color, String address, Dog friend) {
        this.name = name;
        this.age = age;
        this.color = color;
        Address = address;
        this.friend = friend;
    }

    public void setFriend(Dog friend) {
        this.friend = friend;
    }

    public Dog(String name, int age, String color, String address) {
        this.name = name;
        this.age = age;
        this.color = color;
        Address = address;
    }

    public String getAddress() {
        return Address;
    }

    public void setAddress(String address) {
        Address = address;
    }

    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;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                ", Address='" + Address + '\'' +
                '}';
    }

    public Dog(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    public Dog() {
    }
}

测试代码

package design.prototype;

public class Test {
    public static void main(String[] args) throws Exception {
        Dog dog = new Dog("tom",1,"黑白色","德国牧羊犬");
        Dog friend = new Dog("herry",2,"白色");
        dog.setFriend(friend);

        for (int i = 0; i <10 ; i++) {
            Dog d = (Dog)dog.clone();
            System.out.println(d+"--->"+d.getFriend()+"-->"+d.getFriend().hashCode());
        }

    }
}

只能实现值的复制则是浅拷贝;

深度拷贝:

第一种方式:

使用克隆每一个引用对象实现;

第二种范式:

package design.prototype;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ObjClone {
    public static <T> T cloneObj(T obj){
        T retVal =null;
        try {

            // 将对象写入流中
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);

            // 从流中读出对象
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);

            retVal = (T) ois.readObject();

        } catch (Exception e) {
            e.printStackTrace();
        }

        return retVal;
    }
}

rrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);

        // 从流中读出对象
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);

        retVal = (T) ois.readObject();

    } catch (Exception e) {
        e.printStackTrace();
    }

    return retVal;
}

}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值