要是明年別克將這車國產 告訴我你買不買賬?

基於輕量化平台打造后,新君威不僅車重減輕,車身和軸距都相應加長了55mm和92mm,加上車尾採用的溜背設計,未來新君威的後排空間必定提升不少。新君威在未來也有望搭載1。5T/2。0T發動機,其中1。5T車型極有可能搭載7速雙離合變速箱。

相信大家對別克君威都不陌生,這輛中級轎車從以前主打舒適穩重的風格向後期年輕化的運動范轉變。而最近它的姊妹車歐寶Insignia實車也亮相了,新一代君威的外觀基本會與其相同。

可能很多人已經淡忘了歐寶這個品牌,實際上作為通用旗下的子品牌,歐寶、別克和沃克斯豪爾(Vaxuhall)都存在着換標車型。同一款車彼此換上不同的logo,在不同的國家地區發售。

而歐寶Insignia所對應的車型分別是別克的君威和霍頓的Commodore(沒錯,霍頓同樣是通用的子品牌)。這輛未來的新君威走了略帶豪華的運動風格,不論是前臉還是腰線的設計都顯得非常運動,只不過到時進來國產的時候,外觀肯定會有一些調整,而這款Insignia則有可能在明年日內瓦車展發布,而新君威則有望在明年國產。

基於輕量化平台打造后,新君威不僅車重減輕,車身和軸距都相應加長了55mm和92mm,加上車尾採用的溜背設計,未來新君威的後排空間必定提升不少。

新君威在未來也有望搭載1.5T/2.0T發動機,其中1.5T車型極有可能搭載7速雙離合變速箱。只不過在國內眾多消費者對於別克的變速箱印象都很一般,未來國產車型還是得要多花點心思好好調整下了。

本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準

為什麼阿里巴巴Java開發手冊中不允許魔法值出現在代碼中?

在閱讀《阿里巴巴Java開發手冊》時,發現有一條關於關於常量定義的規約,具體內容如下:

圖中的反例是將數據緩存起來,並使用魔法值加鏈路 id 組成 key,這就可能會出現其他開發人員在複製粘貼的時候,少複製 _ 的情況發生,這種錯誤很難去檢查到,因為讀取緩存不存在,可能會去數據庫讀取,很難察覺到。

如果在生產環境中,大量的請求進來,緩存全部失效,直接請求數據庫,導致數據庫連接過多,查詢效率變低的問題發生,因此看來魔法值確實應該避免出現在代碼中。

另外在 《Clean Code》 和 《重構》 等書中也提到了類似的問題,在代碼中出現原始形態数字通常來說是壞現象,應該用命名良好的常量類隱藏它。

靜態常量取代魔法值

像下面這個例子:

if (billCount > 75) {
    //todo
} else {
    //todo
}

如果在不了解這塊的業務的同事,在讀到這塊代碼的時候,可能會想,75 是什麼鬼,為啥和這個數比較,背後深藏着什麼秘密嗎?可能只有當時的開發人員記得了,導致代碼可讀性和可維護性極差。

如果聲明一個常量,來替換該魔法值,可能就會使代碼的可讀性和可維護性大大增加。

static final Integer BASIC_BILL_COUNT = 75;

還有些魔法表達式,比如:

if (value > 60 && value <= 80 && type = 1) {
    // todo
}

比如這個表達式是表示狀態為正常且項目活躍,就可以定義:

boolean isActiveProject = value > 60 && value <= 80 && type = 1;

這樣是不是可讀性就提高了,一眼就可以看出來這塊代碼的邏輯。

枚舉類取代魔法值

還有一種消除魔法值的方式是使用枚舉類代替,下面讓我們舉個例子:

if (eventId == 1) {
    System.out.println("睡覺");
} else if (eventId == 2) {
    System.out.println("吃飯");
} else if (eventId == 3) {
    System.out.println("打豆豆");
}

如上代碼是針對事件 id 去執行相應的事件,如果事件比較少,大家還可以勉強記住每個 eventId 對應的含義,但是隨着事件 id 的增多,很可能會發生,新來的員工把事件 id 給搞混了,導致執行錯誤的事件,發生 bug。

那麼我們可以使用枚舉類來表示相應的事件:

public enum EventEnum {

    /**
     * 睡覺
     */
    SLEEP_EVENT(1, "睡覺"),

    /**
     * 吃飯
     */
    EAT_EVENT(2, "吃飯"),

    /**
     * 打豆豆
     */
    FIGHT_PEA_EVENT(3, "打豆豆");

    private int eventId;
    private String desc;

    EventEnum(int eventId, String desc) {
        this.eventId = eventId;
        this.desc = desc;
    }

    public int getEventId() {
        return eventId;
    }

    public String getDesc() {
        return desc;
    }
}

修改完之後的代碼如下:

if (eventId == EventEnum.SLEEP_EVENT.getEventId()) {
    System.out.println("睡覺");
} else if (eventId == EventEnum.EAT_EVENT.getEventId()) {
    System.out.println("吃飯");
} else if (eventId == EventEnum.FIGHT_PEA_EVENT.getEventId()) {
    System.out.println("打豆豆");
}

是不是可讀性急劇提升,還不快看看自己代碼中有沒有這樣的魔法值出現,有的話趕緊改造起來。

還有如果你需要在不同的地點引用同一數值,魔法數會讓你煩惱不已,因為一旦這些数字發生改變,就必須在程序中找到所有的魔法值,並將它們全部修改一遍,這樣就太費時費力了。

其實不只是 Java 不應該在代碼中使用魔法值,其他語言亦是如此。

總結

本文主要介紹了為什麼不允許在代碼中出現魔法值以及如何將代碼中已有的魔法值去除掉。

代碼可讀性還是比較重要的,你肯定不希望別人在接手你的代碼的時候,罵到這数字啥意思,這代碼寫得跟粑粑一樣。

最好的關係就是互相成就,大家的在看、轉發、留言三連就是我創作的最大動力。

