既然Java反射可以訪問和修改私有成員變數,那封裝成private還有意義麼?
簡單來說,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的各種套路實現,然後給自己後續的維護擴充套件埋坑。
相關文章
- C/C++—— 除了用類成員函式訪問類私有成員變數外,還可以通過類物件地址來直接訪問和修改類的私有成員變數C++函式變數物件
- Java類的設計和封裝及類成員的訪問控制Java封裝
- Java之private關鍵字修飾成員變數Java變數
- Java中變數之區域性變數、本類成員變數、父類成員變數的訪問方法Java變數
- Java中類的成員方法和變數的訪問許可權Java變數訪問許可權
- 反射-通過反射獲取成員變數並使用反射變數
- JAVA之反射學習3-反射獲取成員變數並賦值Java反射變數賦值
- Java 通過反射獲取類的資訊(成員變數,成員方法,構造方法)Java反射變數構造方法
- C++ 突破私有成員訪問限制C++
- java基礎private/封裝篇Java封裝
- java學習筆記day07 成員變數與區域性變數、形式引數、匿名物件、封裝、private、this、構造方法、類詳細講解、staticJava筆記變數物件封裝構造方法
- 類的靜態成員變數和普通成員變數該怎樣去區別定義變數
- C++類內成員變數可以定義引用型別嗎C++變數型別
- 成員變數和區域性變數變數
- Java訪問類變數Java變數
- 『無為則無心』Python物件導向 — 51、私有成員變數(類中資料的封裝)Python物件變數封裝
- Java基礎-成員變數和區域性變數的區別Java變數
- java零基礎自學第七天①,什麼是成員變數和區域性變數,private關鍵字使用,this關鍵字使用Java變數
- 成員變數變數
- 反射修改 static final 變數反射變數
- 通過反射獲取類的類名,方法和內部成員變數反射變數
- Java繼承中成員變數的訪問特點「圖文分析」包含繼承中重名訪問的特點Java繼承變數
- 成員變數和區域性變數的區別變數
- 使用類繼承還是類的成員變數繼承變數
- java類成員中的訪問級別有哪些Java
- 類,物件,成員變數和區域性變數,匿名物件物件變數
- 微課|中學生可以這樣學Python(7.3.1節):私有成員與公有成員Python
- Java 反射修改類的常量值、靜態變數值、屬性值Java反射變數
- 12 ### 各種成員變數變數
- private,public,protected,static不可以修飾區域性變數,方法裡的變數變數
- js中變數和jsp中java程式碼中變數互相訪問解決方案JS變數Java
- 反射可以取到原始呼叫方法的變數名嗎?反射變數
- Python 訪問限制 private publicPython
- 成員變數、全域性變數、例項變數、類變數、靜態變數和區域性變數的區別變數
- Java基礎 成員變數的繼承與覆蓋Java變數繼承
- 開源既然不免費那麼花錢了嗎?
- 子父類中成員變數變數
- Swift 成員變數的get/setSwift變數