设计模式不是代码,是某类问题的通用解决方案,设计模式代表了最佳的实践
所谓的单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类而言只存在一个对象实例,并且该类只提供一个取得其对象实例的方法
1)构造器私有化(防止new)
2)类的内部创建对象
3)向外暴露一个静态的公共方法 getInstance
4)代码实现
以下是实验代码
//饿汉式(静态变量)
public class Singleton1 {
public static void main(String[] args) {
//测试
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance==instance1);//ture
System.out.println("instance.hashCode= "+instance.hashCode());// 相同
System.out.println("instance1.hashCode= "+instance1.hashCode());//相同
}
}
//饿汉式(静态变量)
class Singleton{
//1.构造器私有化,外部不能new
private Singleton(){
}
//2.本类内部创建对象实例
private final static Singleton instance = new Singleton();
//3.对外提供一个公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instance;
}
}
优缺点:
1)优点:写法简单,在类加载的时候就完成实例化,避免了线程同步问题。
2)缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果;如果从始至终没用过这个实例,则会造成内存的浪费。
3)这种方式基于classloader机制避免了多线程的同步问题。不过,instance在类装载就实例化,但是导致类加载的原因有很多,不能确定有没有其他方式(或其他静态方法)导致类装载,这时候初始化instance就没有达到lazy loading的效果
conclusion:这种单例模式可能会造成内存浪费。
专业名词:
lazy loading:懒加载其实就是延时加载,即当对象需要用到的时候再去加载
优缺点:
1)这种方式跟**1.1.1饿汉式(静态常量)**相似,只不过将实例化操作放在静态代码块中,也是在类装载的时候、就执行静态代码块中的代码,初始化类的实例。
conclusion:同样地,有可能会造成内存浪费
专业名词:
静态代码块:随着类的加载而执行,而且只执行一次
扩展:
类中初始化执行顺序:静态代码块----->非静态代码块-------->构造函数
以下是实验代码
//饿汉式(静态代码块)
class Singleton{
//1.构造器私有化,外部不能new
private Singleton(){ }
//2.本类内部创建对象实例
private static Singleton instance;
static{ //在静态代码块中,创建单例对象
instance = new Singleton();
}
//3.对外提供一个公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instance;
}
}
package demo02.singleton.type3;
public class Singleton03 {
public static void main(String[] args) {
System.out.println("懒汉式1,线程不安全");
Singleton insatnce = Singleton.getInsatnce();
Singleton insatnce1 = Singleton.getInsatnce();
System.out.println(insatnce==insatnce1);
System.out.println("instance.hashCode= "+insatnce.hashCode());// 相同
System.out.println("instance1.hashCode= "+insatnce1.hashCode());//相同
}
}
class Singleton{
private static Singleton instance;
private Singleton(){
}
//提供一个静态的公有方法,当使用到该方法时,才去new instance
public static Singleton getInsatnce(){
if(instance ==null)//第一次为空 new Singleton
return instance=new Singleton();
else
return instance;//不为空,直接返回
}
}
优缺点:
1)达到lazy loading效果,但只能单线程下使用
2)如果在多线程的情况,一个线程进入if(instance ==null)判断语句块,还未来得及往下执行,另一个线程也通过了和这个语句,这时会产生多个实例。所以,多线程下不可用这种方式
conclusion:实际开发中,不要用这种方式
class Singleton{
private static Singleton instance;
private Singleton(){
}
//在方法级别上使用synchronized,解决线程安全问题
public static synchronized Singleton getInsatnce(){
if(instance ==null)
return instance=new Singleton();
else
return instance;
}
}
优缺点:
1)解决线程安全问题
2)效率低,每个线程想要得到实例时,执行instance()都要同步进行。但是instance()只需要执行一次,就已经实例化;后面想要获得实例直接return即可。在方法级别进行同步效率低下
conclusion:不推荐使用
class Singleton{
private static Singleton instance;
private Singleton(){
}
public static Singleton getInsatnce(){
if(instance ==null)
synchronized (Singleton.class){
return instance =new Singleton();
}
else
return instance;
}
}
优缺点:
1)这种同步并不能起到线程同步的作用;跟上述1.1.3的线程不安全问题一样;有可能会产生多个实例对象
conclusion:不能使用
class Singleton{
private static volatile Singleton instance;//加入了volatitle关键字
private Singleton(){
}
//加入双重检查代码,解决线程安全问题,同时解决懒加载问题
//同时保证了效率
public static Singleton getInsatnce(){
if(instance ==null)
synchronized (Singleton.class){
if(instance ==null){
return instance =new Singleton();
}
}
return instance;
}
}
优缺点:
1)Double-Check概念是多线程开发中常用到的;如代码中,我们进行了两次if(singleton == null)检查,这样保证了线程安全
2)实例化代码在任何情况下,都只能执行一次
3)线程安全;延迟加载;效率高。
conclusion:推荐使用这种单例设计模式
//静态内部类完成
class Singleton{
private static volatile Singleton instance;//加入了volatitle关键字
//构造器私有化
private Singleton(){ }
//写一个静态内部类,该类中有一个静态属性 Singleton
private static class SingletonInstance{
private static final Singleton INSTANCE=new Singleton();
}
//同时保证了效率
public static Singleton getInsatnce(){
//只有在调用这个方法时,静态内部类才会被装载。
return SingletonInstance.INSTANCE;
}
}
优缺点:
1)采用类装载机制保证初始化实例时只有一个线程
2)静态内部类只有被调用时才会实例化,不会随着主类装载而装载。
3)类的静态属性表明,只有在第一次加载类时才会初始化;在类初始化时,别的线程是无法访问的。
conclusion:推荐使用这种方式
public class Singleton08 {
public static void main(String[] args) {
Singleton instance = Singleton.INSTANCE;
Singleton instance1 = Singleton.INSTANCE;
System.out.println(instance==instance1);
System.out.println("instance.hashCode= "+instance.hashCode());// 相同
System.out.println("instance1.hashCode= "+instance1.hashCode());//相同
}
}
//使用枚举,可以实现单例,推荐使用
enum Singleton{
INSTANCE;
public void OK(){
System.out.println("ok");
}
}
优缺点:
1)借助JDK1.5添加的enum实现,避免了多线程问题,而且能够防止反序列化问题
2)这种方式是 Effective Java 作者Josh Bloch 所提倡的方式
conclusion:推荐使用这种方式