单例模式
什么是单例模式?
保证某个类只有一个对象实例存在并且只提供一个获取对象实例的方法。
通俗来讲就是,不能在外面来new这个类,而是通过类提供的方法来获取这个对象实例。
对于上述要求,我们来想想如何保证不能让他在外面被new出来?
不能被new出来,那我们看看new的时候做了什么?
一个对象被new出来,那么肯定调用了类的无参/有参构造器,那么,我们将构造器私有化,这样不就可以保证对象在外面就不可以被new出来了。
之后在类中提供一个public方法,在方法中返回该该类唯一对象就可以了。
思路就是这样,上代码!
单例模式写法一:饿汉式(静态常量)
/**
*饿汉式
* (1)采用静态常量的方式
* 步骤
* 1.将构造器私有化(防止new)
* 2.在类的内部创建对象
* 3.向外暴露一个静态的公共方法获取对象
* 优缺点:
* 优点:
* 写法简单,避免线程同步问题。
* 在类加载时就创建了实例对象
* 缺点:
* 由于类加载就完成了实例化,无法实现懒加载的效果
* 可能造成内存浪费
*/
class Eh01{
// 首先将构造器私有化
// 为什么?当你需要new一个对象时,肯定是会访问其构造函数的,当你将构造器私有化后,外部就无法访问构造函数,从而无法new
private Eh01(){
}
// 在类中创建一个类对象
private final static Eh01 eh01 = new Eh01();
// 向外暴露一个获取该对象的方法
public static Eh01 getEh01(){
return eh01;
}
}
写法二:饿汉式(静态代码块)
/**
* (2)静态代码块方式
* 由于静态代码块不管生成多少个对象,随着类加载到内存,会执行一次(只执行一次!!!!!!!!)
* 优缺点和静态变量写法一样
*/
class Eh02{
private Eh02(){
}
private static Eh02 eh02 ;
static {
//创建一个类对象
new Eh02();
}
// 向外暴露一个获取该对象的方法
public static Eh02 getEh02(){
return eh02;
}
}
写法三:懒汉式(线程不安全)
/**
* 懒汉式
* 优缺点:
* 起到了懒加载的效果,但这种方式是线程不安全的仅仅适合单线程的情况下使用
* 例:
* 当一个线程进去if判断,另一个线程也刚好进去,在第一个还没有创建时,第二个刚好判断完毕进行创建,这样就会造成对象的多余创建
* 不建议在开发中这样使用
*/
class Lh01{
// 首先将构造器私有化
private Lh01(){
}
// 在类中创建一个类对象
private static Lh01 lh01 ;
// 向外暴露一个获取该对象的方法
public static Lh01 getLh01(){
// 访问该方法时当内存中没有该对象实例再new
if(lh01==null){
lh01 = new Lh01();
}
return lh01;
}
}
写法四:懒汉式(线程安全,效率低)
/**
* 线程安全的懒汉式(采用同步代码块)
*优缺点:
* 解决了线程不安全问题,但是执行效率太低了,每次都阻塞在那
* 实际开发中不推荐该方法
*/
class Lh02{
private static Lh02 lh02;
private Lh02(){
}
// 采用同步代码块,解决线程不安全问题,当一个线程进去该方法就上个锁
public static synchronized Lh02 getLh02(){
if(lh02==null){
lh02 = new Lh02();
}
return lh02;
}
}
写法五:双重检查(推荐使用)
/**
* 双重检查
* 效率高,线程安全,懒加载,推荐开发中使用
*/
class DoubleCheck{
//volatile关键字作用:
//它可以保证当A线程对变量i值做了变动之后,
// 会立即刷回到主内存中,而其它线程读取到该变量的值也作废,
// 强迫重新从主内存中读取该变量的值,这样在任何时刻,AB线程总是会看到变量i的同一个值。
private static volatile DoubleCheck doubleCheck;
private DoubleCheck(){
}
public static DoubleCheck getDoubleCheck(){
// 第一重检查
if(doubleCheck==null){
// 同步代码块,解决线程安全
synchronized (DoubleCheck.class) {
// 第二重检查
if(doubleCheck==null) {
doubleCheck = new DoubleCheck();
}
}
}
return doubleCheck;
}
}
写法六:静态内部类(推荐使用)
/**
* 采用静态内部类
* 原理:
* 当A类装载时,静态内部类不会被装载,当A类方法调用到静态内部类时,静态内部类才开始装载且线程是安全的
* 同时效率很高,推荐使用
*/
class JT{
private JT(){
}
private static class singleClass{
private static final JT jt = new JT();
}
public static JT getJT(){
// 当调用时,内部静态类才加载
return singleClass.jt;
}
}
写法七:枚举(推荐使用)
/**
* 枚举
* 优缺点:
* 避免多线程同步问题,同时防止反序列化重新创建新的对象
* 推荐使用
*/
enum MJ{
SINGLE;//单例属性
public class Case{
}
private Case aCase;
// 私有化构造方法
private MJ(){
aCase = new Case();
}
public Case getACase(){
return aCase;
}
}
什么时候使用单例模式呢?
单例模式保证了系统内存中只有一个实例对象,节省了系统资源。
1.对于一些要频繁创建销毁的对象,使用单例模式可以提高系统的性能。
2.对于一些创建时间久,耗费资源过多的对象(重量级的对象)推荐使用单例模式。
3.对于一些频繁使用到的对象,工具类对象,频繁访问数据库的对象(数据源,session工厂),推荐使用单例模式。