
整个梳理分为 5 个阶段。第一阶段最原始的代码为什么会有问题假设我们现在写代码。在面向对象里class类是图纸new是造房子。我们一开始写了一个普通的类// 普通类publicclassUserSession{publicStringusername;}现在有两个程序员小王写【登录页面】。小李写【个人中心页面】。小王的代码publicclassLoginPage{publicvoidlogin(){UserSessionsessionAnewUserSession();// 造了一栋房子AsessionA.username张三;// 在房子A里放了张三}}小李的代码publicclassProfilePage{publicvoidshow(){UserSessionsessionBnewUserSession();// 造了一栋房子BSystem.out.println(sessionB.username);// 去房子B里找名字}}问题爆发小李打印出来的结果是null。为什么因为小王造的是房子A小李造的是房子B。它们是在内存里两块完全不同的区域。小王在房子A里写了“张三”小李去房子B里找当然找不到。小李能“直接调用”小王的代码吗不能。因为sessionA是login()方法里面的局部变量。方法一执行完sessionA就被内存销毁了。小李在另一个类里根本看不见小王方法里的东西。第二阶段怎么解决单例模式登场为了解决“小王存的数据小李拿不到”的问题我们要搞一个全局唯一的公共容器。无论小王还是小李都必须往这个公共容器里存/取数据。这就是单例模式。我们给UserSession加上三把锁publicclassUserSession{// 第一把锁静态变量用来存唯一的实例全局唯一privatestaticUserSessioninstance;// 第二把锁私有构造函数。外部代码不能自己 new必须走下面那个方法privateUserSession(){}// 第三把锁公开的静态方法作为全局唯一入口publicstaticUserSessiongetInstance(){if(instancenull){instancenewUserSession();// 只有第一次调用时才会造一次}returninstance;// 以后所有人来都拿同一个}publicStringusername;}此时小王和小李的代码变成这样小王登录UserSessionsessionAUserSession.getInstance();// 去公共容器拿拿到了唯一的那一个sessionA.username张三;// 在里面写张三小李个人中心UserSessionsessionBUserSession.getInstance();// 去公共容器拿还是那唯一的一个System.out.println(sessionB.username);// 成功打印出张三为什么这次行了因为sessionA和sessionB在内存里指向的是同一个对象你可以理解为房子A和房子B是同一栋房子。为什么单例要“防止内部代码乱创建”如果你不把构造函数设为private小王可能忘了用getInstance()习惯性地写new UserSession()那他又造了一栋新房子数据又不互通了。单例模式用语法强制**所有人必须用getInstance()不许自己new。**这就是“防止内部代码乱创建保证全局状态一致”。第三阶段多用户怎么办单例是“容器”不是“人”你之前问“多用户的话单例存不下怎么办”这是一个很好的问题。注意单例本身是一个全局唯一的容器但容器里可以装很多东西。如果是多用户系统比如淘宝我们不能把张三和李四的数据直接放在UserSession的属性里会互相覆盖。我们要把UserSession变成普通的类多例然后用一个单例的 Manager来管理它们。// 单例的管理器publicclassSessionManager{privatestaticSessionManagerinstancenewSessionManager();privateSessionManager(){}publicstaticSessionManagergetInstance(){returninstance;}// 容器内部用一个 Map 存无数个普通的 UserSession 对象privateMapString,UserSessionmapnewHashMap();publicvoidadd(Stringtoken,UserSessionsession){map.put(token,session);}publicUserSessionget(Stringtoken){returnmap.get(token);}}张三登录new UserSession(张三)多例然后SessionManager.getInstance().add(token_1, 张三对象)。李四登录new UserSession(李四)多例然后SessionManager.getInstance().add(token_2, 李四对象)。单例SessionManager只有一个但它管理着成千上万个多例UserSession。第四阶段完整梳理你的登录场景现在我们把数据库你的 SQLite和单例串起来用户输入在登录页面输入账号密码。查数据库系统去 SQLite 里查验证账号密码对不对。验证成功假设查到了张三。注入数据系统调用单例把张三的名字存进去。UserSession.getInstance().setUsername(张三);如果是单机单用户或者SessionManager.getInstance().add(token, 张三对象);如果是多用户全局共享用户跳转到“个人中心”页面这个页面调用UserSession.getInstance().getUsername()拿到“张三”并显示。退出登录关键用户点击退出系统必须把单例里的数据清空UserSession.getInstance().setUsername(null);。否则下一个人登录没登录就能看到张三的数据。第五阶段一句话总结普通类每次new都造个新房子数据不互通。单例模式用private构造函数堵死new只留一个getInstance()接口。保证全局只有一栋房子所有人共用数据互通。登录中的作用数据库负责“验证身份”单例负责“在内存里记住当前是谁”。登录成功后把数据注入单例全系统任何界面都能去单例里读到这个人退出时清空。现在从头看这一遍是不是感觉逻辑顺畅了如果还有哪个点觉得跳跃你指出来我们就盯住那一点死磕