前有強敵後有追兵 看起亞新一代K2如何破局

這樣的小細節,正是對起亞追求的時尚和年輕最好的體現。除此外,新一代K2的座椅也是一大亮點。作為一款小型車,亮棕色的運動座椅很好的提升了新車的檔次感。不僅如此,這張座椅的支撐性和包裹性都做的很不錯。整體來說,這套座椅無論是從觀感還是乘坐感受都達到了同級車型第一陣營,即便是對標部分高一級別車型也毫不遜色。

隨着國內購車群體逐年呈年輕化,如今不少“95后”都開始加入了“買車大軍”。那麼問題來了,人生第一台車該怎麼選?回答這個問題之前,我們要弄清楚年輕朋友們喜歡什麼。

看臉的時代,長得漂亮是首要因素。對於年輕朋友,甭管是找對象還是買車,第一眼肯定是看顏值;此外在互聯網下成長起來的年輕人,對車內的科技配置同樣非常看中,因此豐富的配置也是不可或缺的一點;除了以上兩點,還有最重要的就是價格,年輕朋友都買得起的合理售價才是一切的前提,否則都成為空談。

經過一輪篩選,叫獸發現起亞剛剛上市的新一代K2恰好滿足以上幾點。接下來大家就來看看它究竟有那些亮點可以列入到年輕朋友們的備選清單呢?

你如果夠漂亮,我才會對你產生興趣

韓系車一向走的是時尚年輕的路線,老款K2在當年絕對能算得上是同級里的“顏值擔當”,深得年輕朋友們的喜歡。新一代K2採用了起亞最新的家族前臉,中網和大燈變化令新車的視覺衝擊力更強,整體看上去大氣了不少。

側面相比老款變得更加修長,讓人又找到了“小K5”的影子。同時新一代K2的車身尺寸也有所加長,新車的長寬高分別達到4400/1740/1460mm,軸距則由2570mm提升為2600mm。對於一款小型車而言,這樣的變化令叫獸對它的空間表現非常期待。

車尾呈現了簡潔和精緻的一面,時下流行的貫穿式尾燈令新一代K2尾部的辨識度大大提高,同時也體現出東風悅達起亞對時尚和年輕不變的追求。同為年輕人的叫獸表示很喜歡這樣的設計。

叫獸一直在強調一個觀點,作為一款家用車,如果不能達到奔馳那般近乎完美的內飾設計,還是以簡約大方為主比較好,過於繁複反倒會降低乘客的好感度。新一代K2運用了環繞一體型的空間布局,大大強化了人體工程學的設計理念,不少細節處的變化則增添了幾分時尚氣息。正是這樣的設計風格和搭配很好的迎合了年輕客戶的審美。

例如這個全新的三幅式多功能運動方向盤,握感細膩並且大小適中,底部的亮色裝飾堪稱點睛之筆,融入了些許運動感。這樣的小細節,正是對起亞追求的時尚和年輕最好的體現。

除此外,新一代K2的座椅也是一大亮點。作為一款小型車,亮棕色的運動座椅很好的提升了新車的檔次感。不僅如此,這張座椅的支撐性和包裹性都做的很不錯。整體來說,這套座椅無論是從觀感還是乘坐感受都達到了同級車型第一陣營,即便是對標部分高一級別車型也毫不遜色。

沒想到小型車居然有這麼給力的空間

Surprise!

說到了座椅,當然不能少了小型車最受關注的空間問題。正如前文所說,新一代K2的車身尺寸有所加長,那它的空間表現究竟如何呢?

身高175cm的體驗者調整好坐姿以後,頭部尚有1拳以上的空間;來到後排,新一代K2的後排空間達到了接近2拳的距離,這樣的表現已經可以媲美不少緊湊級車型。

最令叫獸驚喜的是,新一代K2的後排隆起非常低,這樣一來後排中間的乘客再也不用憋屈着腿了。

互聯功能將是俘獲年輕朋友的秘密武器

諸如多功能真皮方向盤、倒車影像、日間行車燈甚至是後排出風口都能在新一代K2身上悉數見到,叫獸在這裏要着重講的是中間那塊多功能觸控屏。

在幾乎人手一部智能手機的時代,新一代K2的中控屏加入了CarLife和Carplay兩大智能互聯繫統。值得一提的是,這項配置再也不是蘋果用戶獨享,有了CarLife,安卓用戶同樣可以享用這項功能。這就意味着,99%的客戶都能通過手機在車內實現導航、無線音樂等等功能。要知道這對追求實用至上的小型車來說是一項多麼實用和好玩的配置。

可不只是徒有其表

老K2那套4AT變速箱放到如今來看的確有些不太入流,起亞當然意識到了這一點,於是給新一代K2換裝了全新6速手自一體變速箱和6擋手動變速箱。這樣的升級對新車的油耗和駕駛平順性無疑將會有非常好的改善。

面對強敵,新一代K2擁有媲美緊湊型車的大空間和高配置;面對追兵,新一代K2有着堪稱“顏值擔當”的造型設計以及品牌影響力。這麼一款長得漂亮,空間還大,配置夠潮夠豐富的小型車,我心動了,你呢?本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

※超省錢租車方案

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

十來萬買車要能上班還能自駕游,你的城市適合哪輛車?

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

這款顏值超高的合資小車妹子看了都喜歡,而且可能7萬起

有了這種材質,某些司機要偷着樂了。相對於前臉、車身的個性,尾部的設計要平常許多了。內飾部分整體設計偏向矩形的設計,雙色搭配也是恰到好處,看起來和外觀一樣也是活力十足,C3的設計師說內飾的設計靈感主要來源於家庭室內設計,力求為C3設計出更為現代、舒適的座艙。

最近看到不少讀者在後台留言說想要了解一下雪鐵龍C3的情況,所以今天我們就來看一下這款個性十足的法系車-雪鐵龍C3。

說起雪鐵龍C3,大家的第一反應應該是C3-XR吧,但是這個C3可不是大家熟悉的那個小型SUV C3-XR,而是雪鐵龍的的一款小型車。

C3的歷史要比C3-XR更長,因為第一代的C3早在2002年的時候就推出了。一經推出就獲得了熱銷,到目前為止累計銷量近400萬台,當然,C3的主要戰場還是在歐洲。

全新一代的雪鐵龍C3早在今年9月份就發布了,一發布便引起了熱議,因為這款非常個性的小車給大家留下了深刻的印象。

C3的長度只有3990mm,其他尺寸暫時未知,長度還不到四米,所以C3看起來比較短小,但是較短的尺寸並不會影響法國人天馬行空的設計。C3的造型極其可愛,前臉渾圓一體,造型獨特。

比如前臉獨特的大燈組設計,LED日行燈和引擎蓋部分的鍍鉻裝飾條相互融合,大燈和霧燈造型為凹進去矩形設計,看起來比較別緻。

前臉的造型已經夠獨特了,側面的設計也充滿看點,轉向指示燈裝在了有着黑色塗裝的后視鏡上。整個車身側面的個性塗裝讓C3看起來更加與眾不同。

不過輪圈的造型,也會讓我過目不忘的。

既然喜歡玩個性,那就一玩到底,C3將會提供9種車身顏色和3種車頂顏色,總計36種顏色組合,滿足消費者對個性的需求。

新車還採用了Airbump技術,特殊的TpU柔軟材質可以抵抗輕微的刮蹭。有了這種材質,某些司機要偷着樂了。

相對於前臉、車身的個性,尾部的設計要平常許多了。

內飾部分整體設計偏向矩形的設計,雙色搭配也是恰到好處,看起來和外觀一樣也是活力十足,C3的設計師說內飾的設計靈感主要來源於家庭室內設計,力求為C3設計出更為現代、舒適的座艙。

空調出風口搞成這個樣子,估計只有法國人能幹出來。新車也會有全景天窗,只是不能開啟。

至於空間,肯定是比較局促的,正常體格的成年人坐在後排不管是頭部空間還是腿部空間都不會太寬敞。

C3的發動機為1.2T三缸汽油發動機和1.6T柴油發動機,其中1.2T發動機將會有三種不同的動力調教,變速箱為手動擋和6擋自動。

遺憾的是這個車子目前會在歐洲上市,中國未來只是有可能引進,畢竟雪鐵龍在國內沒有一款小型車。如果真的引進國內國產了,小編希望原封不動的引進,或者盡可能的保持原來的面貌。如果售價能七萬起,憑藉如此個性的C3,絕對會吸引年輕消費者的青睞。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※推薦台中搬家公司優質服務,可到府估價

聽說這是20萬內老婆最滿意的車

它還是會打一個激靈再往前沖。不過調到運動模式之後,你才會更深的感受到這輛車的動力。儀錶盤隨即變為紅色主題,好像是有點鋼炮的意思。只不過它增加的是心理馬力,但是對於速度的提升還是很明顯的。V40的懸挂調校是那種偏硬的感覺,對於車身的支撐是相當足,但是舒適性和1系來比會打點折扣。

自從吉利把沃爾沃收了之後,大家對這個北歐品牌的熟悉程度就更高了。只不過知名度歸知名度,要真是讓你買車選一輛沃爾沃,可能大家都未必願意。所以今天就給大家試一下它的入門級車型-V40。

就在試駕車剛剛到公司,小喬就撲了上去,並引發了以下對話…

