9.6萬起國內才有的合資車 為什麼更值得買?

價格低、配置挺高、空間也比較夠用,所以綜合實力不錯。廣汽本田-凌派指導價:10。98-14。98萬凌派採用了1。8L i-VTEC發動機,這款機型在本田的其他車型上已經服役多年,供油方式是多點電噴,雖然是這樣,但是它的動力輸出一點不差,也很耐用。

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

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

爆款SUV的兩個小兄弟之間有什麼區別?

藍標版本的H2除了前臉的進氣格柵造型不太同,保留了藍標哈弗家族的多邊形大嘴以外,整體設計語言與紅標版本保持一致,都給人以一種偏向穩重厚實的形象。內飾:新創意VS家族風哈弗H2s的內飾設計元素源自手風琴,初次見到這款內飾的時候會覺得這不是哈弗品牌,儘管方向盤是四幅式,但別緻的造型也不會使得哈弗H2s的內飾看上去老氣,相反更具有一種時尚和個性。

哈弗H2s與H2到底啥關係?

月銷七萬多輛哈弗H6的哈弗品牌顯然不太滿足僅憑藉一款車打天下,哈弗最近出了一款名為H2S的小型SUV,作為哈弗H2的“兄弟車型”,此車一出,很多網友在後台給小編留言,問這車是不是H2的改款車型,與現款的哈弗H2之間應該如何抉擇?今天就帶着疑問,給各位有意向於哈弗H2和H2S兩款車之間的朋友進行一番解答。

哈弗H2s

指導價格:8.38-10.28萬

H2s是否就是哈弗H2的改款?相信這是大家最為關心的問題,小編在這裏明確回答,哈弗H2s並不是H2的改款,而是一款完全獨立開發的全新小型SUV。

而且哈弗H2s的設計團隊可謂是來頭不小,它的設計總監pierre Leclercq,曾負責設計第一代寶馬X6和第二代寶馬X5,現在任職長城汽車全球設計總監。pierre入駐長城汽車,也有望在以後的日子里提升哈弗品牌的車型整體設計水平。

外觀:H2S更顯年輕

H2s的外觀設計靈感源自2015年上海車展哈弗品牌發布的Concept B以及Concept R兩款概念車型,而實車誕生之後我們可以看出哈弗H2s在概念車的還原程度上保留了相當高的水平。

無論是紅標版本抑或是藍標版本,哈弗H2s都显示出了一種時尚感非常充沛的視覺感受。藍標版本使用了更多銳利的角度與線條,讓整個前臉顯得非常具有攻擊性。而紅標版則用了更加粗壯的風格,提升了整車的力量感。

而哈弗H2的外觀則顯得與自家銷量王者H6十分的相近,紅標版H2外觀敦實圓潤,整體看上去四平八穩,不仔細分辨的話乍一眼看上去就像是一台小了半號的H6。

藍標版本的H2除了前臉的進氣格柵造型不太同,保留了藍標哈弗家族的多邊形大嘴以外,整體設計語言與紅標版本保持一致,都給人以一種偏向穩重厚實的形象。

內飾:新創意VS家族風

哈弗H2s的內飾設計元素源自手風琴,初次見到這款內飾的時候會覺得這不是哈弗品牌,儘管方向盤是四幅式,但別緻的造型也不會使得哈弗H2s的內飾看上去老氣,相反更具有一種時尚和個性。

哈弗H2的話或許會顯得比較平淡,平直簡練的線條設計與哈弗家族汽車產品非常相近,沒有過多的亮點可言。但是這種設計也有一種好處,便是可以有着非常廣泛的接受人群,可以輕鬆的做到老少咸宜。

空間&動力:差別都不大

同樣是定位於小型SUV,哈弗H2s與哈弗H2使用了同一台1.5T渦輪增壓發動機,最大馬力150匹,峰值扭矩210牛米。但值得一提的是,在哈弗H2s身上搭載的是一套來自格特拉克的7速濕式雙離合變速箱,在這個價位來說使用7速雙離合的車型少之又少,看得出來長城汽車這次挺捨得下本的。

哈弗H2s車身尺寸為4416*1772*1638mm,軸距2550mm而哈弗H2的三圍尺寸為4335*1814*1695mm軸距為2560mm;兩台車的尺寸可謂是半斤八兩,在乘坐空間上,後排表現都屬於比較局促的類型。

購買建議:考慮小型SUV的消費者一般來說對於乘坐空間的訴求並不會特別嚴苛,個人推薦可以考慮最新出的H2s,從設計角度和配件供應角度來看,哈弗H2s將有可能成為哈弗品牌的轉型之作。

加之哈弗品牌一貫而為之的內飾用料比較厚道的做法,哈弗H2s給人的印象高檔感並不差,頂配車型不過是十萬出頭的價格,可以說也符合當下大多數人購車的預算。

全文總結:本次對比主要是為了解答眾多人的問題,那就是哈弗H2s與哈弗H2是彼此間獨立的關係。

而且,從哈弗H2s的推出以及哈弗高端子品牌WEY的發布可以看出,未來哈弗將會進一步優化和豐富產品線布局。相信憑藉這強大的設計團隊和合理的市場定位,未來哈弗爆款的車型將不會僅僅只有哈弗H6一款。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

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

顏值 動力 舒適 操控 這些十多萬的車子都可以滿足你

6L 110馬力、1。2T 110馬力、1。4T 131馬力和1。4T 150馬力,匹配5擋手動和6擋手自一體和7擋雙離合變速箱。1。6L車型的高爾夫和1。6L的308S一樣,都是保守的選擇。1。2T和1。4T車型的雙離合變速箱相對於308S的6AT來說,換擋更快,油耗表現更好,同時提速也很好,駕駛感受非常不錯,要不然也不會賣的那麼好了。

其實買了車開的最多的還是自己,所以一台車有一個比較好的操控確實是駕駛者的福音。作為一個熱血青年,給你一台“老年車”軒逸,那底盤你怎麼能忍受的了?

所以今天小編給大家介紹幾款不但有一些駕駛激情,同時家人坐着也會比較舒服的車子。可以說即爽了自己,同時又能很好的保證家人乘坐的舒適性。

東風標緻-標緻308S

11.27-17.97萬

308S的車身尺寸為4255*1820*1480mm,軸距為2620mm。屬於標準的緊湊型兩廂車,和同級別轎車比起來,寬度尺寸較大,長度和高度差別較小,所以308S看起來比較低矮。308S車頭大燈組看起來炯炯有神,車身側面比較簡介,腰線利落,營造出動感的效果。

308S的動力系統為1.6L 117馬力/1.2T 136馬力+5擋手動/6擋手自一體,1.6T 167馬力+6擋手自一體。保守的選擇就是1.6L車型,雖然動力表現一般,但是好在底盤還是特別紮實。

1.6T車型絕對是最推薦的,動力十足,油耗很低,發動機是標緻的明星發動機。很多主力車型都有它的身影,1.6T車型目前也有着一兩萬的優惠,目前來看這款車還是不錯的。另外308S的底盤向舒適性做出了一些妥協,乘坐起來並不會感到很顛簸。

一汽-大眾-高爾夫

12.19-23.99萬

普通高爾夫的車身尺寸為4255*1799*1452mm,軸距為2637mm,GTI車型就不做過多介紹,銷量很少。高爾夫的外觀和308S差不多,都是看起來很緊湊的兩廂車,不同的就是308S看起來更柔美,高爾夫則更陽剛。

高爾夫的發動機為1.6L 110馬力、1.2T 110馬力、1.4T 131馬力和1.4T 150馬力,匹配5擋手動和6擋手自一體和7擋雙離合變速箱。1.6L車型的高爾夫和1.6L的308S一樣,都是保守的選擇。

1.2T和1.4T車型的雙離合變速箱相對於308S的6AT來說,換擋更快,油耗表現更好,同時提速也很好,駕駛感受非常不錯,要不然也不會賣的那麼好了。雖然大眾的定價更高,但是優惠也更大,綜合下來和308S差不多。

長安馬自達-馬自達3 昂克賽拉

11.49-15.99萬

兩廂版的昂克賽拉車身尺寸為4461*1795*1474mm,三廂版的車身尺寸為4582*1795*1458mm,軸距同為2700mm。 昂克賽拉的外觀自然不必介紹,絕對是數一數二的。單憑這外觀就會有很多人買單的。

昂克賽拉的動力系統為1.5L 117馬力+6擋手動和6擋手自一體,2.0L 158馬力+6擋手自一體。以前經常有人唱衰昂克賽拉,覺得它小眾,價格貴,優惠少,註定不會熱銷。但是實力強的选手總會發光的。

目前昂克賽拉的月銷量穩穩在萬台以上,可以說最終還是得到了大家的肯定,和本田地球夢不同的是,昂克賽拉的創馳藍天技術更多的是側重整個車子,所以昂克賽拉駕駛起來很容易就達到了人車合一的狀態,開起來非常順手。不過昂克賽拉,最好還是買2.0L車型。

總結:308S和高爾夫的底盤都很紮實,高速穩定性很好,雖然308S實力也很強,但是就是銷量被高爾夫完虐,所以說308S只要再加大優惠力度銷量肯定會不錯的。昂克賽拉算是比較另類的日本車,非常強調駕控樂趣,雖然底盤沒辦法和它倆比,但是駕駛感受卻非常不錯。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

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

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

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

潤滑油巨頭強勢發布——雪佛龍金富力

