@lzb1096101803
2016-03-06T09:31:21.000000Z
字数 2935
阅读 392
Java
面试
设计模式
Java中的应用: Spring中的上下文对象,java.lang.Runtime(第一种)
从速度和反应时间角度来讲,非延迟加载(又称饿汉式)好;从资源利用效率上说,延迟加载(又称懒汉式)好
第一种:
public class Dao{
private static Dao dao = new Dao();
private Dao(){};
public static getDaoInstance(){
return dao;
}
}
第二种:
public class Dao{
private static Dao dao;
private Dao(){};
public synchronized static getDaoInstance(){
if(dao==null){
dao = new Dao();
}
return dao;
}
}
第三种:
没有volatile修饰符,可能出现Java中的另一个线程看到个初始化了一半的_instance的情况,但使用了volatile变量后,就能保证先行发生关系(happens-before relationship)。对于volatile变量_instance,所有的写(write)都将先行发生于读(read),
在Java 5之前不是这样,所以在这之前使用双重检查锁有问题。现在,有了先行发生的保障(happens-before guarantee),你可以安全地假设其会工作良好。
public class Dao{
private volatile Dao dao;
private Dao(){};
public static getDaoInstance(){
if(dao==null){
synchronized(Dao.class){
if(dao==null){
dao = new Dao();
}
}
}
return dao;
}
}
第四种:使用枚举
1、 自由序列化;
2、 保证只有一个实例(即使使用反射机制也无法多次实例化一个枚举量);
3、 线程安全;
好处不外乎三点:1.线程安全 2.不会因为序列化而产生新实例
3.防止反射攻击
关于第二点序列化问题,有一篇文章说枚举类自己实现了readResolve()方法,所以抗序列化,这个方法是当前类自己实现的(解决)
关于第一点线程安全,从反编译后的类源码中可以看出也是通过类加载机制保证的,应该是这样吧(解决)
关于第三点反射攻击,我有自己试着反射攻击了以下,不过报错了...看了下方的反编译类源码,明白了,因为单例类的修饰是abstract的,所以没法实例化。(解决)
传统的单例模式的另外一个问题是一旦你实现了serializable接口,他们就不再是单例的了,因为readObject()方法总是返回一个 新的实例对象,就像java中的构造器一样。你可以使用readResolve()方法来避免这种情况,通过像下面的例子中这样用单例来替换新创建的实 例:
publicenum SingletonEnum
{
INSTANCE01,INSTANCE02;
private String name;
publicString getName()
{
returnname;
}
publicvoid setName(String name)
{
this.name = name;
}
}
测试
public class Test
{
public static void main(String[] args)
{
SingletonEnum instance01=SingletonEnum.INSTANCE01;
instance01.setName("terje");
System.out.println(instance01.getName());
SingletonEnum instance02=SingletonEnum.INSTANCE01;
System.out.println(instance02.getName());
SingletonEnum instance03=SingletonEnum.INSTANCE02;
instance03.setName("liu");
System.out.println(instance03.getName());
SingletonEnum instance04=SingletonEnum.INSTANCE02;
instance04.setName("liu");
System.out.println(instance04.getName());
}
}