國外實測新款 Surface Pro X 的 SQ2 處理器效能幾乎跟 SQ1 一樣

微軟在去年十月時正式更新旗下 Surface Pro X 平版電腦,處理器升級至 SQ2,一樣是高通 ARM 架構,不過當時微軟並沒有特別說明這顆處理器的效能表現,而最近終於有外媒實測到實機,但讓人意外的是,跑分結果竟然跟 SQ1 差不多,也就是新款 Surface Pro X 效能幾乎沒有什麼提升。

新款 Surface Pro X 的 SQ2 處理器效能幾乎跟 SQ1 一樣

稍早一間德國媒體網站 Dr. Windows 分享 Surface Pro X 2020 年版的實測報告,共使用三款跑分軟體進行測試,並與舊款 Surface Pro X 比較,結果如下。

首先是 Jetstream 2 瀏覽器測試工具:

  • 搭載 SQ1 處理器的 Surface Pro X 2019 獲得 89,614
  • 搭載 SQ2 處理器的 Surface Pro X 2020 獲得 90,664

安兔兔測試工具:

  • 搭載 SQ1 處理器的 Surface Pro X 2019 獲得 286,740
  • 搭載 SQ2 處理器的 Surface Pro X 2020 獲得 286,271

Geekbench 4 測試工具:

  • 搭載 SQ1 處理器的 Surface Pro X 2019 單核心獲得 3,530、多核心獲得 11,927
  • 搭載 SQ2 處理器的 Surface Pro X 2020 單核心獲得 3,627、多核心獲得 12,042

三個測試結果中,Jetstream 2 與 Geekbench 4 雖然新款確實比較高,但也只有多一點點,安兔兔反而變低。整體來看,幾乎可以說搭載 SQ2 處理器的 Surface Pro X 2020 效能並沒有提升。

另外最近微軟官方的韌體更新中,也都合併支援 SQ1 與 SQ2 處理器:

不確定為何這兩顆效能會這麼接近,但也顯示著,對於注重效能的朋友來說,舊款 Surface Pro X 或許是 CP 值更高的選擇。

當然,除了效能,Surface Pro X 2020 還是有地方提升,像續航力官方就承諾可達到 15 小時,比舊款多 2 小時。

無論如何,有在關注新款 Surface Pro X 2020 的人,建議等更多實測報告現身再做決定。

資料來源:Dr. Windows

微軟最新的廣告,直白地跟你說 Surface Pro 7 就是比 MacBook Pro 好

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

※回頭車貨運收費標準

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

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

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

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

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

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

這幾款10萬級車型投訴率最低,最可靠!竟然沒有日系

吉利遠景指導價:5。39-6。79萬推薦車型:2017款1。5L自動幸福版10月份銷量:15721輛10月份投訴量:4單吉利汽車無論是轎車還是SUV,都對汽車銷量市場貢獻了不少的成績,SUV有博越,遠景SUV和帝豪GS,轎車則有博瑞,帝豪GL和今天介紹的這款遠景,在上個月賣出了1。

根據中國汽車工業協會提供的數據表示,在10月份全國轎車的銷售量達到了117.05萬輛,車主朋友們對轎車的有效投訴達到2538宗,在投訴案例中,大多數朋友投訴的內容為發動機功率不足,車內異味以及變速箱異響的問題。而今天想為大家介紹的這幾款,確實是經過了後市場的考驗,投訴量非常少的質量可靠性轎車,不妨參考參考。

雪佛蘭科沃茲

指導價:7.99-10.99萬

推薦車型:2016款 1.5L自動欣悅版

10月份銷量:13836輛

10月份投訴量:0

在科沃茲還沒正式亮相之前,就有不少的粉絲在後台問這款車如何,而該車推出至今已經有幾個月,論質量論實力,在10萬元代步家轎中,一直保持着不錯的成績,這次通過數據來看,在9月份10月份後市場依然還沒收到科沃茲車主的投訴,這其實就是一個很好的證明了。

科沃茲外觀採用雪佛蘭家族式的雙層前格柵,燈組內部有點綴鍍鉻飾條和全新的雙L形LED光帶式日間行車燈,全系標配,內飾採用傳統飛翼式座艙設計,同時座椅提供更加出色的側向支撐和包裹性。動力上全系採用1.5L自然吸氣發動機,搭配5速手動和6速手自一體變速器,推薦的這個自動欣悅版,標配胎壓監測,ESp車身穩定,上坡輔助,天窗以及發動機啟停裝置,應該有的配置沒有差。

全力加速時轉速爬升得比較慢,大約在2500轉左右,爆發力只能說一般般,不過在加速的時候降擋相當果斷,方向盤力度沉穩,平時在道路上做併線動作也蠻幹脆利索,但是整體的駕駛感受上還是比本田鋒范差點,百公里平均油耗7L。

東風悅達起亞K2

指導價: 7.29-10.39萬

推薦車型: 2017款 三廂 1.4L AT GLS

10月份銷量:16882

10月份投訴量:3

在網絡上曾傳聞着這樣一篇文章“十大最不安全車輛”,在這個由美國評定出來的車型當中,就屬起亞K2首當其沖,雖然在這個等級上舊款起亞K2由於自身問題慘入榜單,但是相信最近剛上市的全新2017款K2能夠帶給大家不一樣的感覺。

全新的起亞K2前臉變化很大,整個車頭設計都讓人煥然一新,虎嘯式格柵面積更小,側面也比舊款顯得更加修長,沒有那麼笨重,車尾則加入了貫穿式尾燈,識別程度非常之高。內飾風格也是全新設計,中控彩色屏幕支持蘋果Carplay和百度Carlife功能,還有倒車雷達和影像,這可能是最實用的功能了,其他像ESp車身穩定和胎壓監測上坡輔助等,都變成後期加裝了,選起亞K2,確實會面臨這個尷尬的問題。

動力上提供1.4L和1.6L兩種排量的自然吸氣發動機,傳統的4AT變速器遭捨棄,升級為6速手自一體和6速手動擋變速器,試駕感受上油門踏板初段有些遲緩,需要捨得給油地深踩才會感覺到動力的提升,懸架偏向於舒適,但過減速帶感覺還是比較明顯。

吉利遠景

指導價:5.39-6.79萬

推薦車型:2017款1.5L自動幸福版

10月份銷量:15721輛

10月份投訴量:4單

吉利汽車無論是轎車還是SUV,都對汽車銷量市場貢獻了不少的成績,SUV有博越,遠景SUV和帝豪GS,轎車則有博瑞,帝豪GL和今天介紹的這款遠景,在上個月賣出了1.5多萬輛的成績,只接到4單車主用戶的投訴,這個成績還是蠻可以的。

在外觀上,2017款對前臉進氣格柵造型做了小調整,統一了家族式風格的“回”字形造型,更加時尚,內飾設計變化不大,這次該款主要是配置升級了一點,配置雖然沒有ESp車身穩定(本田飛度大部分車型也沒有),但其他配置如后倒車雷達及影像,前後攝像頭,定速巡航等功能,在這款6.69萬的1.5L自動擋版本上看到,就已經非常奇迹了,感覺賺到了。