產品在抗腐蝕和抗磨損性,減少油泥與積碳,提升發動機性能和燃油經濟性這幾大方面均優於潤滑油行業最嚴格的標準。新品系列也採用了針對中國市場度身定製的配方和技術,滿足中國消費者對潤滑油產品的更高要求。”雪弗龍在發布會上展示產品的測試結果显示:金富力機油提供卓越的抗磨損保護和抗腐蝕保護,分別比ApI標準高出80%與90%。

2016年12月8日,世界500強、世界領先的一體化能源企業雪弗龍,在浙江嘉興舉行了旗下金富力品牌全系潤滑油的新品發布會。

這次發布會上,雪弗龍為消費者帶來了金富力全新系列產品,包括金富力全合成潤滑油、金富力合成型潤滑油及金富力方程式潤滑油三種。

雪弗龍金富力在此次新品發布會主推油泥防禦盾™技術,雪佛龍(中國)投資有限公司產品技術專家,王琴女士表示:“因為雪佛龍是一家比較獨特的潤滑油生產商,它是目前全球為數不多的一家既有既具備基礎油生產能力,又具備添加劑生產能力的潤滑油生產廠商。所以在研發方面,雪佛龍一直都是比較領先的。而油泥防禦盾™是雪佛龍獨有的科技,它給消費者帶來最大的好處,第一個就是保護性能很強,它能夠很好地防止發動機內部磨損,保護髮動機。另外它能夠延長發動機的使用壽命,因為金富力的抗氧化性能很好,在整個潤滑油的使用過程中,它都能像新油一樣保護髮動機。最後油泥防禦盾™也能很好地提高發動機的燃油經濟性。”

發布會上,雪佛龍潤滑油亞太區技術專家,Joyce女士介紹到:“此次上市的雪佛龍金富力全系產品,擁有包括油泥防禦盾
TM科技在內的很多創新技術。產品在抗腐蝕和抗磨損性,減少油泥與積碳,提升發動機性能和燃油經濟性這幾大方面均優於潤滑油行業最嚴格的標準。新品系列也採用了針對中國市場度身定製的配方和技術,滿足中國消費者對潤滑油產品的更高要求。”

雪弗龍在發布會上展示產品的測試結果显示:金富力機油提供卓越的抗磨損保護和抗腐蝕保護,分別比ApI標準高出80%與90%。在減少機油濾網上的油泥方面,金富力機油的性能比GM Dexos1標準要求高出 10%。採用油泥防禦盾配方的金富力機油的粘度保持性能比GM Dexos1 標準所要 求的高出70%。

雪佛龍一直秉承着專業的研發態度,追求精湛的工藝,力求將產品做到極致。這次發布會上新產品,針對中國消費習慣與獨特的路況問題,提出了可行性的解決方案,為打開中國市場做好鋪墊。隨着雪佛龍金富力的發布,雪佛龍將為中國消費者及合作夥伴帶來全球領導的品牌,一流的產品。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

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

Spring AOP學習筆記02:如何開啟AOP

  上文簡要總結了一些AOP的基本概念,並在此基礎上敘述了Spring AOP的基本原理,並且輔以一個簡單例子幫助理解。從本文開始,我們要開始深入到源碼層面來一探Spring AOP魔法的原理了。

  要使用Spring AOP,第一步是要將這一功能開啟,一般有兩種方式:

  • 通過xml配置文件的方式;
  • 通過註解的方式;

 

1. 配置文件開啟AOP功能

  我們先來看一下配置文件的方式,這個上文也提到過,在xml文件中加上對應的標籤,而且別忘了加上對應的名稱空間(即下面的xmlns:aop。。。):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop = "http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
     
     <aop:aspectj-autoproxy/>

</beans>

  這裡是通過標籤<aop:aspectj-autoproxy/>來完成開啟AOP功能,這是一個自定義標籤,需要自定義其解析,而這些spring都已經實現好了,前面專門寫過一篇文章講述spring是如何解析自定義xml標籤的,我們這裏大致回顧一下解析流程:

  • 定義一個XML文件來描述你的自定義標籤元素;
  • 創建一個Handler,擴展自NamespaceHandlerSupport,用於註冊下面的parser;
  • 創建若干個BeanDefinitionParser的實現,用來解析XML文件中的定義;
  • 將上述文件註冊到Spring中,這裏其實是做一下配置;

  我們就不照着這個步驟來了,我們直接參考spring對這個自定義標籤的解析過程,上面的4個步驟只是作為參考,在整個解析過程中都會涉及到。

  前面講解析自定義xml標籤時候提到過,解析的流程大致如下:

  • 首先會去獲取自定義標籤對應的名稱空間;
  • 然後根據名稱空間找到對應的NamespaceHandler;
  • 調用自定義的NamespaceHandler進行解析;

1.1 獲取名稱空間

  這裏<aop:aspectj-autoproxy/>對應的名稱空間是什麼呢?在上面的開啟aop的配置文件裏面名稱空間那裡給出了一些線索,其實就是下面這個:

http://www.springframework.org/schema/aop

  至於名稱空間的獲取,也無甚好說的,其實就是直接調用org.w3c.dom.Node提供的相應方法來完成名稱空間的提取。

1.2 獲取handler

  然後又是如何根據名稱空間找到對應的NamespaceHandler呢?之前也說到過,在找對應的NamespaceHandler時會去META-INF/spring.handlers這個目錄下加載資源文件,我們來找一下spring.handlers這個文件看看(需要去spring-aop對應的jar報下找):

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

  看到沒,這裡是以key-value的形式維護着名稱空間和對應handler的關係的,所以對應的handler就是這個AopNamespaceHandler。spring根據名稱空間找到這個handler之後,會通過反射的方式將這個類加載,並緩存起來。

1.3 解析標籤

  上面的handler只有一個自定義的方法:

public void init() {
    // In 2.0 XSD as well as in 2.1 XSD.
    registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
    registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
    registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

    // Only in 2.0 XSD: moved to context namespace as of 2.1
    registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}

  這是一個初始化方法,在加載的時候會執行,主要作用就是註冊一些解析器,這裏我們主要關注AspectJAutoProxyBeanDefinitionParser,這就是我們要找的,它的作用就是解析<aop:aspectj-autoproxy/>標籤的。主要流程就是,spring會調用上一步拿到的AopNamespaceHandler的parse()方法,在這個方法裏面,會將解析的工作委託給AspectJAutoProxyBeanDefinitionParser來完成具體解析工作,我們就來看一下具體幹了啥吧。

  開始解析的工作從這裏開始:

return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

  此時我們拿到的handler其實是我們自定義的AopNamespaceHandler了,但是它並沒有實現parse()方法,所以這裏這個應該是調用的父類(NamespaceHandlerSupport)中的parse()方法:

public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 尋找解析器並進行解析操作
    return findParserForElement(element, parserContext).parse(element, parserContext);
}

private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
    // 獲取元素名稱,也就是<aop:aspectj-autoproxy/>中的aspectj-autoproxy
    String localName = parserContext.getDelegate().getLocalName(element);
    // 根據aspectj-autoproxy找到對應的解析器,也就是在registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
    // 註冊的解析器
    BeanDefinitionParser parser = this.parsers.get(localName);
    if (parser == null) {
        parserContext.getReaderContext().fatal(
            "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
    }
    return parser;
}

  首先是尋找元素對應的解析器,然後調用其parse()方法。結合我們前面的示例,其實就是首先獲取在AopNamespaceHandler類中的init()方法中註冊對應的AspectJAutoProxyBeanDefinitionParser實例,並調用其parse()方法進行進一步解析:

public BeanDefinition parse(Element element, ParserContext parserContext) {
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    extendBeanDefinition(element, parserContext);
    return null;
}

// 下面的代碼在AopConfigUtils中
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        ParserContext parserContext, Element sourceElement) {

    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    registerComponentIfNecessary(beanDefinition, parserContext);
}

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

  上面這一堆代碼最核心的部分就在後兩個方法中,就是完成了對AnnotationAwareAspectJAutoProxyCreator類的註冊,到這裏對自定義標籤<aop:aspectj-autoproxy/>的解析也就完成了,可以看到其最核心的部分就是完成了對AnnotationAwareAspectJAutoProxyCreator類的註冊,那為什麼註冊了這個類就開啟了aop功能呢?這裏先賣個關子,後面詳細說。

  這裏再回過頭來看一下上面說到的spring對自定義標籤解析的4個步驟,其實第一步的schema對應的是在org.springframework.aop.config路徑下的spring-aop-3.0.xsd文件,其映射關係是維護在META-INF/spring.schemas文件中的,而spring-aop-3.0.xsd的主要作用就是描述自定義標籤。

  當通過META-INF/spring.handlers找到對應的AopNamespaceHandler,並通過在其加載后執行init()方法過程中完成了AspectJAutoProxyBeanDefinitionParser的註冊,有這個parser再來完成對自定義標籤的解析工作,這對應上面4個步驟中的第二步和第三部。至於第四步的配置工作,無非就是將spring.schemas和spring.handlers這兩個配置文件放在META-INF/目錄下罷了。

  關於這部分解析過程,寫得不是非常詳細,如果有不明白,可以參考之前一篇文章,講spring是如何解析自定義xml標籤。

 

