JAVA 的23中设计模式之一:创建型模式

23中模式分类:

创建型模式(关注对象的创建过程):

               单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。

结构型模式(关注对象和类的组织):

               适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。

行为型模式(关注对象之间的交互,研究运行时对象之间的通信协作):

               模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、

               策略模式、职责链模式、访问者模式。

 

 

1,单例模式(最常用):

核心作用:

            保证一个类有且只有一个实例(对象),并且提供一个访问该实例的全局方法。

使用场景:

          数据库连接池的设计,Spring中每个bean默认就是单例,Spring MVC框架中,控制器对象也是单例。 

优点:

          因为单例只生成一个实例(对象),减少系统性能开销,是一种共享的资源。

常见五种单例模式:

         主要:

                   1,饿汉式(优点:线程安全,调用效率高,但是,不能延时加载。 缺点:如果不调用单例方法,则会一直浪                                            费该单例资源。)

 

/**
 * 饿汉式单例模式
 */
public class SingletonDemo1 {

    //类初始化时,立即加载类对象(即:非延迟加载)。
    private static /*final*/ SingletonDemo1 instance = new SingletonDemo1();

    //私有构造器
    private SingletonDemo1() {};
    
    // 线程安全,虚拟机只会装在一次该类,肯定不会并发访问,可以忽略 synchronized
    // 由于不需要:synchronized,所以调用效率高
    public static SingletonDemo1 getInstance() {
        return instance;
    }


    //测试
    public static void main(String[] args){
        SingletonDemo1 s1 = SingletonDemo1.getInstance();
        SingletonDemo1 s2 = SingletonDemo1.getInstance();
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s1 == s2);
    }
}

 

                  ,2,懒汉式(线程安全,调用效率不高,可以延时加载)

/**
 * 懒汉单例模式
 */
public class SingletonDemo2 {
    private static /*final*/ SingletonDemo2 instance;

    //私有构造器
    private SingletonDemo2() {};

    //必须加上 synchronized 实现单线程访问,保证线程安全
    // 加上 synchronized  避免了并发访问时实例化多个对象违反单例模式设计
    //要点:实现了懒加载(延迟加载),资源利用率高,但是并发效率低了
    public static synchronized SingletonDemo2 getInstance() {

        //如果不加  synchronized 很容易在这一步出现并发访问造成实例化多个对象
        if (instance == null) {
            instance = new SingletonDemo2();
        }
        return instance;
    }
};

 

   其他:

                  3,双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题,不建议使用)

                  4,静态内部类式(线程安全,调用效率高,可以延时加载)

/**
 * 静态内部类模式
 */
public class SingletonDemo3 {

    private SingletonDemo3(){};

    //私有的静态内部类
    private static class SingletonClassInstance{
        private static final SingletonDemo3 instance = new SingletonDemo3();
    }

    // 外部类没有static属性,不会像饿汉式那样立即加载对象。
    //只有调用 getInstance() 方法才加载静态内部类,线程安全
    // instance是static final类型,保证内存中只有一个实例存在,并且只能赋值一次
    //兼备并发高效调用和延迟加载的优势
    public static SingletonDemo3 getInstance(){
        return SingletonClassInstance.instance;
    }
}

                   5,枚举单例(线程安全,调用效率高,不能延时加载,天然防止反射反序列化漏洞)

/**
 * 枚举模式(没有懒加载效果)
 * 优势:避免反射反序列化漏洞
 */
public enum SingletonDemo4 {
    //这个枚举,本身就是单例对象
    INSTANE;

    public void singletonOperation(){
       // TODO 可对枚举对象进行操作
    }

}

如何选用:

             占用 资源少,不需要 延迟加载: 枚举式  好于  饿汉式

             占用 资源少,需要 延迟加载: 静态内部类式  好于 懒汉式

 

(小记:懒加载(延迟加载):使用单例对象时才new出来对象。   非懒加载:类加载以后单例对象既已存在。)

 扩展知识:通过反射和反序列化破解单例模式实现(不包括枚举式单例模型):

       以饿汉式模型举例:

                通过反射:

