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

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("我要教好设计模式");
	}
}
测试类

TeacherTest.java

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

}

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

  

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

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

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

  原因: 

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

  具体解释:

    在饿汉式的例子中,当有多个线程访问时,比如说有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类,而且是饿汉式单例模式。如下:

public class Runtime {
    private static Runtime currentRuntime = new Runtime();
    public static Runtime getRuntime() {
        return currentRuntime;
    }
    private Runtime() {}
}



  




  


  

你可能感兴趣的:(设计模式,java设计模式)