2. 註解方式開啟aop

  另一種開啟spring aop的方式是通過註解的方式,使用的註解是@EnableAspectJAutoProxy,可以通過配置類的方式完成註冊:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}

   也可以在啟動類上直接加上這個註解,這在springboot中比較常見,其實質也是上面的方式。通過這種方式配置之後,就開啟了aop功能,那具體又是如何實現的呢?我們看一下這個註解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

    /**
     * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
     * to standard Java interface-based proxies. The default is {@code false}.
     */
    boolean proxyTargetClass() default false;

    /**
     * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
     * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
     * Off by default, i.e. no guarantees that {@code AopContext} access will work.
     * @since 4.3.1
     */
    boolean exposeProxy() default false;

}

  這裏我們的關注點是其通過@Import(AspectJAutoProxyRegistrar.class)引入了AspectJAutoProxyRegistrar,那這又是什麼?

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * Register, escalate, and configure the AspectJ auto proxy creator based on the value
     * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
     * {@code @Configuration} class.
     */
    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }

}

  看到這裏,是不是有點眼熟了呢?是的,其實它也是和上面說的xml配置使用的方式一樣,通過AopConfigUtils來完成AnnotationAwareAspectJAutoProxyCreator類的註冊。是不是比xml配置文件的方式方便許多呢。

 

4. 開啟aop的魔法

  通過前面的學習我們了解了可以通過Spring自定義配置完成對AnnotationAwareAspectJAutoProxyCreator類型的自動註冊,而這個類到底是做了什麼工作來實現AOP的操作呢?這裏還是先來看一下AnnotationAwareAspectJAutoProxyCreator的類層次結構:

  這裡有一個很重要的點,就是AnnotationAwareAspectJAutoProxyCreator實現了BeanPostProcessor接口。在IOC部分的文章中有詳細說過,Spring在加載Bean的過程中會在實例化bean前後調用BeanPostProcessor的相關方法(相關邏輯是在initializeBean方法中,調用postProcessBeforeInitialization、postProcessAfterInitialization方法),而AOP的魔法就是從這裏開始的。

  每次看到這裏,我內心對spring的軟件架構設計都是湧現出無比的佩服,通過後處理器的方式來做擴展,對原有模塊是沒有任何改動,也不會產生耦合,spring親自踐行着對修改關閉,對擴展開放的原則。

 

3. 總結

   本文我們學習了spring是如何開啟aop功能的,無論是通過xml配置文件方式,還是通過Java config這種註解的方式,其最終都是完成了將AnnotationAwareAspectJAutoProxyCreator這個類註冊到spring容器當中,那這個類又有什麼魔法,可以達到將其註冊到容器即達到開啟aop的功效,其實其繼承自BeanPostProcessor接口,通過後處理器的方式擴展出了開啟spring aop的功能。

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

【其他文章推薦】

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

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

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

※回頭車貨運收費標準

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

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

Oracle數據遷移後由列的直方圖統計信息引起的執行計劃異常

(一)問題背景

在使用impdp進行數據導入的時候,往往在導入表和索引的統計信息的時候,速度非常慢,因此我在使用impdp進行導入時,會使用exclude=table_statistics排除表的統計信息,從而加快導入速度,之後再手動收集統計信息。

                                              圖.impdp導入數據的時導入統計信息速度非常慢

導入語句如下:

impdp user/password directory=DUMPDIR dumpfile=TEST01.dmp logfile=TEST01.log remap_schema=TEST_USER:TEST_USER123 exclude=table_statistics

手動收集統計信息語句如下:

EXEC dbms_stats.gather_table_stats(ownname => 'LIJIAMAN',tabname => 'TEST01');

最近使用以上方法將數據還原到測試環境后,發現與生產環境執行計劃存在偏差,本來應該走全表掃描的,卻走了索引範圍掃描。經過確認,是由於列的直方圖統計信息未收集引發的執行計劃偏差。


(二)列的直方圖統計信息

什麼是列的直方圖統計信息呢?在Oracle數據庫中,Oracle默認列上的值是在最小值與最大值之間均分佈的,當在計算cardinatity時,會以均勻分佈的方式計算,但是在實際生活中某些場景下數據並非均勻分佈。舉個列子,某公司有員工10000人,表A的列COL1記錄員工的績效(分別是:A、B、C、D,A最好,D最差),那麼可能A佔了15%,B佔了60,C佔了20%,D佔了5%。很明顯在該場景下數據並非均勻分佈,假如以均勻分佈的方式去統計員工的績效,可能會導致執行計劃失准。

當列的數據分佈不均勻的時候,就需要統計列上的數據分佈情況,從而走出正確的執行計劃,列的直方圖統計信息就是記錄列上的數據分佈情況的。


(三)異常模擬

STEP1:創建測試表test01

create table test01
(id number,
name varchar2(10)
);
create index idx_test01_id on test01(id);

向test01中插入測試數據

begin
insert into test01 values(1,'a');

for i in 1..10 loop
insert into test01 values(2,'b');
end loop;

for i in 1..100 loop
insert into test01 values(3,'c');
end loop;

for i in 1..1000 loop
insert into test01 values(4,'d');
end loop;

commit;
end;

查看數據分佈情況:

SQL> SELECT ID,NAME,COUNT(*) FROM test01 GROUP BY ID,NAME ORDER BY COUNT(*);

ID          NAME       COUNT(*)
---------- ---------- ----------
1           a          1       
2           b          10
3           c          100
4           d          1000


STEP2:收集統計信息,因為上面查詢過id列,故在收集統計信息的時候,會收集直方圖的統計信息

EXEC dbms_stats.gather_table_stats(ownname => 'LIJIAMAN',tabname => 'TEST01');

查看是否已經收集了直方圖信息,發現id列上已經收集

SQL> SELECT a.OWNER,a.TABLE_NAME,a.COLUMN_NAME,a.LOW_VALUE,a.HIGH_VALUE,a.NUM_BUCKETS,a.HISTOGRAM
2 FROM dba_tab_columns a
3 WHERE a.OWNER = 'LIJIAMAN' AND a.TABLE_NAME = 'TEST01';

OWNER     TABLE_NAME   COLUMN_NAME   LOW_VALUE     HIGH_VALUE    NUM_BUCKETS   HISTOGRAM
--------- -----------  ------------  ------------  ------------  -----------  ---------------
LIJIAMAN  TEST01       ID            C102          C105          4             FREQUENCY
LIJIAMAN  TEST01       NAME          61            64            1             NONE

查看直方圖,已經將id列的4個值放入了4個bucket中:

SQL> SELECT * FROM dba_tab_histograms a WHERE a.OWNER = 'LIJIAMAN' AND a.TABLE_NAME = 'TEST01';

OWNER        TABLE_NAME    COLUMN_NAME    ENDPOINT_NUMBER    ENDPOINT_VALUE ENDPOINT_ACTUAL_VALUE
-----------  ------------  -------------  ---------------    -------------- ----------------------
LIJIAMAN     TEST01        ID                           1                 1 
LIJIAMAN     TEST01        ID                          11                 2 
LIJIAMAN     TEST01        ID                         111                 3 
LIJIAMAN     TEST01        ID                        1111                 4 
LIJIAMAN     TEST01        NAME                         0    5.036527952778 
LIJIAMAN     TEST01        NAME                         1    5.192296858534


STEP3:查看id=1和id=4的執行計劃,當id=1時,走索引範圍掃描,當id=4時,走全表掃描

id列存在直方圖統計信息,當id=1時,走索引範圍掃描 id列存在直方圖統計信息,當id=4時,走全表掃描
SELECT * FROM test01 WHERE ID=1

 Plan Hash Value  : 1151852672 

----------------------------------------------------------------------------------------
| Id  | Operation                     | Name          | Rows | Bytes | Cost | Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |               |    1 |     5 |    2 | 00:00:01 |
|   1 |   TABLE ACCESS BY INDEX ROWID | TEST01        |    1 |     5 |    2 | 00:00:01 |
| * 2 |    INDEX RANGE SCAN           | IDX_TEST01_ID |    1 |       |    1 | 00:00:01 |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
------------------------------------------
* 2 - access("ID"=1)
SELECT * FROM test01 WHERE ID=4

 Plan Hash Value  : 262542483 

-----------------------------------------------------------------------
 | Id  | Operation           | Name   | Rows | Bytes | Cost | Time     |
-----------------------------------------------------------------------
 |   0 | SELECT STATEMENT    |        | 1000 |  5000 |    3 | 00:00:01 |
 | * 1 |   TABLE ACCESS FULL | TEST01 | 1000 |  5000 |    3 | 00:00:01 |
-----------------------------------------------------------------------

Predicate Information (identified by operation id):
 ------------------------------------------
 * 1 - filter("ID"=4)

STEP4:接下來模擬數據遷移,排除統計信息

導出表test01

expdp lijiaman/lijiaman directory=DUMPDIR tables=LIJIAMAN.TEST01 dumpfile =test01.dmp

刪除原來的表:

SQL> drop table test01;
Table dropped

再次導入表,排除統計信息:

impdp lijiaman/lijiaman directory=DUMPDIR dumpfile =test01.dmp exclude=table_statistics

查看錶的統計信息,不存在統計信息:

SQL> SELECT   a.OWNER,a.TABLE_NAME,a.COLUMN_NAME,a.LOW_VALUE,a.HIGH_VALUE,a.NUM_BUCKETS,a.HISTOGRAM
   2  FROM     dba_tab_columns a
   3  WHERE    a.OWNER = 'LIJIAMAN' AND a.TABLE_NAME = 'TEST01';

OWNER          TABLE_NAME      COLUMN_NAME     LOW_VALUE    HIGH_VALUE   NUM_BUCKETS HISTOGRAM
 -------------- --------------- --------------- ------------ ------------ ----------- ---------------
 LIJIAMAN       TEST01          ID                                                    NONE
 LIJIAMAN       TEST01          NAME                                                  NONE

STEP5:手動收集統計信息

EXEC dbms_stats.gather_table_stats(ownname => 'LIJIAMAN',tabname => 'TEST01');

發現統計信息已經收集,但是不存在直方圖的統計信息

