Effective Java之必要时进行保护性拷贝(三十九)
我們來(lái)看一個(gè)不可變對(duì)象的攻守問(wèn)題:
public class Period{private final Date startTime;private finale Date endTime;public Period(Date startTime , Date endTime){if(startTime.compareTo(endTime) > 0){ throw new IllegalArgumentException(“startTime after endTime !”); }this.startTime = startTime;this.endTime = endTime;}pubilc Date start(){ return this.startTime ; }public Date end(){ return this.endTime ; }}這個(gè)類貌似是一個(gè)不可變類 ,因?yàn)閟tartTime和endTime域都是final的,但是它并不是一個(gè)嚴(yán)格的不可變類,因?yàn)镈ate類并不是一個(gè)不可變類,所以Date實(shí)例指向內(nèi)存中的引用地址不可變,但是引用的內(nèi)容可以變,所以可以這樣來(lái)攻擊不可變類
Date startTime = new Date();Date endTime = new Date();Period per = new Period(startTime , endTime );endTime.setYear(78);這個(gè)時(shí)候我們對(duì)于構(gòu)造器進(jìn)行保護(hù)性拷貝
public Period(Date startTime , Date endTime ){this.startTime = new Date (startTime.getTime());this.endTime = new Date(endTime.getTime());if(this.startTime.compareTo(this.endTime) > 0){ throw new IllegalArgumentException(“startTime after endTime !”);}}也就是說(shuō)把這個(gè)可變的Date引用指向了一個(gè)拷貝,這樣endTime.setYear(78)永遠(yuǎn)也修改不了這個(gè)拷貝。
值得注意的是:保護(hù)性拷貝是在檢查參數(shù)有效性之前進(jìn)行的,而且針對(duì)的是拷貝對(duì)象。為什么呢?因?yàn)閾?dān)心并發(fā)情況下檢查有效性通過(guò)之后,另一個(gè)線程改變了可變對(duì)象使其有效性錯(cuò)誤,但是依然能進(jìn)行保護(hù)性拷貝的錯(cuò)誤情況。
同時(shí)我們沒(méi)有用Date的clone方法進(jìn)行保護(hù)性拷貝。為什么呢?
因?yàn)镈ate是非final的,不能保證clone方法返回的就是Date對(duì)象,它有可能返回一個(gè)專門(mén)出于惡意目的而設(shè)計(jì)的不可信子類的實(shí)例。
例如:上面例子中如果別有用心之人新建了一個(gè)Mydate繼承了Date
那么構(gòu)造方法中就可以傳入MyDate對(duì)象,那么調(diào)用的就是MyDate的clone方法,那么他就可以在clone方法上動(dòng)手腳,把實(shí)例的引入記錄起來(lái),供攻擊者訪問(wèn)。
還有一種攻擊方法:
Date start = new Date(); Date end = new Date(); Period p = new Period(start, end); p.end().setYear(78);原因是它的訪問(wèn)方法提供了對(duì)其可變內(nèi)部成員的訪問(wèn)能力。
解決方法是使它返回內(nèi)部域的保護(hù)性拷貝。
總結(jié):參數(shù)的保護(hù)性拷貝策略不僅僅是針對(duì)不可變類,如果類具有從客戶端得到或者返回到客戶端的可變組件,類必須保護(hù)性地拷貝這些組件。如果受到拷貝成本的約束,就應(yīng)該明確向客戶端指明。
避免保護(hù)性拷貝的方法是將對(duì)象組件設(shè)置為不可變組件。或者把客戶端和類放在同一個(gè)包中,不暴露出去。
總結(jié)
以上是生活随笔為你收集整理的Effective Java之必要时进行保护性拷贝(三十九)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Effective Java之检查参数的
- 下一篇: Effective Java之慎用重载(