參考

《Java開發手冊》泰山版

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準

只賣11萬起的大氣 好開大三廂家轎 為何油耗還低?

但是,凌派的百公里加速時間約為10。05秒。其實在同級別中是屬於比較快的車型了,它的動力匹配得比較完善。轉向採用的是電動助力,手感輕盈,所以在日常開的時候,能給你輕鬆、好開的感覺。懸架調校比較中性,在舒適性和支撐性方面表現綜合。

如果您想買一輛10萬左右、空間、動力、外觀設計都表現不錯的合資緊湊型車的話,來自廣汽本田的凌派是一個不錯的選擇!

如今2016款的凌派採用的是CVT無級變速箱,其實在這款車型剛推出的時候,編者就已經深入試駕過它。今天我們就一起來聊聊這款車的駕駛感受吧!

廣汽本田-凌派

指導價:10.98-14.98萬

我覺得凌派的外觀設計極力地營造出大氣、氣派的感覺。而這種感覺的營造是比較成功的,包括中網誇張的大嘴、粗壯的鍍鉻飾條、加入LED光源的大燈等。

動力總成

凌派搭載的是R18系列的發動機,代號為R18ZH,採用了本田特有的i-VTEC技術,最大功率136馬力,峰值扭矩169牛米/4300轉。採用了多點電噴的供油方式。

變速箱採用的是本田自主研發的CVT變速箱,帶有S擋(運動模式)。底盤方面,它採用前麥弗遜式獨立懸架、后扭力梁式非獨立懸架。

駕駛起來如何?

首先,進入到車內,黑色的內飾給人的感覺比較年輕、動感。方向盤的握感不錯,而且多功能按鍵布局簡約,使用起來方便。

凌派的油門響應靈敏,1.8L發動機也有着不錯的低扭輸出,所以每次起步動力都比較充足。

動力響應性是不錯的,而CVT變速箱讓動力輸出均勻、持續。但是由於不像AT那樣每次換擋都帶有鏗鏘感,所以總會讓人有“動力不夠強”的錯覺。

但是,凌派的百公里加速時間約為10.05秒!其實在同級別中是屬於比較快的車型了,它的動力匹配得比較完善。

轉向採用的是電動助力,手感輕盈,所以在日常開的時候,能給你輕鬆、好開的感覺。懸架調校比較中性,在舒適性和支撐性方面表現綜合。

油耗怎樣?

1.8L自動擋車型車主口碑油耗:7.7L/100km

1.8L手動擋車型車主口碑油耗:7.4L/100km

CVT變速箱的加入讓凌派的油耗表現不錯,畢竟它採用的是1.8L的自然吸氣發動機。

競爭對手:

上汽大眾-朗逸

指導價:10.99-15.99萬

凌派的對手很多,因為國內的A級車市場戰火紛飛。而凌派的優惠幅度沒有朗逸、軒逸那麼大。不過凌派的動力總成表現有一定優勢!

編者語:

其實凌派的性價比挺高,不錯的配置、充足的空間、動力總成也表現給力。它並不是主打駕控,但是作為一輛家用車來說,還是很給力的。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準

俄羅斯計畫2032年前清除七艘廢棄核潛艦

摘錄自2020年7月14日自由時報報導

為解決海域核污染問題,俄羅斯正在設計建造能夠打撈沉沒在大海中的核動力潛艦,整個工作預計要進行到2032年。

《Defense World》報導,在第23屆俄羅斯-挪威聯合委員會會議中,改善核能與輻射安全成為主要議題。俄羅斯計畫清除七艘沉沒在北極海域的核潛艇,「K-159」和「K-27」從打撈到拆解預計各自花費四年時間,分別為2025至2028年、2028至2031年,另外5艘則在2029至2032年完成。

報導指出,在1990年代,超過100艘除役的核潛艇被放置在九個不同地點,目前有兩個地點的核潛艇已被清除,自2004年起,放射性廢料減少了一半以上,從1110萬居禮下降至509萬居禮。

能源轉型
國際新聞
俄羅斯
核子潛艦
核污染
核能

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準

昆蟲間諜要出現了?美研發出可由甲蟲搭載的微型攝影機

摘錄自2020年7月19日自由時報報導

用昆蟲機器人監控敵方行動不再是電影畫面了!美國華盛頓大學研發出微型攝影機,搭載在甲蟲上可將其拍攝的畫面傳送到智慧型手機裡,相關研究已刊載在Science Robotics期刊上。

據《BBC》報導,這款微型攝影機整個裝置的重量只有250毫克,是紙牌重量的10%左右。它安裝在可以左右移動的機械臂上,因此可以掃描環境以獲得全景圖像。為了節省電量,研究人員將攝影機改裝成只有在甲蟲移動時才會啟動,如此一來充滿電後可運作六個小時。實驗結束後甲蟲沒有受到傷害,至少再活了一年。

研究團隊透過這次的實驗,製作了如同昆蟲大小的攝影機機器人,其透過振動的方式進行移動,每秒約可前進3公分。研發團隊也坦承微型攝影機機器人可能會引發新的監控問題,不過他們認為更重要的是把這件事放在公共領域進行討論,以便讓人們意識到其中的風險並得到解決方案。

生物多樣性
國際新聞
美國
動物福利
經濟動物

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

Java I/O模型及其底層原理

  Java I/O是Java基礎之一,在面試中也比較常見,在這裏我們嘗試通過這篇文章闡述Java I/O的基礎概念,幫助大家更好的理解Java I/O。
  在剛開始學習Java I/O時,我很迷惑,因為網上絕大多數的文章都是講解Linux網絡I/O模型的,那時我總是搞不明白和Java I/O的關係。後來查了看了好多,才明白Java I/O的原理是以Linux網絡I/O模型為基礎的,理解了Linux網絡I/O模型再學習Java I/O就很方便了,所以這篇文章,我們先來了解I/O的基本概念,再學習Linux網絡I/O模型,最後再看Java中的幾種I/O。

