1. 基础介绍
核心作用:保证一个类只有一个实例,并提供一个访问该实例的全局访问点;优点:由于单例模式只生成一个实例,所以减少了系统的开销,当一个系统启动需要较多的资源时,可以直接在系统启动时产生一个单例对象,然后使其永久驻留内存;单例模式可以在系统设置全局访问点,优化共享资源的访问;两种主要的单例模式实现方式:饿汉式,懒汉式;
2. 饿汉式介绍
线程安全,无法延时加载,也就是只要系统一运行,这个单例模式的类对象就会被加载到内存;简单步骤: 首先,将构造器私有化,这样就无法在类的外部直接创建对象; 然后再类中创建一个本类对象(使用private static修饰,做到全局使用); 再定义一个getInstance()方法,用来在其它的类中创建单例对象;
public class Singleton {
private static Singleton singleton
= new Singleton();
private Singleton() {
}
public static Singleton
getInstance() {
return singleton
;
}
}
3. 懒汉式
线程安全,延时加载,真正使用时创建;调用的效率比较低(因为每一次调用,都会重新创建一个对象);简单步骤: 首先先创建一个空的类实例,使用private static修饰; 然后创建一个私有的构造方法; 最后定义一个加锁的getInstance()方法,用于返回该类的对象;
public class SingletonDemo {
private static SingletonDemo sing
;
private SingletonDemo(){}
public synchronized static SingletonDemo
getInstance(){
if(sing
==null
){
sing
= new SingletonDemo();
}
return sing
;
}
}
4. 双重检测锁实现
上面讲到,可以将synchronized放入到方法里面去,这里就是这样做的,这样可以提高它锁的效率;但是涉及很到jvm底层,目前还没学习,暂做了解; 大致的步骤与上面一致,我就在懒汉式中直接修改了;
public class SingletonDemo {
private static SingletonDemo sing
=null
;
private SingletonDemo(){}
public static SingletonDemo
getInstance(){
if(sing
==null
){
synchronized (SingletonDemo
.class) {
if(sing
==null
)
sing
= new SingletonDemo();
}
}
return sing
;
}
}
5. 登记式/静态内部类实现方式
外部没有static属性,不会像饿汉式一样直接加载对象;只有调用getInstance()方法时加载对象,加载类时,线程安全,因为创建的外部类的对象是static final修饰,导致只有一个实例,只能赋值一次;有着饿汉式的高效调用,懒汉式的延时加载; 基本结构介绍: 首先先定义一个static的静态内部类,在该类中创建一个外部类的对象,并用private static final修饰该对象; 创建一个私有化的外部类构造方法; 最后在外部类中创建一个getInstance方法,用于返回该外部类的对象;
public class SingleStatic {
private String name
= "神";
private SingleStatic() {
}
private static class SingleInside {
private static final SingleStatic sig
= new SingleStatic();
public static String
string(){
String name
=sig
.name
;
return name
;
}
}
public static SingleStatic
getInstance() {
return SingleInside
.sig
;
}
public static String
getname(){
return SingleInside
.string();
}
}
测试类
class test {
public static void main(String
[] args
) {
new Thread(new Runnable() {
@Override
public void run() {
SingleStatic aStatic
= SingleStatic
.getInstance();
System
.out
.println(aStatic
);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
SingleStatic instance
= SingleStatic
.getInstance();
System
.out
.println(instance
);
}
}).start();
System
.out
.println(SingleStatic
.getname());
}
}
结果输出(符合单例模式):
6. 枚举
通过枚举实现单例模式,枚举本身就是一种单例模式,无延时加载,线程安全,调用效率高;
public enum SingleEnum
{
SINGLE_ENUM
;
}
7.总结
很明显,在单例模式中除了枚举的方式之外,其它的方式都可以通过反射的方式,强行获取对象的构造方法;当然也是可以避免的;只需要在创建前再次判断抛出一个异常结束就可以了;还有一些比如反序列化也可以进行破解,这里就不一一记录,感觉这种东西,只要了解,知道怎么规避就可以了,防止反序列化破解的话可以直接使用一个方法 readResolve() ,这个方法原理不是很懂,看别人的博客说是在使用对象流的时候会进行检查,如果有这个方法就会直接返回已有的对象,这个方法的出处没有,仿佛就是自己定义的一样;
Java杂记
a) 内部类可以直接调用外部类包括private的成员变量,构造方法;
b) 而外部类调用内部类需要建立内部类对象,当然,内部类也可以将方法设置为静态的;