Java笔记 —— 设计模式(简单工厂模式,单例模式)

简单工厂模式

简单工厂模式:定义一个工厂类,这个类可以根据传入的参数不同,而返回不同类型的实例对象。通常这些实例对象有着共同的抽象父类或者实现了相同的接口。

这里相当于将不同类创建对象的功能,都放到了一个工厂类里面,然后通过这个工厂类来创建对象。我们需要做的是传入一个参数,而这个参数对应的对象具体是怎么创建的,这个对象又要求什么参数都不用考虑。

假设这里有一家食品制作工厂,这家工厂可以生产各种口味的事物。当我们去购买食物的时候,我们会直接说一份很甜的食物,一份很辣的食物。而不会去具体的描述这个食物应该怎么被制作,应该加多少水,放多少盐。这不是我们需要考虑的事情,这是工厂应该做的事情。我们只需要说一个名字,然后工厂就给我们对应的食物。

另外,通常在具体的工厂类里面,创建对象的方法是静态的,这样我们可以用类名.方法名的方式来调用。因为这样,简单工厂模式又会被称为静态工厂模式。

现在看看具体的代码是怎么实现的
首先是不同的对象应该有相同的父类或者接口

package test.FactoryDemo;

public interface Food {
    public abstract void taste();
}

工厂可以生产的食物——很甜的食物

package test.FactoryDemo;

public class Sweet implements Food{
    @Override
    public void taste() {
        System.out.println("很甜的食物");
    }
}

工厂可以生产的食物——很辣的食物

package test.FactoryDemo;

public class Hot implements Food{
    @Override
    public void taste() {
        System.out.println("很辣的食物");

    }
}

工厂类

package test.FactoryDemo;

public class foodFactory {
//private修饰构造方法,让外界无法创建工厂类的对象
    private foodFactory(){}

    public static Food createFood(String taste){
        if("sweet".equals(taste)){
            return new Sweet();
        }else if("hot".equals(taste)){
            return new Hot();
        }else{
            return null;
        }
    }
}

工厂生产食物并提供给消费者

package test.FactoryDemo;

public class Demo {
    public static void main(String[] args) {
        Food sweet = foodFactory.createFood("sweet");
        sweet.taste();
        Food hot = foodFactory.createFood("hot");
        hot.taste();

        //另外对于工厂制作不了的情况也要有所考虑
        Food salty = foodFactory.createFood("salty");
        if(salty!=null){
            salty.taste();
        }else{
            System.out.println("工厂无法制作这类食物");
        }
    }
}

在这里插入图片描述
可以发现,我们只是提供了一个参数,说明一下食物的口味,然后工厂就会自动匹配对应的食物并创建对象。接着我们就可以使用这个对象,至于对象的具体创建过程我们并不关心。

单例模式

单例模式对一个类做出要求,要求在这个类的内部创建对象,而且只能创建一个对象,同时提供一个访问这个对象的方法。确保这个对象的全局唯一性,其他的所有的类需要使用这个类的对象时,都只能使用这个唯一对象。

如何保证类在内存中只有一个对象:

  1. 构造方法私有 (private)
  2. 在类的成员变量的位置上创建一个对象 (static)
  3. 提供一个公共的方法给外界获取到该对象 (public)

单例模式的应用场景:

  1. 程序运行时要求这个类只能有一个对象
    这个类的功能比较特殊,比如往打印机里面写入文件的类,打印机同一时间只能打印一个文件,所以只能有一个对象调用方法写入,不能有第二个对象调用方法一起写入。
  2. 程序运行时存在会多次被实例化又销毁的对象的时候,可以将这个类变成单例模式
  3. 程序运行时需要多次创建某个类的对象,但是创建消耗的资源太多的时候

单例模式的两种实现形式:

饿汉式

随着类的加载,将这个类的唯一对象也加载好

