|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
但是我同意你的观点,对于大型项目来说,应该是采用框架的一部分,根据功能的不同而改进,欢迎你能再提出些宝贵意见,我会多多学习的。说到jbuilder,我可能是个人感觉,用的时候确实没有vs爽,我最喜欢的IDE是netbeans,谢谢。稳定对象具有很多能更便利地利用它们的特征,包含不严厉的同步需乞降不用思索数据讹误就可以自在地共享和高速缓存对象援用。只管稳定性大概一定关于一切类都成心义,但年夜多半程序中最少有一些类将受害于不成变。在本月的Java实际与理论中,BrianGoetz申明了稳定性的一些优点和机关稳定类的一些原则。请在附带的论坛中与作者和其他读者分享您关于本文的心得。(也能够单击文章顶部或底部的“会商”来会见论坛。)
稳定对象是指在实例化后其内部可见形态没法变动的对象。Java类库中的String、Integer和BigDecimal类就是稳定对象的示例―它们暗示在对象的性命期内没法变动的单个值。
稳定性的优点
假如准确利用稳定类,它们会极年夜地简化编程。由于它们只能处于一种形态,以是只需准确机关了它们,就决不会堕入纷歧致的形态。您不用复制或克隆稳定对象,就可以自在地共享和高速缓存对它们的援用;您能够高速缓存它们的字段或其办法的了局,而不必忧虑值会不会酿成生效的或与对象的别的形态纷歧致。稳定类一般发生最好的映照键。并且,它们原本就是线程平安的,以是不用在线程间同步对它们的会见。
自在高速缓存
由于稳定对象的值没有变动的伤害,以是能够自在地高速缓存对它们的援用,并且能够一定今后的援用仍将援用统一个值。一样地,由于它们的特征没法变动,以是您能够高速缓存它们的字段和其办法的了局。
假如对象是可变的,就必需在存储对其的援用时引发注重。请思索清单1中的代码,个中分列了两个由调剂程序实行的义务。目标是:如今启动第一个义务,而在某一天启动第二个义务。
清单1.可变的Date对象的潜伏成绩Dated=newDate();
Scheduler.scheduleTask(task1,d);
d.setTime(d.getTime()+ONE_DAY);
scheduler.scheduleTask(task2,d);
由于Date是可变的,以是scheduleTask办法必需当心地用提防措施将日期参数复制(大概经由过程clone())到它的外部数据布局中。否则,task1和task2大概都在今天实行,这可不是所希冀的。更糟的是,义务调剂程序所用的外部数据布局会酿成讹误。在编写象scheduleTask()如许的办法时,极为简单健忘用提防措施复制日期参数。假如健忘如许做,您就打造了一个难以捕获的毛病,这个毛病不会即刻展现出来,并且当它表露时人们要花较长的工夫才会捕获到。稳定的Date类不成能产生这类毛病。
固有的线程平安
年夜多半的线程平安成绩产生在当多个线程正在试图并发地修正一个对象的形态(写-写抵触)时,或当一个线程正试图会见一个对象的形态,而另外一个线程正在修正它(读-写抵触)时。要避免如许的抵触,必需同步对共享对象的会见,以便在对象处于纷歧致形态时别的线程不克不及会见它们。准确地做到这一点会很难,必要大批文档来确保准确地扩大程序,还大概对功能发生倒霉成果。只需准确机关了稳定对象(这意味着不让对象援用从机关函数直达义),就使它们免去了同步会见的请求,由于没法变动它们的形态,从而就不成能存在写-写抵触或读-写抵触。
不必同步就可以自在地在线程间共享对稳定对象的援用,能够极年夜地简化编写并发程序的历程,并削减程序大概存在的潜伏并发毛病的数目。
在歹意运转的代码眼前是平安的
把对象看成参数的办法不该变动那些对象的形态,除非文档明白申明能够如许做,大概实践上这些办法具有该对象的一切权。当我们将一个对象传送给一般办法时,一般不但愿对象前往时已被变动。可是,利用可变对象时,完整会是如许的。假如将java.awt.Point传送给诸如Component.setLocation()的办法,基本不会制止setLocation修正我们传进的Point的地位,也不会制止setLocation存储对该点的援用并稍后在另外一个办法中变动它。(固然,Component不如许做,由于它不冒失,可是并非一切类都那末客套。)如今,Point的形态已在我们不晓得的情形下变动了,其了局具有潜伏伤害―当点实践上在另外一个地位时,我们仍以为它在本来的地位。但是,假如Point是稳定的,那末这类歹意的代码就不克不及以云云使人凌乱而伤害的办法修正我们的程序形态了。
优秀的键
稳定对象发生最好的HashMap或HashSet键。有些可变对象依据其形态会变动它们的hashCode()值(如清单2中的StringHolder示例类)。假如利用这类可变对象作为HashSet键,然后对象变动了其形态,那末就会对HashSet完成引发凌乱―假如列举汇合,该对象仍将呈现,但假如用contains()查询汇合,它便可能不呈现。无需多说,这会引发某些凌乱的举动。申明这一情形的清单2中的代码将打印“false”、“1”和“moo”。
清单2.可变StringHolder类,不合适用作键publicclassStringHolder{
privateStringstring;
publicStringHolder(Strings){
this.string=s;
}
publicStringgetString(){
returnstring;
}
publicvoidsetString(Stringstring){
this.string=string;
}
publicbooleanequals(Objecto){
if(this==o)
returntrue;
elseif(o==null||!(oinstanceofStringHolder))
returnfalse;
else{
finalStringHolderother=(StringHolder)o;
if(string==null)
return(other.string==null);
else
returnstring.equals(other.string);
}
}
publicinthashCode(){
return(string!=null?string.hashCode():0);
}
publicStringtoString(){
returnstring;
}
...
StringHoldersh=newStringHolder("blert");
HashSeth=newHashSet();
h.add(sh);
sh.setString("moo");
System.out.println(h.contains(sh));
System.out.println(h.size());
System.out.println(h.iterator().next());
}
什么时候利用稳定类
稳定类最合适暗示笼统数据范例(如数字、列举范例或色彩)的值。Java类库中的基础数字类(如Integer、Long和Float)都是稳定的,别的尺度数字范例(如BigInteger和BigDecimal)也是稳定的。暗示单数或精度恣意的有理数的类将对照合适于稳定性。乃至包括很多团圆值的笼统范例(如向量或矩阵)也很合适完成为稳定类,这取决于您的使用程序。
Flyweight形式
稳定性启用了Flyweight形式,该形式使用共享使得用对象无效地暗示大批细颗粒度的对象变得简单。比方,您大概但愿用一个对象来暗示字处置文档中的每一个字符或图象中的每一个像素,但这一战略的稚嫩完成将会对内存利用和内存办理开支发生高得惊人的消费。Flyweight形式接纳工场办法来分派对稳定的细颗粒度对象的援用,并经由过程仅使一个对象实例与字母“a”对应来使用共享缩减对象数。有关Flyweight形式的更多信息,请参阅典范书本DesignPatterns(Gamma等著;请参阅参考材料)。
Java类库中稳定性的另外一个不错的示例是java.awt.Color。在某些色彩暗示法(如RGB、HSB或CMYK)中,色彩一般暗示为一组有序的数字值,但把一种色彩看成色彩空间中的一个特异值,而不是一组有序的自力可寻址的值更成心义,因而将Color作为稳定类完成是有事理的。
假如要暗示的对象是多个基础值的容器(如:点、向量、矩阵或RGB色彩),是用可变对象仍是用稳定对象暗示?谜底是……要看情形而定。要怎样利用它们?它们次要用来暗示多维值(如像素的色彩),仍是仅仅用作别的对象的一组相干特征汇合(如窗口的高度和宽度)的容器?这些特征多久变动一次?假如变动它们,那末各个组件值在使用程序中是不是有其本人的寄义呢?
事务是另外一个合适用稳定类完成的好示例。事务的性命期较短,并且经常会在创立它们的线程之外的线程中损耗,以是使它们成为稳定的是利年夜于弊。年夜多半AWT事务类都没有作为严厉的稳定类来完成,而是能够有小小的修正。一样地,在利用必定情势的动静传送以在组件间通讯的体系中,使动静对象成为稳定的也许是明智的。
编写稳定类的原则
编写稳定类很简单。假如以下几点都为真,那末类就是稳定的:
它的一切字段都是final
该类声明为final
不同意this援用在机关时代本义
任何包括对可变对象(如数组、汇合或相似Date的可变类)援用的字段:
是公有的
从不被前往,也不以别的体例公然给挪用程序
是对它们所援用对象的独一援用
机关后不会变动被援用对象的形态
最初一组请求仿佛挺庞大的,但其基础上意味着假如要存储对数组或别的可变对象的援用,就必需确保您的类对该可变对象具有独有会见权(由于否则的话,别的类可以变动其形态),并且在机关后您不修正其形态。为同意稳定对象存储对数组的援用,这类庞大性是需要的,由于Java言语没有举措强迫不合错误final数组的元素举行修正。注:假如从传送给机关函数的参数中初始化数组援用或别的可变字段,您必需用提防措施将挪用程序供应的参数或您没法确保具有独有会见权的别的信息复制到数组。不然,挪用程序会在挪用机关函数以后,修正数组的形态。清单3显现了编写一个存储挪用程序供应的数组的稳定对象的机关函数的准确办法(和毛病办法)。
清单3.对稳定对象编码的准确和毛病办法classImmutableArrayHolder{
privatefinalint[]theArray;
//Rightwaytowriteaconstructor--copythearray
publicImmutableArrayHolder(int[]anArray){
this.theArray=(int[])anArray.clone();
}
//Wrongwaytowriteaconstructor--copythereference
//Thecallercouldchangethearrayafterthecalltotheconstructor
publicImmutableArrayHolder(int[]anArray){
this.theArray=anArray;
}
//Rightwaytowriteanaccessor--dontexposethearrayreference
publicintgetArrayLength(){returntheArray.length}
publicintgetArray(intn){returntheArray[n];}
//Rightwaytowriteanaccessor--useclone()
publicint[]getArray(){return(int[])theArray.clone();}
//Wrongwaytowriteanaccessor--exposethearrayreference
//Acallercouldgetthearrayreferenceandthenchangethecontents
publicint[]getArray(){returntheArray}
}
经由过程一些别的事情,能够编写利用一些非final字段的稳定类(比方,String的尺度完成利用hashCode值的惰性盘算),如许大概比严厉的final类实行得更好。假如类暗示笼统范例(如数字范例或色彩)的值,那末您还会想完成hashCode()和equals()办法,如许对象将作为HashMap或HashSet中的一个键事情优秀。要坚持线程平安,不同意this援用从机关函数直达义是很主要的。
偶然变动的数据
有些数据项在程序性命期中一向坚持常量,而有些会频仍变动。常量数据明显切合稳定性,而形态庞大且频仍变动的对象一般不合适用稳定类来完成。那末偶然会变动,但变动又不太频仍的数据呢?有甚么办法能让偶然变动的数据取得稳定性的便当和线程平安的优点呢?
util.concurrent包中的CopyOnWriteArrayList类是怎样既使用稳定性的才能,又仍同意偶然修正的一个优秀示例。它最合适于撑持事务监听程序的类(如用户界面组件)利用。固然事务监听程序的列表能够变动,但一般它变动的频仍性要比事务的天生少很多。
除在修正列表时,CopyOnWriteArrayList其实不变动基础数组,而是创立新数组且放弃旧数组以外,它的举动与ArrayList类十分类似。这意味着当挪用程序取得迭代器(迭代器在外部保留对基础数组的援用)时,迭代器援用的数组实践上是稳定的,从而能够无需同步或冒并发修正的风险举行遍历。这打消了在遍历前克隆列表或在遍历时代对列表举行同步的必要,这两个操纵都很贫苦、易于堕落,并且完整使功能好转。假如遍历比拔出或撤除加倍频仍(这在某些情形下是常有的事),CopyOnWriteArrayList会供应更佳的功能和更便利的会见。
停止语
利用稳定对象比利用可变对象要简单很多。它们只能处于一种形态,以是一直是分歧的,它们原本就是线程平安的,能够被自在地共享。利用稳定对象能够完全打消很多简单产生但难以检测的编程毛病,如没法在线程间同步会见或在存储对数组或对象的援用前没法克隆该数组或对象。在编写类时,问问本人这个类是不是能够作为稳定类无效地完成,老是值得的。您大概会对回覆经常是一定的而感应受惊。
还得说上一点,就java本质而言,是面相对象的,但是你有没有发现,java也不全是,比如说基本类型,int,那他就是整型而不是对象,转换类型是还得借助包装类。 |
|