Java國王:我來告訴你什麼才是真正的封裝!

2021-02-20 合天智匯

Java 帝國第一代國王正式登基,百官前來朝賀。

大臣甲說道:「恭喜陛下登基,為了吸引更多程式設計師加入我國,臣建議儘快完善我們Java語言的OOP特性,封裝、繼承、多態。」

國王說:「一個個來,先說說封裝吧, 我們現在已經可以把數據和方法放到一個類中,接下來想辦法隱藏信息,限制對他們的訪問了, 我聽說現在有不少人在使用C++, 能不能從它借鑑一下啊? 」

大臣乙對C++很有好感,他說:「陛下聖明,C++那裡有pubic, private,protected 等關鍵字,可以用於修飾屬性和方法,我們直接拿過來用得了。」

public class Person{
    private String name;
    private int age;

    public String getName(){
        return name;
    }
    public int getAge(){
        return age;
    }
}

「如此甚好!」   國王表示讚許,不過他眼珠一轉,突然想到了早些年出現的Python, 他問道:「Python是怎麼處理封裝這個問題的?」

大臣甲從Python王國「倒戈」而來,懷著對故國的歉意,十分想把Python的語法帶來一點給Java,聽到國王這麼問起,趕緊說到:「Python的處理比較簡單,用兩個下劃線來表示私有的屬性和方法。」

class Person:
    def __init__(self, name):
        self.name = name
        # 私有屬性
        self.__age = 10 

    # 私有方法
    def __secret(self):
        return  self.__age

p = Person("andy")
#可以訪問
print(p.name)   
#私有屬性,無法訪問
print(p.__age)  
#私有方法,無法訪問
print(p.__secret())  

可是國王卻說:「嗯,這種方式挺簡單的嘛,用下劃線就實現了,很簡潔,我們能不能也這樣啊?」

大臣乙有點瞧不起這個腳本語言,他趕緊說:「萬萬不可,陛下有所不知,這個Python啊,即使加了下劃線,也只是『偽私有』的屬性和方法。」

「什麼是偽私有?」

「就是說外界依然有方法訪問他們!」

#用這種方法,依然可以訪問偽私有屬性和方法
print(p._Person__age)       # 10
print(p._Person__secret())  # 10

「這算哪門子私有的屬性和方法?  一點都不純粹。」 大臣乙繼續補刀。

國王說:「好吧,那不學它了, 那JavaScript呢?他是怎麼實現封裝的?」

朝中的大臣們面面相覷,JavaScript? 這是什麼鬼?怎麼沒有聽說過?

(碼農翻身註:JavaScript的出現時間比Java要晚, 這個Java國王估計是穿越了。)

大臣甲看到自己的想法沒有「得逞」,又另闢蹊徑:「陛下,Python有module的機制,可以把多個類組織到一起,形成一個高內聚的單元,我們Java要不也這麼幹?」

國王瞪了大臣甲一眼,訓斥道:「不要什麼都學Python!  我們也得有點獨特的東西啊。對於如何組織class, 我們可以用package,一個package對應文件系統的一個目錄,下面可以有多個class文件。 如果一個類沒有被public 修飾,那他只能被同一個package下面的類訪問,其他package的類是訪問不到的。這個設計不錯吧?!」 

國王甚為得意。

同一個package下有三個類A,B,C, 只有class A能被外邊的包訪問到,可以充當這個包對外的「接口」 (註:不是java 的interface ), B,C只是包級可見的類, 相當於包內部的實現了,外界是無法new 出來, 防止了被外界誤用。

只要保證A不發生變化,就不會影響外界使用, B和C想怎麼改就怎麼改!

大臣甲小心地問道:「如果我只想把foo.core中的class B暴露給foo.cmd訪問,同時阻止別的包訪問class B,該怎麼辦呢? 」

「怎麼會有這麼『變態』的需求? 」  朝中各位大臣都表示不可思議。

國王沉吟道:「程式設計師的要求是無窮無盡的,例外總是會發生的, 這種需求是存在的, 容朕想想。」

熟悉C++的大臣乙趕緊上奏:「陛下,C++ 有個什麼friend class的概念。 例如在class Node 中聲明了 friend class LinkedList , 那LinkedList 就可以訪問Node 類的屬性和方法了。 」

大臣甲強烈反對這種做法:「不好不好,雖然看起來給程式設計師提供了方便,但是給封裝性撕開了一個大口子,如果被濫用,後果不堪設想。」

國王表示同意:「對,還是放棄這種想法吧,保持簡單性最重要。 如果他實在想訪問class B,可以採用兩種辦法:(1) 把 class B 變成 public  (2) 通過接口class A來進行代理。」

鬥轉星移,轉眼間Java國王已經傳到了第9世。