什麼是I/O?

  I/O是Input、Output的縮寫,即對應計算機中的輸入輸出,以一次文件讀取為例,我們需要將磁盤上的數據讀取到用戶空間,那麼這次數據轉移操作其實就是一次I/O操作,更具體的說是一次文件I/O。我們瀏覽網頁,其中在請求一個網頁時,服務器通過網絡把數據發送給我們,此時程序將數據從TCP緩衝區複製到用戶空間,那麼這次數據轉移操作其實也是一次I/O操作,更具體的說是一次網絡I/O。I/O到處都在,十分重要,Java對I/O對底層操作系統的各種I/O模型進行了封裝,使我們可以輕鬆開發。

Linux網絡I/O模型

  根據UNIX網絡編程對I/O模型的分類,UNIX提供了5種I/O模型,分別是:阻塞I/O(Blocking I/O)、非阻塞I/O(Non-Blacking I/O)、I/O多路復用模型(I/O Multiplexing)、信號驅動式I/O(Signal Driven I/O)、異步I/O(Asynchronous I/O)。我們逐步了解一下其基本原理。

阻塞I/O(Blocking I/O)

  阻塞I/O是最早最基礎的I/O模型,其在讀寫數據過程中會阻塞。通過下圖我們可以看到,當用戶進程調用了recvfrom這個系統調用后,內核開始第一階段的數據準備工作,直到內核等待數據準備完成,然後開始第二階段的將數據從內核複製到用戶空間的工作,最後內核返回結果。整個過程中用戶進程都是阻塞的,直到最後返回結果后才接觸阻塞block狀態。阻塞I/O模型適用於併發量小且對時延不敏感的系統。

非阻塞I/O(Non-Blacking I/O)

  當用戶進程調用recvfrom這個系統調用后,如果內核尚未準備好數據,此時不再阻塞用戶進程,而是立即返回一個EWOULDBLOCK錯誤。用戶進程會不斷髮起系統調用直到內核中數據被準備好(輪詢),此時將執行第二階段的將數據從內核複製到用戶空間的工作,然後內核返回結果。非阻塞I/O模型不斷地輪詢往往需要耗費大量cpu時間。

I/O多路復用模型(I/O Multiplexing)

  I/O多路復用的優點在於單個進程可以同時處理多個網絡連接的I/O,其基本原理就是select/epoll函數可以不斷的輪詢其負責的所有socket,當某個socket有數據到達時,就通知用戶進程。
  如下圖所示,當用戶進程調用select函數時,整個進程會被阻塞block住,但是這裏的阻塞不是被socket I/O阻塞,而是被select這個函數阻塞。同時內核會監聽改select負責的所有socket(這裏的socket一般設置為non-blocking),當任何一個socket中的數據準備好時,select就會返回給用戶進程,這時候用戶進程再此發起一個系統調用,將數據從內核複製到用戶空間,並返回結果。
  對比I/O多路復用模型和阻塞I/O模型的流程,多路復用多了一個系統調用來完成select環節,除此之外沒有太大的不同。Select的優勢在於它可以同時處理多個connection,但是會多一個系統調用。多路復用本質上也不是非阻塞的。

信號驅動式I/O(Signal Driven I/O)

  首先我們開啟socket的信號驅動I/O功能,然後用戶進程發起sigaction系統調用給內核后立即返回並可繼續處理其他工作。收到sigaction系統調用的內核在將數據準備好後會按照要求產生一個signo信號通知給用戶進程。然後用戶進程再發起recvfrom系統調用,完成數據從內核到用戶空間的複製,並返回最終結果。其基礎原理圖示如下:

異步I/O(Asynchronous I/O)

  用戶進程向內核發起系統調用后,就可以開始去做其他事情了。內核收到異步I/O的系統調用后,會直接retrun,所以這裏不會對用戶進程有阻塞。之後內核等待數據準備完成後會繼續將數據從內核拷貝到用戶空間(具體動作可以由異步I/O調用定義),然後內核回給用戶進程發送一個signal,告訴用戶進程I/O操作完成了,整個過程不會導致用戶請求進程阻塞。
  信號驅動I/O模型是內核通知我們可以發起I/O操作了,而異步I/O模式是內核告訴我們I/O操作已經完成了。

  以上就是Linux的5種網絡I/O模型,其中前4中都是同步I/O模型,他們真正的I/O操作環節都會將進程阻塞,只有最後一種異步I/O模型是異步I/O操作。

Java中的I/O模型

  在JDK1.4之前,基於Java的所有socket通信都是使用阻塞I/O(BIO),JDK1.4提供了了非阻塞I/O(NIO)功能,不過雖然名字叫做NIO,實際底層模型是I/O多路復用,JDK1.7提供了針對異步I/O(AIO)功能。

BIO

  BIO簡化了上層開發,但是性能瓶頸問題嚴重,對高併發第時延支持差。
基於消息隊列和線程池技術優化的BIO模式雖然可以對高併發支持有一定幫助,但是還是受限於線程池大小和線程池阻塞隊列大小的制約,當併發數超過線程池的處理能力時,部分請求法務繼續處理,會導致客戶端連接超時,影響用戶體驗。

NIO

  NIO彌補了BIO的不足,簡單說就是通過selector不斷輪詢註冊在自己上面的channel,如果channel上面有新的連接讀寫時間時就會被輪詢出來,一個selector上面可以註冊多個channel,一個線程就可以負責selector的輪詢,這樣就可以支持成千上萬的連接。Selector就是一個輪詢器,channel是一個通道,通過它來讀取或者寫入數據,通道是雙向的,可以用於讀、寫、讀和寫。Buffer用來和channel交互,數據通過channel進出buffer。
NIO的優點是可以可靠性好以及高併發低時延,但是使用NIO的代碼開發較為複雜。

