java设计模式 -------- 创建模式 之 单例模式

  本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020

  

  单例模式也是创建模式中的一种。

单例模式:

  所谓的单例模式,即单一的实例,保证类在内在中只有一个对象。

  举例: windows的打印服务,网络计数器

  应用: 线程池,数据库连接池,Runtime

  

  在应用单例模式前,先来看一个例子。

Student.java

public class Student {

}

StudentTest.java

public class StudentTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Student s1 = new Student();
		Student s2 = new Student();
		System.out.println(s1 == s2);
	}

}

   这个例子很简单,就是新建两个Student类,并判断创建两个的类在内存中的地址是否相等,也就是说,创建的两个类在内存中是否是同一个类。

   很明显,根据大家编程的经验,最终输出的肯定是 false,因此,上面实质上是创建了两个类,而不是一个类。

   而单例模式要保证在内存中只有一个对象,而如果保证类在内存中只有一个对象呢,下面给出步骤

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

  1. 将构造方法私有化,为了不让外界创建对象

  2.  在类中创建一个对象

  3. 通过一个公共的访问方法给外界提供一个入口。

  

  按照这个步骤,我们可以保证类在内存中只有一个对象,但这里有个问题,就是在第二步“在类中创建对象”时,选择什么时候创建对象,是选择在类加载时创建还是在外界需要时创建呢??这里也就引出了单例模式的两种类型,饿汉式和懒汉式,分别如下:

两种类型的单例模式:

1. 饿汉式

   顾名思义,也就是在类加载时就创建对象,在外界访问时直接通过上述第三步中提供的访问入口访问,而不需再创建。如下例:

Student.java

public class Student {
	//1. 为了不让外界访问,我们把构造方法私有化
	private Student(){

	}
	//2. 创建一个对象
	//为了满足静态方法访问,这里也必须加一个静态修饰符。
	//为了不让外界修改s对象,需要在将s私有化,即加私有修饰符private
	private static Student s = new Student();

	//3. 提供一个公共的访问方法
	//为了让外界直接访问,我们需要给该方法加一个静态修饰符。
	public static Student getStudent(){
		return s;
	}

	public void show(){
		System.out.println("我要学好设计模式");
	}

}

测试类:

StudentTest.java

public class StudentTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		//下面实质是创建了两个类
		/*Student s1 = new Student();
		Student s2 = new Student();
		System.out.println(s1 == s2);*/

		Student s1 = Student.getStudent();
		Student s2 = Student.getStudent();
		System.out.println(s1 == s2);

		s1.show();
		s2.show();
	}

}

   这样修改以后,由于将Student类的构造方法私有化了,所以不能直接通过new Student()来创建类了,而是将创建类的工作交给了Student类,为了不让外界对Student类中的创建的Student类直接修改,所以需要将创建的类也私有化,只要给外界提供一个访问对象的方法即可,即上述的getStudent()方法。

  综上所述,饿汉式单例模式是在类加载时就创建对象,即private static Student s = new Student();相应地,在外界需要时创建对象就是懒汉式了,如下。

 

2. 懒汉式

  延迟加载思想:我们什么时候需要,你就什么时候给。(eg. Hibernate)

  也就是说在外界需要的时候创建对象,并且保证类在内存中只有一个,如下例:

Teacher.java

<span style="font-size:12px;">public class Teacher {

	//为了不让外界创建对象,将构造方法私有
	public Teacher() {
		// TODO Auto-generated constructor stub
	}

	//本类创建一个对象,注意,在这里不能new Teacher(),否则就成饿汉式了
	//这里加static是为了保证静态方法可以访问
	//这里加private是为了保证外界不能直接访问
	private static Teacher t = null;

	//提供公共的访问方法
	public static Teacher getTeacher(){
		if(t == null)
			t = new Teacher();
		return t;
	}

	public void show(){
		System.out.println("我要教好设计模式");
	}
}</span>

测试类

TeacherTest.java

<span style="font-size:12px;">public class TeacherTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Teacher t1 = Teacher.getTeacher();
		Teacher t2 = Teacher.getTeacher();
		System.out.println(t1 == t2); //true

		t1.show();
		t2.show();
	}

}
</span>

  可以很容易看出,懒汉式单例模式不仅保证了在内存中只有一个类,而且是在需要的时候创建对象,而不像饿汉式那样,在类加载的时候就创建。

  

  在了解了两种类型的单例模式后,就有一个问题了,在实际的开发和面试中到底选择哪一个呢???

在开发中应该选择哪个单例模式??

  一般我们开发中采用第一种方法,也就是说饿汉式。

  原因: 

    多线程安全问题。饿汉式不存在线程安全问题,而懒汉式存在线程安全问题。

  具体解释:

    在饿汉式的例子中,当有多个线程访问时,比如说有t1和t2同时访问getStudent()时,因为Student 对象是在类一开始加载时就已经创建好的,所以它们不管谁返回都是同一个对象,都是一开始已经创建好的对象,所以不存在线程安全问题。

    在懒汉式的例子中,当有有多个线程访问时,比如有t1和t2同时访问getStudent()时,这个时候,当t1进来后判断这个时候t=null ,所以t1就进去执行if所控制的语句,但是注意了,由于线程的随机性,可能t1刚进去要执行if控制语句,这个时候,被t2抢到了cpu的执行权,这个时候,t2开始执行了,发现,这个时候t还是null,所以t2也进去执行if所控制的语句了,那么将来会有多个对象被创建,因此懒汉式存在线程安全问题。

    因此,在开发中我们一般选择不存在线程安全问题的饿汉式单例模式。 