SQL> SELECT   a.OWNER,a.TABLE_NAME,a.COLUMN_NAME,a.LOW_VALUE,a.HIGH_VALUE,a.NUM_BUCKETS,a.HISTOGRAM
  2  FROM     dba_tab_columns a
  3  WHERE    a.OWNER = 'LIJIAMAN' AND a.TABLE_NAME = 'TEST01';

OWNER     TABLE_NAME  COLUMN_NAME  LOW_VALUE   HIGH_VALUE  NUM_BUCKETS HISTOGRAM
--------- ----------- -----------  ----------- ----------- ----------- ---------------
LIJIAMAN  TEST01      ID           C102        C105                  1 NONE
LIJIAMAN  TEST01      NAME         61          64                    1 NONE

STEP6:再次查看id=1和id=4的執行計劃,當id=1或id=4時,都走索引範圍掃描

id列未收集直方圖統計信息,當id=1時,走索引範圍掃描 id列未收集直方圖統計信息,當id=4時,走索引範圍掃描
SELECT * FROM test01 WHERE ID=1
 Plan Hash Value  : 1151852672 

----------------------------------------------------------------------------------------
| Id  | Operation                     | Name          | Rows | Bytes | Cost | Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |               |  278 |  1390 |    2 | 00:00:01 |
|   1 |   TABLE ACCESS BY INDEX ROWID | TEST01        |  278 |  1390 |    2 | 00:00:01 |
| * 2 |    INDEX RANGE SCAN           | IDX_TEST01_ID |  278 |       |    1 | 00:00:01 |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
------------------------------------------
* 2 - access("ID"=1)
SELECT * FROM test01 WHERE ID=4

 Plan Hash Value  : 1151852672 

----------------------------------------------------------------------------------------
| Id  | Operation                     | Name          | Rows | Bytes | Cost | Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |               |  278 |  1390 |    2 | 00:00:01 |
|   1 |   TABLE ACCESS BY INDEX ROWID | TEST01        |  278 |  1390 |    2 | 00:00:01 |
| * 2 |    INDEX RANGE SCAN           | IDX_TEST01_ID |  278 |       |    1 | 00:00:01 |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
------------------------------------------
* 2 - access("ID"=4)

STEP7:再次收集統計信息,因為使用過了id列作為查詢條件,故再次收集統計信息時,會收集id列的直方圖信息:

EXEC dbms_stats.gather_table_stats(ownname => 'LIJIAMAN',tabname => 'TEST01');

可以看到,此時已經收集了id列的直方圖統計信息:

SQL> SELECT   a.OWNER,a.TABLE_NAME,a.COLUMN_NAME,a.LOW_VALUE,a.HIGH_VALUE,a.NUM_BUCKETS,a.HISTOGRAM
  2  FROM     dba_tab_columns a
  3  WHERE    a.OWNER = 'LIJIAMAN' AND a.TABLE_NAME = 'TEST01';

OWNER                          TABLE_NAME                     COLUMN_NAME                    LOW_VALUE     HIGH_VALUE    NUM_BUCKETS HISTOGRAM
------------------------------ ------------------------------ ------------------------------ ------------- ------------- ----------- ---------------
LIJIAMAN                       TEST01                         ID                             C102          C105                    4 FREQUENCY
LIJIAMAN                       TEST01                         NAME                           61            64                      1 NONE

執行計劃已經按照我們想要的方式走:

id列重新收集直方圖統計信息,當id=1時,走索引範圍掃描 id列重新收集直方圖統計信息,當id=4時,走全表掃描
SELECT * FROM test01 WHERE ID=1

 Plan Hash Value  : 1151852672 

----------------------------------------------------------------------------------------
| Id  | Operation                     | Name          | Rows | Bytes | Cost | Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |               |    1 |     5 |    2 | 00:00:01 |
|   1 |   TABLE ACCESS BY INDEX ROWID | TEST01        |    1 |     5 |    2 | 00:00:01 |
| * 2 |    INDEX RANGE SCAN           | IDX_TEST01_ID |    1 |       |    1 | 00:00:01 |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
------------------------------------------
* 2 - access("ID"=1)
SELECT * FROM test01 WHERE ID=4

 Plan Hash Value  : 262542483 

-----------------------------------------------------------------------
| Id  | Operation           | Name   | Rows | Bytes | Cost | Time     |
-----------------------------------------------------------------------
|   0 | SELECT STATEMENT    |        | 1000 |  5000 |    3 | 00:00:01 |
| * 1 |   TABLE ACCESS FULL | TEST01 | 1000 |  5000 |    3 | 00:00:01 |
-----------------------------------------------------------------------

Predicate Information (identified by operation id):
------------------------------------------
* 1 - filter("ID"=4)

(四)總結

在使用expdp/impdp進行導出/導入數據的時,統計信息是非常重要的,對於大部分統計信息,我們可以在導入結束之後收集獲得。但是對於列的直方圖統計信息,Oracle默認收集的方式是auto,即Oracle會根據用戶對列的使用情況進行判斷是否收集直方圖統計信息,然而數據剛遷移完成,在表還未使用的情況下收集統計信息,往往收集不到列的直方圖信息,這就造成了執行計劃異常,這種情況通常在下一次收集統計信息之後會有所改變。

參考文檔:

DBMS_STATS With METHOD_OPT =>’..SIZE auto’ May Not Collect Histograms (Doc ID 557594.1)

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

【其他文章推薦】

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

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

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

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

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

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

※回頭車貨運收費標準

02 . Zabbix配置監控項及聚合圖形

安裝Zabbix Agent監控本機

安裝agent軟件

與server端不同,Agent只需安裝zabbix-agent包

cat /etc/yum.repos.d/zabbix.repo 
[zabbix]
name=Zabbix Official Repository - $basearch
baseurl=https://mirrors.aliyun.com/zabbix/zabbix/3.4/rhel/7/$basearch/
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-ZABBIX-A14FE591
 
[zabbix-non-supported]
name=Zabbix Official Repository non-supported - $basearch
baseurl=https://mirrors.aliyun.com/zabbix/non-supported/rhel/7/$basearch/
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-ZABBIX
gpgcheck=1


curl https://mirrors.aliyun.com/zabbix/RPM-GPG-KEY-ZABBIX-A14FE591 -o /etc/pki/rpm-gpg/RPM-GPG-KEY-ZABBIX-A14FE591
curl https://mirrors.aliyun.com/zabbix/RPM-GPG-KEY-ZABBIX -o /etc/pki/rpm-gpg/RPM-GPG-KEY-ZABBIX

yum -y install zabbix-agent zabbix-get
配置Agent並啟動
vim /etc/zabbix/zabbix_agentd.conf
Server=39.108.140.0                    # 被動模式 zabbix-server-ip
ServerActive=39.108.140.0              # 主動模式 zabbix-server-ip
Hostname=You-Men                       # Agent端主機名,最終显示在監控頁面上的名字
UnsafeUserParameters=1                 # 是否限制用戶自定義keys使用特殊字符

systemctl restart zabbix-agent
netstat -antp|grep agent
tcp        0      0 0.0.0.0:10050           0.0.0.0:*               LISTEN      3898/zabbix_agentd  
tcp6       0      0 :::10050                :::*                    LISTEN      3898/zabbix_agentd
配置snmp(可以不做)

zabbix除了可以使用agent獲取數據之外,還可以通過snmp獲取數據,為了能夠讓zabbix監控更多的信息,將本機的snmp功能啟動起來.

yum -y install net-snmp net-snmp-utils
vim /etc/snmp/snmpd.conf
com2sec notConfigUser 39.108.140.0 public
access notConfigGroup "" any noauth exact all none none
view all included .1 80
systemctl restart snmpd && systemctl enabel snmpd
ss -anup |grep snmp        # 161端口,udp協議
# 測試snmp協議工作是否正常
# snmpwalk -v 1 -c public 39.108.140.0   .1.3.6
# 使用v1版本,共同體為public,來對192.168.0.1的.1.3.6分支進行walk。

snmpwalk -v 2c -c public 39.108.140.0
# 使用v2c版本,共同體為public,對39.108.140.0進行walk。
# -v        显示當前SNMPWALK命令行版本.
# -
# 獲取cisco設備39.108.140.0的接口類型

接下來我們到web界面上配置如何監控本地主機,我們看到接口上是127.0.0.1,但是我們配置文件寫的是39.108.140.0,我們讓這兩個IP一致.點擊3進去然後修改.

更新完后,跳到下面頁面,稍等一會,重新載入一下頁面就是可用性為綠色了

至此,監控本地主機就完成了,如果想看下監控本地主機的網卡流量就做下面圖2步驟.鼠標依次根據数字挨個點,如果想要監控項是中文的話,可以做Zabbix故障例一,但是4.4版本較以前版本有所改善,監控項不是亂碼,而是英文.

如果想要將這種亂碼換成正常中文

如果是windows在C盤搜索simkai.tff中文楷體,拷貝/上傳到服務器,然後cp到zabbix的字體目錄
3.*版本:
cp  /root/simkai.ttf   /usr/share/zabbix/fonts/

# 不同的安裝方式,路徑會有所不同,所以可以直接find / -type d -type fonts找到類似的文件夾,那就是了
# 注意字體權限問題
vim /usr/share/zabbix/include/defines.inc.php
    define('ZBX_GRAPH_FONT_NAME',    'simkai');
    define('ZBX_FONT_NAME',            'simkai');

Zabbix監控遠程主機

如果遠程主機安裝不上zabbix-agent,可以通過裝的上的zabbix-agent的機器把包傳過去