該車不僅配備1.5L自然吸氣,如果對動力需求更大可以入手1.3T渦輪增壓版本,不過該版本沒有匹配自動變速器,比較可惜,整體車內儲物空間還是蠻多的,這輛車,價格便宜是最大的賣點,且賣出去后,用戶投訴量也非常少,可以考慮。

大眾捷達

指導價:7.99-12.08萬

推薦車型:2015款 質惠版1.6L自動舒適型

10月份銷量:29637輛

10月份投訴量:6單

捷達車型相信大家都相當熟悉了,它在我國存在的年份已經相當之久,雖然很少在網上看到它的宣傳廣告,但它一直在我國的銷量都非常好,10月份總共賣出的車輛就直逼3萬大軍,收到的投訴量也只有6單,如果真要將這4台車型來列個投訴銷量比的話,無疑大眾捷達得到的分數是最高的。

2015款的外觀還是比較圓潤中庸,內飾風格還是那麼簡樸老成,大眾標準內飾設計風格,沒有多大可以描述的地方,針對捷達來說,有着多種發動機排量可以選擇,有着1.4L和1.6L的自然吸氣版本,同時也有1.4T渦輪增壓的大動力版本,根據不同排量的發動機,匹配5速手動,6速自動和7速雙離合變速器,配置上基本不標配ESp,大部分為選裝情況。

可能大家買捷達,就是衝著德系技術,且捷達車耐用好開的特性吧,配置其實並不是絕大多數人考慮的地方,品牌效應早已超過了其他,在試駕過程中深加速時發動機噪音大,底盤比較穩,質感不錯,如今2017款新捷達已經亮相,期待上市后的市場反應。

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

【其他文章推薦】

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

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

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

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

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

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

博越最高性價比車型來了 不是渦輪還值得買嗎

博越這款2。0L自然吸氣發動機,峰值扭矩為178牛米,最大功率141千瓦,傳動系統,配備將會是6速手動變速箱。在動力輸出變現,也是足夠滿足日常使用。現在市面上,暫時還沒有2。0L手動智聯型,只有1。8T和2。0L低配車型,那我們應該等不等這款2。

面對吉利博越賣得水深火熱時,吉利又將放出大招!

我們得到消息,吉利汽車將針對吉利博越新增一款2.0L 手動智聯型,這款車型定位於2.0L手動車型頂配,售價為11.28萬元,其餘,消費者購買這款車型的同時,將會享受到吉利官方4000元的補貼,即是108800元就能購買到這款車型。

那麼,新增這款手動智聯型,都有什麼亮點,值不值得購買呢?

至於外觀上我相信大家已經非常了解,我在這裏就不闡述了。2.0L手動智聯型配置上增加了G-Netlink吉利智能車載系統和7英寸全液晶儀錶盤,包括“你好,博越”智能語音交互系統、GpS導航、多媒體影音、3G+車載Wi-Fi、車載藍牙、iOS+Android手機智能互聯等功能。

值得一提的是,這款2.0L手動智聯型配備了真皮多功能方面盤、上坡輔助、倒車影像、胎壓監測、ESp車身穩定系統、自動駐車、無鑰匙進入/啟動、陡坡緩降、皮質座椅、全景天窗等。可以看出,博越新增2.0L手動智聯型在性價比上說是超高的,其實一點也不過分,畢竟價格只是10萬出頭,還擁有這麼多配置,真是能甩同級別車型幾條街。

雖然很多消費者購買1.8T車型,但是2.0L手動車型也是有不少消費者青睞。博越這款2.0L自然吸氣發動機,峰值扭矩為178牛米,最大功率141千瓦,傳動系統,配備將會是6速手動變速箱。在動力輸出變現,也是足夠滿足日常使用。

現在市面上,暫時還沒有2.0L手動智聯型,只有1.8T和2.0L低配車型,那我們應該等不等這款2.0L手動智聯型進入市場呢,編者認為是值得,因為這款車型性價比非常高,配置多到10個手指數不完,而且售價也很親民。相對來說這款車型還是非常值得購買!小編都覺得要動心了!

競品對手:

上汽集團-榮威RX5

指導價:9.98-18.68萬

榮威RX5可以說是博越最直接競爭對手之一,真是冤家路窄啊!不可否認,榮威底盤功底非常深厚,紮實,行駛質感也很高級,但唯一不足就是榮威RX5入門車型,配置非常低,連最基本的ESp車身穩定系統也沒配備到,這可能只是為了拉低售價。但博越新增這款2.0L手動智聯型正好瞄準這一點上,在配置上、售價上給榮威RX5,狠狠開了一槍!這樣直接的競爭,真的好嗎?本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

※回頭車貨運收費標準

它或許是2017最值得期待的自主品牌SUV

0T的柴油和汽油兩種動力模塊,但是具體的賬面參數和傳動系統的搭載暫且沒有消息。並且為了適應不同的城市訴求,上汽大通D90還將會推出純電動、插電式魂動、燃料電池助力等等多種的驅動方式。編者有話說:上汽大通D90的概念車外觀確實很漂亮,但是小編並不希望實車下地的時候上汽大通會重蹈長安CS95的覆轍。

2016年年底中國自主品牌汽車市場最為驚艷的車型,給廣汽傳祺GS8投上一票相信沒有什麼人會反對,漂亮霸氣的外觀造型,再配合上正式上市后相對親民的售價,讓很大一部分還在躊躇着合資緊湊型SUV的消費者轉向了國產的陣營。然而在剛剛結束的廣州車展上,還有一台SUV車型引起了小編的注意,如無意外,它或許是廣汽傳祺GS8的直接競爭對手——上汽大通D90。

同樣的驚艷,不同的性格

上汽大通D90現在僅僅是處於概念車階段,曝光於今年早些時候的北京車展,整體風格使用了大量的凌厲線條勾勒出運動感,與廣汽傳祺GS8的穩重中略帶粗獷的性格不同,上汽大通D90顯得更加的精緻而且富有活力。

內飾層面的設計顯得十分后現代,畢竟是一款概念車,所以參考價值在小編個人看來並不算太大,但是根據官方透露的消息,該車的配置會十分豐富,6安全氣囊,主動安全系統,自適應巡航等等配置一應俱全。

動力層面據悉會採用2.0T的柴油和汽油兩種動力模塊,但是具體的賬面參數和傳動系統的搭載暫且沒有消息。並且為了適應不同的城市訴求,上汽大通D90還將會推出純電動、插電式魂動、燃料電池助力等等多種的驅動方式。

編者有話說:

上汽大通D90的概念車外觀確實很漂亮,但是小編並不希望實車下地的時候上汽大通會重蹈長安CS95的覆轍。CS95的概念車發布的時候可謂是震驚中外,但是量產版本公布之時卻顯得有些平庸。

作為已經上市的車型,廣汽傳祺GS8可謂是樹立自主品牌中大型SUV全新的標杆,而上汽大通D90的售價預計也是在20萬元左右,同樣為多座位的布局方式,所以可以很自然的聯想到上汽大通D90與廣汽傳祺GS8形成的直接競爭關係,據悉上汽大通D90的量產車型在2017年9月就將正式上市,希望屆時所出現的車型是一款與概念車相比出入不那麼大的高顏值SUV。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

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