這一天,鄰國的Python,  JavaScript派使者來訪,受到了國王的熱情招待,席間談到了java package的存在的問題。

Java package的方式雖然不錯,可是還是有很大的弊端,其中最大的弊端就是很多包中的類都是public的, 這就造成了這樣一種情況。

本來是想讓org.foo.api對外提供接口,讓Client去調用的,但實際上,只要foo.jar放到classpath中,另外兩個package , org.foo.impl, org.foo.core中的類也就暴漏了。

JavaScript使者說道:「奧, 我原來以為貴國的一個jar文件就是一個可復用的模塊, 現在看來還是遠遠不夠啊!」

「怪不得大家都說,你的一個jar文件就是class的壓縮包, classpath就是把這些類給平鋪而已。」 Python使者笑道。

Java國王心中有點生氣,但是臉上沒有表露出來:「貴國是怎麼實現的啊?」

Python使者想了想, 自家的module好像也差不多,並且只能靠約定(給變量和方法的前面添加下劃線)的方式來實現private , 由於是約定的,外界依然可以訪問。

JavaScript想到自己連module, package都沒有,趕緊噤聲。

Java 國王說:「簡單的jar文件缺乏一個重要的特性:隱藏內部實現, 寡人打算做一個重要的改變,定義真正的模塊!」

「看到沒有? 我打算用一個文件module-info.java來定義一個模塊中有哪些包是可以被export的, 只有那些export的包才能被Client所調用, 其他的包對Client都是不可見的。 」

看到這個設計方案,各個大臣都覺得不錯。 有了模塊, 就真正地定義了對外可以訪問的接口,除了接口的那個package之外,其他的package是不可訪問的, 徹底實現了封裝。

Python使者盯著這個圖看了一會兒,說道:「不對吧,假設有這樣的代碼:」

FooService service = new FooServiceImpl();

「其中FooService 是org.foo.api包下面的類, FooServiceImpl是org.foo.impl下面的類, 按照你模塊化的要求,這個FooServiceImpl是不能被Client所訪問的, 那怎麼才能創建FooService呢?」

Java國王心想這Python使者對我Java語言挺熟悉的啊,搞得我下不來臺。

「陛下,臣以為可以用工廠模式解決!」 終於有大臣前來救駕。「創建一個新的類FooServiceFactory,把它放到org.foo.api包下,可以公開調用,這樣不就行了?」

public class FooServiceFactory{
    public static FooService getFooService(){
        return new FooServiceImpl();
    }
}

Python使者卻繼續施壓: 「不過讓人不爽的是, 這個FooServiceFactory雖然屬於api包,但是需要知道impl包的具體實現。 如果想添加一個FooService的實現,還得修改它。還是不妥啊!」

突然,Java國王拍了一下腦袋,對了,我怎麼把ServiceLoader給忘記了呢?

我可以把原來的模塊分成兩個模塊,org.foo.api表示接口, org.foo.provider表示實現。

在org.foo.provider中特別聲明,本模塊可以提供FooService的實現!

provides org.foo.api.FooService with  org.foo.provider.FooServiceImpl

Python使者還是不太明白: 「那客戶端怎麼使用呢?」

「簡單,Client 代碼可以這麼寫:」

Iterable<FooService> iter = 
    ServiceLoader.load(FooService.class);

遍歷iter,獲取FooService並且使用。

這樣在運行時,Client的代碼就可以使用ServiceLoader就可以找到這些具體的實現了, 當然實現可能不僅僅只有一個,Client選擇一個就可以了。

當然,JDK必須得實現這個ServiceLoader,去獲取這些具體的實現。

「這個方案,即不破環封裝性,又提供了足夠的靈活性,相當於在運行時裝配對象,陛下聖明!」  大臣們紛紛拍馬屁。

Python使者見狀,也就不再發言,開始低頭喝酒。

JavaScript 使者半天都沒有開口了,他心裡一直在琢磨,我是不是有點落伍了? Python有模塊,Ruby也有模塊,這Java的模塊化更是搞得如火如荼。模塊化極大地提升了封裝性,如果想進行大型項目的開發這模塊化是少不了的, 想想自家那凌亂不堪的js文件, 是時候做出改變了.

本文轉自公眾號:碼農翻身