既然是北歐的廠商,那必然要帶有北歐特色才行。沒錯,“雷神之錘”大家都聽說過了吧,說的就是沃爾沃的頭燈,V40打開日間行車燈后,視覺效果確實很不錯,但這種畫風更適合年輕買家。相對於能見度較高的A3、1系或A級來說,V40就是小眾但不失氣質的存在。

V40提供了T3/T4/T5三個代號的車型選擇,分別搭載1.5T/2.0T四缸發動機。畢竟是輛買菜車,V40的動力輸出都是相當穩當般的存在。正常模式下想急加速?它還是會打一個激靈再往前沖。

不過調到運動模式之後,你才會更深的感受到這輛車的動力。儀錶盤隨即變為紅色主題,好像是有點鋼炮的意思。只不過它增加的是心理馬力,但是對於速度的提升還是很明顯的。

V40的懸挂調校是那種偏硬的感覺,對於車身的支撐是相當足,但是舒適性和1系來比會打點折扣。值得一提的是V40的隔音相當不錯,但問題是這個不錯只局限於風噪。正常行駛下路面的噪聲就直接從車底傳進來,這很尷尬,但也沒辦法。

V40的內飾還是保持了一貫的沃爾沃風格。最明顯的就是中控台萬年不變的数字鍵。而它又長又大的手剎把就成為了中控台的一道風景。

反觀V40的空間表現,還是能讓家人滿意的。最起碼它的後排空間沒有給人覺得任何憋屈的感覺。

V40作為一輛入門級車型,官方售價終端為18.89-30.99萬元,對於那些追求獨特的年輕車主來說,這是一個不小的誘惑。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

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

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

微服務海量日誌監控平台

前面幾章蜻蜓點水的介紹了elasticsearch、apm相關的內容。本片主要介紹怎麼使用ELK Stack幫助我們打造一個支撐起日產TB級的日誌監控系統

背景

在企業級的微服務環境中,跑着成百上千個服務都算是比較小的規模了。在生產環境上,日誌扮演着很重要的角色,排查異常需要日誌,性能優化需要日誌,業務排查需要業務等等。然而在生產上跑着成百上千個服務,每個服務都只會簡單的本地化存儲,當需要日誌協助排查問題時,很難找到日誌所在的節點。也很難挖掘業務日誌的數據價值。那麼將日誌統一輸出到一個地方集中管理,然後將日誌處理化,把結果輸出成運維、研發可用的數據是解決日誌管理、協助運維的可行方案,也是企業迫切解決日誌的需求。

我們的解決方案

通過上面的需求我們推出了日誌監控系統。

  • 日誌統一收集、過濾清洗。
  • 生成可視化界面、監控,告警,日誌搜索。

功能流程概覽

  • 在每個服務節點上埋點,實時採集相關日誌。
  • 統一日誌收集服務、過濾、清洗日誌後生成可視化界面、告警功能。

我們的架構

  1. 日誌文件採集端我們使用filebeat,運維通過我們的後台管理界面化配置,每個機器對應一個filebeat,每個filebeat日誌對應的topic可以是一對一、多對一,根據日常的日誌量配置不同的策略。除了採集業務服務日誌外,我們還收集了mysql的慢查詢日誌和錯誤日誌,還有別的第三方服務日誌,如:nginx等。最後結合我們的自動化發布平台,自動發布並啟動每一個filebeat進程。
  2. 調用棧、鏈路、進程監控指標我們使用的代理方式:Elastic APM,這樣對於業務側的程序無需任何改動。對於已經在運營中的業務系統來說,為了加入監控而需要改動代碼,那是不可取的,也是無法接受的。Elastic APM可以幫我們收集http接口的調用鏈路、內部方法調用棧、使用的sql、進程的cpu、內存使用指標等。可能有人會有疑問,用了Elastic APM,其它日誌基本都可以不用採集了。還要用filebeat幹嘛?是的,Elastic APM採集的信息確實能幫我們定位80%以上的問題,但是它不是所有的語言都支持的比如:C。其二、它無法幫你採集你想要的非error日誌和所謂的關鍵日誌,比如:某個接口調用時出了錯,你想看出錯時間點的前後日誌;還有打印業務相關方便做分析的日誌。其三、自定義的業務異常,該異常屬於非系統異常,屬於業務範疇,APM會把這類異常當成系統異常上報,如果你後面對系統異常做告警,那這些異常將會幹擾告警的準確度,你也不能去過濾業務異常,因為自定義的業務異常種類也不少。
  3. 同時我們對agent進行了二開。採集更詳細的gc、堆棧、內存、線程信息。
  4. 服務器採集我們採用普羅米修斯。
  5. 由於我們是saas服務化,服務N多,很多的服務日誌做不到統一規範化,這也跟歷史遺留問題有關,一個與業務系統無關的系統去間接或直接地去對接已有的業務系統,為了適配自己而讓其更改代碼,那是推不動的。牛逼的設計是讓自己去兼容別人,把對方當成攻擊自己的對象。很多日誌是沒有意義的,比如:開發過程中為了方便排查跟蹤問題,在if else里打印只是有標誌性的日誌,代表是走了if代碼塊還是else代碼塊。甚至有些服務還打印着debug級別的日誌。在成本、資源的有限條件下,所有所有的日誌是不現實的,即使資源允許,一年下來將是一比很大的開銷。所以我們採用了過濾、清洗、動態調整日誌優先級採集等方案。首先把日誌全量採集到kafka集群中,設定一個很短的有效期。我們目前設置的是一個小時,一個小時的數據量,我們的資源暫時還能接受。
  6. Log Streams是我們的日誌過濾、清洗的流處理服務。為什麼還要ETL過濾器呢?因為我們的日誌服務資源有限,但不對啊,原來的日誌分散在各各服務的本地存儲介質上也是需要資源的哈。現在我們也只是彙集而已哈,收集上來后,原來在各服務上的資源就可以釋放掉日誌佔用的部分資源了呀。沒錯,這樣算確實是把原來在各服務上的資源化分到了日誌服務資源上來而已,並沒有增加資源。不過這隻是理論上的,在線上的服務,資源擴大容易,收縮就沒那麼容易了,實施起來極其困難。所以短時間內是不可能在各服務上使用的日誌資源化分到日誌服務上來的。這樣的話,日誌服務的資源就是當前所有服務日誌使用資源的量。隨存儲的時間越長,資源消耗越大。如果解決一個非業務或非解決不可的問題,在短時間內需要投入的成本大於解決當前問題所帶來收益的話,我想,在資金有限的情況下,沒有哪個領導、公司願意採納的方案。所以從成本上考慮,我們在Log Streams服務引入了過濾器,過濾沒有價值的日誌數據,從而減少了日誌服務使用的資源成本。技術我們採用Kafka Streams作為ETL流處理。通過界面化配置實現動態過濾清洗的規則:
  • 界面化配置日誌採集。默認error級別的日誌全量採集
  • 以錯誤時間點為中心,在流處理中開窗,輻射上下可配的N時間點採集非error級別日誌,默認只採info級別
  • 每個服務可配100個關鍵日誌,默認關鍵日誌全量採集
  • 在慢sql的基礎上,按業務分類配置不同的耗時再次過濾
  • 按業務需求實時統計業務sql,比如:高峰期階段,統計一小時內同類業務sql的查詢頻率。可為dba提供優化數據庫的依據,如按查詢的sql創建索引
  • 高峰時段按業務類型的權重指標、日誌等級指標、每個服務在一個時段內日誌最大限制量指標、時間段指標等動態清洗過濾日誌 
  • 根據不同的時間段動態收縮時間窗口
  • 日誌索引生成規則:按服務生成的日誌文件規則生成對應的index,比如:某個服務日誌分為:debug、info、error、xx_keyword,那麼生成的索引也是debug、info、error、xx_keyword加日期作後綴。這樣做的目的是為研發以原習慣性地去使用日誌

    7. 可視化界面我們主要使用grafana,它支持的眾多數據源中,其中就有普羅米修斯和elasticsearch,與普羅米修斯可謂是無縫對接。而kibana我們主要用於apm的可視分析

日誌可視化

【版權聲明】

本文版權歸作者(深圳伊人網網絡有限公司)和博客園共有,歡迎轉載,但未經作者同意必須在文章頁面給出原文鏈接,否則保留追究法律責任的權利。如您有任何商業合作或者授權方面的協商,請給我留言:siqing0822@163.com

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

【其他文章推薦】

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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

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

深入理解JVM(③)判斷對象是否還健在?

前言

因為Java對象主要存放在Java堆里,所以垃圾收集器(Garbage Collection)在對Java堆進行回收前,第一件事情就是要確定這些對象之中哪些還“存活”着,哪些已經“死去”(不被引用了)。

判斷對象是否健在的算法

1.引用計數算法

引用計數算法,很容易理解,在對象中添加一個引用計數器,每有一個地方引用它時,計數器值就加一;當引用失效是,計數器值就減一;任何時刻計數器為零的對象就是不可以能再被使用的對象
引用計數算法的原理簡單,判定效率也很高。市面上也確實有一些技術使用的此類算法來判定對象是否存活,像ActionScript 3 的FlashPlayer、Python語言等。但是在主流的Java虛擬機裏面都沒有選用引用計算法來管理內存,主要是使用此算法時,必須要配合大量的額外處理才能保證正確的工作,例如要解決對象之間的相互循環引用的問題。

public class OneTest {

    public Object oneTest = null;

    private static final int _1MB = 1024 * 1024;

