1.最基本的单例模式:
package com.skiff.www;
/**
* @author 一叶扁舟(skiff)
* @ClassName: Singleton
* @Description:单例设计模式
* 有三个要点:
* 1.一个类必须只有一个实例
* 2.这个类必须自己创建实例对象
* 3.这个类能够自行向整个系统提供访问这个实例的方法
*
*
* @create 2018-10-18 20:52
**/
public class Singleton {
//静态的私有成员变量
private static Singleton instance = null;
// 私有的构造函数,构造函数必须是私有的,主要是不能让外部创建这个对象
private Singleton(){};
// 公有的静态工厂方法
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
2.饿汉单例模式:
package com.skiff.www;
/**
* @author 一叶扁舟(skiff)
* @ClassName: EagerSingleton
* @Description:懒汉的单例设计模式
* @create 2018-10-18 21:22
**/
public class EagerSingleton {
//静态的私有成员变量instance在类加载的时候就就会被实例化,此时类的私有构造函数将会被调用
public static final EagerSingleton instance = new EagerSingleton();
//私有的构造函数,防止外部创建对象
private EagerSingleton (){
System.out.println("饿汉模式,实例化对象,构造函数");
}
//公有的
public static EagerSingleton getInstance(){
System.out.println("获取实例");
return instance;
}
}
3.第一种懒汉式单例:
package com.skiff.www;
/**
* @author 一叶扁舟(skiff)
* @ClassName: LazySingleton
* @Description:懒汉单例设计模式第一种,获取实例加线程控制,使用关键字synchronized,这样在类加载的时候
* 并不会实例化对象,只有当调用的时候才会实例化
* @create 2018-10-18 21:34
**/
public class LazySingletonFirst {
public static LazySingletonFirst instance = null;
private LazySingletonFirst (){}
//使用synchronized对方法加速,确保在任何时候该方法只能被一个线程执行
synchronized public static LazySingletonFirst getInstance(){
if(instance == null){
instance = new LazySingletonFirst();
}
return instance;
}
}
4第二种懒汉式单例:
package com.skiff.www;
/**
* @author 一叶扁舟(skiff)
* @ClassName: LazySingletonSecond
* @Description:在第一种懒汉模式中,加了synchronized,使得任何时刻只有一个线程执行获取实例的方法,但是影响了效率
* @create 2018-10-18 21:45
**/
public class LazySingletonSecond {
//使用volatile修饰的变量,确保该变量能够被多个线程正确处理,但是使用volatile关键字会屏蔽jvm的优化
// 会导致系统运行的效率降低
private static volatile LazySingletonSecond instance = null;
private LazySingletonSecond(){}
public static LazySingletonSecond getInstance(){
// 第一层判空
if(instance == null){
// 锁定代码块
synchronized (LazySingletonSecond.class){
// 第二层判空
if(instance == null) {
instance = new LazySingletonSecond();
}
}
}
return instance;
}
}
5.静态内部类式单例:
package com.skiff.www;
/**
* @author 一叶扁舟(skiff)
* @ClassName: InnerSingleton
* @Description: 使用内部类创建单一的对象
* 使用饿汉的单例模式:不能实现延迟加载,无论未来用不用这个实例,始终占据着内存;
* 懒汉的单例设计模式:可以实现延迟加载,线程安全的控制繁琐,而且性能受到影响
* 单例类中添加一个内部类,在内部类中,创建单例对象,再将该单例对象通过getInstance()方法返回给外部使用。
*
*
* @create 2018-10-18 22:06
**/
public class InnerSingleton {
private InnerSingleton (){
System.out.println("构造函数");
}
//静态的内部类
//在第一次调用getInstance()方法,将加载内部类HolderClass,该内部类定义的静态成员变量instance,首先会被初始化
// 这个是又jvm保证线程安全,jvm确保这个成员变量只能被实例化一次,同时getInstance()没有被线程锁定,因此性能很好
private static class HolderClass{
private final static InnerSingleton instance = new InnerSingleton();
}
public static InnerSingleton getInstance(){
System.out.println("获取实例对象");
return HolderClass.instance;
}
}
6.测试单例模式:
package com.skiff.www;
import org.junit.Test;
/**
* @author 一叶扁舟(skiff)
* @ClassName: ClientTest
* @Description:
* @create 2018-10-18 20:52
**/
public class ClientTest {
@Test
public void testSingleton(){
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
if(instance1 == instance2){
System.out.println("两个对象相对,是单例设计模式");
}else{
System.out.println("两个对象不相等");
}
}
@Test
public void testEagerSingleton(){
EagerSingleton instance1 = EagerSingleton.getInstance();
EagerSingleton instance2 = EagerSingleton.getInstance();
if(instance1 == instance2){
System.out.println("两个对象相对,是单例设计模式");
}else{
System.out.println("两个对象不相等");
}
//输出结果:
//饿汉模式,实例化对象,构造函数
//获取实例
//获取实例
//两个对象相对,是单例设计模式
}
@Test
public void testInnerSingleton(){
InnerSingleton instance1 = InnerSingleton.getInstance();
InnerSingleton instance2 = InnerSingleton.getInstance();
if(instance1 == instance2){
System.out.println("两个对象相对,是单例设计模式");
}else{
System.out.println("两个对象不相等");
}
//输出结果:
//获取实例对象
//构造函数
//获取实例对象
//两个对象相对,是单例设计模式
}
}
7.总结:
(1).单例模式的目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例类拥有一个私有构造函数,确保用户无法通过new关键字直接实例化它。除此之外,该模式中包含一个静态私有成员变量与静态公有的工厂方法。该工厂方法负责检验实例的存在性并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。
(2).单例模式的主要优点在于提供了对唯一实例的受控访问并可以节约系统资源;其主要缺点在于因为缺少抽象层而难以扩展,且单例类职责过重。
(3).单例模式适用情况包括:系统只需要一个实例对象;客户调用类的单个实例只允许使用一个公共访问点。