yum -y instlal yum-utils
# 下載到指定目錄
yum install zabbix-agent -y --downloadonly --downloaddir=/root
1.安裝zabbix agent
    # 方法一(國外源zabbix好像下載不下來包了,用上面的源):
    # rpm -Uvh https://repo.zabbix.com/zabbix/4.4/rhel/7/x86_64/zabbix-release-4.4-1.el7.noarch.rpm
    yum clean all
    yum -y install zabbix-agent
    # 方法二:(使用別的機器傳過來的zabbix-agent包直接rpm安裝即可)
    rpm -ivh zabbix-agent-4.4.1-1.el7.x86_64.rpm

# 修改zabbix-agent配置並啟動服務

    vim /etc/zabbix/zabbix_agentd.conf
    Server=192.168.244.144
    Server=192.168.244.144            //監控主機IP地址
    Hostname=agent1.zabbix.com        //被監控主機到監控主機的名字
    UnsafeUserParameters=1

    systemctl start zabbix-agent
    ss -antp |grep 10050
# 接下來我們到web端進行操作
# 為了服務方便管理和易於查看。
# 監控系統中往往根據被監控的主機角色或其他屬性將同類主機劃分到同一個主機組中.

如果等上一段時間,可用性哪裡沒有紅色警告,就說明這台主機被添加進來了,但是因為沒有掛載模板和創建監控項,所以我們接下來嘗試着掛載一下模板,然後再去創建監控項.

我們到agent端裝一個nginx,然後去zabbix的web端找到此模板並掛載.

yum -y install nginx
systemctl start nginx

測試監控主機

接下來我們用瀏覽器或者elinks訪問一下nginx,產生一些數據,然後去zabbix上查看變化

elinks --dump 116.196.83.113

我們以後自定義Key監控項時,先看看最新數據有沒有數據過來,如果數據都不會過來,就別提圖形觸發器報警什麼了.

至此,添加本地主機,遠程主機,創建主機組,掛載模板就已經完了

Zabbix監控項

監控項(Items)簡介

監控項是Zabbix中獲得數據的基礎,沒有監控項,就沒有數據——因為一個主機只有監控項定義了單一的指標或者需要獲得的數據,監控項適用於採集數據的,多個同類的監控項可以定義成一個應用集,如,mysql增刪改查以及每秒鐘的讀表,寫錶速度可以寫成一個Mysql應用集.

對於監控項的示例,需要輸入以下必要的信息

名稱

輸入CPU Load作為值,在列表中和其他地方,都會显示這個值作為監控項名稱.

手動輸入system.cpu.load作為值,這是監控項的一個技術上的名稱,用於識別獲取信息的類型,這個特定值需要是Zabbix Agent預定義值的一種.
https://www.zabbix.com/documentation/3.4/manual/config/items/itemtypes/zabbix_agent # 此網址就是zabbix官網的預定義值.

信息類型

在此處選擇Numeric(float),這個屬性定義了獲得數據的格式
你也需要減少監控項歷史保留的天數,7或者14天,對於數據庫而言,最佳實踐是避免數據庫保留過多的歷史數據.
我們選擇了數據類型后,暫時保持其他選項的默認值.
1> 磁盤容量Units一般為B
2> 網卡流量單位為bps
3> Mysql每秒訪問量qps,例如MySQL每秒select,insert Mysql serlect

點擊添加,新的監控項就出現在監控項列表中了

查看數據

當一個監控項定義完成后,你可能好奇他具體獲取了什麼值,前往監控首頁,點擊最新數據,選擇相應的主機.看數據能不能過來以及是不是自己想要的類型.

圖表

當監控項運行了一段時間后,可以查看可視化圖表,如果沒有可以自己創建一個,下面會有詳細介紹

常用監控項

1.服務器網絡接口進出流量和總流量
    net.if.in[if,<mode>]
    net.if.out[if,<mode>]
    net.if.total[if,<mode>]

2.服務器啟動分區剩餘空間
    vfs.fs.size[fs,<mode>]
    vfs.fs.size[/boot,free]

