我们会在这里介绍我所涉及到的设计模式相关的面试点,本篇内容持续更新
我们会介绍下述设计模式的相关面试点:
- 单例模式
单例模式
下面我们来介绍单例模式的相关面试点
五种单例模式实现方式
我们下面来介绍单例模式的五种实现方式
饿汉式
我们给出饿汉式构建单例模式的基本框架:
/*饿汉式*/
public class Singleton implements Serializable{
// 首先我们需要拥有一个私有的构造方法(为了防止其他对象调用构造方法产生新对象)
private Singleton(){
// 这里我们需要做一个判断,如果已存在单例对象,且其他对象调用构造方法,直接报错(为了预防反射获得类然后新创对象)
if( INSTANCE != null){
throw new RuntimeException("单例对象不可重复创建");
}
System.out.println("private Singleton");
}
// 饿汉式:在加载类时就直接创建单例对象,我们这里直接用static创建一个静态单例对象(随着类加载创建单例对象)
private static final Singleton INSTANCE = new Singleton();
// 由于单例对象是private,我们需要一个公共方法获得对象
public static Singleton getInstance(){
return INSTANCE;
}
// 其他方法
public static void otherMethod(){
System.out.println("otherMethod");
}
// readResolve方法,用于阻止反序列化获得新对象
public Object readResolve(){
return INSTANCE:
}
// 需要注意:Unsafe类破坏单例对象是无法阻止的!!!
}
枚举饿汉式
我们给出枚举饿汉式构建单例模式的基本框架:
/*枚举*/
enum Sex{
MALE,FAMALE;
}
/*枚举饿汉式*/
public enum Singleton{
// 单例对象
INSTANCE;
// getStance方法
public static Singleton getInstance(){
return INSTANCE;
}
// 枚举方法自带 反射 反序列化 生成对象的阻止方法
// 枚举方法也不能阻止Unsafe类生成对象
}
懒汉式
我们给出懒汉式构建单例模式的基本框架:
/*懒汉式*/
public class Singleton{
// 首先我们需要一个构造方法
private Singleton(){
System.out.println("private Singleton");
}
// 懒汉式:该对象创建之后不赋值,等到使用时在进行赋值
private static Singleton INSTANCE = null;
// getStance:我们在获得 STANCE 对象时再进行赋值并且反馈(注意:需要加锁处理多线程问题)
public static synchronized Singleton getStance(){
// 首先判断是否存在,若不存在创建并返回,若存在直接返回
if(INSTANCE == null){
INSTANCE = new Singleton();
}
return INSTANCE;
}
// 其他方法
public static void otherMethod(){
System.out.println("otherMethod");
}
}
DCL懒汉式
我们给出DCL懒汉式构建单例模式的基本框架:
/*DCL懒汉式*/
// DCL:Double Check Lock 双重检查锁
public class Singleton{
private Singleton(){
System.out.println("private Singleton");
}
// 这里需要加上volatile,为了保证语句的有序性
// 在getStance的赋值操作中INSTANCE = new Singleton()语句属于init初始化和static初始化
// 两者之间可能出现优化状态,可能导致先进行ISNTANCE赋值,再进行init初始化
// 但是在这个间隙线程2可能会通过INSTANCE判断,然后直接返回INSTACE,这时返回的并不是完整体的INSTANCE,可能出错
private static volatile Singleton INSTANCE = null;
// 我们将这里的锁进行更改,之前我们将getStance方法上锁,所有进程调用均需要锁处理,效率较慢
// 实际上,我们只有第一次创建时,需要上锁处理,所以我们采用双重检查锁,判定只有未赋值时进行锁处理
public static Singleton getStance(){
// 首先判断是否赋值,若没赋值,进入锁赋值判断阶段
if(INSTANCE == null){
// 需要锁处理:这里就是并发部分
synchronized(Singleton.class){
// 需要二次判断,因为当线程1进行赋值操作时,线程2可能已经通过了第一次null判断,到这里还需要重新判断
if(INSTANCE == null){
// 赋值操作
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
public static void otherMethod(){
System.out.println("otherMethod");
}
}
内部类懒汉式
我们给出内部类懒汉式构建单例模式的基本框架:
/*内部类懒汉式*/
// 属于最简洁的一种懒汉式创建方式
public class Singleton{
// 构造方法
private Singleton(){
System.out.println("private Singleton");
}
// 内部类,用于存储单例对象(static内部类会随着类加载而加载,其单例性由JVM控制)
private static class Holder{
// 内部元素只有当类使用时被创建
static Singleton INSTANCE = new Singleton();
}
// get方法调用内部类的INSTANCE
public static Singleton getInstance(){
return Holder.INSATNCE;
}
public static void otherMethod(){
System.out.println("otherMethod");
}
}
五种单例模式JDK体现
我们简单介绍一下JDK中使用单例模式的类:
/*饿汉式Runtime*/
public class Runtime {
// 单例对象,直接创建
private static Runtime currentRuntime = new Runtime();
// 直接返回单例对象
public static Runtime getRuntime() {
return currentRuntime;
}
}
/*枚举饿汉式NaturalOrderComparator*/
class Comparators {
private Comparators() {
throw new AssertionError("no instances");
}
/**
* Compares {@link Comparable} objects in natural order.
*
* @see Comparable
*/
enum NaturalOrderComparator implements Comparator<Comparable<Object>> {
INSTANCE;
@Override
public int compare(Comparable<Object> c1, Comparable<Object> c2) {
return c1.compareTo(c2);
}
@Override
public Comparator<Comparable<Object>> reversed() {
return Comparator.reverseOrder();
}
}
}
/*DCL懒汉式System.Console*/
class System{
// Console
private static volatile Console cons = null;
// Console双检索
public static Console console() {
if (cons == null) {
synchronized (System.class) {
cons = sun.misc.SharedSecrets.getJavaIOAccess().console();
}
}
return cons;
}
}
/*内部类懒汉式Collections*/
class Conllections{
// emptyNavigableMap对象
public static final <K,V> NavigableMap<K,V> emptyNavigableMap() {
return (NavigableMap<K,V>) UnmodifiableNavigableMap.EMPTY_NAVIGABLE_MAP;
}
/**
* @serial include(内部类)
*/
private static class EmptyMap<K,V>
extends AbstractMap<K,V>
implements Serializable
{
private static final long serialVersionUID = 6428348081105594320L;
public int size() {return 0;}
public boolean isEmpty() {return true;}
public boolean containsKey(Object key) {return false;}
public boolean containsValue(Object value) {return false;}
public V get(Object key) {return null;}
public Set<K> keySet() {return emptySet();}
public Collection<V> values() {return emptySet();}
public Set<Map.Entry<K,V>> entrySet() {return emptySet();}
public boolean equals(Object o) {
return (o instanceof Map) && ((Map<?,?>)o).isEmpty();
}
public int hashCode() {return 0;}
// Override default methods in Map
@Override
@SuppressWarnings("unchecked")
public V getOrDefault(Object k, V defaultValue) {
return defaultValue;
}
@Override
public void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
}
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Objects.requireNonNull(function);
}
@Override
public V putIfAbsent(K key, V value) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object key, Object value) {
throw new UnsupportedOperationException();
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
throw new UnsupportedOperationException();
}
@Override
public V replace(K key, V value) {
throw new UnsupportedOperationException();
}
@Override
public V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
throw new UnsupportedOperationException();
}
@Override
public V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
@Override
public V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
@Override
public V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
// Preserves singleton property
private Object readResolve() {
return EMPTY_MAP;
}
}
}
结束语
目前关于设计模式的面试点就到这里,该篇文章会持续更新~
附录
参考资料:
- 黑马Java八股文面试题视频教程:基础篇-56-单例模式_方式1_饿汉式_哔哩哔哩_bilibili