[关闭]
@lzb1096101803 2016-03-06T09:31:21.000000Z 字数 2935 阅读 391

单例模式

Java 面试 设计模式


一、设计模式六大原则

  1. “开闭”原则(OCP, Open-Closed Principle)
    一个软件实体应当对扩展开放,对修改关闭。
    “开”是指对于组件功能的扩展是开放的,允许对其进行功能扩展
    "闭"是指对于原有代码的修改是封闭的,即不应该修改原有的代码
  2. 单一职责原则(SCP, Single Responsibility Principle)
    一个类应该只有一个引起它变化的原因。即一个类应该只有一个职责,如果一个类有一个以上的职责,这些职责会耦合在一起。当一个职责方式变化时,可能会影响其他职责。也会影响复用性。
  3. 依赖倒置原则(DIP, Dependence Inversion Principle)
    要依赖于抽象,而不要依赖于具体实现。针对抽象编程,不要针对实现编程,降低用户与实现模块间的耦合。
  4. 接口隔离原则(ISP, Interface Segregation Principle)
    使用多个隔离的接口,分离不同的接口行为,也就是说一个类对另外类的依赖性应该建立在最小接口上,应当将不同的角色交给不同的接口处理。
  5. 里氏替换原则(Liskov Substitution Principle)
    是对“开-闭”原则的补充。继承必须确保基类所拥有的性质在子类中仍然成立。也就是说,子类应该可以替换任何基类能够出现的地方。
  6. 迪米特原则(LoD, Law of Demeter)
    一个软件实体应当尽可能少地与其他实体发生相互作用。

二、设计模式分类

创建型模式(5种)

结构型模式(7种)

行为型模式(11种)

三、几种常见的面试

1、 单例模式的书写(3种)

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());

}

}

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注