    private byte[] bigSize = new byte[256 * _1MB];


    /**
     * 這個成員屬性的唯一意義就是占點內存,以便能在GC日誌中看清楚是否有回收過。
     */
    @Test
    public void testGC(){

        OneTest test1 = new OneTest();
        OneTest test2 = new OneTest();

        test1.oneTest = test2;
        test2.oneTest = test1;

        test1 = null;
        test2 = null;

        // 假設在這行發生GC,test1和test2是否能被回收?
        System.gc();

    }

}

分析代碼,test1和test2對象都被設置成了null,在後面發生GC的時候,如果按照引用計數算法,這兩個對象雖然都被設置成了null,但是test1引用了test2,test2又引用了test1,所以這兩個對象的引用計數值都不為0,所以都不會被回收,但是真正的實際運行結果是,這兩個對象都被回收了,這也說明HotSpot虛擬機並不是用引用計數法來進行的內存管理。

2. 可達性分析算法

當前主流的商用程序語言(Java、C#等),都是通過可達性分析(Reachability Analysis)算法來判斷對象是否存活的。這個算法的基本思路就是通過一一系列稱為“GC Roots” 的根對象作為起始節點集,從這些節點開始根據引用關係向下搜索,搜索走過的的路徑稱為“引用鏈”(Reference Chain),如果某個對象到GC Roots 間沒有任何引用鏈相連,或者從GC Roots 到這個對象不可達時,則證明此對象是不可能再被使用的。
如下圖,object10、object11、object12這三個對象,雖然互相有關聯,但是它們到GC Roots是不可達的,因此它們會被判定為可回收的對象。

在Java程序中,固定可作為GC Roots 的對象包括以下幾種:

  • 在虛擬機棧(棧幀中的本地變量表)中引用的對象,譬如各個現場被調用的方法堆棧中使用到的參數、局部變量、臨時變量等。
  • 在方法區中類靜態屬性引用的對象,譬如Java類的引用類型靜態變量。
  • 在方法區中常量引用的對象,譬如字符串常量池(String Table)里的引用。
  • 在本地方法棧中JNI(即通常所說的Native方法)引用的對象。
  • Java虛擬機內部的引用,如基本數據類型對應的Class對象,一些常駐的異常對象(NullPointException、OutOfMemoryError)等,還有系統類加載器。
  • 所有被同步鎖(synchronized關鍵字)持有的對象。
  • 反映Java虛擬機內部情況的JMXBean、JVMTI中註冊的回調、本地代碼緩存等。
    除了這些固定的GC Roots集合以外,根據垃圾收集器以及當前回收的呢村區域不同,還會有其他對象“臨時性”的加入,如果只針對Java堆中某一塊兒區域發起垃圾收集時(例如只針對年輕代的垃圾收集),必須考慮到當前區域內的對象是否有被其他區域的對象所引用,這個時候就需要把這些關聯區域的對象一併加入GC Roots集合中,來保證可達性分析的正確性。

重申引用

無論是通過引用計數算法判斷對象的引用數量,還是通過可達性分析算法判斷對象是否引用鏈可達,判斷對象是否存活都和“引用”離不開關係。在JDK1.2之前,Java里對引用的概念是:如果reference類型的數據中存儲的數值代表的是另外一塊兒內存的地址,就稱該reference數據是代表某塊內存、某個對象的引用。
在JDK1.2版之後,Java對引用的概念進行了擴充,將引用分為強引用(Strongly Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)4種,這4種引用強度依次逐漸減弱。

  • 強引用是最傳統的“引用”的定義,指引用複製,即類似
Object obj = new Object()

這種引用關係。無論在任何情況下,只要強引用關係還存在,垃圾收集器就不會回收掉被引用的對象。

  • 軟引用是用來描述一些還有用,但非必須的對象。在系統發生內存溢出前,會先對軟引用對象進行第二次回收,如果回收后還沒有足夠的內存,才會拋出內存溢出的異常。
  • 弱引用也是用來描述那些非必須的對象,但是它的強度比軟引用更弱一些,弱引用的對象,只能生存到下一次垃圾收集發生為止。當垃圾收集器開始工作,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象。
  • 虛引用也稱為“幽靈引用”或“幻影引用”,它是最弱的一種引用關係。為一個對象設置虛引用關聯的唯一目的只是為了能在這個對象被收集器回收時收到一個系統通知。

判斷對象是生是死的過程

即使在可達性分析算法中,判斷為不可達的對象,也不是“非死不可”的,要真正宣告一個對象死亡,至少要經歷兩次標記過程:

  • 如果第一次對象在進行可達性分析后發現與GC Roots 不可達,將進行第一次標記。
  • 隨後對此對象進行一次是否有必要執行finalize()方法進行篩選,假如對象沒有覆蓋finalize()方法,或者finalize()方法已經被虛擬機調用過,都視為“沒有必要執行”。
    如果對象被判定有必要執行finalize()方法,會將對象放置在一個名為F-Queue的隊列中,並在由一條由虛擬機自動建立的、低調度的線程區執行它們的finalize()方法。但並不承諾一定會等待它們運行結束。

需要注意的是:任何一個對象的finalize()方法都只會被系統自動調用一次,如果對象面臨第二次回收,它的finalize()方法不會被再次執行。
還有一點就是Java官方已經明確聲明不推薦手動調用finalize()方法了,因為它的運行代價高昂,不確定性大,無法保證各個對象的調用順序,並且finanlize()能做的所有工作,使用try-finally或其他方式都可以做的更好、更及時。

回收方法區

方法區垃圾收集的“性價比”通常比較低,並且方法區回收也有過於苛刻的判定條件。
方法區的垃圾收集主要回收兩部分內容:廢棄的常量不再使用的類型,回收廢棄常量時,如果當前系統沒有一個常量的值是當前常量值,且虛擬機中也沒有其他地方引用這個常量。如果這個時候發生垃圾回收,常量就會被系統清理出常量池。
判定一個類型是否屬於“不再使用的類”的條件就比較苛刻了,要同時滿足如下三個條件:

  • 該類所有的實例都已經被回收,也就是Java堆中不存在該類及其任何派生子類的實例。
  • 加載該類的類加載器已經被回收,這個條件除非是經過精心設計的可替換類加載器的場景,如OSGi、JSP的沖加載等,否則通常很難達成的。
  • 該類對應的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。

同時滿足了上述的三個條件后,也只是被允許進行回收了,關於是否要對類型進行回收還要對虛擬機進行一系列的參數設置,這裏就不贅述了,感興趣的可以自己去查詢。

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

【其他文章推薦】

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

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

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

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

漲見識了,在終端執行 Python 代碼的 6 種方式!

原作:BRETT CANNON

譯者:豌豆花下貓@Python貓

英文:https://snarky.ca/the-many-ways-to-pass-code-to-python-from-the-terminal

為了我們推出的 VS Code 的 Python 插件 [1],我寫了一個簡單的腳本來生成變更日誌 [2](類似於Towncrier [3],但簡單些,支持 Markdown,符合我們的需求)。在發布過程中,有一個步驟是運行python news ,它會將 Python 指向我們代碼中的”news”目錄。

前幾天,一位合作者問這是如何工作的,似乎我們團隊中的每個人都知道如何使用-m ?(請參閱我的有關帶 -m 使用 pip 的文章 [4],了解原因)(譯註:關於此話題,我也寫過一篇更為詳細的文章 )

這使我意識到其他人可能不知道有五花八門的方法可以將 Python 指向要執行的代碼,因此有了這篇文章。

1、通過標準輸入和管道

因為如何用管道傳東西給一個進程是屬於 shell 的內容,我不打算深入解釋。毋庸置疑,你可以將代碼傳遞到 Python 中。

# 管道傳內容給 python
echo "print('hi')" | python

如果將文件重定向到 Python,這顯然也可以。

# 重定向一個文件給 python
python < spam.py

歸功於 Python 的 UNIX 傳統,這些都不太令人感到意外。

2、通過-c 指定的字符串

如果你只需要快速地檢查某些內容,則可以在命令行中將代碼作為字符串傳遞。

# 使用 python 的 -c 參數
python -c "print('hi')"

當需要檢查僅一行或兩行代碼時,我個人會使用它,而不是啟動 REPL(譯註:Read Eval Print Loop,即交互式解釋器,例如在 windows 控制台中輸入python, 就會進入交互式解釋器。-c 參數用法可以省去進入解釋器界面的過程) 。

3、文件的路徑

最眾所周知的傳代碼給 python 的方法很可能是通過文件路徑。

# 指定 python 的文件路徑
python spam.py

要實現這一點的關鍵是將包含該文件的目錄放到sys.path 里。這樣你的所有導入都可以繼續使用。但這也是為什麼你不能/不應該傳入包含在一個包里的模塊路徑。因為sys.path 可能不包含該包的目錄,因此所有的導入將相對於與你預期的包不同的目錄。

4、對包使用 -m

執行 Python 包的正確方法是使用 -m 並指定要運行的包名。

python -m spam

它在底層使用了runpy [5]。要在你的項目中做到這點,只需要在包里指定一個__main__.py 文件,它將被當成__main__ 執行。而且子模塊可以像任何其它模塊一樣導入,因此你可以對其進行各種測試。

我知道有些人喜歡在一個包里寫一個main 子模塊,然後將其__main__.py 寫成:

from . import main

if __name__ == "__main__":
    main.main()

就我個人而言,我不感冒於單獨的main 模塊,而是直接將所有相關的代碼放入__main__.py ,因為我感覺這些模塊名是多餘的。

(譯註:即作者不關心作為入口文件的”main”或者“__main__”模塊,因為執行時只需用它們的包名即可。我認為這也暗示了入口模塊不該再被其它模塊 import。我上篇文章 [6]比作者的觀點激進,認為連那句 if 語句都不該寫。)

5、目錄

定義__main__.py也可以擴展到目錄。如果你看一下促成此博客文章的示例,python news 可執行,就是因為 news 目錄有一個 __main__.py 文件。該目錄就像一個文件路徑被 Python 執行了。

現在你可能會問:“為什麼不直接指定文件路徑呢?”好吧,坦白說,關於文件路徑,有件事得說清楚。在發布過程中,我可以簡單地寫上說明,讓運行python news/announce.py ,但是並沒有確切的理由說明這種機制何時存在。

再加上我以後可以更改文件名,而且沒人會注意到。再加上我知道代碼會帶有輔助文件,因此將其放在目錄中而不是單獨作為單個文件是有意義的。

當然,我也可以將它變為一個使用 -m 的包,但是沒必要,因為 announce 腳本很簡單,我知道它要保持成為一個單獨的自足的文件(少於 200 行,並且測試模塊也大約是相同的長度)。

況且,__main__.py 文件非常簡單。

import runpy
# Change 'announce' to whatever module you want to run.
runpy.run_module('announce', run_name='__main__', alter_sys=True)

現在顯然必須要處理依賴關係,但是如果你的腳本僅使用標準庫或將依賴模塊放在__main__.py 旁邊(譯註:即同級目錄),那麼就足夠了!

(譯註:我覺得作者在此有點“炫技”了,因為這種寫法的前提是得知道 runpy 的用法,但是就像前一條所寫的用 -m 參數運行一個包,在底層也是用了 runpy。不過炫技的好處也非常明顯,即__main__.py 里不用導入 announce 模塊,還是以它為主模塊執行,也就不會破壞原來的依賴導入關係)

6、執行一個壓縮文件

如果你確實有多個文件和/或依賴模塊,並且希望將所有代碼作為一個單元發布,你可以用一個__main__.py ,放置在一個壓縮文件中,並把壓縮文件所在目錄放在 sys.path 里,Python 會替你運行__main__.py 文件。

# 將一個壓縮包傳給 Python
python app.pyz

人們現在習慣上用 .pyz 文件擴展名來命名此類壓縮文件,但這純粹是傳統,不會影響任何東西;你當然也可以用 .zip 文件擴展名。

為了簡化創建此類可執行的壓縮文件,標準庫提供了zipapp [7]模塊。它會為你生成__main__.py並添加一條組織行(shebang line),因此你甚至不需要指定 python,如果你不想在 UNIX 上指定它的話。如果你想移動一堆純 Python 代碼,這是一種不錯的方法。

不幸的是,僅當壓縮文件包含的所有代碼都是純 Python 時,才能這樣運行壓縮文件。執行壓縮文件對擴展模塊無效(這就是為什麼 setuptools 有一個 zip_safe [8]標誌的原因)。(譯註:擴展模塊 extension module,即 C/C++ 之類的非 Python 文件)

要加載擴展模塊,Python 必須調用 dlopen() [9]函數,它要傳入一個文件路徑,但當該文件路徑就包含在壓縮文件內時,這顯然不起作用。

我知道至少有一個人與 glibc 團隊交談過,關於支持將內存緩衝區傳入壓縮文件,以便 Python 可以將擴展模塊讀入內存,並將其傳給壓縮文件,但是如果內存為此服務,glibc 團隊並不同意。

但是,並非所有希望都喪失了!你可以使用諸如shiv [10]之類的項目,它會捆綁(bundle)你的代碼,然後提供一個__main__.py 來處理壓縮文件的提取、緩存,然後為你執行代碼。儘管不如純 Python 解決方案理想,但它確實可行,並且在這種情況下算得上是優雅的。

(譯註:翻譯水平有限,難免偏差。我加註了部分內容,希望有助於閱讀。請搜索關注“Python貓”,閱讀更多優質的原創或譯作。)

參考鏈接

[0] https://snarky.ca/the-many-ways-to-pass-code-to-python-from-the-terminal/

[1] https://marketplace.visualstudio.com/items?itemName=ms-python.python

[2] https://github.com/microsoft/vscode-python/tree/master/news

[3] https://pypi.org/project/towncrier

[4] https://snarky.ca/why-you-should-use-python-m-pip

[5] https://docs.python.org/3/library/runpy.html#module-runpy

[6] https://mp.weixin.qq.com/s/1ehySR5NH2v1U8WIlXflEQ

[7] https://docs.python.org/3/library/zipapp.html#module-zipapp

[8] https://setuptools.readthedocs.io/en/latest/setuptools.html#setting-the-zip-safe-flag

[9] https://linux.die.net/man/3/dlopen

[10] https://pypi.org/project/shiv/

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

【其他文章推薦】

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

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

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

如何監控 Linux 服務器狀態?

Linux 服務器我們天天打交道,特別是 Linux 工程師更是如此。為了保證服務器的安全與性能,我們經常需要監控服務器的一些狀態,以保證工作能順利開展。

本文介紹的幾個命令,不僅僅適用於服務器監控,也適用於我們日常情況下的開發。

1. watch

watch 命令我們的使用頻率很高,它的基本作用是,按照指定頻率重複執行某一條指令。使用這個命令,我們可以重複調用一些命令來達到監控服務器的作用。

默認情況下,watch 命令的執行周期是 2 秒,但我們可以使用 -n 選項來指定運行頻率,比如我們想要每隔 5 秒執行 date 命令,可以這麼執行:

$ watch -n 5 date

一台服務器肯定有多人在用,特別是本部門的小夥伴。對於這些小夥伴有沒渾水摸魚,我們可以使用一些命令來監控他們。

我們可以每隔 10 秒執行 who 命令,來看看都有誰在使用服務器。

$ watch -n 10 who
Every 10.0s: who                             butterfly: Tue Jan 23 16:02:03 2019

shs      :0           2019-01-23 09:45 (:0)
dory     pts/0        2019-01-23 15:50 (192.168.0.5)
alvin     pts/1        2019-01-23 16:01 (192.168.0.15)
shark    pts/3        2019-01-23 11:11 (192.168.0.27)

如果發現系統運行很慢,我們可以調用 uptime 命令來查看系統平均負載情況。

$ watch uptime
Every 2.0s: uptime                           butterfly: Tue Jan 23 16:25:48 2019

 16:25:48 up 22 days,  4:38,  3 users,  load average: 1.15, 0.89, 1.02

一些關鍵的進程肯定不能掛,否則可能會影響到業務開展,所以我們可以重複統計服務器中的所有進程數量。

$ watch -n 5 'ps -ef | wc -l'
Every 5.0s: ps -ef | wc -l                   butterfly: Tue Jan 23 16:11:54 2019

245

想動態知道服務器內存使用情況,可以重複執行 free 命令。

$ watch -n 5 free -m
Every 5.0s: free -m                          butterfly: Tue Jan 23 16:34:09 2019

              total        used        free      shared  buff/cache   available
Mem:           5959         776        3276          12        1906        4878
Swap:          2047           0        2047

當然不僅僅是這些,我們還可以重複調用很多命令來對服務器一些關鍵參數進行監控,

2. top

使用 top 命令我們可以知道系統的很多關鍵參數,而且是動態更新的。默認情況下,top 監控的是系統的整體狀態,如果我們只想知道某個人的使用情況,可以使用 -u 選項來指定這個人。

$ top -u alvin
top - 16:14:33 up 2 days,  4:27,  3 users,  load average: 0.00, 0.01, 0.02
Tasks: 199 total,   1 running, 198 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.2 sy,  0.0 ni, 99.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   5959.4 total,   3277.3 free,    776.4 used,   1905.8 buff/cache
MiB Swap:   2048.0 total,   2048.0 free,      0.0 used.   4878.4 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
23026 alvin      20   0   46340   7820   6504 S   0.0   0.1   0:00.05 systemd
23033 alvin      20   0  149660   3140     72 S   0.0   0.1   0:00.00 (sd-pam)
23125 alvin      20   0   63396   5100   4092 S   0.0   0.1   0:00.00 sshd
23128 alvin      20   0   16836   5636   4284 S   0.0   0.1   0:00.03 zsh

在這個結果里,你不僅僅可以看到 alvin 這個用戶運行的所有的進程數,也可以看到每個進程所消耗的系統資源(CPU,內存),同時依然可以看到整個系統的關鍵參數。

3. ac

如果你想知道每個用戶登錄服務器所使用的時間,你可以使用 ac 命令。這個命令需要你安裝 acct 包(Debian)或 psacct 包(RHEL,Centos)。

如果我們想知道所有用戶登陸服務器所使用的時間之和,我們可以直接運行 ac 命令,無需任何參數。

$ ac
        total     1261.72

如果我們想知道各個用戶所使用時間,可以加上 -p 選項。

$ ac -p
        shark                                5.24
        alvin                                5.52
        shs                               1251.00
        total     1261.76

我們還可以通過加上 -d 選項來查看具體每一天用戶使用服務器時間之和。

$ ac -d | tail -10
Jan 11  total        0.05
Jan 12  total        1.36
Jan 13  total       16.39
Jan 15  total       55.33
Jan 16  total       38.02
Jan 17  total       28.51
Jan 19  total       48.66
Jan 20  total        1.37
Jan 22  total       23.48
Today   total        9.83

小結

我們可以使用很多命令來監控系統的運行狀態,本文主要介紹了三個:watch 命令可以讓你重複執行某一條命令來監控一些參數的變化,top 命令可以查看某個用戶運行的進程數以及消耗的資源,而 ac 命令則可以查看每個用戶使用服務器時間。你經常使用哪個命令呢?歡迎留言討論!

公眾號:良許Linux

有收穫?希望老鐵們來個三連擊,給更多的人看到這篇文章

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

【其他文章推薦】

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

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

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

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

※超省錢租車方案

※回頭車貨運收費標準

教科書級講解,秒懂最詳細Java的註解

所有知識體系文章,GitHub已收錄,歡迎Star!再次感謝,願你早日進入大廠!

GitHub地址: https://github.com/Ziphtracks/JavaLearningmanual

Java註解

一、Java註解概述

註解(Annotation),也叫元數據。一種代碼級別的說明。它是JDK1.5及以後版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,註釋。

二、註解的作用分類

  • 編寫文檔: 通過代碼里標識的元數據生成文檔【生成文檔doc文檔】
  • 代碼分析: 通過代碼里標識的元數據對代碼進行分析【使用反射】
  • 編譯檢查: 通過代碼里標識的元數據讓編譯器能夠實現基本的編譯檢查【Override等】

編寫文檔

首先,我們要知道Java中是有三種註釋的,分別為單行註釋、多行註釋和文檔註釋。而文檔註釋中,也有@開頭的元註解,這就是基於文檔註釋的註解。我們可以使用javadoc命令來生成doc文檔,此時我們文檔的內元註解也會生成對應的文檔內容。這就是編寫文檔的作用。

代碼分析

我們頻繁使用之一,也是包括使用反射來通過代碼里標識的元數據對代碼進行分析的,此內容我們在後續展開講解。

編譯檢查

至於在編譯期間在代碼中標識的註解,可以用來做特定的編譯檢查,它可以在編譯期間就檢查出“你是否按規定辦事”,如果不按照註解規定辦事的話,就會在編譯期間飄紅報錯,並予以提示信息。可以就可以為我們代碼提供了一種規範制約,避免我們後續在代碼中處理太多的代碼以及功能的規範。比如,@Override註解是在我們覆蓋父類(父接口)方法時出現的,這證明我們覆蓋方法是繼承於父類(父接口)的方法,如果該方法稍加改變就會報錯;@FunctionInterface註解是在編譯期檢查是否是函數式接口的,如果不遵循它的規範,同樣也會報錯。

三、jdk的內置註解

3.1 內置註解分類

  • @Override: 標記在成員方法上,用於標識當前方法是重寫父類(父接口)方法,編譯器在對該方法進行編譯時會檢查是否符合重寫規則,如果不符合,編譯報錯。
  • @Deprecated: 用於標記當前類、成員變量、成員方法或者構造方法過時如果開發者調用了被標記為過時的方法,編譯器在編譯期進行警告。
  • @SuppressWarnings: 壓制警告註解,可放置在類和方法上,該註解的作用是阻止編譯器發出某些警告信息。

3.2 @Override註解

標記在成員方法上,用於標識當前方法是重寫父類(父接口)方法,編譯器在對該方法進行編譯時會檢查是否符合重寫規則,如果不符合,編譯報錯。

這裏解釋一下@Override註解,在我們的Object基類中有一個方法是toString方法,我們通常在實體類中去重寫此方法來達到打印對象信息的效果,這時候也會發現重寫的toString方法上方就有一個@Override註解。如下所示:

image-20200604203535421

於是,我們試圖去改變重寫后的toString方法名稱,將方法名改為toStrings。你會發現在編譯期就報錯了!如下所示:

image-20200604203645332

那麼這說明什麼呢?這就說明該方法不是我們重寫其父類(Object)的方法。這就是@Override註解的作用。

3.3 @Deprecated註解

用於標記當前類、成員變量、成員方法或者構造方法過時如果開發者調用了被標記為過時的方法,編譯器在編譯期進行警告。

我們解釋@Deprecated註解就需要模擬一種場景了。假設我們公司的產品,目前是V1.0版本,它為用戶提供了show1方法的功能。這時候我們為產品的show1方法的功能又進行了擴展,打算髮布V2.0版本。但是,我們V1.0版本的產品需要拋棄嗎?也就是說我們V1.0的產品功能還繼續讓用戶使用嗎?答案肯定是不能拋棄的,因為有一部分用戶是一直用V1.0版本的。如果拋棄了該版本會損失很多的用戶量,所以我們不能拋棄該版本。這時候,我們對功能進行了擴展后,發布了V2.0版本,我們給予用戶的通知就可以了,也就是告知用戶我們在V2.0版本中為功能進行了擴展。可以讓用戶自行選擇版本。

但是,除了發布告知用戶版本情況之外,我們還需要在原來版本的功能上給予提示,在上面的模擬場景中我們需要在show1方法上方加@Deprecated註解給予提示。通過這種方式也告知用戶“這是舊版本時候的功能了,我們不建議再繼續使用舊版本的功能”,這句話的意思也就正是給用戶做了提示。用戶也會這麼想“奧,這版本的這個功能不好用了,肯定有新版本,又更好用的功能。我要去官網查一下下載新版本”,還會有用戶這麼想“我明白了,又更新出更好的功能了,但是這個版本的功能我已經夠用了,不需要重新下載新版本了”。

那麼我們怎麼查看我上述所說的在功能上給予的提示呢?這時候我需要去創建一個方法,然後去調用show1方法,並查看調用時它是如何提示的。

圖已經貼出來了,你是否發現的新舊版本功能的異同點呢?很明顯,在方法中的提示是在調用的方法名上加了一道橫線把該方法劃掉了。這就體現了show1方法過時了,已經不建議使用了,我們為你提供了更好的。

回想起來,在我們的api中也會有方法是過時的,比如我們的Date日期類中的方法有很多都已經過時了。如下圖:

image-20200604210154348 image-20200604210416762

如你所見,是不是有很多方法都過時了呢?那它的方法上是加了@Deprecated註解嗎?來跟着我的腳步,我帶你們看一下。

我們已經知道的Date類中的這些方法已經是過時的了,如果我們使用該方法並執行該程序的話。執行的過程中就會提示該方法已過時的內容,但是只是提示,並不影響你使用該方法。如下:

image-20200604221938895

OK!這也就是@Deprecated註解的作用了。

3.4 @SuppressWarnings註解

壓制警告註解,可放置在類和方法上,該註解的作用是阻止編譯器發出某些警告信息,該註解為單值註解,只有 一個value參數,該參數為字符串數組類型,參數值常用的有如下幾個。

  • unchecked:未檢查的轉化,如集合沒有指定類型還添加元素
  • unused:未使用的變量
  • resource:有泛型未指定類型
  • path:在類路徑,原文件路徑中有不存在的路徑
  • deprecation:使用了某些不贊成使用的類和方法
  • fallthrough:switch語句執行到底沒有break關鍵字
  • rawtypes:沒有寫泛型,比如: List list = new ArrayList();
  • all:全部類型的警告

壓制警告註解,顧名思義就是壓制警告的出現。我們都知道,在Java代碼的編寫過程中,是有很多黃色警告出現的。但是我不知道你的導師是否教過你,程序員只需要處理紅色的error,不需要理會黃色的warning。如果你的導師說過此問題,那是有原因的。因為在你學習階段,我們認清處理紅色的error即可,這樣可以減輕你學習階段在腦部的記憶內容。如果你剛剛加入學習Java的隊列中,需要大腦記憶的東西就有太多了,也就是我們目前不需要額外記憶其他的東西,只記憶重點即可。至於黃色warning嘛,在你的學習過程中慢慢就會有所了解的,而不是死記硬背的。

那為了解釋@SuppressWarnings註解,我們還使用上一個例子,因為在那個例子中就有黃色的warning出現。

而每一個黃色的warning都會有警告信息的。比如,這一個圖中的警告信息,就告知你show2()方法沒有被使用,簡單來說,你創建的show2方法,但是你在代碼中並沒有調用過此方法。以後你便會遇到各種各樣黃色的warning。然後, 我們就可以使用不同的註解參數來壓制不同的註解。但是在該註解的參數中,提供了一個all參數可以壓制全部類型的警告。而這個註解是需要加到類的上方,並賦予all參數,即可壓制所有警告。如下:

image-20200604213943722

我們加入註解並賦予all參數后,你會發現use方法和show2方法的警告沒有了,實際上導Date包的警告還在,因為我們Date包導入到了該類中,但是我們並沒有創建Date對象,也就是並沒有寫入Date在代碼中,你也會發現那一行是灰色的,也就證明了我們沒有去使用導入這個包的任何信息的說法,出現這種情況我們就需要把這個沒有用的導包內容刪除掉,使用Ctrl + X刪除導入沒有用到的包即可。還有一種辦法就是在包的上方修飾壓制警告註解,但是我認為在一個沒有用的包上加壓制註解是毫無意義的,所以,我們直接刪除就好。

然後,我們還見到上圖,註解那一行出現了警告信息提示。這一行的意思是冗餘的警告壓制。這就是說我們壓制以下的警告並沒有什麼意義而造成的冗餘,但是如果我們使用了該類並做了點什麼的話,壓制註解的冗餘警告就會消失,畢竟我們使用了該類,此時就不會早場冗餘了。

上述解釋@SuppressWarnings註解也差不多就這些了。OK,繼續向下看吧。持續為大家講解。

3.5 @Repeatable註解

@Repeatable 表明標記的註解可以多次應用於相同的聲明或類型,此註解由Java8版本引入。我們知道註解是不能重複定義的,其實該註解就是一個語法糖,它可以重複多此使用,更適用於我們的特殊場景。

首先,我們先創建一個可以重複使用的註解。

package com.mylifes1110.anno;

import java.lang.annotation.Repeatable;

@Repeatable(Hour.class)
public @interface Hours {
    double[] hours() default 0;
}

你會發現註解要求傳入的值是一個類對象,此類對象就需要傳入另外一個註解,這裏也就是另外一個註解容器的類對象。我們去創建一下。

package com.mylifes1110.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//容器
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Hour {
    Hours[] value();
}

其實,這兩個註解的套用,就是將一個普通的註解封裝了一個可重複使用的註解,來達到註解的復用性。最後,我們創建一下測試類,隨後帶你去看一下源碼。

package com.mylifes1110.java;

import com.mylifes1110.anno.Hours;

@Hours(hours = 4)
@Hours(hours = 4.5)
@Hours(hours = 2)
public class Worker {
    public static void main(String[] args) {
        //通過Hours註解類型來獲取Worker中的值數組對象
        Hours[] hours = Worker.class.getAnnotationsByType(Hours.class);
        //遍曆數組
        for (Hours h : hours) {
            System.out.println(h);
        }
    }
}

測試類,是一個工人測試類,該工人使用註解記錄早中晚的工作時間。測試結果如下:

image-20200606183652359

然後我們進入到源碼一探究竟。

image-20200606183737877

我們發現進入到源碼后,就只看見一個返回值為類對象的抽象方法。這也就驗證了該註解只是一個可實現重複性註解的語法糖而已。

四、註解分類

4.1 註解分類

註解可以根據註解參數分為三大類:

  • 標記註解: 沒有參數的註解,僅用自身的存在與否為程序提供信息,如@Override註解,該註解沒有參數,用於表示當前方法為重寫方法。
  • 單值註解: 只有一個參數的註解,如果該參數的名字為value,那麼可以省略參數名,如 @SuppressWarnings(value = “all”),可以簡寫為@SuppressWarnings(“all”)。
  • 完整註解: 有多個參數的註解。

4.2 標記註解

說到@Override註解是一個標記註解,那我們進入到該註解的源碼查看一下。從上往下看該註解源碼,發現它繼承了導入了java.lang.annotation.*,也就是有使用到該包的內容。然後下面就又是兩個看不懂的註解,其實發現註解的定義格式是public修飾的@Interface,最終看到該註解中方法體並沒有任何參數,也就是只起到標記作用。

4.3 單值註解

在上面我們用到的@SuppressWarnings註解就是一個單值註解。那我們進入到它的源碼看一下是怎麼個情況。其實,和標記註解比較,它就多一個value參數而已,而這就是單值註解的必要條件,即只有一個參數。並且這一個參數為value時,我們可以省略value。

4.4 完整註解

上述兩個類型註解講解完,至於完整註解嘛,這下就能更明白了。其中的方法體就是有多個參數而已。

五、自定義註解

5.1 自定義註解格式

格式: public @Interface 註解名 {屬性列表/無屬性}

注意: 如果註解體中無任何屬性,其本質就是標記註解。但是與其標註註解還少了上邊修飾的元註解。

如下,這就是一個註解。但是它與jdk自定義註解有點區別,jdk自定義註解的上方還有註解來修飾該註解,而那註解就叫做元註解。元註解我會在後面詳細的說到。

image-20200606104149069

這裏我們的確不知道@Interface是什麼,那我們就把自定義的這個註解反編譯一下,看一下反編譯信息。反編譯操作如下:

image-20200606104818131

反編譯后的反編譯內容如下:

public interface com.mylifes1110.anno.MyAnno extends java.lang.annotation.Annotation {
}

首先,看過反編譯內容后,我們可以直觀的得知他是一個接口,因為它的public修飾符後面的關鍵字是interface。

其次,我們發現MyAnno這個接口是繼承了java.lang.annotation包下的Annotation接口。

所以,我們可以得知註解的本質就是一個接口,該接口默認繼承了Annotation接口。

既然,是繼承的Annotation接口,那我們就去進入到這個接口中,看它定義了什麼。以下是我抽取出來的接口內容。我們發現它看似很常見,其實它們不是很常用,作為了解即可。

public interface Annotation {
    boolean equals(Object obj);
    int hashCode();
    String toString();
    Class<? extends Annotation> annotationType();
}

最後,我們的註解中也是可以寫有屬性的,它的屬性不同於普通的屬性,它的屬性是抽象方法。既然註解也是一個接口,那麼我們可以說接口體中可以定義什麼,它同樣也可以定義,而它的修飾符與接口一樣,也是默認被public abstract修飾。

而註解體中的屬性也是有要求的。其屬性要求如下:

  • 屬性的返回值類型必須是以下幾種:
  • 基本數據類型
  • String類型
  • 枚舉類型
  • 註解
  • 以上類型的數組
  • 注意: 在這裏不能有void的無返回值類型和以上類型以外的類型
  • 定義的屬性,在使用時需要給註解中的屬性賦值
  • 如果定義屬性時,使用default關鍵字給屬性默認初始化值,則使用註解時可以不為屬性賦值,它取的是默認值。如果為它再次傳入值,那麼就發生了對原值的覆蓋。
  • 如果只有一個屬性需要賦值,並且屬性的名稱為value,則賦值時value可以省略,可以直接定義值
  • 數組賦值時,值使用{}存儲值。如果數組中只有一個值,則可以省略{}

5.2 自定義註解屬性的返回值

屬性返回值既然有以上幾種,那麼我就在這裏寫出這幾種演示一下是如何寫的。

首先,定義一個枚舉類和另外一個註解備用。

package com.mylifes1110.enums;

public enum Lamp {
    RED, GREEN, YELLOW
}
package com.mylifes1110.anno;

public @interface MyAnno2 {
}

其次,我們來定義上述幾種類型,如下:

package com.mylifes1110.anno;

import com.mylifes1110.enums.Lamp;

public @interface MyAnno {
    //基本數據類型
    int num();

    //String類型
    String value();

    //枚舉類型
    Lamp lamp();

    //註解類型
    MyAnno2 myAnno2();

    //以上類型的數組
    String[] values();
    Lamp[] lamps();
    MyAnno2[] myAnno2s();
    int[] nums();
}

5.3 自定義註解的屬性賦值

這裏我們演示一下,首先,我們使用該註解來進行演示。

package com.mylifes1110.anno;

public @interface MyAnno {
    //基本數據類型
    int num();

    //String類型
    String value();
}

隨後創建一個測試類,在類的上方寫上註解,你會發現,註解的參數中會讓你寫這兩個參數(int、String)。

image-20200606113037920

此時,傳參是這樣來做的。格式為:名稱 = 返回值類型參數。如下:

上述所說,如果使用default關鍵字給屬性默認初始化值,就不需要為其參數賦值,如果賦值的話,就把默認初始化的值覆蓋掉了。

當然還有一個規則,如果只有一個屬性需要賦值,並且屬性的名稱為value,則賦值時value可以省略,可以直接定義值。那麼,我們的num已經有了默認值,就可以不為它傳值。我們發現,註解中定義的屬性就剩下了一個value屬性值,那麼我們就可以來演示這個規則了。

image-20200606113849685

這裏,我並沒有寫屬性名稱value,而是直接為value賦值。如果我將num的default關鍵字修飾去掉呢,那意思也就是說在使用該註解時必須為num賦值,這樣可以省略value嗎?那我們看一下。

image-20200606114216801

結果,就是我們所想的,它報錯了,必須讓我們給num賦值。其實想想這個規則也是很容易懂的,定義一個為value的值,就可以省略其value名稱。如果定義多個值,它們可以省略名稱就無法區分定義的是那個值了,關鍵是還有數組,數組內定義的是多個值呢,對吧。

5.4 自定義註解的多種返回值類型賦值

這裏我們演示一下,上述的多種返回值類型是如何賦值的。這裏我們定義這幾個參數來看一下,是如何為屬性賦值的。

num是一個int基本數據類型,即num = 1

value是一個String類型,即value = "str"

lamp是一個枚舉類型,即lamp = Lamp.RED

myAnno2是一個註解類型,即myAnno2 = @MyAnno2

values是一個String類型數組,即values = {"s1", "s2", "s3"}

values是一個String類型數組,其數組中只有一個值,即values = "s4"

注意: 值與值之間是,隔開的;數組是用{}來存儲值的,如果數組中只有一個值可以省略{};枚舉類型是枚舉名.枚舉值

六、元註解

6.1 元註解分類

元註解就是用來描述註解的註解。一般使用元註解來限制自定義註解的使用範圍、生命周期等等。

而在jdk的中java.lang.annotation包中定義了四個元註解,如下:

元註解 描述
@Target 指定被修飾的註解的作用範圍
@Retention 指定了被修飾的註解的生命周期
@Documented 指定了被修飾的註解是可以Javadoc等工具文檔化
@Inherited 指定了被修飾的註解修飾程序元素的時候是可以被子類繼承的

6.2 @Target

@Target 指定被修飾的註解的作用範圍。其作用範圍可以在源碼中找到參數值。

屬性 描述
CONSTRUCTOR 用於描述構造器
FIELD(常用) 用於描述屬性
LOCAL_VARIABLE 用於描述局部變量
METHOD(常用) 用於描述方法
PACKAGE 用於描述包
PARAMETER 用於描述參數
TYPE(常用) 用於描述類、接口(包括註解類型) 或enum聲明
ANNOTATION_TYPE 用於描述註解類型
TYPE_USE 用於描述使用類型

由此可見,該註解體內只有一個value屬性值,但是它的類型是一個ElementType數組。那我們進入到這個數組中繼續查看。

進入到該數組中,你會發現他是一個枚舉類,其中定義了上述表格中的各個屬性。

了解了@Target的作用和屬性值后,我們來使用一下該註解。首先,我們要先用該註解來修飾一個自定義註解,定義該註解的指定作用在類上。如下:

而你觀察如下測試類,我們把註解作用在類上時是沒有錯誤的。而當我們的註解作用在其他地方就會報錯。這也就說明了,我們@Target的屬性起了作用。

注意: 如果我們定義多個作用範圍時,也是可以省略該參數名稱了,因為該類型是一個數組,雖然能省略名稱但是,我們還需要用{}來存儲。

6.3 @Retention

@Retention 指定了被修飾的註解的生命周期

屬性 描述
RetentionPolicy.SOURCE 註解只在源碼階段保留,在編譯器進行編譯時它將被丟棄忽視。
RetentionPolicy.CLASS 註解只被保留到編譯進行時的class文件,但 JVM 加載class文件時候被遺棄,也就是在這個階段不會讀取到該class文件。
RetentionPolicy.RUNTIME(常用) 註解可以保留到程序運行的時候,它會被加載進入到 JVM 中,所以在程序運行時可以獲取到它們。

注意: 我們常用的定義即是RetentionPolicy.RUNTIME,因為我們使用反射來實現的時候是需要從JVM中獲取class類對象並操作類對象的。

首先,我們要了解反射的三個生命周期階段,這部分內容我在Java反射機制中也是做了非常詳細的說明,有興趣的小夥伴可以去看看我寫的Java反射機制,相信你在其中也會有所收穫。

這裏我再次強調一下這三個生命周期是源碼階段 – > class類對象階段 – > Runtime運行時階段

那我們進入到源碼,看看@Retention註解中是否有這些參數。

我們看到該註解中的屬性只有一個value,而它的類型是一個RetentionPolicy類型,我們進入到該類型中看看有什麼參數,是否與表格中的參數相同呢?

image-20200606145449931

至於該註解怎麼使用,其實是相同的,用法如下:

這就證明了我們的註解可以保留到Runtime運行階段,而我們在反射中大多數是定義到Runtime運行時階段的,因為我們需要從JVM中獲取class類對象並操作類對象。

6.4 @Documented

@Documented 指定了被修飾的註解是可以Javadoc等工具文檔化

@Documented註解是比較好理解的,它是一個標記註解。被該標記註解標記的註解,生成doc文檔時,註解是可以被加載到文檔中显示的。

image-20200606152526551

還拿api中過時的Date中的方法來說,在api中显示Date中的getYear方法是這樣的。

正如你看到的,註解在api中显示了出來,證明該註解是@Documented註解修飾並文檔化的。那我們就看看這個註解是否被@Documented修飾吧。

然後,我們發現該註解的確是被文檔化了。所以在api中才會显示該註解的。如果不信,你可以自己使用javadoc命令來生成一下doc文檔,看看被該註解修飾的註解是否存在。

至於Javadoc文檔生成,我在javadoc文檔生成一文中有過詳細記載,大家可以進行參考,生成doc文檔查看。

6.5 @Inherited

@Inherited 指定了被修飾的註解修飾程序元素的時候是可以被子類繼承的

首先進入到源碼中,我們也可以清楚的知道,該註解也是一個標記註解。而且它也是被文檔化的註解。

其次,我們去在自定義註解中,標註上@Inherited註解。

演示@Inherited註解,我需要創建兩個類,同時兩個類中有一層的繼承關係。如下:

我們在Person類中標記了@MyAnno註解,由於該註解被@Inherited註解修飾,我們就可以得出繼承於Person類的Student類也同樣被@MyAnno註解標記了,如果你要獲取該註解的值的話,肯定獲取的也是父類上註解值的那個”1″。

七、使用反射機制解析註解

自定義註解

package com.mylifes1110.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @InterfaceName Sign
 * @Description 描述需要執行的類名和方法名
 * @Author Ziph
 * @Date 2020/6/6
 * @Since 1.8
 */

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Sign {
    String methodName();

    String className();
}

Cat

package com.mylifes1110.java;

/**
 * @ClassName Cat
 * @Description 描述一隻貓的類
 * @Author Ziph
 * @Date 2020/6/6
 * @Since 1.8
 */

public class Cat {
    /*
     * @Description 描述一隻貓吃魚的方法 
     * @Author Ziph
     * @Date 2020/6/6
     * @Param []
     * @return void
     */

    public void eat() {
        System.out.println("貓吃魚");
    }
}

準備好,上述代碼后,我們就可以開始編寫使用反射技術來解析註解的測試類。如下:

首先,我們先通過反射來獲取註解中的methodName和className參數。

package com.mylifes1110.java;

import com.mylifes1110.anno.Sign;

/**
 * @ClassName SignTest
 * @Description 要求創建cat對象並執行其類中eat方法
 * @Author Ziph
 * @Date 2020/6/6
 * @Since 1.8
 */

@Sign(className = "com.mylifes1110.java.Cat", methodName = "eat")
public class SignTest {
    public static void main(String[] args) {
        //獲取該類的類對象
        Class<SignTest> signTestClass = SignTest.class;
        //獲取類對象中的註解對象
        //原理實際上是在內存中生成了一個註解接口的子類實現對象
        Sign sign = signTestClass.getAnnotation(Sign.class);
        //調用註解對象中定義的抽象方法(註解中的屬性)來獲取返回值
        String className = sign.className();
        String methodName = sign.methodName();
        System.out.println(className);
        System.out.println(methodName);
    }
}

此時的打印結果證明我們已經成功獲取到了該註解的兩個參數。

image-20200606162810165

注意: 獲取類對象中的註解對象時,其原理實際上是在內存中生成了一個註解接口的子類實現對象並返回的字符串內容。如下:

public class SignImpl implements Sign {
    public String methodName() {
        return "eat";
    }

    public String className() {
        return "com.mylifes1110.java.Cat";
    }
}

繼續編寫我們後面的代碼,代碼完整版如下:

完整版代碼

package com.mylifes1110.java;

import com.mylifes1110.anno.Sign;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @ClassName SignTest
 * @Description 要求創建cat對象並執行其類中eat方法
 * @Author Ziph
 * @Date 2020/6/6
 * @Since 1.8
 */

@Sign(className = "com.mylifes1110.java.Cat", methodName = "eat")
public class SignTest {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //獲取該類的類對象
        Class<SignTest> signTestClass = SignTest.class;
        //獲取類對象中的註解對象
        //原理實際上是在內存中生成了一個註解接口的子類實現對象
        Sign sign = signTestClass.getAnnotation(Sign.class);
        //調用註解對象中定義的抽象方法(註解中的屬性)來獲取返回值
        String className = sign.className();
        String methodName = sign.methodName();
        //獲取className名稱的類對象
        Class<?> clazz = Class.forName(className);
        //創建對象
        Object o = clazz.newInstance();
        //獲取methodName名稱的方法對象
        Method method = clazz.getMethod(methodName);
        //執行該方法
        method.invoke(o);
    }
}