AIO

  AIO,或者說叫做NIO2.0,引入了異步channel的概念,提供了異步文件channel和異步socket channel的實現,開發者可以通過Future類來表示異步操作的結果,也可以在執行異步操作時傳入一個channels,實現CompletionHandler接口作為回調。AIO不用開發者單獨開發獨立線程的selector,異步回調操作有JDK地城思安城池負責驅動,開發起來比NIO簡單一些,同時保持了高可靠高併發低時延的優點。

參考:
https://blog.csdn.net/historyasamirror/article/details/5778378
https://juejin.im/post/5cce5019e51d453a506b0ebf

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

世界經濟論壇:綠色振興將可創造一年300兆元收益

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

掌握SpringBoot-2.3的容器探針:實戰篇

歡迎訪問我的GitHub

https://github.com/zq2599/blog_demos

  • 內容:原創文章分類匯總,及配套源碼,涉及Java、Docker、K8S、DevOPS等
    經過多篇知識積累終於來到實戰章節,親愛的讀者們,請將裝備就位,一起動手體驗SpringBoot官方帶給我們的最新技術;

關於《SpringBoot-2.3容器化技術》系列

  • 《SpringBoot-2.3容器化技術》系列,旨在和大家一起學習實踐2.3版本帶來的最新容器化技術,讓咱們的Java應用更加適應容器化環境,在雲計算時代依舊緊跟主流,保持競爭力;
  • 全系列文章分為主題和輔助兩部分,主題部分如下:
  1. 《體驗SpringBoot(2.3)應用製作Docker鏡像(官方方案)》;
  2. 《詳解SpringBoot(2.3)應用製作Docker鏡像(官方方案)》;
  3. 《掌握SpringBoot-2.3的容器探針:基礎篇》;
  4. 《掌握SpringBoot-2.3的容器探針:深入篇》;
  5. 《掌握SpringBoot-2.3的容器探針:實戰篇》;
  • 輔助部分是一些參考資料和備忘總結,如下:
  1. 《SpringBoot-2.3鏡像方案為什麼要做多個layer》;
  2. 《設置非root賬號不用sudo直接執行docker命令》;
  3. 《開發階段,將SpringBoot應用快速部署到K8S》;

SpringBoot-2.3容器探針知識點小結

經過前面的知識積累,我們知道了SpringBoot-2.3新增的探針規範以及適用場景,這裏做個簡短的回顧:

  1. kubernetes要求業務容器提供一個名為livenessProbe的地址,kubernetes會定時訪問該地址,如果該地址的返回碼不在200到400之間,kubernetes認為該容器不健康,會殺死該容器重建新的容器,這個地址就是存活探針
  2. kubernetes要求業務容器提供一個名為readinessProbe的地址,kubernetes會定時訪問該地址,如果該地址的返回碼不在200到400之間,kubernetes認為該容器無法對外提供服務,不會把請求調度到該容器,這個地址就是就緒探針
  3. SpringBoot的2.3.0.RELEASE發布了兩個新的actuator地址,/actuator/health/liveness/actuator/health/readiness,前者用作存活探針,後者用作就緒探針,這兩個地址的返回值來自兩個新增的actuator:Liveness StateReadiness State
  4. SpringBoot應用根據特殊環境變量是否存在來判定自己是否運行在容器環境,如果是,/actuator/health/liveness/actuator/health/readiness這兩個地址就有返回碼,具體的值是和應用的狀態有對應關係的,例如應用啟動過程中,/actuator/health/readiness返回503,啟動成功后返回200
  5. 業務應用可以通過Spring系統事件機制來讀取Liveness StateReadiness State,也可以訂閱這兩個actuator的變更事件;
  6. 業務應用可以通過Spring系統事件機制來修改Liveness StateReadiness State,此時/actuator/health/liveness和/actuator/health/readiness的返回值都會發生變更,從而影響kubernetes對此容器的行為(參照第一點和第二點),例如livenessProbe返回碼變成503,導致kubernetes認為容器不健康,從而殺死容器;

小結完畢,接下來開始實打實的編碼和操作實戰,驗證上述理論;

實戰環境信息

本次實戰有兩個環境:開發和運行環境,其中開發環境信息如下:

  1. 操作系統:Ubuntu 20.04 LTS 桌面版
  2. CPU :2.30GHz × 4,內存:32G,硬盤:1T NVMe
  3. JDK:1.8.0_231
  4. MAVEN:3.6.3
  5. SpringBoot:2.3.0.RELEASE
  6. Docker:19.03.10
  7. 開發工具:IDEA 2020.1.1 (Ultimate Edition)

運行環境信息如下:

  1. 操作系統:CentOS Linux release 7.8.2003
  2. Kubernetes:1.15

事實證明,用Ubuntu桌面版作為開發環境是可行的,體驗十分順暢,IDEA、SubLime、SSH、Chrome、微信都能正常使用,下圖是我的Ubuntu開發環境:

實戰內容簡介

本次實戰包括以下內容:

  1. 開發SpringBoot應用,部署在kubernetes;
  2. 檢查應用狀態和kubernetes的pod狀態的關聯變化;
  3. 修改Readiness State,看kubernetes是否還會把請求調度到pod;
  4. 修改Liveness State,看kubernetes會不是殺死pod;