3.監控虛擬機內存
    vm.memory.size[<mode>]
    vm.memory.size[total
    vm.memory.size[free]
    vm.memory.size[wired]

4.服務器服務狀態
    net.tcp.listen[port]
    net.tcp.port[<ip>,port]
    net.tcp.service[service,<ip>,<port>]
    net.tcp.service.perf[service,<ip>,<port>]

5.服務器進程數量
        proc.num[<name>,<user>,<state>,<cmdine>]
        zabbix_get -s 39.108.140.0 -k proc.num
    121
    zabbix_get -s 39.108.140.0 -k proc.num[,,run]
    3
    zabbix_get -s 39.108.140.0 -k proc.num[,,sleep]
    118

6.服務器CPU狀態(浮點型,無單位)    
    system.cpu.intr
    system.cpu.load[<cpu>,<mode>]
    system.cpu.num
    system.cpu.switches
    system.cpu.util[<cpu>,<type>,<mode>]
    zabbix_get -s 39.108.140.0 -k system.cpu.load[all,avg1]
    0.000000
    zabbix_get -s 39.108.140.0 -k system.cpu.load[,avg5]
    0.010000

7.磁盤IO情況
    vfs.dev.read[device,<type>,<mode>]
    vfs.dev.write[device,<type>,<mode>]
zabbix_get -s 39.108.140.0 -k vfs.dev.read[/dev/vda1]

8.監控文件修改
    vfs.file.chsum[file]        # 如監控/etc/passwd ,/etc/group 文件從而知道是否有新用戶創建
    vfs.file.md5sum[file]
    vfs.file.size[file]        # 通常用來監控日誌
    vfs.fs.size[fs,<mode>]

9.磁盤總和.
監控網卡流量

我們先創建一個應用集,這樣的話之後創建的網卡上傳,下載,總流量不會顯的很亂,都在一個Network應用集裏面,而且能導出成xml文件,放到其他的zabbxi主機上能直接用.

我們此刻做的創建監控項是利用zabbix安裝好自帶的監控項,跟自定義Key差不多,都是寫一個監控腳本然後傳參,每一個鍵值相當於一個監控腳本

接下來我們檢測---> 主機群組裡面去查看下最新數據,我們可以從下圖看到是有數據的

下行寬帶和上行寬帶.

下載就是in,下行寬帶,你發出去的就是out,作為一個服務器來說上行寬帶肯定要高,在家裡就是下行寬帶高,對服務器來說他需要接收很少的數據包,回復很多的數據包,而在家裡我們是發出去一個很小的數據包,返回來整個網頁.

接下來我們再去創建一個網卡輸出流量,然後將他們做成一個圖標,以圖形化展示出來

接下來我們再去監測裏面去查看最新數據,可以養成這個習慣,因為最新數據過來了才是說明當中數據流向沒有問題,如果數據都沒有過來你去創建圖形,圖表說沒有數據,你覺得得等一會,浪費時間影響效率

可以看到,兩個監控項都是有數據的,接下來我們去創建圖形

接下來我們去查看監測 ---> 圖形,選擇相應群組,相應的主機及創建的圖形

這台主機可以裝一個nginx,然後上傳一張大一點圖片到網站根目錄,然後訪問,再查看網絡波動圖.

或者我們直接上傳一個大點的rpm、tar包到其他主機.這樣看着明顯

監控CPU

跟剛才一樣,創建一個CPU應用集,方便管理歸納

接下來我們創建應用集的監控項,cpuintr,cpu中斷數

接下來我們創建一個cpu每隔一分鐘的負載監控項
通過下圖,我們可以看到,每個監控項都是有數據過來的,接下來我們去創建圖形

我們可以看到,數據是可以實時轉換成圖標的,接下來我們去做一個聚合圖形

創建聚合圖形

至此,我們第一個構造函數完成,另外一個構造函數同理,此處就不寫了,直接看結果圖.

創建系統定義好的監控項,跟上面兩個都差不多,多做做自然就會了,如果不習慣使用官方定義好的key,我們可以根據公司環境自己寫腳本自定義key,此章完結.

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

【其他文章推薦】

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

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

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

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

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

※回頭車貨運收費標準

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

秒懂系列,超詳細Java枚舉教程!!!

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

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

深入理解Java枚舉

一、什麼是枚舉

1.1 什麼是枚舉?

至於枚舉,我們先拿生活中的枚舉來入手,然後再引申Java中的枚舉,其實它們的意義很相似。

談到生活中的枚舉,假如我們在玩擲骰子的遊戲,在我們手中有兩個骰子,要求擲出兩個骰子的點數和必須大於6的概率,那麼在此情此景,我們就需要使用枚舉法一一列舉出骰子點數的所有可能,然後根據列舉出來的可能,求出概率。

可能有的小夥伴發現,這就是數學啊?這就是數學中的概率學和統計學。對,我們的枚舉法就是常用於概率統計中的。

1.2 Java中的枚舉類

Java 5.0引入了枚舉,枚舉限制變量只能是預先設定好的值。使用枚舉可以減少代碼中的 bug,方便很多場景使用。

二、Java枚舉的語法

枚舉類中的聲明

1訪問修辭符 enum 枚舉名 {
2    枚舉成員,
3    枚舉成員,
4    ...
5};

class類中枚舉的聲明

1訪問修飾符 class 類名 {
2    enum 枚舉名 {
3        枚舉成員,
4        枚舉成員,
5        ...
6    }
7}

三、Java枚舉類的使用規則和應用場景

3.1 Java枚舉類的使用規則

至於枚舉你也有所了解了,Java中的枚舉也是一樣的。而Java中枚舉類的使用,也有特定的規則和場景。如果你看了以下的規則不明白的話,沒有關係,繼續向下學你就會明白,因為我在下面都會有講解到這些規則。如下幾個規則:

  • 類的對象是確定的有限個數。
  • 當需要定義一組常量時,建議使用枚舉。
  • 如果枚舉類中只有一個對象,則可以作為單例模式的實現方法。
  • 枚舉類不能被繼承
  • 枚舉類不能被單獨的new創建對象
  • 枚舉類中的枚舉成員是用`,`隔開的,多個枚舉成員之間用`_`隔開
  • 如果枚舉類中的只有一個或多個枚舉成員,其他什麼都沒有,我們在用`,`隔開的同時。最後可以省略`;`結束符。

注意: 如果關於枚舉單例設計模式不太了解的小夥伴可以參考深度學習單例設計模式一文,你肯定會有意想不到收穫,請相信我!

3.2 Java枚舉類的應用場景

根據Java中使用枚舉類的規則,有以下幾種場景適合來使用枚舉類,如下:

  • 星期: Monday(星期一)、Tuesday(星期二)、Wednesday(星期三)、Thursday(星期四)、Firday(星期五)、Saturday(星期六)、Sunday(星期日)
  • 性別: Man(男)、Woman(女)
  • 季節: Spring(春天)、Summer(夏天)、Autumn(秋天)、Winter(冬天)
  • 支付方式: Cash(現金)、WeChatPay(微信)、Alipay(支付寶)、BankCard(銀行卡)、CreditCard(信用卡)
  • 訂單狀態: Nonpayment(未付款)、Paid(已付款)、Fulfilled(已配貨)、Delivered(已發貨)、Return(退貨)、Checked(已確認)
  • 線程狀態: Establish(創建)、Ready(就緒)、Run(運行)、Obstruct(阻塞)、Die(死亡)
  • 等等……

四、枚舉類的基本使用步驟解析

那我們就解釋以下這兩個規則,我們在上述中已經了解了枚舉的作用。Java中枚舉也不例外,也是一一列舉出來方便我們拿出來一個或多個使用。這有點像我們的多選框,我們把需要用到的所有選項內容放在各個多選框後面,當我們在使用的時候只需要勾選自己需要的勾選框即可,這就代表了我們需要被選中多選框後面的內容。

那麼,Java中的枚舉類是如何使用呢?

這裏我們簡單的模擬一個場景,假設你的女朋友十分的喜歡喝點冷飲或熱奶茶之類的飲品,在生活中也有很多像蜜雪冰城等等這種類型的飲品店。當你為女朋友買她愛喝的珍珠奶茶時,服務員會問你,要大杯、中杯還是小杯的。當然,為了滿足女朋友,你通常會選擇大杯。這就意味着店內不允許顧客點規則外的飲品。

注意: 如果你是初學者或是不了解枚舉類的使用,此基本使用不懂沒有關係,請繼續往下看即可!

於是,我用Java代碼來實現一下,上述場景。

首先,創建枚舉類。分別為珍珠奶茶添加大、中、小杯杯型。

 1package com.mylifes1110.java;
2
3/**
4 * @ClassName PearlMilkTea
5 * @Description 為珍珠奶茶添加三個杯型:大、中、小
6 * @Author Ziph
7 * @Date 2020/6/8
8 * @Since 1.8
9 */

10public enum PearlMilkTea {
11    //注意:這裏枚舉類中只有枚舉成員,我在此省略了;結束符
12    SMALL, MEDIUM, LARGE
13}

其次,創建珍珠奶茶對象,再有方法來判斷枚舉類中的大、中、小杯。最後打印女朋友喝哪個杯型的珍珠奶茶!

 1package com.mylifes1110.test;
2
3import com.mylifes1110.java.PearlMilkTea;
4
5/**
6 * @ClassName PearlMilkTeaTest
7 * @Description 為女朋友買哪個杯型的珍珠奶茶(默認大杯)
8 * @Author Ziph
9 * @Date 2020/6/8
10 * @Since 1.8
11 */

12public class PearlMilkTeaTest {
13    public static void main(String[] args) {
14        //創建大杯的珍珠奶茶對象
15        PearlMilkTea pearlMilkTea = PearlMilkTea.LARGE;
16        PearlMilkTeaTest.drinkSize(pearlMilkTea);
17    }
18
19    //判斷為女朋友買哪個杯型的珍珠奶茶
20    public static void drinkSize(PearlMilkTea pearlMilkTea) {
21        if (pearlMilkTea == PearlMilkTea.LARGE) {
22            System.out.println("我為女朋友買了一大杯珍珠奶茶!");
23        } else if (pearlMilkTea == PearlMilkTea.MEDIUM) {
24            System.out.println("我為女朋友買了一中杯珍珠奶茶!");
25        } else {
26            System.out.println("我為女朋友買了一小杯珍珠奶茶!");
27        }
28    }
29}

image-20200608151052517

雖然,我們了解了枚舉類中的基本使用,但是我們在語法中還介紹了一種在類中定義的枚舉。正好,在此也演示一下。如下:

1public class PearlMilkTea {
2    enum DrinkSize {
3        SMALL,
4        MEDIUM, 
5        LARGE
6    }
7}

如果這樣創建就可以在class類中去創建enum枚舉類了。想想前面例子中的代碼其實並不合理,這是為什麼呢?因為我們寫代碼要遵循單一職責原則和見命知意的命名規範。所以,我寫的代碼是在珍珠奶茶的枚舉類中列舉的大、中、小的三種杯型枚舉成員。所以根據規範來講,我們珍珠奶茶中不能擁有杯型相關的枚舉,畢竟我們在生活中的這類飲品店中喝的所有飲品種類都有這三種杯型,因此我們的所有飲品種類中都需要寫一個枚舉類,顯然這是很不合理的。

如果讓它變的更加合理化,我們就細分飲品種類來創建飲品枚舉類和杯型的枚舉類並分別兩兩適用即可。也許有小夥伴會問我為什麼我要說這些合理不合理呢?因為自我感覺這是對枚舉類應用的思想鋪墊,所以你品、你細品!

五、自定義枚舉類

5.1 自定義枚舉類步驟

關於第四章枚舉類的基本使用,也許小夥伴們對枚舉的陌生,而並不知道為什麼這樣去創建枚舉對象。接下來,我來帶你使用常量來自定義枚舉類,試試是不是那個效果。

既然,上述第三章我舉出了這麼多枚舉類的應用場景,那我們挑選一個比較經典的春夏秋冬來實現自定義枚舉類。

首先,我們先創建一個季節類,分別提供屬性、私有構造器、春夏秋冬常量、Getter方法和toString方法,步驟如下:

 1package com.mylifes1110.java;
2
3/**
4 * 自定義季節的枚舉類
5 */

6public class Season {
7    //聲明Season對象的屬性,為private final修飾
8    private final String seasonName;
9
10    //私有化構造器,併為對象賦值
11    private Season(String seasonName) {
12        this.seasonName = seasonName;
13    }
14
15    //提供當前枚舉的多個對象,為public static final修飾
16    public static final Season SPRING = new Season("春天");
17    public static final Season SUMMER = new Season("夏天");
18    public static final Season AUTUMN = new Season("秋天");
19    public static final Season WINTER = new Season("冬天");
20
21    //提供外界通過getter方法來獲取枚舉對象的屬性
22    public String getSeasonName() {
23        return seasonName;
24    }
25
26    //重寫toString方法,以便打印出枚舉結果
27    @Override
28    public String toString() {
29        return "Season{" +
30                "seasonName='" + seasonName + '\'' +
31                '}';
32    }
33}

其次,我們去創建一個測試類,來使用該自定義枚舉類創建對象。由此看來,我們就可以根據類名來句點出常量對象了!

 1package com.mylifes1110.test;
2
3import com.mylifes1110.java.Season;
4
5/**
6 * 測試類
7 */

8public class SeasonTest {
9    public static void main(String[] args) {
10        Season spring = Season.SPRING;
11        System.out.println(spring);
12    }
13}

最後打印結果是春天的對象,由於我們覆蓋了toString方法,即可見對象內的內容。

image-20200608160220000

5.2 使用帶有參枚舉類

如果你在第三章時Java枚舉類的基本使用不明白,估計看完自定義枚舉類也了解的大差不差了。但是你有沒有發現我們自定義枚舉類是使用的有參數的對象呢?那我們怎樣使用真正的枚舉類來實現有參數的枚舉類呢?繼續看吧那就!

在這裏我將自定義枚舉類改裝了一下,改裝成了enum枚舉類實現的使用有參對象。如下:

 1package com.mylifes1110.java;
2
3public enum Season {
4    SPRING("春天"),
5    SUMMER("夏天"),
6    AUTUMN("秋天"),
7    WINTER("冬天");
8
9    private final String seasonName;
10
11    Season1(String seasonName) {
12        this.seasonName = seasonName;
13    }
14
15    public String getSeasonName() {
16        return seasonName;
17    }
18}

不知道你有沒有發現少了點什麼,少的部分其實就是我們創建常量對象的部分,而且在這個枚舉類中我也沒有去重寫toString方法,至於為什麼,下面就告訴你。

注意: 枚舉對象之間用,隔開!

其次,去創建了該枚舉類的測試類,我們測試以下,並看一下沒有重寫toString方法打印出來的結果。

 1package com.mylifes1110.test;
2
3import com.mylifes1110.java.Season;
4
5public class Seaso1Test {
6    public static void main(String[] args) {
7        Season1 spring = Season.SPRING;
8        System.out.println(spring);                     //SPRING
9        System.out.println(spring.getSeasonName());     //春天
10    }
11}

這裏我將打印的結果放在了打印語句後面的註釋中。我們發現沒有重寫toString方法竟然打印出來的是SPRING,這是為什麼呢?這應該從我們的繼承關係中分析,如果繼承的是基類Object的話,沒有重寫toString方法會打印對象地址。那麼我們就可以斷定,enum枚舉類的父類不是Object。那它的父類是誰呢?我們可以藉助來對象來獲取其父類,如下:

1System.out.println(Season.class.getSuperclass());        //class java.lang.Enum

同樣,答案放在了代碼後面的註釋中。我們發現它默認繼承的是Enum類。那麼,我們稍後就來就看看這個類中到底寫了些什麼方法。

六、Enum常用方法的使用

6.1 Enum中的所有方法

關於Enum類中的所有方法我以表格的方式列舉出來!

返回值 方法 描述
String name() 獲取枚舉成員的名稱
static T valueOf(Class<T> enumType, String name) 獲取指定枚舉成員名稱和類型的枚舉成員
String[] values() 獲取枚舉成員的所有值
int compareTo(E o) 比較此枚舉與指定對象的順序
int hashCode() 獲取枚舉成員的哈希值
int ordinal() 獲取枚舉成員的序數(第一個枚舉成員位置為0)
String toString() 返回枚舉成員名稱
Class<E> getDeclaringClass() 獲取枚舉成員的類對象

6.2 name和toString

關於name方法和toString方法,其實很簡單。name()就是根據枚舉成員來獲取該枚舉成員的字符串名稱。而同String方法也是用來獲取枚舉成員的字符串名稱。雖然作用都是相同的,但是name方法是用final修飾的不能被重寫,而toString是可以被重寫的。這裏我們還使用季節的案例來演示,打印結果並放在了代碼後面的註釋中,如下:

1System.out.println(Season.SUMMER.name());            //SUMMER
2System.out.println(Season.SUMMER.toString());        //SUMMER

6.3 valueOf

此方法的作用是傳入一個字符串,然後將它轉換成對應的枚舉成員。這裏傳入的字符串必須與定義的枚舉成員的名稱一致,嚴格區分大小寫。如果傳入的字符串並沒有找到其對應的枚舉成員對象,就會拋出異常。如下:

1System.out.println(Season.valueOf("WINTER"));            //WINTER
2System.out.println(Season.valueOf("WIN"));                //java.lang.IllegalArgumentException

image-20200608173858862

6.4 values

values方法的名字中就帶有一個s,再加上它的返回值是一個字符串數組。所以我們就可以得出它的作用是獲取枚舉成員的所有值,這些值並以數組的形式存儲。

1Season[] seasons = Season.values();
2for (Season season : seasons) {
3    System.out.print(season + " ");
4}

結果為:

1SPRING SUMMER AUTUMN WINTER 

6.5 ordinal

該方法是獲取枚舉成員的序數,其第一個枚舉成員位置為0。其實,為了好理解的話,可以把它看作數組中的索引。數組中的第一個元素位置同樣也是從0開始。那我們打印一下,看看結果如何,如下:

1//獲取指定枚舉成員的次序
2System.out.println(Season.SUMMER.ordinal());
3
4//獲取所有成員的次序
5Season[] seasons = Season.values();
6for (Season s : seasons) {
7    System.out.println(s + " -> " + s.ordinal());
8}

結果為:

image-20200608175529079

其源碼就是返回了一個從0開始int類型的值,從源碼中也可以看出最大值是int取值範圍的最大值。如下:

image-20200608180839568

6.6 compareTo

compareTo方法相信我們已經是很熟悉了。其作用就是用來比較的。但是在枚舉類中它比較的是什麼呢?實際上compareTo方法比較的是兩個枚舉成員的次序數,並返回次序相減后的結果。

首先,我們要知道SUMMER的次序數為1,WINTER的次序數為3。當使用前者比較後者,打印的結果是前者與後者相減后的差值,即1-3=-2

1System.out.println(Season.SUMMER.compareTo(Season.WINTER));            //-2

它的源碼是怎麼做的呢?那我們進入查看一下。

其中,前面的操作都是在判斷比較的雙方是否是一個枚舉類,如果不是的話就拋出異常。如果為枚舉類的話,就直接將次序數做了相減操作並返回。

image-20200608180532795

七、Java枚舉的高級特性

7.1 常量

我們知道,常量是用public static final修飾的。1.5之後有了枚舉,我們就可以把相關的常量放在一個枚舉容器中,而且使用枚舉的好處還在於枚舉為我們提供了很多便捷的的方法。

示例:

1public enum Season {
2    SPRING, SUMMER, AUTUMN, WINTER
3}

7.2 switch語句

你了解的switch語句都支持哪種類型呢?我這裏說一下,switch語句支持的類型有如下幾種:

  • 基本數據類型: byte、short、char、int
  • 包裝數據類型: Byte、Short、Character、Integer
  • 枚舉類型: Enum
  • 字符串類型: String(jdk7+ 開始支持)

具體枚舉類與switch語句的使用是如何實現呢?枚舉又是如何為switch語句提供便利的呢?來看一下吧。

 1package com.mylifes1110.java;
2
3public class WeekTest {
4    public static void main(String[] args) {
5        Week week = Week.MONDAY;
6        switch (week) {
7            case MONDAY:
8                System.out.println("星期一");
9                break;
10            case TUESDAY:
11                System.out.println("星期二");
12                break;
13            case WEDNESDAY:
14                System.out.println("星期三");
15                break;
16            case THURSDAY:
17                System.out.println("星期四");
18                break;
19            case FRIDAY:
20                System.out.println("星期五");
21                break;
22            case SATURDAY:
23                System.out.println("星期六");
24                break;
25            case SUNDAY:
26                System.out.println("星期日");
27                break;
28            default:
29                System.out.println("null");
30        }
31    }
32}
33
34enum Week {
35    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
36}

7.3 枚舉中定義多個參數與方法

有參枚舉在5.2中我已經做了詳細說明,我們在定義枚舉時不只是可以定義多個參數,還可以定義其他的普通方法來使用,而關於普通方法的使用是根據場景的,這裏我就不再做過多的贅述了。

 1package com.mylifes1110.java;
2
3public enum Season {
4    SPRING("春天"),
5    SUMMER("夏天"),
6    AUTUMN("秋天"),
7    WINTER("冬天");
8
9    private final String seasonName;
10
11    public static String getName(int index) {  
12        for (Season s : Season.values()) {  
13            if (c.getIndex() == index) {  
14                return c.name;  
15            }  
16        }  
17        return null;  
18    }
19
20    Season1(String seasonName) {
21        this.seasonName = seasonName;
22    }
23
24    public String getSeasonName() {
25        return seasonName;
26    }
27}

7.4 枚舉類實現接口

雖然枚舉類不能繼承,但是可以實現接口。以下是一個實現過程。

首先,創建一個接口。

1package com.mylifes1110.inter;
2
3public interface Show {
4    void show();
5}

其次,讓我們的四季枚舉類實現該接口並重寫方法。

 1package com.mylifes1110.java;
2
3import com.mylifes1110.inter.Show;
4
5public enum Season implements Show {
6    SPRING("春天"),
7    SUMMER("夏天"),
8    AUTUMN("秋天"),
9    WINTER("冬天");
10
11    private final String seasonName;
12
13    Season1(String seasonName) {
14        this.seasonName = seasonName;
15    }
16
17    public String getSeasonName() {
18        return seasonName;
19    }
20
21    @Override
22    public void show() {
23        System.out.println("嚮往四季如春");
24    }
25}

最後,當我們使用每一個枚舉類都可以調用show方法,而打印的結果也都是“嚮往四季如春”

1Season.WINTER.show();                //嚮往四季如春

聰明的你我相信發現了這個缺點,我們不管使用哪一個枚舉成員時,調用的show方法都是同一個。所以,我們在實現接口后,可以這樣重寫方法,如下:

 1package com.mylifes1110.java;
2
3import com.mylifes1110.inter.Show;
4
5public enum Season1 implements Show {
6    SPRING("春天") {
7        @Override
8        public void show() {
9            System.out.println("春天是個踏青的季節");
10        }
11    },
12    SUMMER("夏天") {
13        @Override
14        public void show() {
15            System.out.println("夏天是個炎熱的季節,我要吃冰棍");
16        }
17    },
18    AUTUMN("秋天") {
19        @Override
20        public void show() {
21            System.out.println("秋天還算是涼爽");
22        }
23    },
24    WINTER("冬天") {
25        @Override
26        public void show() {
27            System.out.println("冬天的雪還不錯,就是有點冷");
28        }
29    };
30
31    private final String seasonName;
32
33    Season1(String seasonName) {
34        this.seasonName = seasonName;
35    }
36
37    public String getSeasonName() {
38        return seasonName;
39    }
40}

我們在枚舉成員的後面加了{},而重寫的方法可以寫在各個枚舉成員中,這樣就接觸了上述所有的那個限制。這下,我們使用哪個枚舉成員對象調用show方法都是不同的。是不是非常NICE?

7.5 使用接口對枚舉分類

使用接口對枚舉分類,我們需要創建一個接口容器,裏面存放着此接口容器所存放的多個枚舉類,然後將各個枚舉類實現此接口,以這樣的方式可實現對枚舉分類。代碼如下,打印結果放在了代碼後面的註釋中:

 1package com.mylifes1110.inter;
2
3public interface Weeks {
4    //工作日
5    enum WorkingDay implements Weeks {
6        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY
7    }
8
9    //雙休日
10    enum Weekend implements Weeks {
11        SATURDAY, SUNDAY
12    }
13}
14
15class WeeksTest {
16    public static void main(String[] args) {
17        System.out.print("雙休日:");
18        for (Weeks.Weekend weekend : Weeks.Weekend.values()) {
19            System.out.print(weekend + " ");        //雙休日:SATURDAY SUNDAY
20        }
21
22        //換行
23        System.out.println();
24
25        System.out.print("工作日:");
26        for (Weeks.WorkingDay workingDay : Weeks.WorkingDay.values()) {
27            System.out.print(workingDay + " ");     //工作日:MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY
28        }
29
30        //換行
31        System.out.println();
32
33        Weeks.WorkingDay friday = Weeks.WorkingDay.FRIDAY;
34        System.out.println("星期五:" + friday);      //星期五:FRIDAY
35    }
36}

image-20200608194649335

八 枚舉類集合

8.1 EnumSet集合

關於Set集合,我們知道其集合中的元素是不重複的。其中的方法有以下幾種:

返回值 方法 描述
static EnumSet<E> allOf(Class<E> elementType) 創建一個包含指定元素類型的所有元素的枚舉 set。
EnumSet<E> clone() 返回一個set集合。
static EnumSet<E> complementOf(EnumSet<E> s) 創建一個其元素類型與指定枚舉set相同的set集合(新集合中包含原集合所不包含的枚舉成員)
static EnumSet<E> copyOf(EnumSet<E> s) 創建一個其元素類型與指定枚舉 set 相同的枚舉 set集合(新集合中包含與原集合相同的枚舉成員)
static EnumSet<E> copyOf(Collection<E> s) 創建一個從指定 collection 初始化的枚舉 set
static EnumSet<E> noneOf(Class<E> elementType) 創建一個具有指定元素類型的空枚舉 set
static EnumSet<E> range(E from, E to) 創建一個最初包含由兩個指定端點所定義範圍內的所有元素的枚舉 set。
static EnumSet<E> of 創建一個最初包含指定元素的枚舉 set。注意:可以指定多個元素,所以在這裏我沒有列舉參數
8.1.1 allOf

allOf方法需要我們傳入一個枚舉的類對象,它會根據傳入的枚舉類對象生成一個具有該類對象枚舉成員的Set集合。

1//創建一個包含Week所有枚舉元素的Set集合
2EnumSet<Week> weeks = EnumSet.allOf(Week.class);
3System.out.println(weeks);              //[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
4
5//打印Set集合中的元素
6for (Week week1 : weeks) {
7    System.out.print(week1 + " ");      //MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY SUNDAY
8}
8.1.2 clone

clone方法與直接打印枚舉的Set集合結果相同!

1//返回一個Set集合
2System.out.println(weeks.clone());      //[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
8.1.3 range

上面詳細講過枚舉是有序數的,而且枚舉類中的枚舉成員是秉承着從左向右的順序。所以我們可以使用range方法來創建指定枚舉成員端點的Set集合,也就是說我們需要傳入枚舉成員的起始與結束去創建一個該擁有該範圍枚舉成員的Set集合。如下:

1//創建一個最初包含由兩個指定端點所定義範圍內的所有元素的枚舉 set。
2System.out.println(EnumSet.range(Week.MONDAY, Week.FRIDAY));        //[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY]
8.1.4 complementOf

該方法有點特殊,它根據EnumSet去創建一個新Set集合。而新Set集合中枚舉成員相當於舊Set集合中枚舉成員的取反。

我們用場景來模擬一下,當前Week枚舉類中有星期一到星期日7個枚舉成員。我們使用range方法創建一個從星期一到星期五的Set集合(s1),隨後我在將使用complementOf方法根據s1生成新的Set集合(s2),最後打印s2查看集合中的元素就只有星期六和星期日。

注意: 如果我們的舊Set集合佔據了枚舉類中的所有枚舉成員,在使用complementOf方法生成的新Set集合,新集合中的元素打印後為空Set,即[]

1//創建一個其元素類型與指定枚舉set相同的set集合(新集合中包含原集合所不包含的枚舉成員)
2EnumSet<Week> weeks1 = EnumSet.complementOf(weeks);
3System.out.println(weeks1);             //[]
4
5EnumSet<Week> range = EnumSet.range(Week.MONDAY, Week.FRIDAY);
6EnumSet<Week> weeks3 = EnumSet.complementOf(range);
7System.out.println(weeks3);                //[SATURDAY, SUNDAY]
8.1.5 copyOf

copyOf方法與complementOf相反,它創建一個新Set集合。而新Set集合中的枚舉成員與舊Set集合中的枚舉成員相同,這相當於就是Copy(複製功能)。如果你理解了complementOf方法,這個方法對你來說也是沒有挑戰。以下我使用copyOf方法複製了一份weeks,其枚舉成員一個不少。

注意: copyOf方法還有一個可以複製connection集合來創建Set集合,其connection集合中必須存儲的是枚舉成員。

1//創建一個其元素類型與指定枚舉 set 相同的枚舉 set集合(新集合中包含與原集合相同的枚舉成員)
2EnumSet<Week> weeks2 = EnumSet.copyOf(weeks);
3System.out.println(weeks2);             //[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
1//複製存儲枚舉成員的HashSet集合
2Set set = new HashSet();
3set.add(Week.MONDAY);
4EnumSet set1 = EnumSet.copyOf(set);
5System.out.println(set1);        //[MONDAY]
8.1.6 of

of方法為我們提供了選擇性的便利,我們可以挑選任意枚舉成員成為Set集合的元素。

1//創建一個最初包含指定元素的枚舉 set。
2System.out.println(EnumSet.of(Week.MONDAY,Week.FRIDAY));            //[MONDAY, FRIDAY]
8.1.7 noneOf

傳入一個枚舉的類對象去創建一個空Set集合

1EnumSet<Week> noneOf = EnumSet.noneOf(Week.class);
2System.out.println(noneOf);                     //[]

8.2 EnumMap集合

8.2.1 EnumMap集合的方法列表

關於Map集合,我們知道它是由鍵和值組成。EnumMap集合與HashMap集合的效率比較來說,EnumMap的效率高些。

關於EnumMap集合的使用與HashMap是一致的,沒有什麼特殊的。至於EnumMap集合的方法,我這裏列舉一下。

返回值 方法 描述
void clear() 移除所有映射關係。
EnumMap clone() 返回EnumMap集合。
boolean containsKey(Object key) 包含此鍵,則返回true
boolean containsValue(Object value) 包含一個或多個鍵映射到的該指定值,則返回true
Set > entrySet() 返回映射鍵值關係的Set集合
boolean equals(Object o) 比較對象與映射的相等關係
V get(Object key) 獲取指定鍵映射的值,如果沒有,返回null
Set<K> keySet() 返回所有鍵的Set集合
V put(K key, V value) 將指定鍵值存儲在EnumMap集合中
void putAll(Map m) 將所有鍵值對存儲在集合中
V remove(Object key) 如果存在映射關係,則移除該映射關係
int size() 返回存在映射關係的數量
Collection<V> values() 返回此映射中所包含值的 Collection集合
8.2.2 EnumMap集合的基本使用

由於EnumMap集合與HashMap集合基本相似,這裏我就演示一下基本使用與HashMap不同的地方。

EnumMap集合是我們new出來的對象,創建出來的對象需要傳入一個枚舉的類對象,才返回一個Map集合。Map集合是鍵值對形式存儲,所以我們在寫EnumMap集合的泛型時,根據需求來寫,如果需要鍵是某枚舉類型,我們泛型就寫它。如果有枚舉類是值的要求,那就泛型中的值寫枚舉類。鍵值對都要求是枚舉那也是OK的,我們寫泛型時都寫需求的枚舉類即可。除了創建對象和存儲對象需要指定枚舉類外,其他的與HashMap基本相同。

如下,我在創建EnumMap集合時執行的Week枚舉類的類對象,泛型的鍵寫的是Week枚舉類,值寫的Integer,這就意味着我們在put(存儲鍵值對)的時候,鍵需要存儲Week枚舉類中的枚舉成員,值需要存儲Integer數值。

1EnumMap<Week, Integer> map = new EnumMap<>(Week.class);
2map.put(Week.MONDAY, 1);
3map.put(Week.THURSDAY, 4);
4System.out.println(map);            //{MONDAY=1, THURSDAY=4}

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

【其他文章推薦】

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

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

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

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

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

※回頭車貨運收費標準

台中搬家公司費用怎麼算?

致G20領袖 全球醫療工作者呼籲採納綠色振興

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

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

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

印尼展開人工造雨 避免往年森林大火重演

摘錄自2020年5月28日中央社報導

印尼年年發生嚴重森林大火,產生的有毒霧霾瀰漫東南亞,迫使學校關閉及引發擔憂。印尼政府今年已展開人造雨作業,試圖避免往年的災難重演。

法新社報導,印尼去年因為乾旱天候遭逢2015年以來的最嚴重森林大火,約160萬公頃土地遭失控野火焚毀,大多發生在蘇門答臘島(Sumatra)及婆羅洲島(Borneo)。印尼森林大火往往是人為引發,以清理土地供農業使用,包括生產棕櫚油及紙漿的種植園。

過去兩週以來,印尼開始在蘇門答臘島火災熱點廖內省(Riau Province)進行人工造雨,且計畫在蘇門答臘島及婆羅洲島其他地區展開人造雨計畫。印尼乾燥季節預計9月左右結束,人造雨作業將在整個乾季持續進行。

生態保育
生物多樣性
國際新聞
印尼
人工增雨
森林大火
森林

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

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

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

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