//测试
    public static void main(String[] args) throws Exception {
        //正常情况下调用
        SingletonDemo1 s1 = SingletonDemo1.getInstance();
        SingletonDemo1 s2 = SingletonDemo1.getInstance();
        System.out.println(s1);
        System.out.println(s2);
        System.out.println("-------------------------");

        //通过反射破解单例模式
        //通过反射加载类对象
        Class<SingletonDemo1> aClass = (Class<SingletonDemo1>) Class.forName("com.panger.singleton.SingletonDemo1");
        //获取无参构造器
        Constructor<SingletonDemo1> c = aClass.getDeclaredConstructor(null);
        //跳过 定义了 private 的成员 权限检查设置
        c.setAccessible(true);
        SingletonDemo1 s3 = c.newInstance();
        SingletonDemo1 s4 = c.newInstance();
        System.out.println(s3);
        System.out.println(s4);

        // TODO 运行结果可发现 s3 != s4 
    }

           避免这种情况的发生,可在构造方法中进行校验:

               

                   通过反序列化:

 //测试
    public static void main(String[] args) throws Exception {
        //正常情况下调用
        SingletonDemo1 s1 = SingletonDemo1.getInstance();
        SingletonDemo1 s2 = SingletonDemo1.getInstance();
        System.out.println(s1);
        System.out.println(s2);
        System.out.println("-------------------------");

       //通过反序列化方式破解单例:
        //序列化对象
        FileOutputStream fos = new FileOutputStream("d:/a.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(s1);
        oos.close();
        fos.close();

        //反序列化对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
        SingletonDemo1 s3 = (SingletonDemo1) ois.readObject();
        System.out.println(s3);
        // TODO 运行发现 被序列化的对象 s1  反序列化变为s3后  s1 != s3
    }

                      避免这种情况的发生:

2,工厂模式:

    核心作用:

                      帮助我们new对象出来,实现创建者和调用者分离。

常见的工厂模式:

             1,简单工厂模式(常用,也叫静态工厂模式,传递参数不同通过静态类方法返回不同的对象实例  缺点:不好扩展)

/**
 * 简单工厂模式
 * 
 */
public class FactoryDemo1 {

    public static Fruits create(int type) {
        if (type == 1) {
            return new Apple();
        } else {
            return new Banana();
        }
    }

    //调用工厂模式
    public static void main(String[] args) {
        FactoryDemo1.create(1);
        FactoryDemo1.create(2);

    }
}

class Fruits {

}

class Apple extends Fruits {
    public Apple() {
        System.out.println("苹果。。。");
    }
}

class Banana extends Fruits {
    public Banana() {
        System.out.println("香蕉。。。");
    }
}

 

             2,工厂方法模式 (相当于一个实例创建了对应的工厂  可扩展性好   缺点:工厂类过多)

1、
public interface Fruit {
    public void print();
    
}

2、
public class Apple implements Fruit {

    @Override
    public void print() {
        System.out.println("苹果。。。。");
    }
}

3、
public class Banana implements Fruit {

    @Override
    public void print() {
        System.out.println("香蕉。。。");
    }
}

4、工厂父类创建
public interface FruitFactory {
    Fruit getFruits();
}

5、apple工厂类创建
public class AppleFactory implements FruitFactory {
    public Fruit getFruits() {
        return new Apple();
    }
}

6、banana工厂类创建
public class BananaFactory implements FruitFactory {
    public Fruit getFruits() {
        return new Banana();
    }
}

7、
/**
 * 工厂方法模式
 */
public class FactoryDemo2 {
    public static void main(String[] args){
        Fruit apple = new AppleFactory().getFruits();
        Fruit banana = new BananaFactory().getFruits();
        apple.print();
        banana.print();
    }
}

 

             3,抽象工厂模式(生产不同产品族的全部产品 ,不可增加产品,但可以增加产品族)

应用场景:

  •             JDK中Calendar的getInstance方法。
  •             JDBC中的Connection对象的获取。
  •              Spring中的IOC容易创建管理bean对象。
  •              反射中Class对象的newInstance()......

3,建造者模式:

 应用场景:  

             当你需要创建一个复杂型的产品时(比如:一辆汽车需要按着步骤组装起来,比如SpringBuilder的append方法),我们所需要的对象构建也很复杂,有很多步骤并且还需要按一定顺序进行处理时。

  举一个例子(假如要构建一个航天飞船):

1、
/**
 * 宇宙飞船类
 */
public class AirShip {
    private OrbitalModule orbitalModule; //轨道舱
    private Engine engine; //发动机
    private EscapeTower escapeTower; //逃生舱

    public OrbitalModule getOrbitalModule() {
        return orbitalModule;
    }

    public void setOrbitalModule(OrbitalModule orbitalModule) {
        this.orbitalModule = orbitalModule;
    }

    public Engine getEngine() {
        return engine;
    }

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public EscapeTower getEscapeTower() {
        return escapeTower;
    }

    public void setEscapeTower(EscapeTower escapeTower) {
        this.escapeTower = escapeTower;
    }

    public void launch(){
        System.out.println("飞船发射!");
    }
}

//轨道类
class OrbitalModule{
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
//发动机类
class Engine{
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
//逃生舱类
class EscapeTower{
    private String name;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


2、
/**
 * 飞船组件提供接口
 */
public interface AirShipBuilder {
    Engine builderEngine();
    OrbitalModule builderOrbitalModule();
    EscapeTower builderEscapeTower();
}

3、
/**
 * 飞船组件实例化类
 */
public class SxtAirShipBuilder implements AirShipBuilder {
    public Engine builderEngine() {
        System.out.println("构建发动机");
        return new Engine();
    }

    public OrbitalModule builderOrbitalModule() {
        System.out.println("构建轨道舱");
        return new OrbitalModule();
    }

    public EscapeTower builderEscapeTower() {
        System.out.println("构建逃生舱");
        return new EscapeTower();
    }
}


4、
/**
 * 创建飞船接口
 */
public interface AirShipDirector {

    AirShip directAirShip();
}

5、
/**
 * 装配者
 */
public class SxtAirShipDirector implements AirShipDirector {

    private AirShipBuilder airShipBuilder;

    //当前类加载时  创建 AirShipBuilder 对象
    public SxtAirShipDirector(AirShipBuilder airShipBuilder){
        this.airShipBuilder = airShipBuilder;
    }

    // 将飞船组件进行装配并返回飞船
    public AirShip directAirShip() {
        AirShip airShip = new AirShip();
        airShip.setEngine(airShipBuilder.builderEngine());
        airShip.setEscapeTower(airShipBuilder.builderEscapeTower());
        airShip.setOrbitalModule(airShipBuilder.builderOrbitalModule());
        System.out.println("飞船创建成功:"+airShip);
        return airShip;
    }

    public static void main(String[] args){
        //进行组件装配
        SxtAirShipDirector sxtAirShipDirector = new SxtAirShipDirector(new SxtAirShipBuilder());
        //获取飞船
        AirShip airShip = sxtAirShipDirector.directAirShip();
        airShip.launch();
    }
}

 

 

4,原型模式(prototype):

应用场景:

             简单来说就是克隆或者拷贝一个已经实例化并且属性赋值的 对象  给另一个新的对象使用。

原型模式实现:

        1,创建原型类:

/**
 * 要想使用该类的原型模式,必须实现 Cloneable 接口
 */
public class Sheep implements Cloneable {
    private String name;
    private Date birthday;

    public Sheep(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }


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

        2,浅克隆:

/**
     * 浅克隆:当源对象中的属性 birthday 的时间被修改了,那么克隆对象的 birthday也会被修改
     */
    public static void main(String[] args) throws CloneNotSupportedException {
        Date date = new Date(23123124324L);
        Sheep s1 = new Sheep("原型羊", date);
        System.out.println("源对象实例化赋值:"+s1.birthday);

        //进行克隆 克隆后 name  birthday 的值和s1一样
        Sheep s2 = (Sheep) s1.clone();
        System.out.println("克隆出 s2 之后:"+s2.birthday);

        //重新给 s1 的属性赋值以后  会发现即便 s2已经被克隆出来 但是其属性跟s1也一样会变化
        date.setTime(2345678945645L);
        System.out.println("对源对象的属性修改值以后的s1:"+s1.birthday);

        System.out.println("对源对象的属性修改值以后的s2:"+s2.birthday);
        // TODO  可对s2的属性再次进行修改,不会影响s1的属性值
    }

        3,深度克隆:

1、
// 修改 Sheep 中的代码   
@Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj = super.clone();
        // 若想使用深度克隆:将源对象的属性也进行克隆以保证 源对象和克隆对象不会公用一个属性值
        Sheep s = (Sheep) obj;
        // 对 birthday 进行克隆
        s.birthday = (Date) this.birthday.clone();
        return obj;
    }




2、
/**
     * 深度克隆:当源对象中的属性 birthday 的时间被修改了,克隆对象的属性值不变
     */
    public static void main(String[] args) throws CloneNotSupportedException {
        Date date = new Date(23123124324L);
        Sheep s1 = new Sheep("原型羊", date);
        System.out.println("源对象实例化赋值:"+s1.birthday);

        //进行克隆 克隆后 name  birthday 的值和s1一样
        Sheep s2 = (Sheep) s1.clone();
        System.out.println("克隆出 s2 之后:"+s2.birthday);

        date.setTime(2345678945645L);
        System.out.println("对源对象的属性修改值以后的s1:"+s1.birthday);

        System.out.println("对源对象的属性修改值以后的s2:"+s2.birthday);
        // TODO  可对s2的属性再次进行修改
    }

        

            4,还可使用序列化和反序列化进行深度克隆,此处不再实现,有兴趣的朋友可百度。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值