这是一个班主任的类,老师可以有很多,但是班主任只有一位。家长需要找班主任了解情况,校长需要找班主任开会,学生需要找班主任请假,但是不可能谁找一次班主任,就创建一个新的班主任来满足要求。班主任显然只有一个人,同一时间也只能满足家长,校长,学生其中的一位的要求。所以班主任类改为单例模式,只有班主任这一个实例。谁要找班主任,就去获取这个班主任对象。

package test.SimpleCase;

public class Teacher {
    private static Teacher teacher = new Teacher();

    private Teacher(){

    }

    public static Teacher getTeacher(){
        return teacher;
    }

}

package test.SimpleCase;

public class TeacherDemo {
    public static void main(String[] args) {
        Teacher t1 = Teacher.getTeacher();
        Teacher t2 = Teacher.getTeacher();

        System.out.println(t1);
        System.out.println(t2);
        System.out.println(t1==t2);
    }
}

在这里插入图片描述
可以看出来从始到终都只有一个对象。
另外这里如果用 Teacher t = new Teacher()的方式来创建对象的话,会直接报错

懒汉式

饿汉式是随着类的加载创建对象
懒汉式是类加载的时候不会创建对象,而是延迟加载,等到需要这个对象的时候才创建
但是这样延迟加载的话会出现线程安全问题,当多个线程都需要这个对象的时候,有可能会出现创建多个对象的情况,比如说

public static Student getStudent(){
        if(s==null){  //步骤一
            s = new Student(); //步骤二
        }
        return s; //步骤三
    }

当多个线程用这个方法来获取对象时,可能线程一在步骤二加载对象时,线程三在步骤一进行判断,即判断对象是否为空。此时线程一刚刚加载完对象还没有到步骤三返回对象,导致线程三会判断 s 为空,即这个类的对象还没有被创建,于是线程三就会接着创建一个新的对象。

因此这里需要用到synchronized

班长类

package test.SimpleCase;

public class Student {
    private static Student s = null;
    private Student(){}
    public synchronized static Student getStudent(){
        if(s==null){
            s = new Student();
        }
        return s;
    }
}

package test.SimpleCase;

public class StudentDemo {
    public static void main(String[] args) {
        Student s1 = Student.getStudent();
        Student s2 = Student.getStudent();

        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s1==s2);
    }
}

在这里插入图片描述
这里试着用线程来调用对象

班长类

package test.SimpleCase;

public class Student {
    private static Student s = null;
    private Student(){}
    private int age = 12;
    public synchronized static Student getStudent(){
        if(s==null){
            s = new Student();
        }
        return s;
    }

    public void setAge(int age){
        this.age = age;
    }

    public int getAge(){
        return age;
    }

    public void homeWork(){
        System.out.println("The monitor collects the class homework");
    }

}

线程一

package test.SimpleCase;

public class ThreadDemo extends Thread {
    @Override
    public void run(){
        Student s = Student.getStudent();
        synchronized (s){
            s.setAge(15);
            System.out.println(Thread.currentThread().getName()+"--"+"正在调用对象");
            s.homeWork();
            System.out.println(s.getAge());
        }

    }
}

线程二

package test.SimpleCase;

public class ThreadDemo2 extends Thread {
    @Override
    public void run(){
        Student s = Student.getStudent();
        synchronized (s){
            s.setAge(18);
            System.out.println(Thread.currentThread().getName()+"--"+"正在调用对象");
            s.homeWork();
            System.out.println(s.getAge());
        }

    }
}

测试方法

package test.SimpleCase;

public class StudentDemo {
    public static void main(String[] args) {
        ThreadDemo t1 = new ThreadDemo();
        ThreadDemo2 t2 = new ThreadDemo2();

        t1.start();
        t2.start();

        Student s1 = Student.getStudent();
        System.out.println("Student's age is "+s1.getAge());
    }
}

在这里插入图片描述
结果说明,两个线程获取的都是相同的学生对象

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一纸春秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值