既然Java反射可以訪問和修改私有成員變數,那封裝成private還有意義麼?

weixin_33860722發表於2018-11-05

簡單來說,private並不是解決“安全”問題的。

安全是指不讓程式碼被非法看到/訪問。但是隻要人能拿到程式碼,總會有辦法去檢視和改變程式碼。其他答案提到反射可以用SecurityManager來防止private被訪問。但是從更高一層的角度,即便使用了SecurityManager,還是可以通過各種方式拿到java的bytecode,並做任意修改。比如有asm這樣的lib,也有instrument api這種東西可以幫你。所以記得,如果你真有一段程式碼不允許被別人看/用,就不要把這段程式碼放到其他人可以碰到的地方,而是做一個server,通過介面允許有限制的訪問。其他人想破解,只能破解你的伺服器閘道器和跳板機器。

如果想理解什麼才是“安全性”,可以參考很多軟體的啟用伺服器的工作原理

private想表達的不是“安全性”的意思,而是OOP的封裝概念,是一種編譯器可以幫助你的設計上的hint。這就像是一家沒人的店掛了個牌子“閒人免進”,但你真要進去還是有各種辦法可以辦到。所以private,以及所有其他的access modifier都有一層隱含的含義:如果你按照遵守這套規則,開發者可以保證不問題(不考慮bug的情況下);否則,後果自負。

比如,你在用spring的IoC的時候,你知道你要“注入”,不管它是不是private的,你知道“注入”是你自己控制的,是你設計好的效果。那麼通過spring的IoC利用反射幫你注入一些private property是再正常不過的用法。

再比如,單元測試,你就想測一個private方法。但是因為private的緣故就是測不了。於是你可以用反射繞開這個限制,開心的做測試。

有些人堅持“不應該測試private方法,而應該通過測試其他方法間接測試private方法,但並沒有形成廣泛的共識。這裡不對這個問題展開。

雖然能繞開,但繞開的程式碼很繁瑣。久而久之就會厭倦。畢竟,程式碼應該為你工作,而不是你為程式碼工作。因此,我的經驗是通常會用protected或者default來代替private。我曾設想runtime應該給一種執行模式,通過設定一個啟動引數使其不管private這類的限制,這樣做UT,做profiling等工作都會輕鬆許多。等到最後釋出時,再用普通模式。但可惜現實當中並沒有這種設定。

我之所以敢用protected/default來代替private是因為現實當中非private不可的情景非常少見。實際上,很多時候private帶來的麻煩比起帶來的好處要多,這是因為很多時候對OOP的誤用造成的。OOP的誤用造成了無謂的private,然後逼著你必須得繞開private。

其實private就是個約定而已。看看其他語言,比如python,它的“private“是一種很鬆散的約定,所有private的成員都用下劃線開頭,告訴呼叫者“不要隨便呼叫我哦”,但是如果真呼叫了也就呼叫了。C++,通過指標就能繞開private。有人說,private會避免新手誤用。但問題是,大家從出道開始,自己或者周圍的同事朋友有誰曾經出過這個問題?IDE知道一個成員當前不能訪問,就根本就不會提示。如果一個人已經開始通過原始碼/反編譯研究“我能不能呼叫這個私有方法了“,他還算是一個菜鳥嗎?他會不知道這裡的潛在風險嗎?如果真的誤用了,code review能過嗎?測試能過嗎?如果一個公司因為誤用private成員,造成了重大的損失,那這個公司就活該倒閉算了,不要在世上丟人。

OOP是一種程式設計思想,是眾多程式設計思想中的一種。是開發者決定了一個問題應該用OOP合適,並且用了Java這樣的語言來簡化自己開發OOP程式碼時的工作。如果抱著這種態度,就不會誤用,因為private在開發者的心中。其他人也不太可能誤用,如果他上過幾天java培訓。不要因為語言是OOP的就去套,把不適合的OOP的程式碼強用OOP的各種套路實現,然後給自己後續的維護擴充套件埋坑。

相關文章