JavaSE面试题:单例设计模式
编程题:写一个Singleton示例
什么是Singleton?
Singleton:在 Java中指单例设计模式。
单例模式:即某个类在整个系统中只有一个实例对象可被获取和使用的代码模式。
例如:代表JVM运行环境的Runtime类
要点
-
单例类只能有一个实例。
- 构造函数私有化
-
单例类必须自己创建自己的唯一实例。
- 含有一个该类的静态变量来保存这个唯一实例
-
单例类必须给所有其他对象提供这一实例。
-
对外提供获取该实例对象的方式
(1)直接暴露;(2)用静态变量的get方法获取
-
单例模式常见的形式
- 饿汉式:直接创建对象,不存在线程安全的问题
- 直接实例化饿汉式(简洁直观)
- 枚举式(最简洁)
- 静态代码块饿汉式(适合复杂实例化)
- 懒汉式:延迟创建对象
- 线程不安全 (适合单线程)
- 线程安全(适合多线程)
- 静态内部类形式(适合多线程)
饿汉式
(1) 直接实例化饿汉式
package com.sunyan.interview.single;
/**
* 饿汉式:直接创建实例对象,不管是否需要这个对象都会创建
*
* 1.构造器私有化
* 2.自行创建,并且用静态变量保存
* 3.向外提供这个实例
* 4.强调这是一个单例,可以用final修饰
*/
public class Singleton1 {
public static final Singleton1 INSTANCE = new Singleton1();
private Singleton1() {
}
}
(2)枚举式
package com.sunyan.interview.single;
/**
* 枚举类型:表示该类型的对象是有限的几个
* 可以限定为一个,就成了单例
*/
public enum Singleton2 {
INSTANCE;
}
(3) 静态代码块
public class Singleton3 {
public static final Singleton3 INSTANCE;
private String info;
static {
try {
Properties properties = new Properties();
properties.load(Singleton3.class.getClassLoader().getResourceAsStream("single.properties"));
INSTANCE = new Singleton3(properties.getProperty("info"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public Singleton3(String info) {
this.info = info;
}
......
}
single.properties文件
info=aaaa
测试
@Test
public void test1() {
Singleton3 instance = Singleton3.INSTANCE;
System.out.println(instance);
}
src/main/java:里面的java文件只能直接加载src/main/resources下的资源,不能直接加src/test/resources下的资源;
src/test/java: 里面的java文件既能加载src/test/resources下的资源,又能加载src/main/resources下的资源,当两个resources下都有要加载的同名资源时候,优先选择src/test/java下的资源.
懒汉式
(1)线程不安全
package com.sunyan.interview.single;
/**
* 懒汉式:延迟创建这个实例对象
*
* 1.构造器私有化
* 2.用一个静态变量保存这个唯一实例
* 3.提供一个静态方法,获取这个实例对象
*/
public class Singleton4 {
private static Singleton4 instance;
private Singleton4(){}
public static Singleton4 getInstance(){
if (instance == null){
instance = new Singleton4();
}
return instance;
}
}
(2) 线程安全
public class Singleton5 {
private static Singleton5 instance;
private Singleton5() {
}
public static Singleton5 getInstance() {
if (instance == null) {
synchronized (Singleton5.class) {
if (instance == null) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new Singleton5();
}
}
}
return instance;
}
}
(3)静态内部类 线程安全
package com.sunyan.interview.single;
/**
* 懒汉式:延迟创建这个实例对象
* 在内部类被加载和初始化时,才创建INSTANCE实例对象
* 静态内部类不会自动随着外部类的加载和初始化而初始化,他是单独加载和初始化的
* 因为是在内部类加载和初始化时,创建的,所以是线程安全的
*/
public class Singleton6 {
private Singleton6() {
}
private static class Inner {
private static final Singleton6 INSTANCE = new Singleton6();
}
public static Singleton6 getInstance() {
return Inner.INSTANCE;
}
}
静态内部类不会自动随着外部类的加载而初始化
总结
- 饿汉式,枚举形式最简单
- 懒汉式,静态内部类形式最简单
考点
懒汉式
笔试:请写一个延迟加载的单例设计模式示例
//懒汉式
class Single {
private static Single s = null;
private Single() {}
public static Single getInstance() {
if (s == null) {
synchronized(Single.class) {
if (s == null) {
s = new Single();
}
}
}
return s;
}
}
面试:
懒汉式和饿汉式有什么不同?
答:懒汉式的特点是实例的延迟加载。
懒汉式延迟加载有没有问题?
答:有。如果多线程访问时会出现安全问题。
怎么解决?
答:可以加同步来解决。加同步的方式,用同步代码块和同步函授都行,但是稍微有些低效。用双重判断的形式能解决效率问题。
加同步的时候,使用的锁是哪一个?
答:该类所属的字节码文件对象。
package com.sun.thread;
/**
* 单例设计模式。
* @author Sun
*
*/
//饿汉式
/*
class Single {
private static final Single s = null;
private Single(){}
public static Single getInstance() {
return s;
}
}
*/
//懒汉式
class Single {
private static Single s = null;
private Single() {}
public static Single getInstance() {
if (s == null) {
synchronized(Single.class) {
if (s == null) {
s = new Single();
}
}
}
return s;
}
}