※回頭車貨運收費標準

CHR還沒出生,繽智又多一個對手?全新小型SUV專利圖曝光

88-18。98萬本田XR-V指導價格:12。78-16。28萬豐田C-HR指導價格:暫

SUV產品的豐富並不是僅僅存在自主品牌當中,自本田推出繽智和XRV以來,豐田CHR的曝光量越來越大,而作為日系品牌在中國銷量也非常出色的日產,進來曝光了一款國內的小型SUV專利圖,這或許意味着,日產品牌旗下的小型SUV誕生之日就在不久的將來。

日產先期在2014年巴西聖保羅車展已經曝光一款小型SUV概念車——Nissan-Kicks,據悉在今年年底就會量產在拉丁美洲銷售,根據日產的計劃,這款小型SUV有投放全球計劃的構想,從曝光的專利圖上看,未來國產的日產品牌小型SUV非常有可能與Kicks衍生於同一平台。

Nissan—Kicks的外觀造型顯得非常精緻,四個輪拱處都使用了肌肉紋理,彰顯出了一定的力量感,而整車包圍裙邊顯得運動風格十足,色差較大的車身與車頂的差異讓kicks看上去更顯得年輕動感。

我們不難從專利圖上看出這款全新的日產小型SUV與kicks的相似程度極高,前臉造型較為犀利,整車線條同樣是以緊湊動感為主要設計語言,關於該車的動力、配置、等等消息暫時還沒有獲得較為確切的證據,但從定位上我們或許可以猜測其將會搭載與逍客相同的1.2T發動機。

競品車型:

本田繽智

指導價格:12.88-18.98萬

本田XR-V

指導價格:12.78-16.28萬

豐田C-HR

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

【其他文章推薦】

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

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

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

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

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

※回頭車貨運收費標準

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

都是13萬能買到 這兩款大眾轎車誰更好

乘坐感受速騰雖然用上了后獨立懸架,但是舒適性方面還是有所欠缺,關鍵是當顛簸比較多的時候,會發現懸架有點處理不過來,餘震較多。但是有一點比較好,速騰的底盤滲進車廂內的細碎震動不多。另外有一點值得表揚的就是,後排空間較大且座墊的長度足夠。

前言速騰和朗逸可以說是大眾家族中的兩張銷量王牌,那麼在14萬左右想買這兩者中的一輛,該如何來選呢,下面編者我就來對比一下。

一汽-大眾-速騰

2017款 230TSI 自動舒適型

指導價:16.08萬

優惠幅度:3萬元左右(僅供參考)

上汽大眾-朗逸

2015款 230TSI DSG舒適版

指導價:14.69萬

優惠幅度:1.3萬左右(僅供參考)

駕駛感受

兩輛車的動力總成都一樣,均為1.4T發動機+7速DSG變速箱,最大輸出131馬力和225牛米。略有區別的是最大扭矩轉速,朗逸在1400轉便爆發,而速騰要到1500轉,不過兩者都在3500轉以後開始衰減。

懸架的選用上兩者也有很大差別,速騰后懸架採用的是多連桿獨立懸架,而朗逸用的是扭力梁式非獨立懸架。儘管如此,一切還是要從實際表現出發。

速騰的發動機艙

速騰的動力還是很充足的,無論平時超車,還是高速上開,都不會覺得車子有乏力的情況。變速箱的換擋動作也很平順,不會給駕駛員帶來什麼滋擾。然而,懸架總體來說還是偏軟,高速過彎時的側傾會比較大。轉向則略微有點重,特別是在不停挪車時,轉向的力度有點讓人受不了。

朗逸的發動機艙

朗逸的駕駛感受其實也和速騰大同小異,1.4T的動力隨傳隨到,不拖泥帶水。懸架抑制側傾的能力依舊有限,不算很出色。轉向上會比速騰來得略輕一點點。

乘坐感受

速騰雖然用上了后獨立懸架,但是舒適性方面還是有所欠缺,關鍵是當顛簸比較多的時候,會發現懸架有點處理不過來,餘震較多。但是有一點比較好,速騰的底盤滲進車廂內的細碎震動不多。另外有一點值得表揚的就是,後排空間較大且座墊的長度足夠。

朗逸的表現就沒有速騰那麼好了,首先座墊偏短,乘客的大腿大部分位置得不到有效承托。不過,座椅靠背角度較斜,坐進去感覺還是挺放鬆。其次就是,從底盤傳入車廂內的細碎震動較多,過大坑時會顯得較為單薄,幸好在過小顛簸時懂得如何去以柔克剛。

配置對比

這個環節可以說是兩者差別最大的,因為速騰的優惠幅度更高,所以配置會更為豐富。不僅多了無鑰匙啟動與進入,還多了不少配置,詳情可看下錶。

油耗對比

由於動力總成完全一致,且車身尺寸差距不大,所以兩者的油耗很相近。速騰的百公里綜合油耗為7.3L,而朗逸則為7.2L。

編者總結:

其實,速騰與朗逸這兩台車的調性是很相似的,都是偏居家的風格,而速騰在舒適性與配置上會優於朗逸。但是,每次看到速騰,我腦海中都閃現出一句歌詞“有多少愛可以重來”,曾經的速騰斷軸門讓大家都傷透了心。儘管現在速騰已更換了后獨立懸架,但是心中的芥蒂是否就此放下呢?如果已經放下了,那速騰確是一款適合家用的車。如若依舊介懷,就不必買輛車讓自己提心吊膽。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

※回頭車貨運收費標準

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

除了FastJson,你也應該了解一下Jackson(一)

在上月末的時候收到一條關於fastjson安全漏洞的消息,突然想到先前好像已經有好多次這樣的事件了(在fastjson上面)。關於安全方面,雖然中槍的機率微小,但是在這個信息越來越複雜的時代,安全性也變得越來越重要,就像DevSecOps的誕生,在軟件交付的整個價值流中我們也需要注重安全這方面。當然我們現在不談關於FastJson的優劣,因為我們本文的目標是讓大家了解和掌握Jackson。

概覽

Jackson是一個非常流行和高效的基於Java的庫,它可以序列化java對象或將java對象映射到JSON,反之亦然。當然除了Jackson,在Java中同類型的優秀的庫也有很多,比如:

  • Gson
  • json-io
  • Genson

關於哪一個最好或者哪一個最流行,沒有明確的答案。技術的種類繁多,每個人對與不同技術的態度也不一樣。言歸正傳,文章主要還是討論Jackson的。本文主要講解我們處理Json中最常見的兩個操作:

  • 將Java對象序列化為JSON
  • JSON字符串反序列化為Java對象

引入依賴

由於在Spring/SpringBoot中很多組件已經自帶了Jackson庫,所以很多情況下不需要手動引入Jackson的依賴。

手動引入依賴:

<dependency> 
  <groupId>com.fasterxml.jackson.core</groupId> 
  <artifactId>jackson-databind</artifactId> 
  <version>2.9.8</version>
</dependency>

這個依賴關係還將傳遞地向類路徑添加以下庫:

  1. jackson-annotations-2.9.8.jar
  2. jackson-core-2.9.8.jar
  3. jackson-databind-2.9.8.jar