執行結果

執行后成功的調用了eat方法,並打印了貓吃魚的結果,如下:

八、自定義註解改變JDBC工具類

首先,我們在使用JDBC的時候是需要通過properties文件來獲取配置JDBC的配置信息的,這次我們通過自定義註解來獲取配置信息。其實使用註解並沒有用配置文件好,但是我們需要了解這是怎麼做的,獲取方法也是魚使用反射機制解析註解,所謂“萬變不離其宗”,它就是這樣的。

自定義註解
package com.mylifes1110.java.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @InterfaceName DBInfo
 * @Description 給予註解聲明周期為運行時並限定註解只能用在類上
 * @Author Ziph
 * @Date 2020/6/6
 * @Since 1.8
 */

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBInfo {
    String driver() default "com.mysql.jdbc.Driver";

    String url() default "jdbc:mysql://localhost:3306/temp?useUnicode=true&characterEncoding=utf8";

    String username() default "root"
;

    String password() default "123456";
}
數據庫連接工具類

為了代碼的健全我也在裏面加了properties文件獲取連接的方式。

package com.mylifes1110.java.utils;

import com.mylifes1110.java.anno.DBInfo;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

/**
 * @ClassName DBUtils
 * @Description 數據庫連接工具類
 * @Author Ziph
 * @Date 2020/6/6
 * @Since 1.8
 */

