??xml version="1.0" encoding="utf-8" standalone="yes"?>
接下来,我用与log4j相对应的JUnit来测试单例类Q它会诏I在q篇文章余下的部分。如果你对JUnit或log4j不很熟悉Q请参考相兌源?BR>
?是一个用JUnit试?的单例模式的案例Q?BR>?.一个单例模式的案例
?两次调用ClassicSingleton.getInstance()Qƈ且把q回的引用存储在成员变量中。方法testUnique()会检查这些引用看它们是否相同。例3是这个测试案例的输出Q?BR>?.是这个测试案例的输出
正如前面的清单所C,?的简单测试顺利通过----通过ClassicSingleton.getInstance()获得的两个单例类的引用确实相?然而,你要知道q些引用是在单线E中得到的。下面的部分着重于用多U程试单例cR?BR>
多线E因素的考虑
在例1中的ClassicSingleton.getInstance()Ҏ׃下面的代码而不是线E安全的Q?BR>
如果一个线E在W二行的赋D句发生之前切换,那么成员变量instance仍然是nullQ然后另一个线E可能接下来q入到if块中。在q种情况下,两个不同的单例类实例p创徏。不q的是这U假定很发生,q样q种假定也很隑֜试期间出现Q译注:在这可能是作者对很少出现q种情况而导致无法测试从而Z放松警惕而感到叹惜)。ؓ了演C个线E轮换,我得重新实现?中的那个cR例4是修订后的单例c:
?.Zؓ安排的方?/B>
除了在这个清单中的单例类强制使用了一个多U程错误处理Q例4cM于例1中的单例cR在getInstance()ҎW一ơ被调用Ӟ调用q个Ҏ的线E会休眠50毫秒以便另外的线E也有时间调用getInstance()q创Z个新的单例类实例。当休眠的线E觉醒时Q它也会创徏一个新的单例类实例Q这h们就有两个单例类实例。尽例4是h为如此的Q但它却模拟了第一个线E调用了getInstance()q在没有完成时被切换的真实情形?BR>?试了例4的单例类Q?BR>?.p|的测?/B>
?的测试案例创Z个线E,然后各自启动Q等待完成。这个案例保持了一个对单例cȝ静态引用,每个U程都会调用Singleton.getInstance()。如果这个静态成员变量没有被讄Q那么第一个线E就会将它设为通过调用getInstance()而得到的引用Q然后这个静态变量会与一个局部变量比较是否相{?BR>在这个测试案例运行时会发生一pd的事情:W一个线E调用getInstance()Q进入if块,然后休眠;接着Q第二个U程也调用getInstance()q且创徏了一个单例类的实例。第二个U程会设|这个静态成员变量ؓ它所创徏的引用。第二个U程查这个静态成员变量与一个局部备份的相等性。然后测试通过。当W一个线E觉醒时Q它也会创徏一个单例类的实例,q且它不会设|那个静态成员变量(因ؓW二个线E已l设|过了)Q所以那个静态变量与那个局部变量脱d步,相等性测试即告失败。例6列出了例5的输出:
?.?的输?/B>
到现在ؓ止我们已l知道例4不是U程安全的,那就让我们看看如何修正它?BR>
同步
要?的单例类为线E安全的很容?---只要像下面一个同步化getInstance()ҎQ?BR>
在同步化getInstance()Ҏ后,我们可以得C5的测试案例返回的下面的结果:
q此Q这个测试案例工作正常,q且多线E的烦恼也被解决;然而,机敏的读者可能会认识到getInstance()Ҏ只需要在W一ơ被调用时同步。因为同步的性能开销很昂贵(同步Ҏ比非同步Ҏ能降低到100ơ左叻IQ或许我们可以引入一U性能改进ҎQ它只同步单例类的getInstance()Ҏ中的赋D句?BR>
一U性能改进的方?/H3>
L一U性能改进ҎӞ你可能会选择像下面这样重写getInstance()ҎQ?BR>
q个代码片段只同步了关键的代码,而不是同步整个方法。然而这D代码却不是U程安全的。考虑一下下面的假定Q线E?q入同步块,q且在它lsingleton成员变量赋g前线E?被切换。接着另一个线E进入if块。第二个U程等待直到第一个线E完成,q且仍然会得C个不同的单例cd例。有修复q个问题的方法吗Q请M厅R?BR>
双重加锁?/H3>
初看上去Q双重加锁检查似乎是一U懒汉式实例化为线E安全的技术。下面的代码片段展示了这U技术:
如果两个U程同时讉KgetInstance()Ҏ会发生什么?惛_一下线E?q行同步块马上又被切换。接着Q第二个U程q入if 块。当U程1退出同步块ӞU程2会重新检查看是否singleton实例仍然为null。因为线E?讄了singleton成员变量Q所以线E?的第二次查会p|Q第二个单例cd例也׃会被创徏。似乎就是如此?BR>不幸的是Q双重加锁检查不会保证正常工作,因ؓ~译器会在Singleton的构造方法被调用之前随意lsingleton赋一个倹{如果在singleton引用被赋g后而被初始化之前线E?被切换,U程2׃被返回一个对未初始化的单例类实例的引用?BR>
一个改q的U程安全的单例模式实?/H3>
?列出了一个简单、快速而又是线E安全的单例模式实现Q?BR>?.一个简单的单例c?/B>
q段代码是线E安全的是因为静态成员变量一定会在类被第一ơ访问时被创建。你得到了一个自动用了懒汉式实例化的线E安全的实现;你应该这样用它Q?BR>
当然万事q不完美Q前面的Singleton只是一个折LҎ;如果你用那个实玎ͼ你就无法改变它以便后来你可能惌允许多个单例cȝ实例。用一U更折哀的单例模式实?通过一个getInstance()Ҏ获得实例)你可以改变这个方法以便返回一个唯一的实例或者是数百个实例中的一个.你不能用一个公开且是静态的Qpublic static)成员变量q样做.
你可以安全的使用例7的单例模式实现或者是例1的带一个同步的getInstance()Ҏ的实玎ͼ然而,我们必须要研I另一个问题:你必d~译期指定这个单例类Q这样就不是很灵z.一个单例类的注册表会让我们在运行期指定一个单例类Q?BR>
]]>
单例模式是最单的设计模式之一Q但是对于Java的开发者来_它却有很多缺陗在本月的专栏中QDavid Geary探讨了单例模式以及在面对多线E(multithreading)、类装蝲器(classloadersQ和序列?serialization)时如何处理这些缺陗?/U>
单例模式适合于一个类只有一个实例的情况Q比如窗口管理器Q打印缓冲池和文件系l,它们都是原型的例子。典型的情况是,那些对象的类型被遍及一个Y件系l的不同对象讉KQ因此需要一个全局的访问指针,q便是众所周知的单例模式的应用。当然这只有在你信你不再需要Q何多于一个的实例的情况下?BR>单例模式的用意在于前一D中所兛_的。通过单例模式你可以:
管单例设计模式如在下面的图中的所昄的一h最单的设计模式Q但对于_心的Java开发者来说却呈现多缺陗这文章讨Z单例模式q揭CZ那些~陷?BR>注意Q你可以?A class=l2 target=_blank>Resources下蝲q篇文章的源代码?BR>
单例模式
在《设计模式》一书中Q作者这h叙述单例模式的:保一个类只有一个实例ƈ提供一个对它的全局讉K指针?BR>下图说明了单例模式的cd?BR>Q图1Q?BR>
单例模式的类?/B>
正如你在上图中所看到的,q不是单例模式的完整部分。此图中单例cM持了一个对唯一的单例实例的静态引用,q且会从静态getInstance()Ҏ中返回对那个实例的引用?BR>?昄了一个经典的单例模式的实现?BR>?.l典的单例模?BR>
在例1中的单例模式的实现很Ҏ理解。ClassicSingletoncM持了一个对单独的单例实例的静态引用,q且从静态方法getInstance()中返回那个引用?BR>关于ClassicSingletonc,有几个让我们感兴的地方。首先,ClassicSingleton使用了一个众所周知的懒汉式实例化去创徏那个单例cȝ引用;l果Q这个单例类的实例直到getInstance()Ҏ被第一ơ调用时才被创徏。这U技巧可以确保单例类的实例只有在需要时才被建立出来。其ơ,注意ClassicSingleton实现了一个protected的构造方法,q样客户端不能直接实例化一个ClassicSingletoncȝ实例。然而,你会惊奇的发C面的代码完全合法Q?BR>
前面q个代码片段Z能在没有l承ClassicSingletonq且ClassicSingletoncȝ构造方法是protected的情况下创徏其实例?{案是protected的构造方法可以被其子cM及在同一个包中的其它c调用。因为ClassicSingleton和SingletonInstantiator位于相同的包Q缺省的包)Q所以SingletonInstantiatorҎ能创建ClasicSingleton的实例?BR>q种情况下有两种解决ҎQ一是你可以使ClassicSingleton的构造方法变化私有的QprivateQ这样只有ClassicSingleton的方法能调用?然而这也意味着ClassicSingleton不能有子cR有时这是一U很合意的解x法,如果实如此Q那声明你的单例cMؓfinal是一个好LQ这h图明,q且让编译器M用一些性能优化选项。另一U解x法是把你的单例类攑ֈ一个外在的包中Q以便在其它包中的类Q包括缺省的包)无法实例化一个单例类?BR>关于ClassicSingleton的第三点感兴的地方是,如果单例׃同的c装载器装入Q那便有可能存在多个单例cȝ实例。假定不是远端存取,例如一些servlet容器Ҏ个servlet使用完全不同的类装蝲器,q样的话如果有两个servlet讉K一个单例类Q它们就都会有各自的实例?BR>W四点,如果ClasicSingleton实现了java.io.Serializable接口Q那么这个类的实例就可能被序列化和复原。不怎样Q如果你序列化一个单例类的对象,接下来复原多个那个对象,那你׃有多个单例类的实例?BR>最后也许是最重要的一点,是?中的ClassicSingletoncM是线E安全的。如果两个线E,我们U它们ؓU程1和线E?Q在同一旉调用ClassicSingleton.getInstance()ҎQ如果线E?先进入if块,然后U程2q行控制Q那么就会有ClassicSingleton的两个的实例被创建?BR>
正如你从前面的讨Z所看到的,管单例模式是最单的设计模式之一Q在Java中实现它也是决非惌的那么简单。这文章接下来会揭CJava规范对单例模式进行的考虑Q但是首先让我们q水楼台的看看你如何才能试你的单例cR?BR>
未完待箋?
]]>
FACTORY?人才市场Q以往是要哪个人才Q就扑֓个h才,效率低,现在有了人才市场Q我们只需
直接Mh才市场挑一个好了;
BUILDER?生水U:以前是手工业作坊式的人工单个单个的生产零件然后一步一步组装做Q好
比有了工业革命,现在都由生水U代替了。如要造丰田汽车,先制定汽车的构造如pR胎、方
向盘、发动机l成。再以此构造标准生产丰田汽车的车胎、方向盘、发动机。然后进行组装。最?BR>得到丰田汽RQ?BR>
PROTOTYPE?印刷术的发明Q以前只能贴才能保持和别h的字q基本相同,直从印刷技术发明,
从而保证了复制得和原物一模一P
SINGLETON?唯一Q以前是商标满天飞,相同的商标难免造成侉|Q直从有商标保护法后Q就保证
了不会再产生W家企业使用相同的商标;
l构型模?BR>
ADAPTER?集众ZU,成一׃公:武当zּ三丰会太极拳Q少林派智空大师会金刚般若掌Q如
果他们两个都成ؓ我的师傅Q我既会太极拳Q又会金刚般若掌了;
DECORATOR?青出于蓝而胜于蓝Q武当派张三C太极拻I是我师傅Q他教会了我太极拻I但我?BR>p会点蒙古式摔交,张三丰却不会。于是我成了DECORATOR模式的实玎ͼ
BRIDGE?白马非马Q马之颜色有黑白Q马之性别有公母。我们说”这是马”太抽象Q说”这是黑?BR>的公马”又太死板,只有颜色与性别和马动态组合,”这是(黑色的或白色的)Q公或母Q马?BR>才显得灵z而飘逸,如此bridge模式_N得矣?BR>
COMPOSITE?大家族:子又生孙Q孙又生子,子子孙孙Q无I尽也,众多纷杂的人口l织成一?BR>按辈分排列的大家族即是此模式的实玎ͼ
FACADE?求同存异Q高中毕业需d中和高中Q博士也需d中和高中Q因此国家将初中和高中普
及成九年制义务教Ԍ
FLYWEIGHT?一x逸:认识三千汉字Q可以应付日常读书与写字Q可见头脑中存在q个汉字库的
重要Q?BR>
PROXY?垂帘听政Q犹如清朝康熙年间的四大府臣Q很多权利不在皇帝手里,必须通过辅佐大臣?BR>办;
行ؓ模式
CHAIN OF RESPONSIBLEITY?U房Q以前ؓ了找房到处打听,效率低且找不到好的房源。现在有?BR>房屋中介Q于是向房屋中介提出U房hQ中介提供一个合适的房源Q满意则不再hQ不满意
l箋看房Q直到满意ؓ止;
COMMAND?借刀杀人:以前是想杀谁就杀Q但一D|间后领悟刎ͼ长此以往必将l仇太多Q于是假
手他人,挑拨他h之间的关pM而达到自q目的Q?BR>
INTERPRETER?文言文注释:一D|a文,它译成白话文Q?BR>
ITERATOR?赶尽杀l:一个一个的搜烦Q绝不放掉一个;
MEDIATOR?三角债:本来千头万A的债务关系Q忽出来一中介Q包揽其一切,于是三角关系变成?BR>独立的三ҎW四方中介的关系Q?BR>
MEMENTO?有福同nQ我有多,你就有多;
OBSERVER?看守者:一旦被看守者有什么异常情况,定会及时做出反应Q?BR>
STATE?q出自由Q如一扇门Q能q能出,如果有很多h随时q进出出必定昑־杂ؕ而安全,如今
设一保安限制其进出,如此各hq出才显得规范;
STRATEGY?久病成良医:如h生病可以有各U症Ӟ但经q长期摸索,可以ȝ出感冒、肺病?BR>肝炎{几U;
TEMPLATE METHOD??理论不一定要实践Q教l的学生会游泛_行了Q至于教l会不会则无关紧要;
VISITOR?依法ȝQ因张三杀被处死,李四L要被|款。由此势必制定处|制度,故制?BR>法律写明杀人、放火、偷H等|要受什么处|,l通过后须变动要小。今后有人犯|不是谁,?BR>共条例处|即是,q就是访问者模式诞生的全过E;
]]>
设计模式和设计原则是寚w向对象程序设计的一个具体应用,为此我们首先需要回֒ȝ一下面向对象程序设计的一些基本内容,同时也是Z引出设计模式和设计原则?/SPAN>
1、子cȝ承所有父cȝ属性和Ҏ吗?什么是隐藏Q什么是覆盖Q?/STRONG>
解释一Q?/SPAN>子类是承了父类的所有属性和ҎQ但是ƈ不一定都可见Q可认ؓ是间接可见?/SPAN>所谓承就是指你有的我全有Q即父类有的子类全都有?/SPAN>
下面一个问题给Z子类是否可见的一个标准?/SPAN>
作用?/SPAN>public,private,protected,以及不写时的区别Q?/SPAN>不写旉认ؓfriendlyQ?/SPAN>
{:区别如下Q?/SPAN>
作用?/SPAN> |
当前c?/SPAN> |
同一package |
子孙c?/SPAN> |
其他package |
Public |
?/SPAN> |
?/SPAN> |
?/SPAN> |
?/SPAN> |
Protected |
?/SPAN> |
?/SPAN> |
?/SPAN> |
× |
Friendly |
?/SPAN> |
?/SPAN> |
× |
× |
Private |
?/SPAN> |
× |
× |
× |
如下囄出父cd子类的承关p,l色部分为父c(其中包括两部分,卛_子类不可见①和对子类可见②两部分Q,l色加上U色部分③(由子cL生的卛_cȝ有的Ҏ)为子cR说明:①虽然对于子cM可见部分但却是子cd不可的部分Q因为③可能会调用②Q而②又可能会调用①,如果子类中没有①Q那么程序就会出错?/SPAN>
解释二:关于隐藏是指子类与父cd?/SPAN>同名但是有不同解?/SPAN>的属性和ҎQ那么父cMq些属性和Ҏ会被隐藏。D例,在父cM定义一个属性变?/SPAN>iQ同时在子类中也定义一个属性变?/SPAN>iQ此时父cM的属性变?/SPAN>i被隐藏或者说屏蔽Q程序调用属性变?/SPAN>i时则默认是对子类的属性变?/SPAN>i的调用,同样Ҏ也是如此?/SPAN>
解释三:关于覆盖是指指子cM父类存在相同头部的方法,那么父类中这些的Ҏ会被覆盖或者说屏蔽。如果程序中需要调用父cM被覆盖的Ҏ则?/SPAN>super关键字?/SPAN>
2、对象代词的意义Q?/SPAN>super?/SPAN>this
superQ针寚w藏和覆盖来说Q大多数情况下我们都是指当前的子c,当我们需要特?/SPAN>
指明讉K父类的时候,则?/SPAN>super.[属?/SPAN>][Ҏ]
thisQ既焉认情冉|指当前的子类也就是说this.加不加都是一LQ那Z么还要用thisq个词呢Q这是因?/SPAN>thisq不是针?/SPAN>super产生的,而是因ؓ我们在程序中的很多的Ҏ需要返回当前对象,q时?/SPAN>this有重要的作用。因为在很多对象的内部我们是不能直接提到该对象的名称的,E序是不识别的。比如张三说他自己时用“我”而非“张三”,否则q是不符合常规的?/SPAN>
3、重载和覆盖的关p?
相同点:两者都是对一个方法的多次实现Q或者都体现了多态性(一个内Ҏ多种形式Q?/SPAN>
区别Q覆盖是指方法头相同时则产生覆盖。如父类和子cL两个Ҏ头完全相同的ҎQ此时父cȝq个Ҏ被覆盖?/SPAN>
重蝲是指Ҏ名相同但是参C同,注意重蝲不区分父cd子类Q即q两个方法即可以都在父类中也可以都在子类中或者一个在父类中一个在子类中。这样当E序调用Ҏ时是Ҏ其中的参敎ͼ个数、类型)q行匚w的?/SPAN>
4、接口的作用Q?/STRONG>
接口主要是ؓ了解军_重承中l承冗余的问题。所谓承冗余是指如?/SPAN>Bl承AQ?/SPAN>C也?/SPAN>AQ?/SPAN>Dl承B同时也?/SPAN>CQ那?/SPAN>D中重复出CA的内容(既有来自B的也有来?/SPAN>C的)Q这样就产生的承冗余的问题。接口实际上相当于一个抽象类Q但是它和类的最大区别就是它允许实现多个。通过cdC码复用叫做?/SPAN>extendsQ通过接口实现代码复用叫做实现implements。可以说接口是变相的实现了多重承同时又避免的承冗余,所以对于面向对象的E序设计来说是不可或~的。关键字interface l承extends 实现implements
5、异常处理的意义Q?/STRONG>
是ؓ了摒弃错误?/SPAN>try监视可能出错的代码,catch处理机制通过异常对象的类型匹配来选择通过哪种机制处理q个错误。意义所在:(1)、解决了E序错误的问题,可以人工q预、提C或者中止程序?2)、引入了层次的问题,没有异常处理机制时程序遇到错误采用的办法是以return形式层层传递来处理Q?/SPAN>