JavaObject to Json

ObjectMapper

ObjectMapper是一個映射器(或數據綁定器或編解碼器),提供了在Java對象(bean的實例)和JSON之間進行轉換的功能。

首先定義一個簡單的Java類

public class Car {
    private String color;
    private String type;
    // standard getters setters
}

將Java對象轉換成Json

我們使用ObjectMapper的writeValue相關Api來對Java對象進行序列化操作

ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car("blue","c1");
System.out.println(objectMapper.writeValueAsString(car));

此時輸出

{"color":"blue","type":"c1"}

更多

ObjectMapper的writeValue相關Api還提供了很多便利的Json序列化操作方法,比如:將對象序列化成Json字節數組的writeValueAsBytes()方法、自定義輸出源的writeValue()方法…

ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car("blue","c1");
objectMapper.writeValue(new File("./xxx.txt"),car);

運行上述代碼,Java對象的序列化Json將被輸出到xxx.txt文件。

Json to JavaObject

將Json String轉換成Java Object

ObjectMapper objectMapper = new ObjectMapper();
String json = "{\"color\":\"blue\",\"type\":\"c1\"}";
Car car = objectMapper.readValue(json, Car.class);

readValue()方法也接受其他形式的輸入,比如包含JSON字符串的文件:

ObjectMapper objectMapper = new ObjectMapper();
Car car = objectMapper.readValue(new File("./xxx.txt"), Car.class);
System.out.println(car);

JSON to Jackson JsonNode

JsonNode

一個JSON可以被解析成一個JsonNode對象,用來從一個特定的節點檢索數據.

使用readTree()方法,我們可以將Json字符串轉換成JsonNode

ObjectMapper objectMapper = new ObjectMapper();
String json = "{ \"color\" : \"Black\", \"type\" : \"FIAT\" }";
JsonNode jsonNode = objectMapper.readTree(json);
System.out.println(jsonNode.findValue("type").asText());
// 打印出“FAIT”

JSONArrayString to JavaList

ObjectMapper objectMapper = new ObjectMapper();
String jsonCarArray =
    "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : 3. \"Red\", \"type\" : \"FIAT\" }]";
List<Car> listCar = objectMapper.readValue(jsonCarArray, new TypeReference<List<Car>>() {});

JSONString to JavaMap

ObjectMapper objectMapper = new ObjectMapper();
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
Map<String, Object> map = objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {
});

Jackson庫最大的優點之一是高度可定製的序列化和反序列化過程。接下來將介紹一些高級特性,其中輸入或輸出JSON響應可以與生成或使用響應的對象不同。

配置序列化和反序列化特性

String jsonString = "{ \"color\" : \"Black\", \"type\" : \"Fiat\", \"year\" :\"1970\" }";

假設使用如上json字符串來反序列化成Java對象,按照默認解析過程將導致UnrecognizedPropertyException異常,因為其中存在Car類中未包含的新字段year。

通過配置序列化和反序列化特性來解決此問題:

ObjectMapper objectMapper = new ObjectMapper();
String jsonString = "{ \"color\" : \"Black\", \"type\" : \"Fiat\", \"year\" :\"1970\" }";
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Car car = objectMapper.readValue(jsonString, Car.class);

如上,我們在ObjectMapper中配置了DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES=false,從而實現忽略新的字段。

類似:另一個選項FAIL_ON_NULL_FOR_PRIMITIVES,它定義了是否允許原始值的空值;FAIL_ON_NUMBERS_FOR_ENUM控制是否允許enum值被序列化/反序列化為数字……

自定義序列化器或反序列化器

自定義序列化器

public static class CustomCarSerializer extends StdSerializer<Car> {
    public CustomCarSerializer() {
        this(null);
    }

    public CustomCarSerializer(Class<Car> t) {
        super(t);
    }

    @Override
    public void serialize(Car car, JsonGenerator jsonGenerator, SerializerProvider serializer) throws IOException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("car_brand", car.getType());
        jsonGenerator.writeEndObject();
    }
}

如上,通過繼承StdSerializer類,我們實現了一個自定義的序列化器。

使用自定義的序列化器:

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("CustomCarSerializer", new Version(1, 0, 0, null, null, null));
module.addSerializer(Car.class, new CustomCarSerializer());
mapper.registerModule(module);
Car car = new Car("yellow", "enault");
System.out.println(mapper.writeValueAsString(car));
//輸出{"car_brand":"enault"}

自定義反序列化器

public static class CustomCarDeserializer extends StdDeserializer<Car> {

        public CustomCarDeserializer() {
            this(null);
        }

        protected CustomCarDeserializer(Class<?> vc) {
            super(vc);
        }

        @Override
        public Car deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            Car car = new Car();
            ObjectCodec codec = p.getCodec();
            JsonNode node = codec.readTree(p);
            // try catch block
            JsonNode colorNode = node.get("color");
            String color = colorNode.asText();
            car.setColor(color);
            return car;
        }
    }

如上,通過繼承StdDeserializer類,我們實現了一個自定義的序列化器。

使用自定義的反序列化器:

String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\"}";
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("CustomCarDeserializer", new Version(1, 0, 0, null, null, null));
module.addDeserializer(Car.class, new CustomCarDeserializer());
mapper.registerModule(module);
Car car = mapper.readValue(json, Car.class);
//此時的car {color='Black', type='null'}

處理時間格式

️:此處僅展示對於Java8的LocalDate&LocalDateTime的處理

首先創建一個帶日期時間字段的Car類

public class Car {
    private String color;
    private String type;
  	@JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDateTime produceTime;
    // standard getters setters
}

自定義時間格式處理

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
Car car = new Car().setColor("blue").setType("c1").setProduceTime(LocalDateTime.now());
String carAsString = objectMapper.writeValueAsString(car);
System.out.println(carAsString);
//此時輸出:{"color":"blue","type":"c1","produceTime":"2020-06-06"}

處理集合

DeserializationFeature類提供的另一個小但有用的特性是能夠從JSON數組響應生成我們想要的集合類型。

String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\"}, { \"color\" : \"Red\", \"type\" : \"FIAT\"}]";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
Car[] cars = objectMapper.readValue(jsonCarArray, Car[].class);

如上,我們將一個JsonArray字符串轉換成了對象數組。

我們也可以將其轉換成集合:

String jsonCarArray = "[{ \"color\" : \"Black\", \"type\" : \"BMW\"}, { \"color\" : \"Red\", \"type\" : \"FIAT\"}]";
ObjectMapper objectMapper = new ObjectMapper();
List<Car> listCar = objectMapper.readValue(jsonCarArray, new TypeReference<List<Car>>(){});

總結

Jackson是一個可靠而成熟的用於Java的JSON序列化/反序列化庫。ObjectMapper API提供了一種簡單的方法來解析和生成JSON響應對象,具有很大的靈活性。

歡迎訪問筆者博客:blog.dongxishaonian.tech

關注筆者公眾號,推送各類原創/優質技術文章 ⬇️

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

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

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

機器學習——十大數據挖掘之一的決策樹CART算法

本文始發於個人公眾號:TechFlow,原創不易,求個關注