在面试中一般会选择哪种单例模式??

  在面试中,一般会选择懒汉式单例模式,而且主要是面试以下几个问题:

    A: 延迟加载思想

    B: 线程安全问题

       a: 线程安全问题是怎么产生的??

       b: 线程安全问题是如何解决的??

           ---在存在线程安全问题地方加同步关键字"synchronized"。

              因为被同步的代码,在某一个时刻只能被一个线程访问。

  因此,可以在getTeacher()方法处加入同步关键字"synchronized",以解决懒汉式存在的线程安全问题。

  其实,在JDK中,已经有单例模式的应用了,就是Runtime类,而且是饿汉式单例模式。如下:

<span style="font-size:12px;">public class Runtime {
    private static Runtime currentRuntime = new Runtime();
    public static Runtime getRuntime() {
        return currentRuntime;
    }
    private Runtime() {}
}</span>

  

  

  

时间: 2024-10-10 14:13:22

java设计模式 -------- 创建模式 之 单例模式的相关文章

java设计模式 -------- 创建模式 之 抽象工厂模式

本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020 工厂方法模式和抽象工厂模式不好区分清楚: 工厂方法模式特点: 1. 一个抽象产品类,可以派生出多个具体产品类. 2. 一个抽象工厂类,可以派生出多个具体工厂类. 3. 每个具体工厂类只能创建一个具体产品类的实例. 抽象工厂模式特点: 1. 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类. 2. 一个抽象工厂类,可以派生出多个具体工厂类. 3. 每个具体工厂类可以创建多个

java设计模式 -------- 创建模式 之 简单工厂模式

本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020 工厂模式是最常用的模式,因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a = new A(),工厂模式也是用来创建用来创建实例对象的,所以以后new时要多个心眼,是否可以考虑使用工厂模式. 简单工厂模式又可以分成三种模式: 1) 普通简单工厂模式 2) 多方法简单工厂模式 3) 多静态方法简单工厂模式 普通简单工厂模式: 举例:(简单的

java设计模式 -------- 创建模式 之 工厂方法模式

本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020 上节的简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到工厂方法模式,创建一个工厂接口和创建多个工厂实现类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码. 依然用上节的例子,四则运算,采用工厂方法模式,各部分的关系如下图: 工厂方法

java设计模式--创建模式--原型模式

原型模式: 1 原型模式 2 概述 3 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 4 5 适用性 6 1.当一个系统应该独立于它的产品创建.构成和表示时. 7 8 2.当要实例化的类是在运行时刻指定时,例如,通过动态装载. 9 10 3.为了避免创建一个与产品类层次平行的工厂类层次时. 11 12 4.当一个类的实例只能有几个不同状态组合中的一种时. 13 14 建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些. 15 16 参与者 17 1.

java设计模式--创建模式--工厂方法

工厂方法定义: 1 工厂方法 2 3 概述 4 定义一个用于创建对象的接口,让子类决定实例化哪一个类.FactoryMethod使一个类的实例化延迟到其子类. 5 6 适用性 7 1.当一个类不知道它所必须创建的对象的类的时候. 8 9 2.当一个类希望由它的子类来指定它所创建的对象的时候. 10 11 3.当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候. 12 参与者 13 1.Product 14 定义工厂方法所创建的对象的接口. 1

java设计模式(二)单例模式 建造者模式

(三)单例模式 单例模式应该是最常见的设计模式,作用是保证在JVM中,该对象只有一个实例存在. 优点:1.减少某些创建比较频繁的或者比较大型的对象的系统开销. 2.省去了new操作符,降低系统内存使用频率,减轻GC压力. 3.保证核心代码的唯一性,比如交易引擎. 单例模式看似是最简单的设计模式. public class Singleton { //私有构造方法,防止实例化 private Singleton(){ } //创建类的实例的时候加载 private static Factory f

Java设计模式-单利模式

单例模式 作为对象的创建模式,单例模式确保其某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类.单例模式有以下特点: 1.单例类只能有一个实例 2.单例类必须自己创建自己的唯一实例 3.单例类必须给其他所有对象提供这一实例 下面看一下单例模式的三种写法,除了这三种写法,静态内部类的方式.静态代码块的方式.enum枚举的方式也都可以,不过异曲同工,这三种方式就不写了. 首先声明就是 在我们项目工程中 我们完全不用使用懒汉式 因为有锁使用的地方就有效率低的存在: 饿汉式

Java设计模式(二)-单例模式

单例模式建议保证某个对象仅仅只有一个实例,当只有一个对象来协调整个系统的操作时,在计算机系统中,线程池.缓存.日志对象.对话框.打印机.显卡的驱动程序对象常被设计成单例,总之,选择单例模式就是为了避免不一致状态,避免政出多头. 下面是单例模式的类图:包括的private和static修饰的实例instance.一个private的构造函数.一个static的getInstance函数 单例模式主要有三种:懒汉式单例.饿汉式单例.登记式单例三种 1.饿汉式单例:在类的初始化时候,自行创建了实例 c

Java 设计模式 -- 复合模式之二

接着上文的鸭鸣例子:Java 设计模式 -- 复合模式之一 上文中,我们的鸭鸣实现了 装饰者模式  适配器模式  工厂模式的结合 现在,又需要进行改动了,上文,是可以统计一群鸭子的叫声,现在需要能够观察个别鸭子的行为 引入观察者模式: 任何想被观察的Quackable都必须实现下面的接口 public interface QuackObservable { public void registerObserver(Observer observer); public void notifyobs