在编程时,不管自己写代码或者看人家的代码,常遇到一种叫单例(Singleton)的设计模式,这种模式要解决的问题是在一个系统中提供一种全局的唯一的实例对象,听着很绕口的一句话,我用一张图来说明下:
就如上面这张图所示,我们来做个假设:
-
把宇宙比作软件系统;
-
把宇宙中的星球比作系统中的不同对象;
-
把人类比作想要调用/使用系统中的这些对象的调用方(使用者);
这时候,我们从用户角度来看,这里的地球这个对象就是一个全局的唯一的对象,不管是张三、李四还是王五来调用地球上的资源(水资源,煤矿资源等),他们都得调用地球这个对象,而不管是他们中的谁,每次拿到的都是同一个对象,不可能有谁可以获取一个独特的地球来享用其资源,所以地球这个对象在宇宙这个系统中对所有人都是共享的,开放的,全局的,唯一的,这就是单例使用的场景。
在实际软件开发的时候,单例这种模式常被用来构建一个全局唯一的共享实例,不妨先抛开设计模式这个诅咒,来单独思考下所谓的单例模式是到底长什么样子,通过上面图中宇宙、地球和人类的关系,我们现在要构建的就是地球这样一个对象,那问题就变成了:如何保证我们构建的地球对象是全局的、唯一的、共享的?
我们先创建一个地球这样的类,可以先忽略地球上所存在的资源等要素,这里会以Java作为举例,但是要说明的是,单例模式跟语言不相关:
/**
星球初成,寸草不生,不急,咱慢慢来
*/
public class Earth {
}
我们建了一个地球的类,这个类满足了全局、共享的特性,谁都可以来调用他,只要通过以下代码就能做到:
Earch earch = new Earch();
那么唯一性怎么保证呢,因为我们知道,如果有3个人调用上面👆这行代码,就会生成3个不同的对象,这样地球的唯一性就不存在了,所以我们要解决唯一性的问题,不得不谈到构造函数的概念,构造函数就是当new一个对象的时候,自动调用的一个初始化函数,虽然在建立Earth类的时候并没有去显示声明这个构造函数,但其实它隐式带了一个默认的构造函数,如下所示:
/**
星球初成,寸草不生,不急,咱慢慢来
*/
public class Earth {
// 大家好,我就是构造函数
public Earth(){
System.out.println("构造函数“);
}
}
所以当我们初始化对象:new Earth()的时候,构造函数就被打印出来了,来做一个小测试👇:
/**
星球初成,寸草不生,不急,咱慢慢来
*/
public class Earth {
// 大家好,我就是构造函数
public Earth(){
System.out.println("构造函数“);
}
}
public class SingletonTest{
public static void main(String[] args) {
new Earth();
}
}
结果👇我们可以看到,这一行被打印出来了,我们只是在其他类中仅仅new了一下:
接着,我们继续回到唯一性的问题上来,大胆猜想下,要做到唯一,就得有自主权,Earth这个类需要管控被谁创建的权利,不是人人都能创建的,所以必须要对对象创建的这个权利做一个收敛👇:
/**
星球初成,寸草不生,不急,咱慢慢来
*/
public class Earth {
// 我就是构造函数,但是谁都不给用
private Earth(){
System.out.println("构造函数“);
}
}
收敛的方式就是把构造函数前的public改成了private,这就是一个权限收敛,原本是公共访问,变成了私有访问,这时候我们再来new一下试试👇:
public class SingletonTest{
public static void main(String[] args) {
new Earth();
}
}
结果在编译的时候就已经报错了,因为无法访问Earth中的私有方法:
到这里我们的权限收敛策略已经基本实行成功,外部的类已经没法调用Earth了,只要Earth类自己能够创建自己,并且提供给外部访问,就达成对象唯一性的目的了;
public class Earth {
private static Earth earth = new Earth();
private Earth(){
System.out.println("构造函数");
}
public static Earth getInstance(){
return earth;
}
}
我们在地球这个类👆里面自己new了一下,生成了一个对象,并且提供了一个公共访问的方法getInstance来给外界调用,这样就达成了全局、共享、唯一的原则了,最后我们用多线程的方式来模拟多个用户,测试一下👇是否所有用户都拿到了同一个对象。
public class SingletonTest{
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
//通过打印对象的地址,来看是否大家都获取到了同一个对象
System.out.println(Earth.getInstance());
}
};
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
Thread thread3 = new Thread(runnable);
Thread thread4 = new Thread(runnable);
Thread thread5 = new Thread(runnable);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
}
}
结果是符合预期的,这样,我们就构建成了一个最基本最简单的单例,后面我们再继续讨论在地球这个类上再干点其他有趣的事🤔。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)