今天是機器學習專題的第23篇文章,我們今天分享的內容是十大數據挖掘算法之一的CART算法。

CART算法全稱是Classification and regression tree,也就是分類回歸樹的意思。和之前介紹的ID3和C4.5一樣,CART算法同樣是決策樹模型的一種經典的實現。決策樹這個模型一共有三種實現方式,前面我們已經介紹了ID3和C4.5兩種,今天剛好補齊這最後一種。

算法特點

CART稱為分類回歸樹,從名字上我們也看得出來,它既能支持分類又可以支持回歸。的確如此,決策樹的確支持回歸操作,但是我們一般不會用決策樹來進行回歸。這裏面的原因很多,除了樹模型擬合能力有限效果不一定好之外,還與特徵的模式有關係,樹回歸模型受到特徵的影響非常大。這個部分我們不做太多深入,之後會在回歸樹的文章當中詳細探討。

正因為回歸樹模型效果表現都不太理想,所以CART算法實現決策樹基本都是用來做分類問題。那麼在分類問題上,它與之前的ID3算法和C4.5算法又有什麼不同呢?

主要細究起來大約有兩點,第一點是CART算法使用Gini指數而不是信息增益來作為劃分子樹的依據,第二點是CART算法每次在劃分數據的時候,固定將整份數據拆分成兩個部分,而不是多個部分。由於CART每次將數據拆分成兩個部分,所以它對於拆分的次數沒有限制,而C4.5算法對特徵進行了限制,限制了每個特徵最多只能使用一次。因為這一點,同樣CART對於剪枝的要求更高,因為不剪枝的話很有可能導致樹過度膨脹,以至於過擬合。

Gini指數

在ID3和C4.5算法當中,在拆分數據的時候用的是信息增益和信息增益比,這兩者都是基於信息熵模型。信息熵模型本身並沒有問題,也是非常常用的模型。唯一的問題是,在計算熵的時候需要涉及到log運算,相比於四則運算來說,計算log要多耗時很多

Gini指數本質上也是基於信息熵模型,只是我們在計算的時候做了一些轉化,從而避免了使用log進行計算,加速了計算的過程。兩者的內在邏輯是一樣的。那怎麼實現的加速計算呢?這裏用到了高等數學當中的泰勒展開,我們將log運算通過泰勒公式展開,轉化成多項式的計算,從而加速信息熵的計算。

我們來做一個簡單的推導:

\[\begin{aligned} \ln(x) \approx \ln(x_0) + (x-x_0)\ln'(x_0) + o(x) \end{aligned} \]

我們把\(x_0 =1\)代入,可以得到:\(\ln(x)=x – 1 + o(x)\),其中o(x)是關於x的高階無窮小。我們把這個式子套入信息熵的公式當中:

\[\begin{aligned} H(x) &= -\sum_{i=1}^k p_i\ln p_i \\ &\approx \sum_{i=1}^k p_i(1-p_i) \end{aligned} \]

這個就是Gini指數的計算公式,這裏的pi表示類別i的概率,其實就是類別i的樣本佔全體樣本的比例。那麼上面的式子也可以看成是從數據集當中抽取兩條樣本,它們類別不一致的概率。

因此Gini指數越小,說明數據集越集中,也就是純度越高。它的概念等價於信息熵,熵越小說明信息越集中,兩者的概念是非常近似的。所以當我們使用Gini指數來作為劃分依據的時候,選擇的是切分之後Gini指數盡量小的切分方法,而不是盡量大的。

從上面的公式當中,我們可以發現相比於信息熵的log運算,Gini指數只需要簡單地計算比例和基礎運算就可以得到結果了,顯然運算速度要快得多。並且由於是通過泰勒展開逼近的,整體的性能也並不差,我們可以看下下面這張經典的圖感受一下:

從上圖當中可以看出來,Gini指數和信息熵的效果非常接近,一樣可以非常好地反應數據劃分的純度。

拆分與剪枝

剛才我們介紹CART算法特性的時候提到過,CART算法每次拆分數據都是二分的,這點和C4.5處理連續性特徵的邏輯很像。但有兩點不同,第一點是CART對於離散型和連續性特徵都如此操作,另外一點是,CART算法當中一個特徵可以重複使用。

舉個例子,在之前的算法當中,比如說西瓜的直徑是一個特徵。那麼當我們判斷過西瓜的直徑小於10cm之後,西瓜的直徑這個特徵就會從數據當中移除,之後再也不會用到。但是在CART算法當中不是如此,比如當我們先後根據西瓜的直徑以及西瓜是否有藤這兩個特徵對數據進行拆分之後,對於ID3和C4.5算法來說,西瓜的直徑這個特徵已經不可以再用來作為劃分的依據了,但是CART算法當中可以,我們仍然可以繼續使用之前已經用過的特徵。

我們用一張圖來展示,大概是下面這個樣子:

我們觀察一下最左側的子樹,直徑這個特徵出現了不止一次,這其實是很合理的。然而這也會有一個問題,就是由於沒有了特徵只能用一次這個限制,這樣會導致這棵樹無限膨脹,尤其是在連續性特徵很多的情況下,很容易陷入過擬合。為了放置過擬合,增加模型的泛化能力,我們需要對生成的這棵樹進行剪枝。

剪枝的方案主流的有兩種,一種是預剪枝,一種是后剪枝。所謂的預剪枝,即是在生成樹的時候就對樹的生長進行限制,防止過度擬合。而後剪枝則是在樹已經生成之後,對過擬合的部分進行修剪。其中預剪枝比較容易理解,比如我們可以限制決策樹在訓練的時候每個節點的數據只有在達到一定數量的情況下才會進行分裂,否則就成為恭弘=叶 恭弘子節點保留。或者我們可以限制數據的比例,當節點中某個類別的佔比超過閾值的時候,也可以停止生長。

后剪枝相對來說複雜一些,需要我們在生成樹之後通過一些機制尋找可以剪枝的部分,對整棵樹進行修剪。比如在CART算法當中常用的剪枝策略是CCP,它的英文全寫是Cost-Complexity Pruning,即代價複雜度剪枝。這個策略設計了一個指標來衡量一棵子樹的複雜度代價,我們可以對這個代價設置閾值來進行剪枝。

這個策略的精髓在於下面這個式子:

\[c = \frac{R(t) – R(T_t)}{|N_t| – 1} \]

這個式子當中的c就是指的剪枝帶來的代價,t代表剪枝之後的子樹,\(T_t\)表示剪枝之前的子樹。R(t)表示剪枝之後的誤差代價\(R(T_t)\)表示剪枝之前的誤差代價。其中誤差代價的定義是:\(R(t) = r(t) * p(t)\),r(t)是節點t的誤差率,p(t)是t上數據占所有數據的比例。

我們來看個例子:

假設我們知道所有數據一共有100條,那麼我們代入公式算一下,可以得到\(R(t) = r(t) * p(t) = \frac{11}{23} * \frac{23}{100} = \frac{11}{100}\)

子樹的誤差代價是:

\[R(T_t) = \sum R(i)=(\frac{2}{6}*\frac{6}{100})+ (\frac{0}{3}*\frac{3}{100}) + (\frac{2}{8}*\frac{8}{100})=\frac{4}{100} \]

所以可以得到\(c=\frac{11/100 – 4/100}{3 – 1}=\frac{7}{200}\)

c越大說明剪枝帶來的偏差越大,也就是說越不能剪,相反c很小說明偏差不大,可以減掉。我們只需要設置閾值,然後計算每一棵子樹的c來判斷是否能夠剪枝即可。

代碼實現

我們之前已經實現過了C4.5算法,再來實現CART可以說是非常簡單了,因為它相比於C4.5還少了離散類型這種情況,可以全部當做是連續型類型來處理。

我們只需要把之前的信息增益比改成Gini指數即可:

from collections import Counter

def gini_index(dataset):
    dataset = np.array(dataset)
    n = dataset.shape[0]
    if n == 0:
        return 0
    # sigma p(1-p) = 1 - sigma p^2
    counter = Counter(dataset[:, -1])
    ret = 1.0
    for k, v in counter.items():
        ret -= (v / n) ** 2
    return ret

def split_gini(dataset, idx, threshold):
    left, right = [], []
    n = dataset.shape[0]
    # 根據閾值拆分,拆分之後計算新的Gini指數
    for data in dataset:
        if data[idx] < threshold:
            left.append(data)
        else:
            right.append(data)
    left, right = np.array(left), np.array(right)
   	# 拆分成兩半之後,乘上所佔的比例
    return left.shape[0] / n * gini_index(left) + right.shape[0] / n * gini_index(right)

然後選擇拆分的函數稍微調整一下,因為Gini指數越小越好,之前的信息增益和信息增益比都是越大越好。代碼的框架基本上也沒有變動,只是做了一些微調:

def choose_feature_to_split(dataset):
    n = len(dataset[0])-1
    m = len(dataset)
    # 記錄最佳Gini,特徵和閾值
    bestGini = 1.0
    feature = -1
    thred = None
    for i in range(n):
        threds = get_thresholds(dataset, i)
        for t in threds:
            # 遍歷所有的閾值,計算每個閾值的信息增益比
            ratio = split_gini(dataset, i, t)
            if ratio < bestGini:
                bestGini, feature, thred = ratio, i, t
    return feature, thred

建樹和預測的部分都和之前C4.5算法基本一致,只需要去掉離散類型的判斷即可,大家可以參考一下之前文章當中的代碼。

總結

到這裏,我們關於決策樹模型的內容就算是結束了,我們從基本的決策樹原理,再到ID3、C4.5以及CART算法,都已經囊括了。這些知識儲備足以應對面試當中關於決策樹模型的問題了

雖然在實際的生產過程當中,我們已經用不到決策樹了,還不是基本用不到,幾乎是完全用不到。但是它的思想非常重要,是後續很多模型的基礎,比如隨機森林、GBDT等模型,都是在決策樹的基礎上建立起來的。所以我們深入理解決策樹的原理對於我們後續的進階學習非常重要。

最後, 我把完整的代碼發在了paste.ubuntu上,需要的同學可以在公眾號後台回復“決策樹”獲取。

如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

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

【其他文章推薦】

※超省錢租車方案

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

※回頭車貨運收費標準

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

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

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

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

從“中英文思維回譯法”看中英思維差異

1、中英文在表達意思的方式上的不同 

中英文表達的差異很重要一部分體現在思維上。例如中文說:

“不知道怎麼回事,我筆記本連不上網了”

地道的英文不是:

My laptop can’t connect to the internet for no reason.

而是:

I couldn’t access the internet from my laptop for some reason.

這個句子至少體現了以下幾處中英文思維的不同:

  • 中文用“我的電腦”做主語,英語用“我”做主語。
  • “上網”是 access the internet 而不是 connect to the internet.
  • 不知道怎麼回事是 for some reason 而不是 for no reason.
  • 中文先說“不知道怎麼回事”,英文後說 for some reason.

 

2、中英文思維的差異

作為跨文化交流的翻譯不僅只是兩種語言的簡單轉換,也是兩種思維方式之間的轉換。 拿英漢兩種語言來說.由於文化背景的差異等諸多因素,這兩種語言的思維方式也不盡相同,概括來說主要就是:

  • 句子重心的差異
  • 語態的差異
  • 否定視角的差異

0x1:句子重心的差異

句子重心包括兩個層面:

  • 一個是語義重心
  • 另一個是結構重心

英語和漢語的語義重點基本沒有大的區別,這兩種語言句子重心的差異主要體現在結構重心的差異上。

西方人說話喜歡直截了當、開門見山,他們在表達思想時喜歡先安排主要內容,然後再藉助各種連接手段補充其他信息,因而英語句子的結構重心通常在前,句子常常頭短尾長。

而中國人則喜歡水到渠成、漸入佳境。所以他們在表達思想時喜歡先對所述的事物進行鋪墊和渲染,然後再點出中心意思,因而漢語句子的結構重心往往靠後,句子頭長尾短。

我們來看下面的例句:

我們再來看一個例句:

這句話的結構重心為後半句“許多小船載着各種雜貨快速向客輪駛來”。通過上面的分析,這句話在翻譯時應首先將結構重心置前,先說主要內容:

0x2:語態的差異

雖然英漢兩種語言在句式上都有主動和被動之分,但表現形式和使用場合卻不盡相同。

  • 英語中被動語態的使用範圍非常廣,除了大量的及物動詞使用被動語態外,不少相當於及物動詞的短語也可以使用被動語態。
  • 相比之下,漢語中被動語態的使用就低得多了,大多數情況下漢語都使用主動形式來表達被動意義。

以“He is loved by all of us.”這句話為例,這句話在英語中司空見慣,而如果要譯成“他被我們大家喜歡着”就很彆扭,必須要使用主動語態,譯成“我們大家都喜歡他”才通順。

我們來看下面的例句:

 

被動語態能使句子的核心詞“an interesting discovery”更為突出。

我們再來看一個例句:

中文原句是主動語態,但由於“開挖隧道”和“派狗到雪地里”這兩個動作的發出者在中文里並沒有出現,所以在譯成英語時就只能使用被動語態了。

中文中有大量的無主句,在將這些句子譯成英文時都可以使用被動語態。

0x3:否定視角的差異

英語的否定方式非常豐富,大體可分為:

  • 顯形否定:通常是藉助否定詞或,含否定意義的句型來表達(如never、unless、hardly、rather than、in the absence of、too …. to …)
  • 隱形否定:沒有明顯的否定詞,其形式上是否定的,它的否定含義要通過上下文或語境來推定

與英語比起來,漢語的否定結構就單一多了,一般是在相應的詞語前面加上“不”、“沒有”等進行“詞的否定”。

所以,在漢譯英的過程中,對於漢語中相對單一的否定結構,英文中可以有多種不同的表達方式。

我們來看下面的例句:

可以看到,譯文採用了隱形否定的形式來表達。

我們再來看一個例句:

 

中文原文是肯定句,而譯文中使用了否定結構“never fail to”不僅表達了肯定的意思,還顯得更為精彩,更有力量。

3、中英文回譯法

在翻譯練習中,有一種非常有效翻譯方法叫“回譯法”,也就是對譯文進行再次翻譯后與原文進行對照,在對比中找到差異所在。 概括來說就是:

  • 從地道英語直譯成中文
  • 由直譯的中文再譯成地道的中文
  • 仔細分析記錄地道英語和地道中文的區別
  • 由地道中文再回譯成地道英文

0x1:英譯中 – 從 A 到 B

我們先徹底理解英文原文的意思,然後嘗試着翻譯成中文。如果覺得直譯很彆扭的話,那就說明這裡有值得一學的英語思維! 在這一步,我們要做到讓中文譯文符合英文原文的表述,盡量直譯,基本通順即可。 舉個例子: 一位商務人士因為電腦有問題連不上網,沒能及時回復重要郵件,等修好了,他趕緊給別人發郵件解釋,郵件開頭是這麼說的:  

這句話直譯過來就是:

我很抱歉沒能更早回復你,出於某些原因,我用筆記本電腦無法連上網。

0x2:重建中文 – 從 B 到 C

這一步,我們要引入中文思維,展開想象:

在真實的中文語境中,相同的場景中,人們會怎麼說這句話?

這一步需要你完全拋棄英文原文的影響,去想中國人真正會使用出來的中文,不能有任何的翻譯痕迹。你可以假象在同樣的場景中,你自己會如何表達。例如:

不好意思啊,這麼晚回復你,我的本子一直連不上網,不知道怎麼回事。

到這一步我們發現,經過重建的中文,已經和之前的譯文在表達順序和結構上有明顯區別了。

0x3:連接通過 – 找到 A 和 C 之間的關聯

這一步也叫“埋鈎子”,就是說,埋下一些幫助記憶的“小鈎子”,把兩種思維掛上,讓自己以後能回想起來。

我們要觀察的內容主要是三方面:

  • 多了什麼?
  • 少了什麼?
  • 改了什麼?

比如:

  • 句子結構是怎麼變的?
  • 哪些詞被捨棄了?
  • 哪些表達式被徹底替換了?
  • 為什麼有些信息乾脆就不說了?
  • 有些信息反而要被加上去?

以這句話為例,我會注意到,

  • 我們中國人通常說的“這麼晚回復你”,英文的表達思路不一定用so late,還可以用not sooner,即否定句式。
  • 連上網,這句話里用的是access這個詞,而不是我們更熟悉的connect to。
  • 句子的主語也變了,一般中文會說“我的筆記本連不上網”,主語是筆記本,但是英文原文的說法是“i coudn’t access the internet”,主要是 I,也就是我。是我沒法通過本子來連網,而不是說本子連不上網。

0x4:思維逆轉 – 把 C 譯回 A

最後一步,我們要把整個過程逆推回去,在只看到 C 的情況下,回想起 A 是怎麼說的。如果你能做到準確完成回譯,說明你已經成功地建立了通道,從中文思維跳到了英文思維。

 

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

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

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

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

Spring 源碼學習 – 單例bean的實例化過程

本文作者:geek,一個聰明好學的同事

1. 簡介

開發中我們常用@Commpont,@Service,@Resource等註解或者配置xml去聲明一個類,使其成為spring容器中的bean,以下我將用從源碼角度看以AnnotationConfigApplicationContext為例看spring如何把帶有註解的類生成spring中bean。

2. 示例代碼

public class TestContext {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		SingleBean singleBean = context.getBean(SingleBean.class);
		System.out.println("<=====>"+singleBean.getTestStr());
	}
}
@ComponentScan("com.geek")
public class AppConfig {
}
@Component
public class SingleBean {
	private String testStr = "testStr";

