承接上文!
【java】关于java单例的思考(上) :https://blog.youkuaiyun.com/lsr40/article/details/94195394
-3.静态内部类模式
该模式也是解决一开始就new对象占用内存的问题,将new对象放到静态内部类中,这样只有实际调用到该静态内部类的时候,才会构造出iceCreamFactory对象,并且也没有线程不安全的问题(由于自始至终类只会加载一次,所以即使在多线程的情况下,也能够保持单例的性质。)
public class IceCreamFactory {
private IceCreamFactory(){}
public static IceCreamFactory getInstance() {
return IceCreamFactoryHolder.iceCreamFactory;
}
private static class IceCreamFactoryHolder{
private static IceCreamFactory iceCreamFactory = new IceCreamFactory(count);
}
}
优点:代码量少,较为简单的实现了单例,并且没有线程不安全的问题
缺点:需要多加载一个类,并且静态内部类有着一个致命的缺点,就是传参的问题,由于是静态内部类的形式去创建单例的,故外部无法传递参数(传入构造该对象时候的一些上下文context)进去。所以,如果自己定义的类是单例时,还是要斟酌下使用哪种模式更为合理!
如下代码演示了尝试传参,并且失败的例子:
public class IceCreamFactory {
private IceCreamFactory(int count){
System.out.println("外部传入的count:"+count);
}
public static IceCreamFactory getInstance(int count) {
IceCreamFactoryHolder.count = count;
System.out.println("input.count:"+count);
System.out.println("IceCreamFactory.count:"+IceCreamFactoryHolder.count);
return IceCreamFactoryHolder.iceCreamFactory;
}
public void printAAFunction(){
System.out.println("aa");
};
private static class IceCreamFactoryHolder{
static int count;
private static IceCreamFactory iceCreamFactory = new IceCreamFactory(count);
}
}
//====================调用===================
public class test {
public static void main(String[] args) {
IceCreamFactory iceCreamFactory = IceCreamFactory.getInstance(100);
iceCreamFactory.printAAFunction();
}
}
//==========结果=========
//外部传入的count:0
//input.count:100
//IceCreamFactory.count:100
//aa
-4.单例注册表模式
前面说了3种模式,但是真正在做java应用的时候,很少有机会自己写单例。
一般都是spring帮你实现了这些东西(比如你的controller其实默认是单例的,当然也可以设置成不是单例,这里就不再多讲了,可以百度到相关的知识的:https://www.cnblogs.com/maohuidong/p/7837688.html(作者:毛会懂))
那么相信大家会有和我一样的疑惑,那spring实现单例又是什么模式呢?
对的,就是这个单例注册表模式!
大概讲下,其实就是直接new一个ConcurrentHashMap,然后当调用到了某个单例对象的时候,就去map里面寻找,有的话直接返回,没有就创建,并添加该对象到map中!(这也是一种很好的思路,当你的需要设置单例的类比较多的时候)
//在DefaultSingletonBeanRegistry类中,85行有着如下代码
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
//调用这个map的代码在AbstractBeanFactory类中的235行的doGetBean方法
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
//具体代码我就不贴出来了,大家有兴趣可以去研究
}
如果大家想看关于这方面的知识,可以自行百度或者观看如下网址:
https://www.cnblogs.com/zhaww/p/8467196.html?tdsourcetag=s_pcqq_aiomsg
https://www.codercto.com/a/4924.html
那,我也来写个例子:
public class SingleRegisterMap {
/** Cache of singleton objects: bean name --> bean instance */
private static final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
private volatile static SingleRegisterMap singleRegisterMap;
//获取SingleRegisterMap单例的方法,如果为调用该方法singleRegisterMap这个变量为null
public static SingleRegisterMap getInstance() {
if (singleRegisterMap == null) {
synchronized (IceCreamFactory.class) {
if (singleRegisterMap == null) {
singleRegisterMap = new SingleRegisterMap();
}
}
}
return singleRegisterMap;
}
private SingleRegisterMap(){
}
//获得Map中bean的方法
public static Object getBean(String name) {
//传入的name为空,直接返回空
if(name != null) {
synchronized (SingleRegisterMap.class) {
//name不为空,判断map中是否有该对象,有就返回,没有就创建再返回
if (singletonObjects.get(name) == null) {
try {
singletonObjects.put(name, Class.forName(name).newInstance());
return singletonObjects.get(name);
} catch (Exception e) {
e.printStackTrace();
}
} else {
return singletonObjects.get(name);
}
}
}
return null;
}
}
//======================测试==========================
//=================随便建一个Dog类=====================
public class Dog {
}
//===================================================
public class test {
public static void main(String[] args) {
Dog a = (Dog)SingleRegisterMap.getBean("com.Dog");
System.out.println( a instanceof Dog);
Dog b = (Dog)SingleRegisterMap.getBean("com.Dog");
System.out.println(a);
System.out.println(b);
}
}
//===============结果(两个dog对象是一样的)================
//true
//com.Dog@12bb4df8
//com.Dog@12bb4df8
优点:可以让getBean传入更多的参数,用于构建不同的Dog对象(可以传context构建单例),并且想拿同一个对象就拿同一个对象,那拿不同的对象就拿不同的对象,还是很方便的
缺点:代码多了那么一点点,并且getBean要考虑通用性~嘿嘿(我个人觉得这种模式还是很666的)