相關焦點

  • 通過「訪問修飾符」java想告訴我們什麼?
    在平時編寫代碼的時候我們一定會用到public、private這些「java訪問修飾符「,可是你知道java為什麼要設計這些「訪問修飾符」嗎?今天我們就一起來看看廢話少說,先看定義!修改如下:細心的小夥伴可能發現了,我只是加了認證方法,money的訪問修飾符還是public這樣是不可以的,別人依舊可以繞過我們的認證方法,直接去操作我們的money。
  • java簡單的封裝翻譯框架
    1,準備工作:註冊一個百度帳號,在http://api.fanyi.baidu.com/api/trans/product/apidoc個網址裡面拿到appid和key值2,直接開始擼代碼:java簡單的封裝翻譯框架goodit 2018-02-06 11:421,準備工作:註冊一個百度帳號,在http://api.fanyi.baidu.com
  • 我來告訴你,為什麼java培訓班出來就業太難
    為什麼java培訓班出來就業太難很多人看中it行業的高薪,高福利,以及未來前景。但是培訓一結束就面臨著就業困難這個問題。其實,我剛開始的時候也是這樣的,在這裡我為大家分析分析現在為什麼java培訓班出來就業難的問題。
  • java基礎知識乾貨——封裝
    那到底什麼是面向對象程序設計呢?要弄懂它,首先我們簡單了解一下另一種程序設計。  面向過程程序設計就是自頂向下順序執行,逐步求精;其程序結構是按照功能劃分為若干個基本板塊,這些板塊形成一個樹狀結構;各模塊之間的關係儘可能的簡單,在功能上相對獨立;每一板塊內部均是由順序、選擇和循環三種基本結構組成的;其板塊化實現的具體方法是使用與子程序。程序流程在寫程序時就已決定。
  • java 小工具 | 封裝通用的 Mybatis 生成模板 |1 秒寫完增刪改查
    項目背景前段時間我們介紹了如何使用thymeleaf去生成mybatis相關的模板:https://www.lixiang.red/articles/2019/07/23/1563857782748.html今天小刀和各位小夥伴們一起來深入下這個問題,我們來研究下怎麼去封裝一個通用的
  • Java面向對象中的封裝和訪問控制符詳解
    封裝面向對象編程有三大概念:封裝、繼承、多態,今天我們就先從封裝開始講起。封裝(Encapsulation)是面向對象方法的重要原則,就是把對象的屬性和操作(或服務)結合為一個獨立的整體,並儘可能隱藏對象的內部實現細節。封裝是把過程和數據包圍起來,對數據的訪問只能通過已定義的接口。
  • 通俗易懂的告訴你「策略模式」在java多線程中的應用
    下面我們就一起來看看「策略模式」是如何運用在javaduo線程中的。01什麼是「策略模式」廢話少說,先看定義!針對一組算法,將每一種算法都封裝到具有共同接口的獨立的類中,從而使它們可以相互替換。組成:環境類(Context):用來操作策略的上下文環境,也就是我們遊客。
  • Java基礎學習心得筆記
    面向過程就是把你的代碼封裝成函數,然後依次去做一件事情,面向過程是把你要做的事情抽象成對象,告訴對象去做。所以要想學好java入門,必須知道類和對象的概念。類是對生活中事物的抽象描述,比如人類,動物類,交通工具類;對象即是對類的具體實例化,比如張三是人,貓是動物,飛機是交通工具(對象類)。
  • java培訓班不會告訴你的真相
    至於這20個人是不是包裝學歷和經驗進去的,是不是連試用期都沒過就跑了的,培訓班才不會告訴你。培訓班用的倖存者偏差這招,真是百試不爽。讓很多人一時頭腦發熱,覺得自己只要交了錢就會出人頭地,登上塔頂。然而結局終究會打臉,從而讓他們先後陷入迷茫、後悔、絕望的境地。而且培訓班可操作餘地也很大。
  • 給Java新手的一些建議——Java知識點歸納(Java基礎部分)
    為什麼強調要知道這個呢,知道了java最純粹的啟動方式之後,你才能在啟動出問題的時候,去分析當時啟動的目錄多少,執行命名如何,參數如何,是否有缺失等。 這樣有利於你真正開發中去解決那些奇奇怪怪的可能和環境相關的問題。
  • 坑爹代碼 | 這樣的日誌封裝到底是坑爹還是有用呢?
    那麼怎麼用日誌框架才是最佳的實踐呢?下面這段代碼難道真的只是一個無用的封裝嗎?import org.slf4j.Marker;// 當時完全不懂包裝slf4j什麼意思,還以為自己針對市面上的logger進行了封裝處理,上次開會的時候這麼說,我為了logger的時候像andriod能寫個tag,WTF,難道你logger.info('tag:{},xxxxx',tag)這樣不行麼?
  • 為什麼都說java比較容易入門?
    java語言寫出他們自己的東西,而且bug不多,並且不容易出現致命的問題;那麼java到底有什麼優勢呢?中沒有了取而代之的是虛擬機,什麼事都由虛擬機來做,內存的分配與管理都是他的活;而且有了虛擬機,可以讓你的程序之編譯一次就可以在不同平臺運行,不像前兩個需要換平臺就要重新編譯,對編譯器也有一定的要求,而且編譯的時候出現的問題也是很鬱悶的;所以正常的說來,Java就是比c與c++要入門一些,而且可以做的事情也很多,但是針無兩頭利,java也有弊端,就是他的速度運行速度相對c++與c就差一些了
  • 不知道Java的封裝到底怎麼用?超簡單理解封裝的概念及應用-09
    一、什麼是訪問修飾符?前面講了面向對象,對象都有屬性,比如人類,有姓名,有年齡,這些其實都是隱私,不會輕易告訴任何人那既然所有人的姓名、年齡、性別都是隱私,也就是人類的這些屬性是隱私的,對不對。比如Person的年齡每年自增,那如何自增的邏輯沒必要讓其他人看到吧,因為代碼太多,他可能只是需要使用這個類來獲取這個人的年齡,所以,把年齡自增的邏輯封裝起來(private修飾符),僅僅提供一個獲取年齡的方法就可以了(public修飾符)。這樣調用你的類同事是不是只要去看你公開了哪些可以使用的方法就可以了?
  • Java面試總結之Java基礎
    在這篇文章裡就來為大家總結一下經常會被問到的Java基礎題。你需要對這大三特徵有比較深刻的認識才可以。封裝表面上看就把描述一個對象的屬性和行為封裝成一個類,把業務邏輯封裝成方法,封裝的另一層含義在於通過修飾符控制類的訪問屬性(公有,私有等)。繼承主要是實現了代碼的復用,所有子類公用的行為和屬性可以抽取為一個父類,所有子類繼承了父類的屬性和行為,java中繼承是單一性的。
  • 為什麼java程式設計師的年薪有40W,我來告訴你答案
    做java的大公司也就那麼幾個,那麼到底各公司裡面的薪資到底怎麼樣呢?先來看看 馬雲爸爸的阿里巴巴的級別定義:阿里薪資結構:一般是12+1+3=16薪年底的獎金為0-6個月薪資,90%人可拿到3個月如果你想拿高薪的,想學習的,想就業前景好的,想跟別人競爭能取得優勢的,想進阿里面試但擔心面試不過的,你都可以來,群號為:71859422註:加群要求1、具有1-5工作經驗的,面對目前流行的技術不知從何下手,需要突破技術瓶頸的可以加。2、在公司待久了,過得很安逸,但跳槽時面試碰壁。需要在短時間內進修、跳槽拿高薪的可以加。
  • 你必須掌握的 21 個 Java 核心技術!
    這個知識點是最最基本的java開發者需要掌握的,第一個肯定是教你如何在命令行中執行java程序,但是很多人一旦把java學完了,IDE用上了,就把這個都忘了。為什麼強調要知道這個呢,知道了java最純粹的啟動方式之後,你才能在啟動出問題的時候,去分析當時啟動的目錄多少,執行命名如何,參數如何,是否有缺失等。這樣有利於你真正開發中去解決那些奇奇怪怪的可能和環境相關的問題。
  • Java、JavaScript、PHP、Python是什麼?能開發什麼?
    程式語言是什麼?程式語言(programming language),是用來定義電腦程式的形式語言。它是一種被標準化的交流技巧,用來向計算機發出指令。如果我們想用電腦或者讓一個應用按照你的指令工作,這時就需要用程式語言來把指令翻譯成電腦能懂的,從而去完成指令。
  • 像寫SQL一樣編寫Java數據應用
    究其原因,是因為SQL和Java之間是割裂的,如果封裝不到位,做Java的人太難使用;如果封裝得太多,在做一些用複雜SQL的時候又非常麻煩。話說企業應用,一般離不開資料庫。究其原因,是因為SQL和Java編程之間是割裂的,如果封裝得不到位,做Java的人太難使用;如果封裝得太多,在做一些用複雜SQL的時候又非常麻煩。比如:Hibernate就採用了封裝HQL的方式來解決這方面的問題。iBatis對於SQL支持比較好,但是又會有一些割裂感,同時在解決時還要引入動態SQL來解決需要根據一些運行時條件來處理的問題,一定程度上又增加了使用的複雜度。
  • 我用 Java 8 寫了一段邏輯,同事直呼看不懂,你試試看..
    Java 8 實現因為我知道 Java 8 可以處理這類的需求,所以我從來沒想過用最原始的方式去實現,直接把就用 Java 8 來實現了:/*** 公眾號:Java技術棧/private String[] getFulfillments(XxxOrder xxxOrder
  • 真的嗎,Java 的 JSP 已經被淘汰了?
    大中型公司需要專業人才,小公司需要全才,但是對於個人職業發展來說,我建議是分開。你要是這輩子就吃java這碗飯,就不要去研究什麼css,js等等。把你的精力專注在java,jvm原理,spring原理,mysql鎖,事務,多線程,大並發,分布式架構,微服務,以及相關的項目管理等等,這樣你的核心競爭力才會越來越高,正所謂你往生活中投入什麼,生活就會反饋給你什麼。