这两天,学习了一下Java设计模式中最为简单的一种模式——单例模式。
据说,在Java的设计模式中,大体上总共可以分为23种:单例模式、抽象工厂模式、建造者模式、原型模式等。
设计模式:实际上是一套被反复使用、多数人知晓的、经过分类编写目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易的被他人理解。并且保证代码的可靠性。
单例模式:
在实际的应用当中,有些对象我们只需要一个就足够了,比如:配置文件、工具类、线程池、缓存、日志对象、数据库连接对象等
如果创造出了多个实例,就有可能会导致许多问题,比如占用过多资源,不一致的结果等,这个时候就可以使用单例模式来解决这一问题,保证整个应用程序中某个实例有且只有一个。
常用的单例模式:
饿汉模式、懒汉模式
使用方式:
一、饿汉模式
package com.demo;
/**
* 单例模式 SingleTon
* 应用场合:在实际应用中,有些对象只需要一个就足够了,如配置文件,数据库连接对象等
* 作用:保证整个应用程序中某个实例有且只有一个
* 类型:饿汉模式、懒汉模式
* @author likang
* 当前:饿汉模式
*/
public class SingleTon {
/**
* 第一步:将构造方法私有化,不允许外部直接创建对象,只能类自己去创建
*/
private SingleTon(){
}
/**
* 第二步:创建类的唯一实例,在类的内部由类自己去创建,那这个时候,问题来了,如果只是这样的话,外部
* 无法直接调用类的实例,所以还必须加上static关键字进行修饰,将实例变成类的成员对象,那么外部就
* 可以直接通过类.成员变量获取这个实例了
*/
private static SingleTon instsnce = new SingleTon();
/**
* 第三步:根据面向对象的思想,为了控制外部不能直接访问类的实例,还需将类的成员对象进行
* 一定的封装,这里通过private关键字进行修饰,只能类自己去访问。外部无法直接访问,
* 如此一来,那么外部就不能通过类.成员对象来获取实例了,,所以在类的内部还必须提供一个公有
* 的方法(由public修饰)以供外部调用获取实例,并且该方法也需要通过static关键字进行
* 修饰,变成类的成员,(因为无法通过new关键字去创建。)
* @return
*/
public static SingleTon getInstance(){
/**
* 饿汉模式:因为instance声明为了static修饰,属于类所有,而静态成员变量
* 是随着类的加载而加载的,不管用户有没有调用这个类,有没有使用这个类,只要这个类一旦加载了,
* 那么静态成员变量也会跟着加载,就像没吃饱一样,形象地称之为“饿汉模式”
*/
return instsnce;
}
}
二、懒汉模式
package com.demo;
/**
* 懒汉模式
* @author likang
*
*/
public class SingleTon2 {
/**
* 第一步:将构造方法私有化,不允许外部直接创建实例
*/
private SingleTon2(){
}
/**
* 第二步:只是声明类的唯一实例,并不创建,使用private static修饰
*/
private static SingleTon2 instance;
/**
* 第三步:提供一个获取实例的方法以供外部调用,使用public static修饰
* @return
*/
public static SingleTon2 getInstance(){
/**
* 懒汉模式:在类加载的时候,只是声明了一个类的实例,并不去创建,
* 而在用户真正使用,真正调用的时候,再去创建类的实例,如果是第一次
* 被调用,instance显然为空,则创建一个,而以后再次调用的时候,
* 就可以不用创建,直接使用了。
*/
if(instance == null){
instance = new SingleTon2();
}
return instance;
}
}
测试结果:
package com.demo;
public class test {
public static void main(String[] args) {
/*SingleTon s1 = SingleTon.instsnce;
SingleTon s2 = SingleTon.instsnce;*/
SingleTon s1 = SingleTon.getInstance();
SingleTon s2 = SingleTon.getInstance();
//判断一下s1和s2这两个实例是否是同一个实例
if(s1 == s2){
System.out.println("s1和s2是同一个实例");
}else{
System.out.println("s1和s2不是同一个实例");
}
SingleTon2 s3 = SingleTon2.getInstance();
SingleTon2 s4 = SingleTon2.getInstance();
//判断一下s3和s4这两个实例是否是同一个实例
if(s3 == s4){
System.out.println("s3和s4是同一个实例");
}else{
System.out.println("s3和s4不是同一个实例");
}
}
}
关于饿汉模式和懒汉模式的区别:
1、饿汉模式的特点是加载类时比较慢,但运行时获取对象的速度比较快,线程安全
2、懒汉模式的特点是加载类时比较快,但运行时获取对象的速度比较慢(第一次调用),线程不安全
a、饿汉式即静态初始化的方式,它是类一加载就实例化的对象,所以要提前占用系统资源。线程安全
b、懒汉式,面临着多线程访问的安全性问题,需要做双重锁定才能保证安全。线程不安全
c、饿汉式的单例类已经足够满足我们的需求
因为饿汉模式,是在类加载完成之后,就创建实例,所以会占用一定的系统资源,会慢一些,但是运行时就可以直接使用,速度会比较快。
而懒汉模式,当类加载的时候,只是声明了实例,并没有去创建,而是在用户真正调用的时候再去创建实例,只是首次调用需要去创建,再次则可以直接使用。
关于线程安全的问题现在不是很懂,以后再慢慢去学习。额,可能有些啰嗦,因为自己的脑子向来不太好使,常常别人几下就能领悟的东西而我往往需要花费更多的时间才能去慢慢理解,这种方式也只是为了便于自己能够更好的去理解单例这种设计模式。恩,慢慢进步吧!