	public String getTestStr() {
		return testStr;
	}
}

注意:以上代碼僅需要引入spring-context依賴即可。

3. 源碼分析

​ 上面的demo在調用AnnotationConfigApplicationContext構造函數的時候,AppConfig類會被註冊到AnnotatedBeanDefinitionReader,由這個reader把AppConfig解釋為beanDefination,從而被spring獲取到要實例化的類信息,以下為bean生產的源碼及其註釋。(源碼基於springFramework 5.1.X)

3.1 創建入口

​ 單例bean的創建的入口為DefaultListableBeanFactory.java#preInstantiateSingletons,下面源碼可見創建的bean條件為非抽象,非@LazyInit註解,Scope為singleTon(默認為singleTon)。

@Override
	public void preInstantiateSingletons() throws BeansException {
		if (logger.isTraceEnabled()) {
			logger.trace("Pre-instantiating singletons in " + this);
		}
		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
		//所有可能需要去實例化的class(lazy,scope)
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// Trigger initialization of all non-lazy singleton beans...
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			/**
			 * 非抽象,非懶初始化,單例bean
			 */
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						final FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
											((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
							getBean(beanName);
						}
					}
				}
				//非工廠bean實例化
				else {
					getBean(beanName);
				}
			}
		}
		/**
		 * 實例化完成后觸發實現了SmartInitializingSingleton方法的bean
		 * 的afterSingletonsInstantiated方法
 		 */
		// Trigger post-initialization callback for all applicable beans...
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}

3.2 創建前doGetBean代碼邏輯

