JavaSE面试题之单例模式
Posted JadeXu07
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaSE面试题之单例模式相关的知识,希望对你有一定的参考价值。
一、单例模式介绍
单例,即单个实例,即某个类在整个系统中只能有一个实例对象可被获取和使用。
例如:代表JVM运行环境的Runtime类
所谓单例,那就得:
1、某个类只能有一个实例,即不能被外界new出来。所以构造器需私有
2、这个类必须自行创建这单个实例
3、这个类需要自行向外界提供这个实例,所以需要有个静态变量或静态方法去调用
二、单例模式的几种常见实现形式
总结有两大种:
饿汉式:直接创建对象,不存在线程安全问题
- 直接实例化(简洁直观)
- 枚举式(最简洁,JDK1.5才开始有枚举)
- 静态代码块(适合初始化时需要传参的类)
懒汉式:只有调用对象才会创建对象,即延迟创建对象
- 线程不安全(适合单线程)
- 线程安全(适合多线程)
- 静态内部类(适合多线程)
1、饿汉式
1. 直接实例化
/**
* @author jadexu
* 饿汉式1:直接实例化
* 1、构造器私有化
* 2、类加载时就创建
* 3、向外提供这个实例
* 4、用final修饰体现单例不能被修改
*
* 适合简单直接,初始化时不需要有参构造的场景
*/
public class Singleton1 {
/**
* 公共的,静态,final
*/
public final static Singleton1 INSTANCE = new Singleton1();
/**
* 构造函数私有化
*/
private Singleton1(){
}
}
2. 枚举式
/**
* @author jadexu
* 饿汉式2:枚举
* JDK1.5开始出现枚举类型,枚举表示某类型的有限个对象
* 枚举默认不能实例化,变量是final修饰的
* 所以又不能构造,又不能修改
* 那么限定为一个,就相当于单例了
*/
public enum Singleton2 {
INSTANCE
}
测试
@Test
public void singleton2Test(){
/**
* 报错,枚举类型不能被实例化
*/
// Singleton2 singleton2 = new Singleton2();
/**
* 报错,枚举类型是final的,不能被修改
*/
// Singleton2.INSTANCE = null;
/**
* 只能直接调用唯一实例
*/
Singleton2 singleton2 = Singleton2.INSTANCE;
}
3. 静态内部块
/**
* @author jadexu
* 饿汉式3:静态代码块
* 和直接实例化类似
* 就是会在静态代码块里创建实例
*
* 适合初始化时需要有参构造的场景
*/
public class Singleton3 {
public final static Singleton3 INSTANCE;
static {
//假装这是初始化时需要有参构造
String info = "test";
INSTANCE = new Singleton3(info);
}
private Singleton3(String info){
}
}
2、懒汉式
1. 线程不安全
/**
* @author jadexu
* 懒汉式1:线程不安全
* 1、构造器私有化
* 2、用一个私有的静态变量保存唯一实例
* 3、提供一个静态方法,获得这个实例
*
* 适合单线程
*/
public class Singleton4 {
private static Singleton4 instance;
private Singleton4(){
}
public static Singleton4 getInstance() throws InterruptedException {
//判断静态变量是否为空,如果为null就创建一个,不为null就直接返回
if (instance == null){
instance = new Singleton4();
}
return instance;
}
}
测试
@Test
public void singleton4Test() throws ExecutionException, InterruptedException {
Callable<Singleton4> callable = new Callable<Singleton4>() {
@Override
public Singleton4 call() throws Exception {
return Singleton4.getInstance();
}
};
ExecutorService service = Executors.newFixedThreadPool(2);
Future<Singleton4> f1 = service.submit(callable);
Future<Singleton4> f2 = service.submit(callable);
Singleton4 s1 = f1.get();
Singleton4 s2 = f2.get();
System.out.println(s1 == s2);
service.shutdown();
}
2. 线程安全
/**
* @author jadexu
* 懒汉式2:线程安全(双检锁)
* 适合多线程场景,但是效率低
*/
public class Singleton5 {
private static volatile Singleton5 instance;
public static Singleton5 getInstance(){
if (instance == null){
synchronized (Singleton5.class){
if (instance == null){
instance = new Singleton5();
}
}
}
return instance;
}
}
测试
@Test
public void singleton5Test() throws ExecutionException, InterruptedException {
Callable<Singleton5> callable = new Callable<Singleton5>() {
@Override
public Singleton5 call() throws Exception {
return Singleton5.getInstance();
}
};
ExecutorService service = Executors.newFixedThreadPool(2);
Future<Singleton5> f1 = service.submit(callable);
Future<Singleton5> f2 = service.submit(callable);
Singleton5 s1 = f1.get();
Singleton5 s2 = f2.get();
System.out.println(s1 == s2);
service.shutdown();
}
3. 静态内部类
/**
* @author jadexu
* 懒汉式3:静态内部类
* 1、构造器私有
* 2、私有静态内部类
* 3、提供一个公共的静态方法
*
* 静态内部类不会随着外部类的加载而加载
* 当调用公共静态方法getInstance时,才会加载Inner静态内部类
* 然后创建实例
* 因为是在内部类加载和初始化时创建的,因此是线程安全的
*
* 适合于多线程的场景
*/
public class Singleton6 {
private Singleton6(){
}
private static class Inner{
private static final Singleton6 INSTANCE = new Singleton6();
}
public static Singleton6 getInstance(){
return Inner.INSTANCE;
}
}
以上是关于JavaSE面试题之单例模式的主要内容,如果未能解决你的问题,请参考以下文章