@DBInfo()
public class DBUtils {
    private static final Properties PROPERTIES = new Properties();
    private static String driver;
    private static String url;
    private static String username;
    private static String password;

    static {
        Class<DBUtils> dbUtilsClass = DBUtils.class;
        boolean annotationPresent = dbUtilsClass.isAnnotationPresent(DBInfo.class);
        if (annotationPresent) {
            /**
             * DBUilts類上有DBInfo註解,並獲取該註解
             */

            DBInfo dbInfo = dbUtilsClass.getAnnotation(DBInfo.class);
//            System.out.println(dbInfo);
            driver = dbInfo.driver();
            url = dbInfo.url();
            username = dbInfo.username();
            password = dbInfo.password();
        } else {
            InputStream inputStream = DBUtils.class.getResourceAsStream("db.properties");
            try {
                PROPERTIES.load(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() {
        try {
            return DriverManager.getConnection(url, username, password);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }

    public static void closeAll(Connection connection, Statement statement, ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
                resultSet = null;
            }

            if (statement != null) {
                statement.close();
                statement = null;
            }
            if (connection != null) {
                connection.close();
                connection = null;
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}
測試類
package com.mylifes1110.java.test;

import com.mylifes1110.java.utils.DBUtils;

import java.sql.Connection;

/**
 * @ClassName GetConnectionDemo
 * @Description 測試連接是否可以獲取到
 * @Author Ziph
 * @Date 2020/6/6
 * @Since 1.8
 */

public class GetConnectionDemo {
    public static void main(String[] args) {
        Connection connection = DBUtils.getConnection();
        System.out.println(connection);
    }
}
測試結果

為了證明獲取的連接是由註解的配置信息獲取到的連接,我將properties文件中的所有配置信息刪除后測試的。

九、自定義@MyTest註解實現單元測試

我不清楚小夥伴們是否了解,Junit單元測試。@Test是單元測試的測試方法上方修飾的註解。此註解的核心原理也是由反射來實現的。如果有小夥伴不知道什麼是單元測試或者對自定義@MyTest註解實現單元測試感興趣的話,可以點進來看看哦!

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

【其他文章推薦】

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

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

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

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

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

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

※回頭車貨運收費標準

ASP.NET Core Blazor WebAssembly實現一個簡單的TODO List

基於blazor實現的一個簡單的TODO List

最近看到一些大佬都開始關注blazor,我也想學習一下。做了一個小的demo,todolist,僅是一個小示例,參考此vue項目的實現http://www.jq22.com/code1339

先看實現的效果圖

不BB,直接可以去看

源碼與預覽地址

  • 示例地址 http://baimocore.cn:8081/
  • 源碼地址 BlazorAppTodoList

源碼介紹

我們這裏刪除了默認的一些源碼。只保留最簡單的結構,在Pages/Index.razor中。

@code代碼結構中寫如下內容

  1. 創建一個類,裡面包含 id,label,isdone三個屬性值。
public class TodoItem
{
    public TodoItem () { }
    public TodoItem (int id, string label, bool isDone)
    {
        Id = id;
        Label = label;
        IsDone = isDone;
    }
    public int Id { get; set; }
    public string Label { get; set; }
    public bool IsDone { get; set; }
}
  1. 我們可以通過override重寫初始化,並給Todos設置一些數據。
private IList<TodoItem> Todos;
private int id = 0;
protected override void OnInitialized ()
{
    Todos = new List<TodoItem> ()
    {
        new TodoItem (++id, "Learn Blazor", false),
        new TodoItem (++id, "Code a todo list", false),
        new TodoItem (++id, "Learn something else", false)
    };
}

展示還有多少未完成的任務

<h1>
        Todo List(@Todos.Count(todo => !todo.IsDone))
        <span>Get things done, one item at a time.</span>
</h1>

當任務沒有時,我們展示默認效果,提示用戶無任務

<p class="emptylist" style="display: @(Todos.Count()>0?"none":"");">Your todo list is empty.</p>

新增一個任務

<form name="newform">
    <label for="newitem">Add to the todo list</label>
    <input type="text" name="newitem" id="newitem" @bind-value="Label">
    <button type="button" @onclick="AddItem">Add item</button>
</form>

這裏我們用了一個Label變量,一個onclick事件。

private string Label;

private void AddItem()
{
    if (!string.IsNullOrWhiteSpace(Label))
    {
        Todos.Add (new TodoItem { Id = ++id, Label = Label });
        Label = string.Empty;
    }
    this.SortByStatus();
}

this.SortByStatus
因為我們這裏還實現一個功能,就是當勾選(當任務完成時,我們將他移到最下面)

<div class="togglebutton-wrapper@(IsActive==true?" togglebutton-checked":"")">
    <label for="todosort">
        <span class="togglebutton-Label">Move done items at the end?</span>
        <span class="tooglebutton-box"></span>
    </label>
    <input type="checkbox" name="todosort" id="todosort" value="@IsActive" @onchange="ActiveChanged">
</div>

一個IsActive的變量,用於指示當前checkbox的樣式,是否開啟已完成的任務移動到最下面。當勾選時,改變IsActive的值。並調用排序的功能。

private bool IsActive = false;
private void ActiveChanged()
{
    this.IsActive = !this.IsActive;
    this.SortByStatus();
}
private void SortByStatus()
{
    if (this.IsActive)
    {
        Todos = Todos.OrderBy(r => r.IsDone).ThenByDescending(r => r.Id).ToList();
    }
    else
    {
        Todos = Todos.OrderByDescending(r => r.Id).ToList();
    }
}

對於列表的展示我們使用如下ul li @for實現

<ul>
    @foreach (var item in Todos)
    {
        <li stagger="5000" class="@(item.IsDone?"done":"")">
            <span class="label">@item.Label</span>
            <div class="actions">
                <button class="btn-picto" type="button"
                        @onclick="@((e)=> {MarkAsDoneOrUndone(item);})"
                        title="@(item.IsDone ? "Undone" :"Done")"
                        aria-label="@(item.IsDone ? "Undone" :"Done")">
                    <i aria-hidden="true" class="material-icons">@(item.IsDone ? "check_box" : "check_box_outline_blank")</i>
                </button>
                <button class="btn-picto" type="button"
                        @onclick="@((e)=> { DeleteItemFromList(item); })"
                        aria-Label="Delete" title="Delete">
                    <i aria-hidden="true" class="material-icons">delete</i>
                </button>
            </div>
        </li>
    }
</ul>

循環Todos,然後,根據item.IsDone,改變li的樣式,從而實現一个中劃線的功能,二個按鈕的功能,一個是勾選任務表示此任務已完成,另一個是刪除此任務。同理,我們仍然通過IsDone來標識完成任務的圖標,標題等。

  • 任務設置已完成/設置為未完成: @onclick調用方法MarkAsDoneOrUndone,並將當前的一行記錄item傳給方法,需要使用一個匿名函數調用@code中的方法,將isDone取相反的值,並重新排序。
private void MarkAsDoneOrUndone(TodoItem item)
{
    item.IsDone = !item.IsDone;
    this.SortByStatus();
}
  • 刪除一個任務,同理,使用匿名函數,DeleteItemFromList直接通過IList的方法Remove刪除一個對象,並排序。
private void DeleteItemFromList(TodoItem item)
{
    Todos.Remove(item);
    this.SortByStatus();
}

當然,我們可以 在ul,外包裹一層,根據Count判斷有沒有任務,從而显示這個列表。

<div style="display: @(Todos.Count()>0?"":"none");"><ul>xxx</ul></div>

其他的樣式與圖標,請看最上面的源碼wwwroot/css目錄獲取。

deploy(部署)

  • 有興趣的可以看官網 https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/blazor/?view=aspnetcore-3.1&tabs=visual-studio

在項目根目錄執行如下命令

dotnet publish -c Release

我們就能得到一個發布包,他的位置在 (BlazorAppTodoList\bin\Release\netstandard2.1\publish) ,我們把他複製到服務器上,這裏我放到/var/www/todolilst目錄中。

它相當於一個靜態文件,你可以將他部署到任何一個web服務器上。

這裏我們把他放到nginx中,並在目錄/etc/nginx/conf.d/ 新建一個文件 todolist.conf,然後放入如下內容。

 server {
        listen 8081;

        location / {
            root /var/www/todolist/wwwroot;
            try_files $uri $uri/ /index.html =404;
        }
}

記得在etc/nginx/nginx.conf中配置gzip壓縮。

gzip  on;
gzip_min_length 5k; #gzip壓縮最小文件大小,超出進行壓縮(自行調節)
gzip_buffers 4 16k; #buffer 不用修改
gzip_comp_level 8; #壓縮級別:1-10,数字越大壓縮的越好,時間也越長
gzip_types text/plain application/x-javascript application/javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/octet-stream; #  壓縮文件類型 
gzip_vary on; # 和http頭有關係,加個vary頭,給代理服務器用的,有的瀏覽器支持壓縮,有的不支持,所以避免浪費不支持的也壓縮,所以根據客戶端的HTTP頭來判斷,是否需要壓縮

我遇到dll,wasm,後綴的文件壓縮無效。因為gzip_types ,沒有配置他們的Content-Type。我們在瀏覽器中找到響應頭Content-Type: application/octet-stream,然後在上文中的nginx配置文件中,gzip_types加上application/octet-stream,

最後執行

nginx -t
nginx -s reload

打開網站看效果

http://baimocore.cn:8081

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

【其他文章推薦】

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

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

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

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

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

※回頭車貨運收費標準

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