getBean方法進來后便是直接調用doGetBean,doGetBean執行的源碼解釋如下:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
		/**
		 * 獲取beanName
		 * 1,去掉factortoryBean前綴&
		 * 2,帶有別名的bean轉換為原來名字
		 */
		final String beanName = transformedBeanName(name);
		Object bean;
		// Eagerly check singleton cache for manually registered singletons.
		/**
		 * 從DefaultSingletonBeanRegistry的singletonObjects
		 * (spring內部用來緩存單例bean的currentHashMap)檢查是否存在該bean,不存在則創建
		 * 涉及單例模式下的循環依賴解決
		 */
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isTraceEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			/**
			 *獲取給定bean實例的對象,如果是Factory Bean,
			 * 則可以是bean實例本身或其創建的對象。
			 */
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}
		else {
			// Fail if we're already creating this bean instance:
			// We're assembly within a circular reference.
			/**
			 * 原型模式下的bean存在循環依賴則會拋異常
			 */
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}
			// Check if bean definition exists in this factory.
			/**
			 * 找不到則從父容器中查找
			 */
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				String nameToLookup = originalBeanName(name);
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else if (requiredType != null) {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
				else {
					return (T) parentBeanFactory.getBean(nameToLookup);
				}
			}
			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}
			try {
				/**
				 * 父容器中也找不到該bean,則需要重新實例化
				 * 1,獲取要實例化bean的beanDefinition,
				 * 2,檢查bean的實例化需要依賴的其他bean
				 */
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						/**
						 * 若給定的依賴 bean 已經註冊為依賴給定的bean
						 */
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						registerDependentBean(dep, beanName);
						try {
							/**
							 * 遞歸調用getBean,優先創建依賴的bean
							 */
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}
				// Create bean instance.
				if (mbd.isSingleton()) {
					/**
					 * 通過調用DefaultSingletonBeanRegistry的getSingleton,從而調用核心方法createBean創建
					 */
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}
		/**
		 * 檢查需要的bean類型是否符合
		 */
		// Check if required type matches the type of the actual bean instance.
		if (requiredType != null && !requiredType.isInstance(bean)) {
			try {
				T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
				if (convertedBean == null) {
					throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
				}
				return convertedBean;
			}
			catch (TypeMismatchException ex) {
				if (logger.isTraceEnabled()) {
					logger.trace("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}

doGetBean的操作流程如下:

1,執行transformedBeanName方法轉換beanName

​ 傳遞的參數可能是bean的alias或者為FactoryBean,transformedBeanName執行的操作:(1)若傳進來的FactoryBean(FactoryBean以&作為前綴標記),去掉&修飾符。(2)經過(1)的處理后,有alias的bean則從aliasMap中獲取bean的原始beanName。

2,從容器的緩存中獲取bean

​ getSingleton先從spring三級緩存中的第一級singletonObjects(Map結構)中獲取,若不存在,則檢查該bean是否正在創建isSingletonCurrentlyInCreation(beanName)) ,正在創建的bean會從二級緩存earlySingletonObjects(Map結構)獲取。獲取到緩存的bean後會調用AbstractBeanFactory#getObjectForBeanInstance轉換bean的實例本身返回。因為從緩存中拿到的可能是factoryBean,所以getObjectForBeanInstance需要把是通過從緩存factoryBeanObjectCache獲取或通過factory.getObject()獲得相應的bean返回。

3,bean實例化前檢查

​ (1)先檢查是否原型模式下的bean是否存在循環依賴,是則會拋異常。

​ (2)檢查父類工廠(parentBeanFactory)是否存在,存在則從parentBeanFactory中遞歸調用doGetBean。

​ (3)獲取改bean的beanDefinition,檢查該bean實例化過程中是否涉及依賴了其他的bean,若是則遞歸調用getBean,優先創建依賴的bean。(涉及單例下的循環以來解決,下篇文章詳細介紹)。

​ (4)對創建bean代碼加synchronized和執行beforeSingletonCreation(beanName)前置處理。

3.3 創建前createBean邏輯

​ 經過前面的doGetBean的一輪檢查與準備后,便在AbstractAutowireCapableBeanFactory#createBean中開始bean的創建。

/**
	 * Central method of this class: creates a bean instance,
	 * populates the bean instance, applies post-processors, etc.
	 * 解析指定 BeanDefinition 的 class
	 * 處理 override 屬性
	 * 實例化的前置處理
	 * 創建 bean
	 * @see #doCreateBean
	 */
	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		if (logger.isTraceEnabled()) {
			logger.trace("Creating instance of bean '" + beanName + "'");
		}
		RootBeanDefinition mbdToUse = mbd;

		// Make sure bean class is actually resolved at this point, and
		// clone the bean definition in case of a dynamically resolved Class
		// which cannot be stored in the shared merged bean definition.
		/**
		 * 解釋bean的class,看beanDefinition是否有class,否則load class
		 */
		Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
		if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
			mbdToUse = new RootBeanDefinition(mbd);
			mbdToUse.setBeanClass(resolvedClass);
		}
		/**
		 * 對bean不存在lookup-method 和 replace-method
		 * 標記其方法的overloaded為false
		 */
		// Prepare method overrides.
		try {
			mbdToUse.prepareMethodOverrides();
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
					beanName, "Validation of method overrides failed", ex);
		}

		try {
			/**
			 * 調用實現實現BeanPostProcessor的bean後置處理生成代理對象,
			 * 有代理對象則直接返回代理對象
			 */
			// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
				return bean;
			}
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
					"BeanPostProcessor before instantiation of bean failed", ex);
		}

		try {
			/**
			 * 無需代理的bean實例化
			 */
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
		catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
			// A previously detected exception with proper bean creation context already,
			// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
		}
	}

createBean的操作流程如下:

1,resolveBeanClass

​ 解釋beanDefinition的class,並且保存在beanDefinition中。

2,prepareMethodOverrides

​ 處理bean中的lookup-method (在單例bean用 @Lookup註解標記的方法,註解的方法返回的對象是原型)和 replace-method( 標記的方法,標記bean中A的方法被實現被另外一個實現MethodReplacer接口的B方法替代)。

3,resolveBeforeInstantiation

​ 調用實現實現BeanPostProcessor的bean後置處理生成代理對象,有代理對象則直接返回代理對象。(spring AOP則是基於此處實現)

3.4 bean的真正實例化createBeanInstance

​ 在AbstractAutowireCapableBeanFactory#createBeanInstance中,真正創建bean,源碼及註釋如下:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
		Class<?> beanClass = resolveBeanClass(mbd, beanName);
		if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
		}
		/**
		 * 通過提供supplier回調方法創建
		 */
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}
		/**
		 * 通過工廠方法創建 bean 實例,可以是靜態工廠方法或者實例工廠
		 */
		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}
		// Shortcut when re-creating the same bean...
		boolean resolved = false;
		boolean autowireNecessary = false;
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				/**
				 * 查找已經bean已經緩存解析的構造函數或者工廠方法
				 */
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					resolved = true;
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		/**
		 * 已經有緩存的構造函數或者工廠方法,直接實例化
		 */
		if (resolved) {
			if (autowireNecessary) {
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				return instantiateBean(beanName, mbd);
			}
		}
		/**
		 * 通過實現BeanPostProcessor的代理類autowired的構造函數實例化
		 */
		// Candidate constructors for autowiring?
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			return autowireConstructor(beanName, mbd, ctors, args);
		}
		/**
		 * 通過本身帶有autowired的構造函數實例化,通過調用反射newInstance實現
		 */
		// Preferred constructors for default construction?
		ctors = mbd.getPreferredConstructors();
		if (ctors != null) {
			return autowireConstructor(beanName, mbd, ctors, null);
		}
		/**
		 * 無參構造函數實例化,通過調用反射newInstance實現
		 */
		// No special handling: simply use no-arg constructor.
		return instantiateBean(beanName, mbd);
	}

由上述源碼可以看出,實例化bean操作流程如下:

1,如果存在 Supplier 回調,則通過提供supplier回調方法創建,如以下方式定義的bean:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Spring5Application.class)
public class BeanRegistrationTest {
    @Autowired
    private GenericWebApplicationContext context;
    
    context.registerBean(A.class, () -> new A());
}

2,如果存在工廠方法,則通過工廠方法創建 bean 實例,可以是靜態工廠方法或者實例工廠,如以下方式定義的bean:

public class AFactory implements FactoryBean<A> {
	@Override
	public A getObject() throws Exception {
		return new A();
	}
	@Override
	public Class<?> getObjectType() {
		return A.class;
	}
}
//或:
@Configuration
public class BeanConfigration {
	@Bean
	public A a(){
		return new A();
	}

}

3,已經有緩存的構造函數或者工廠方法,直接實例化。

4,以上三點都不存在,則使用帶參構造函數與無參構造函數實例化。如以下方式定義的bean:

@Commponet
public class A{}

4.總結

​ spring單例bean的實例化流程大概就是這樣,很多細節地方,包括循環依賴處理,bean屬性填充等細節點下一章介紹。

參考

  • 《Spring 源碼深度解析》- 郝佳
  • 《死磕spring源碼》-chenssy

查看更多文章關注公眾號:好奇心森林

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

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