源碼下載

  1. 本次實戰用到了一個普通的SpringBoot工程,源碼可在GitHub下載到,地址和鏈接信息如下錶所示(https://github.com/zq2599/blog_demos):
名稱 鏈接 備註
項目主頁 https://github.com/zq2599/blog_demos 該項目在GitHub上的主頁
git倉庫地址(https) https://github.com/zq2599/blog_demos.git 該項目源碼的倉庫地址,https協議
git倉庫地址(ssh) git@github.com:zq2599/blog_demos.git 該項目源碼的倉庫地址,ssh協議
  1. 這個git項目中有多個文件夾,本章的應用在probedemo文件夾下,如下圖紅框所示:

開發SpringBoot應用

  1. 請在IDEA上安裝lombok插件:
  1. 在IDEA上新建名為probedemo的SpringBoot工程,版本選擇2.3.0
  1. 該工程的pom.xml內容如下,注意要有spring-boot-starter-actuatorlombok依賴,另外插件spring-boot-maven-plugin也要增加layers節點:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.bolingcavalry</groupId>
    <artifactId>probedemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>probedemo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.0.RELEASE</version>
                <!--該配置會在jar中增加layer描述文件,以及提取layer的工具-->
                <configuration>
                    <layers>
                        <enabled>true</enabled>
                    </layers>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  1. 應用啟動類ProbedemoApplication是個最普通的啟動類:
package com.bolingcavalry.probedemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ProbedemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProbedemoApplication.class, args);
    }
}
  1. 增加一個監聽類,可以監聽存活和就緒狀態的變化:
package com.bolingcavalry.probedemo.listener;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.AvailabilityState;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

/**
 * description: 監聽系統事件的類 <br>
 * date: 2020/6/4 下午12:57 <br>
 * author: willzhao <br>
 * email: zq2599@gmail.com <br>
 * version: 1.0 <br>
 */
@Component
@Slf4j
public class AvailabilityListener {

    /**
     * 監聽系統消息,
     * AvailabilityChangeEvent類型的消息都從會觸發此方法被回調
     * @param event
     */
    @EventListener
    public void onStateChange(AvailabilityChangeEvent<? extends AvailabilityState> event) {
        log.info(event.getState().getClass().getSimpleName() + " : " + event.getState());
    }
}
  1. 增加名為StateReader的Controller的Controller,用於獲取存活和就緒狀態:
package com.bolingcavalry.probedemo.controller;

import org.springframework.boot.availability.ApplicationAvailability;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Date;

@RestController
@RequestMapping("/statereader")
public class StateReader {

    @Resource
    ApplicationAvailability applicationAvailability;

    @RequestMapping(value="/get")
    public String state() {
        return "livenessState : " + applicationAvailability.getLivenessState()
               + "<br>readinessState : " + applicationAvailability.getReadinessState()
               + "<br>" + new Date();
    }
}
  1. 增加名為StateWritter的Controller,用於設置存活和就緒狀態:
package com.bolingcavalry.probedemo.controller;

import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.LivenessState;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Date;

/**
 * description: 修改狀態的controller <br>
 * date: 2020/6/4 下午1:21 <br>
 * author: willzhao <br>
 * email: zq2599@gmail.com <br>
 * version: 1.0 <br>
 */
@RestController
@RequestMapping("/staterwriter")
public class StateWritter {

    @Resource
    ApplicationEventPublisher applicationEventPublisher;

    /**
     * 將存活狀態改為BROKEN(會導致kubernetes殺死pod)
     * @return
     */
    @RequestMapping(value="/broken")
    public String broken(){
        AvailabilityChangeEvent.publish(applicationEventPublisher, StateWritter.this, LivenessState.BROKEN);
        return "success broken, " + new Date();
    }

    /**
     * 將存活狀態改為CORRECT
     * @return
     */
    @RequestMapping(value="/correct")
    public String correct(){
        AvailabilityChangeEvent.publish(applicationEventPublisher, StateWritter.this, LivenessState.CORRECT);
        return "success correct, " + new Date();
    }

    /**
     * 將就緒狀態改為REFUSING_TRAFFIC(導致kubernetes不再把外部請求轉發到此pod)
     * @return
     */
    @RequestMapping(value="/refuse")
    public String refuse(){
        AvailabilityChangeEvent.publish(applicationEventPublisher, StateWritter.this, ReadinessState.REFUSING_TRAFFIC);
        return "success refuse, " + new Date();
    }

    /**
     * 將就緒狀態改為ACCEPTING_TRAFFIC(導致kubernetes會把外部請求轉發到此pod)
     * @return
     */
    @RequestMapping(value="/accept")
    public String accept(){
        AvailabilityChangeEvent.publish(applicationEventPublisher, StateWritter.this, ReadinessState.ACCEPTING_TRAFFIC);
        return "success accept, " + new Date();
    }

}
  1. 增加名為Hello的controller,此接口能返回當前pod的IP地址,在後面測試時會用到:
package com.bolingcavalry.probedemo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;

/**
 * description: hello demo <br>
 * date: 2020/6/4 下午4:38 <br>
 * author: willzhao <br>
 * email: zq2599@gmail.com <br>
 * version: 1.0 <br>
 */
@RestController
public class Hello {

    /**
     * 返回的是當前服務器IP地址,在k8s環境就是pod地址
     * @return
     * @throws SocketException
     */
    @RequestMapping(value="/hello")
    public String hello() throws SocketException {
        List<Inet4Address> addresses = getLocalIp4AddressFromNetworkInterface();
        if(null==addresses || addresses.isEmpty()) {
            return  "empty ip address, " + new Date();
        }

        return addresses.get(0).toString() + ", " + new Date();
    }

    public static List<Inet4Address> getLocalIp4AddressFromNetworkInterface() throws SocketException {
        List<Inet4Address> addresses = new ArrayList<>(1);
        Enumeration e = NetworkInterface.getNetworkInterfaces();
        if (e == null) {
            return addresses;
        }
        while (e.hasMoreElements()) {
            NetworkInterface n = (NetworkInterface) e.nextElement();
            if (!isValidInterface(n)) {
                continue;
            }
            Enumeration ee = n.getInetAddresses();
            while (ee.hasMoreElements()) {
                InetAddress i = (InetAddress) ee.nextElement();
                if (isValidAddress(i)) {
                    addresses.add((Inet4Address) i);
                }
            }
        }
        return addresses;
    }

    /**
     * 過濾迴環網卡、點對點網卡、非活動網卡、虛擬網卡並要求網卡名字是eth或ens開頭
     * @param ni 網卡
     * @return 如果滿足要求則true,否則false
     */
    private static boolean isValidInterface(NetworkInterface ni) throws SocketException {
        return !ni.isLoopback() && !ni.isPointToPoint() && ni.isUp() && !ni.isVirtual()
                && (ni.getName().startsWith("eth") || ni.getName().startsWith("ens"));
    }

    /**
     * 判斷是否是IPv4,並且內網地址並過濾迴環地址.
     */
    private static boolean isValidAddress(InetAddress address) {
        return address instanceof Inet4Address && address.isSiteLocalAddress() && !address.isLoopbackAddress();
    }
}

以上就是該SpringBoot工程的所有代碼了,請確保可以編譯運行;

製作Docker鏡像

  1. 在pom.xml所在目錄創建文件Dockerfile,內容如下:
# 指定基礎鏡像,這是分階段構建的前期階段
FROM openjdk:8u212-jdk-stretch as builder
# 執行工作目錄
WORKDIR application
# 配置參數
ARG JAR_FILE=target/*.jar
# 將編譯構建得到的jar文件複製到鏡像空間中
COPY ${JAR_FILE} application.jar
# 通過工具spring-boot-jarmode-layertools從application.jar中提取拆分后的構建結果
RUN java -Djarmode=layertools -jar application.jar extract

# 正式構建鏡像
FROM openjdk:8u212-jdk-stretch
WORKDIR application
# 前一階段從jar中提取除了多個文件,這裏分別執行COPY命令複製到鏡像空間中,每次COPY都是一個layer
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
  1. 先編譯構建工程,執行以下命令:
mvn clean package -U -DskipTests 
  1. 編譯成功后,通過Dockerfile文件創建鏡像:
sudo docker build -t bolingcavalry/probedemo:0.0.1 .
  1. 鏡像創建成功:

SpringBoot的鏡像準備完畢,接下來要讓kubernetes環境用上這個鏡像;

將鏡像加載到kubernetes環境

此時的鏡像保存在開發環境的電腦上,可以有以下三種方式加載到kubernetes環境:

  1. push到私有倉庫,kubernetes上使用時也從私有倉庫獲取;
  2. push到hub.docker.com,kubernetes上使用時也從hub.docker.com獲取,目前我已經將此鏡像push到hub.docker.com,您在kubernetes直接使用即可,就像nginx、tomcat這些官方鏡像一樣下載;
  3. 在開發環境執行docker save bolingcavalry/probedemo:0.0.1 > probedemo.tar,可將此鏡像另存為本地文件,再scp到kubernetes服務器,再在kubernetes服務器執行docker load < /root/temp/202006/04/probedemo.tar就能加載到kubernetes服務器的本地docker緩存中;

以上三種方法的優缺點整理如下:

  1. 首推第一種,但是需要您搭建私有倉庫;
  2. 由於springboot-2.3官方對鏡像構建作了優化,第二種方法也就執行第一次的時候上傳和下載很耗時,之後修改java代碼重新構建時,不論上傳還是下載都很快(只上傳下載某個layer);
  3. 在開發階段,使用第三種方法最為便捷,但如果kubernetes環境有多台機器,就不合適了,因為鏡像是存在指定機器的本地緩存的;

我的kubernetes環境只有一台電腦,因此用的是方法三,參考命令如下(建議安裝sshpass,就不用每次輸入帳號密碼了):

# 將鏡像保存為tar文件
sudo docker save bolingcavalry/probedemo:0.0.1 > probedemo.tar

# scp到kubernetes服務器
sshpass -p 888888 scp ./probedemo.tar root@192.168.50.135:/root/temp/202006/04/ 
  
# 遠程執行ssh命令,加載docker鏡像
sshpass -p 888888 ssh root@192.168.50.135 "docker load < /root/temp/202006/04/probedemo.tar"

kubernetes部署deployment和service

  1. 在kubernetes創建名為probedemo.yaml的文件,內容如下,注意pod副本數是2,另外請關注livenessProbe和readinessProbe的參數配置:
apiVersion: v1
kind: Service
metadata:
  name: probedemo
spec:
  type: NodePort
  ports:
    - port: 8080
      nodePort: 30080
  selector:
    name: probedemo
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: probedemo
spec:
  replicas: 2
  template:
    metadata:
      labels:
        name: probedemo
    spec:
      containers:
        - name: probedemo
          image: bolingcavalry/probedemo:0.0.1
          tty: true
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 8080
            initialDelaySeconds: 5
            failureThreshold: 10
            timeoutSeconds: 10
            periodSeconds: 5
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 8080
            initialDelaySeconds: 5
            timeoutSeconds: 10
            periodSeconds: 5
          ports:
            - containerPort: 8080
          resources:
            requests:
              memory: "512Mi"
              cpu: "100m"
            limits:
              memory: "1Gi"
              cpu: "500m"
  1. 執行命令kubectl apply -f probedemo..yaml,即可創建deployment和service:
  1. 這裏要重點關注的是livenessProbeinitialDelaySecondsfailureThreshold參數,initialDelaySeconds等於5,表示pod創建5秒后檢查存活探針,如果10秒內應用沒有完成啟動,存活探針不返回200,就會重試10次(failureThreshold等於10),如果重試10次后存活探針依舊無法返回200,該pod就會被kubernetes殺死重建,要是每次啟動都耗時這麼長,pod就會不停的被殺死重建;
  2. 執行命令kubectl apply -f probedemo.yaml,創建deployment和service,如下圖,可見在第十秒的時候pod創建成功,但是此時還未就緒:
  1. 繼續查看狀態,創建一分鐘后兩個pod終於就緒:
  1. kubectl describe命令查看pod狀態,事件通知显示存活和就緒探針都有失敗情況,不過因為有重試,因此後來狀態會變為成功:

至此,從編碼到部署都完成了,接下來驗證SpringBoot-2.3.0.RELEASE的探針技術;

驗證SpringBoot-2.3.0.RELEASE的探針技術

  1. 監聽類AvailabilityListener的作用是監聽狀態變化,看看pod日誌,看AvailabilityListener的代碼是否有效,如下圖紅框,在應用啟動階段AvailabilityListener被成功回調,打印了存活和就緒狀態:
  1. kubernetes所在機器的IP地址是192.168.50.135,因此SpringBoot服務的訪問地址是http://192.168.50.135:30080/xxx

  2. 訪問地址http://192.168.50.135:30080/actuator/health/liveness,返回碼如下圖紅框,可見存活探針已開啟:

  1. 就緒探針也正常:
  1. 打開兩個瀏覽器,都訪問:http://192.168.50.135:30080/hello,多次Ctrl+F5強刷,如下圖,很快就能得到不同結果,證明響應來自不同的Pod:
  1. 訪問:http://192.168.50.135:30080/statereader/get,可以得到存活和就緒的狀態,可見StateReader的代碼已經生效,可以通過ApplicationAvailability接口取得狀態:
  1. 修改就緒狀態,訪問:http://192.168.50.135:30080/statewriter/refuse,如下圖紅框,可見收到請求的pod,其就緒狀態已經出現了異常,證明StateWritter.java中修改就緒狀態后,可以讓kubernetes感知到這個pod的異常
  1. 用瀏覽器反覆強刷hello接口,返回的Pod地址也只有一個,證明只有一個Pod在響應請求:
  1. 嘗試恢復服務,注意請求要在服務器後台發送,而且IP地址要用剛才被設置為refuse的pod地址
curl http://10.233.90.195:8080/statewriter/accept
  1. 如下圖,狀態已經恢復:
  1. 最後再來試試將存活狀態從CORRECT改成BROKEN,瀏覽器訪問:http://192.168.50.135:30080/statewriter/broken
  2. 如下圖紅框,重啟次數變成1,表示pod被殺死了一次,並且由於重啟導致當前還未就緒,證明在SpringBoot中修改了存活探針的狀態,是會觸發kubernetes殺死pod的
  1. 等待pod重啟、就緒探針正常后,一切恢復如初:
  1. 強刷瀏覽器,如下圖紅框,兩個Pod都能正常響應:

官方忠告

  • 至此,《掌握SpringBoot-2.3的容器探針》系列就全部完成了,從理論到實踐,咱們一起學習了SpringBoot官方帶給我們的容器化技術,最後以一段官方忠告來結尾,大家一起將此忠告牢記在心:
  • 我對以上內容的理解:選擇外部系統的服務作為探針的時候要謹慎(外部系統可能是數據庫,也可能是其他web服務),如果外部系統出現問題,會導致kubernetes殺死pod(存活探針問題),或者導致kubernetes不再調度請求到pod(就緒探針問題);(再請感謝大家容忍我的英語水平)

歡迎關注我的公眾號:程序員欣宸

https://github.com/zq2599/blog_demos

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

自動擋!大空間!ESP!這款不到10萬的SUV值得考慮

所以在10萬元以內的購車預算,選擇一台國產車很划算,不僅配置高、而且車身尺寸更大,看起來更大氣。作為一款擁有超高性價比的家用SUV車型,森雅R7自動擋順應了那些注重家庭,銳意進取,年輕時尚且懂得享受高品質生活的人群需求,剛上市首月就有1萬多的訂單也足以說明它具備成為熱門車型的潛力,而這一次上市的自動擋車型,更能進一步豐富了車型的產品線,而對小型SUV市場來說,又是一次強有力的衝擊。

隨着我們生活質量的不停提高,人們對於購車的需求越來越強烈,而今已經有越來越多的消費者將購車的計劃擺在了首位。來總結一下我國消費者的購車需求。目前汽車還算得上是一件奢飾品,很多人將買車當成一件漲面子的事情,所以人們買車都喜歡選擇一些尺寸大、顏值高、配置高的車子。

SUV之所以流行,除了它本身擁有高底盤高通過性的優勢以外,假如SUV和轎車的尺寸相差不大,兩者中SUV看起來更高檔次!消費者在選車時,除了看臉,內在也很重要,一個好的內飾、一堆逆天的配置更能吸引到消費者的關注。說實話,為什麼這麼多人在10萬以內都傾向於選擇國產車?因為合資車配置車型都太落後了呀!所以在10萬元以內的購車預算,選擇一台國產車很划算,不僅配置高、而且車身尺寸更大,看起來更大氣!

作為一款擁有超高性價比的家用SUV車型,森雅R7自動擋順應了那些注重家庭,銳意進取,年輕時尚且懂得享受高品質生活的人群需求,剛上市首月就有1萬多的訂單也足以說明它具備成為熱門車型的潛力,而這一次上市的自動擋車型,更能進一步豐富了車型的產品線,而對小型SUV市場來說,又是一次強有力的衝擊。而森雅R7自動擋車型目前有舒適型、豪華型、智能型、尊貴型,售價7.89~9.29萬之間。為了滿足用戶的更高需求,還將推出“森雅R7 AT版 旗艦型”,售價 9.99萬元。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

[C#.NET 拾遺補漏]05:操作符的幾個騷操作

閱讀本文大概需要 1.5 分鐘。

大家好,這是極客精神【C#.NET 拾遺補漏】專輯的第 5 篇文章,今天要講的內容是操作符。

操作符的英文是 Operator,在數值計算中習慣性的被叫作運算符,所以在中文的概念中,運算符是操作符的一個子集。

操作符是很基礎的知識了,基礎歸基礎,我們來回顧一下 C# 操作符那些比較騷的操作,能想到的不多,請大家補充。

操作符的重載

操作符重載大部分語言都沒有,而 C# 有。C# 允許用戶定義類型對操作符進行重載,方式是使用 operate 關鍵字把操作符寫成公開靜態函數。下面來演示一下重載 + 這個操作符。

我們創建一個 Complex 結構類型來代表一個複數,我們知道複數有實數和虛數組成,於是可以這樣定義:

public struct Complex
{
    public double Real { get; set; }
    public double Imaginary { get; set; }
}

現在我們想實現複數的相加操作,即:

Complex a = new Complex() { Real = 1, Imaginary = 2 };
Complex b = new Complex() { Real = 4, Imaginary = 8 };
Complex c = a + b;

默認情況,自定義類是不能進行算術運算的,以上 a + b 會編譯報錯,我們需要對 + 進行操作符重載:

public static Complex operator +(Complex c1, Complex c2)
{
    return new Complex
    {
        Real = c1.Real + c2.Real,
        Imaginary = c1.Imaginary + c2.Imaginary
    };
}

C# 中像加減乘除等這類操作符都可以重載,也有些操作符是不能重載的,具體請查看文末參考鏈接。

隱式和顯式轉換操作符

我們知道子類可以隱式轉換為父類,在某種情況下(如父類由子類賦值而來)父類可以顯式轉換為子類。

在 C# 中,對於沒有子父類關係的用戶定義類型,也是可以實現顯式和隱式轉換的。C# 允許用戶定義類型通過使用 implicitexplicit 關鍵字來控制對象的賦值和對象的類型轉換。它的定義形式如下:

public static <implicit/explicit> operator <結果類型>(<源類型> myType)

這裏以結果類型為方法名,源類型對象作為參數,只能是這一個參數,不能定義第二個參數,但可以通過該參數對象訪問其類的私有成員。下面是一個既有顯式又有隱式轉換操作符的例子:

public class BinaryImage
{
    private readonly bool[] _pixels;

    // 隱式轉換操作符示例
    public static implicit operator ColorImage(BinaryImage bm)
    {
        return new ColorImage(bm);
    }

    // 顯式轉換操作符示例
    public static explicit operator bool[](BinaryImage bm)
    {
        return bm._pixels;
    }
}

public class ColorImage
{
    public ColorImage(BinaryImage bm) { }
}

這樣,我們就可以把 BinaryImage 對象隱式轉換為 ColorImage 對象,把 BinaryImage 對象顯式轉換為 bool 數組對象:

var binaryImage = new BinaryImage();
ColorImage colorImage = binaryImage; // 隱式轉換
bool[] pixels = (bool[])binaryImage; // 顯式轉換

而且轉換操作符可以定義為雙向显示和隱式轉換。既可從你的類型而來,亦可到你的類型而去:

public class BinaryImage
{
    public BinaryImage(ColorImage cm) { }

    public static implicit operator ColorImage(BinaryImage bm)
    {
        return new ColorImage(bm);
    }

    public static explicit operator BinaryImage(ColorImage cm)
    {
        return new BinaryImage(cm);
    }
}

我們知道 as 操作符也是一種顯式轉換操作符,那它適用於上面的這種情況嗎,即:

ColorImage cm = myBinaryImage as ColorImage;

你覺得這樣寫有問題嗎?請在評論區告訴我答案。

空條件和空聯合操作符

空條件(Null Conditional)操作符 ?. 和空聯合(Null Coalescing)操作符 ??,都是 C# 6.0 的語法,大多數人都很熟悉了,使用也很簡單。

?. 操作符會在對象為 null 時立即返回 null,不為 null 時才會調用後面的代碼。其中的符號 ? 代表對象本身,符號 . 代表調用,後面不僅可以是對象的屬性也可以是索引器或方法。以該操作符為分隔的每一截類型相同時可以接龍。示例:

var bar = foo?.Value; // 相當於 foo == null ? null : foo.Value
var bar = foo?.StringValue?.ToString(); // 每一截類型相同支持接龍
var bar = foo?.IntValue?.ToString(); // 每一截類型不同,不能接龍,因為結果類型無法確定

如果是調用索引器,則不需要符號 .,比如:

var foo = new[] { 1, 2, 3 };
var bar = foo?[1]; // 相當於 foo == null ? null : foo[1]

空聯合操作符 ??,當左邊為空時則返回右邊的值,否則返回左邊的值。同樣,每一截的類型相同時支持接龍。

var fizz = foo.GetBar() ?? bar;
var buzz = foo ?? bar ?? fizz;

=> Lambda 操作符

Lambda 操作符,即 =>,它用來定義 Lambda 表達式,也被廣泛用於 LINQ 查詢。它的一般定義形式如下:

(input parameters) => expression

示例:

string[] words = { "cherry", "apple", "blueberry" };
int minLength = words.Min((string w) => w.Length);

實際應用中我們一般省略參數的類型聲明:

int minLength = words.Min(w => w.Length);

Lambda 操作符的後面可以是表達式,可以是語句,也可以是語句塊,比如:

// 表達式
(int x, int y) => x + y

// 語句
(string x) => Console.WriteLine(x)

// 語句塊
(string x) => {
    x += " says Hello!";
    Console.WriteLine(x);
}

這個操作符也可以很方便的用來定義委託方法(其實 Lambda 操作符就是由委託演變而來)。

單獨定義委託方法:

void MyMethod(string s)
{
    Console.WriteLine(s + " World");
}
delegate void TestDelegate(string s);
TestDelegate myDelegate = MyMethod;
myDelegate("Hello");

使用 Lambda 操作符:

delegate void TestDelegate(string s);
TestDelegate myDelegate = s => Console.WriteLine(s + " World");
myDelegate("Hello");

在一個類中,當實現體只有一句代碼時,也可以用 Lambda 操作符對方法和 Setter / Getter 進行簡寫:

public class Test
{
    public int MyProp { get => 123; }
    public void MyMethod() => Console.WriteLine("Hello!");
}

以上是幾種比較有代表性的操作符的“騷”操作,當然還有,但大多都過於基礎,大家都知道,就不總結了。

C# 雖然目前不是最受歡迎的語言,但確實是一門優美的語言,其中少不了這些操作符語法糖帶來的功勞。

參考:https://bit.ly/3h5yKNr

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化