漢蘭達見到它都得認慫,這款大7座豪華SUV越看越霸氣

雖然兩車都採用了四驅驅動,但RX8使用的是全時四驅,搭載了ALL-DRIVE智能全領域駕駛系統,共有自動、運動、兩驅、雪地、越野和低速四驅6種模式選擇。此外,RX8還採用了非承載式的車身設計,應對爛路時的抗扭強度更高。雙叉臂式的前懸設計,規格更為高級。

在過去很長一段時間中,7座大SUV的頭把交椅都被漢蘭達佔據着。但隨着一些自主中型SUV的加入,這個市場變得不再平靜。尤其是最近RX8的上市,更是將這種競爭帶到白熱化狀態。

儘管漢蘭達與RX8的價錢重合度不高,但是計劃買7座大SUV的人難免心裏會想自己是否有必要加多小10萬來買漢蘭達,又或者在價格接近下,是否應該選配置更多的RX8。針對這些問題,還是給大家來對比一下兩款車。

這次拿指導價為22.88萬的榮威RX8 2018款 30T 智聯網四驅至尊版與25.88萬的漢蘭達 2018款 2.0T 四驅精英版 7座進行對比。

外形上,RX8擁有面積更大的鍍鉻裝飾中網,比漢蘭達看起來要霸氣不少。都說大燈是汽車的眼睛,LED大燈顯得炯炯有神,亮度更高;相比之下,漢蘭達的鹵素大燈就黯淡了不少。同時,RX8的霧燈也採用了LED光源,造型獨特,亮度更高,更顯力量感。

側面來看,RX8的線條平直,力量感較強,五輻式輪圈大氣耐看。相比之下,漢蘭達的側面設計就較為平庸,沒什麼線條感,就像一個油膩的中年大叔。

RX8的尾部相比漢蘭達也更加有設計感,凹凸有致,鍍鉻雙出排氣口帶出了不錯的高級感。

內飾設計上,RX8延續了榮威品牌家族化橫向舒展的設計主題,既豪華又精緻。座艙採用環繞式設計,整體設計富有層次感。相比之下,漢蘭達的內飾布局會更為複雜,充斥着各種物理按鍵,少了幾分精緻感。

通過上圖已經能清楚地看到,RX8與漢蘭達的配置差距極為誇張,可謂一個天一個地。無論是在舒適性配置,還是在安全性配置上,RX8都有壓倒性的優勢,這也是自主品牌面對合資品牌的最大優勢。此外,榮威RX8搭載的全新一代互聯網汽車智能系統- 基於AliOS的斑馬智行解決方案,是漢蘭達不具備的。

動力上,RX8無論是最大馬力還是最大扭矩,均要優於漢蘭達,且兩款車型都是採用愛信6AT變速箱,在可靠性上是沒有太大問題。RX8發動機和變速箱均採用縱置布局,優化前後軸配重,提升底盤極限下的平衡性縱置動力。漢蘭達的動力輸出平順,沒什麼太多可挑刺的,在動力總成上的對比,兩車可謂不相伯仲。

雖然兩車都採用了四驅驅動,但RX8使用的是全時四驅,搭載了ALL-DRIVE智能全領域駕駛系統,共有自動、運動、兩驅、雪地、越野和低速四驅6種模式選擇。此外,RX8還採用了非承載式的車身設計,應對爛路時的抗扭強度更高。雙叉臂式的前懸設計,規格更為高級。

相比之下,漢蘭達的四驅系統就黯淡不少,僅僅是採用了適時四驅系統,有運動、節能和雪地3種模式可選。承載式的車身設計,在應對一些稍微惡劣的環境時不如RX8從容,麥弗遜式的前懸結構還是稍顯簡單。

一直以來,大家都覺得SUV的第三排只能應急使用。即便是像漢蘭達這樣的體型,它的第三排也是表現一般。不過得益於更大的車身尺寸,RX8的第三排乘客可以坐得更為體面。從上圖也能看到,RX8的第三排座椅可調幅度更大。

RX8的第三排座墊長度比漢蘭達的多了50mm,對腿部的承托更為充分。結合上面的視頻,不難發現RX8的第三排在乘坐體驗上比漢蘭達要好。前兩排的表現兩車都差不多,座椅填充物料柔軟,對身體的包裹也較為充足。

RX8的一二排

漢蘭達的一二排

經過上面的一輪對比,能看到在相同價格下,榮威RX8擁有更高的價值體驗。那麼同為2.0T頂配,RX8與漢蘭達相比又是否還有優勢呢?

上圖能清晰看到,即便2.0T漢蘭達頂配比RX8頂配多出了將近8萬塊,但在配置上依舊占不了太大的便宜。更何況,RX8的第三排空間還更大呢。

總結

過往自主品牌給人一種廉價的感覺,但經過這些年的發展,自主品牌早非吳下阿蒙。上汽貴為中國第一車企,本身就擁有強大的產品開發能力。榮威作為旗下最重要的自主品牌,也是時候向更高端的市場發起衝擊。

從榮威RX8所展現出來的產品力來看,確實相當強大。比產品力更重要的是外形設計,畢竟長安CS95就是前車之鑒,所以RX8的整體造型偏重於磅礴大氣,這也是它要成功的基本條件。

就目前來看,或許RX8與漢蘭達最大的差距就在於口碑。漢蘭達畢竟在中國深耕多年,累積了不少良好口碑。RX8雖然是一款新車型,但上汽榮威作為一線自主品牌,還是值得我們投下信任票。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

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

這3台中型SUV一出來,什麼奔馳寶馬統統得靠邊

頂配的3。6T版本4。8 S的百公里加速性能更是令人折服。當然,300mm的涉水深度令人匪夷所思。而中控台上的空白按鍵時刻在提醒你:囊中羞澀以致某項配置未能選裝。此外,保時捷的售價就像你跟媽媽說晚上9點準時回家一樣不靠譜。

2018北京車展於4.25正式拉開帷幕,因工作關係得以參与這場人山人海的車界盛會。在車展上,除了各大傳統車企紛紛推出SUV新品外,諸多造車新勢力所亮相或展示的車型也是SUV為多,由此可見SUV的市場熱度一直未曾消減。

那些展出的SUV近乎都是:外觀愈發年輕、愈加運動“猙獰”,再難覓得一平庸之輩。若是將它們置於三五年前,必定都是明星車型。可在同質化異常嚴重的當下,已鮮有車型能在這股趨於一致的“年輕運動”的SUV洪流之中脫穎而出。

當然,也有例外。這幾款集情懷、血統、顏值、性能於一身的“尤物”備受青睞。

點評:一貫擅長打造英倫優雅紳士風的捷豹,強盛的藝術生命力在這款SUV上得以延續,俊朗精緻的外觀頗有幾分F-TYpE的神韻。年度風雲車、年度最佳設計等大獎便是對這走在時尚尖端寵兒的肯定。在老東家福特的EcoBoost 2.0T替換為自家的Ingenium 2.0T(2018沃德十佳發動機之一)后,性格變得愈加“狂豹”,低沉的聲浪時刻撩撥着駕駛者的慾望;早已滲透骨子里的獨特基因,令其在拐彎抹角的山間道路中犹如一隻飛馳的野豹。

點評:在卡宴身上嘗得SUV甜頭后,保時捷推出與老款Q5共享MLB平台(新款Q5L出自MLB EVO平台)的另一搖錢樹—Macan。萬年不變的青蛙臉令人百看不厭;跑車基因的加持,操控性能自然是無與倫比;頂配的3.6T版本4.8 S的百公里加速性能更是令人折服。當然,300mm的涉水深度令人匪夷所思;而中控台上的空白按鍵時刻在提醒你:囊中羞澀以致某項配置未能選裝。此外,保時捷的售價就像你跟媽媽說晚上9點準時回家一樣不靠譜。

點評:作為阿爾法羅密歐品牌百年歷史中第一款SUV產品,意大利人對藝術獨到的見解與對性能的痴迷在其身上得以極佳體現。精準的轉向、汽車愛好者極易上手的完美操控、恰到好處的零百以及超高的彎道極限令人愛不釋手。在北京車展上完成亞洲首秀的Stelvio QV版本更是紐北最快量產SUV圈速的記錄保持者。只是FCA的尿性,大家心照不宣,難怪有媒體朋友曾言:等我有錢了,就買它個兩台,一台修理一台開。

俗語言:逆水行舟,不進則退。在主流的市場里,品牌的差異性將變得越來越小,不論是國際品牌還是本土品牌。因此,“變通”是明智的生存之道。多數人認為保時捷、阿爾法羅密歐等豪華品牌推出SUV獲取更大市場份額意味着喪失了品牌的獨有特質。

但恰恰是這種“變通”,洞察出市場真正的需求點,才讓人看到更多好看的皮囊,感受到更多有趣的靈魂。畢竟只有存活下來,才能讓自身的基因得到延續。如果保時捷只生產跑車,捷豹只生產轎車,我們將錯過多少“尤物”呢?本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

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

威廉王子廣發英雄帖 「修復地球獎」鼓勵創新做環保

摘錄自2020年10月8日中央社報導

英國威廉王子(William)今天創設「修復地球獎」(Earthshot Prize),透過獎勵創新方法,來解決地球面臨的幾大問題。該獎項將在未來10年內每年獎勵五位得主,藉此「刺激改變、協助修復地球」。

威廉王子辦公室肯辛頓宮(Kensington Palace)形容,這是「史上最具聲望的全球性環境獎項」,靈感來自美國前總統甘迺迪1960年代的「射月」(Moonshot)計畫。

這項計畫聚焦在2030年前做到五個「簡單但具雄心」的目標,包括「保護並修復自然」、「清淨我們的空氣」、「恢復海洋生機」、「打造零浪費的世界」、「修復我們的氣候」。

獎項將由個人、機構以及商界組成的聯盟贊助,每年在全球不同城市舉辦頒獎典禮,2021年首屆典禮將在英國倫敦舉行。獎項可頒給個人或團隊不拘,科學家、運動人士、經濟學家、政府、商界領袖甚至國家都有資格獲獎,得主可獲獎金100萬英鎊(約新台幣3713萬元)。

海洋
氣候變遷
污染治理
國際新聞
英國
威廉王子
環保獎

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

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

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

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

這台5米多的SUV氣場無敵!10個男人9個都想開

放倒第三排座椅后領航員的後備箱空間十分寬敞,而且第三排座椅還是電動收放,用起來十分得心應手,即使是女性使用也是比較方便的,因此對於領航員的尾箱空間確實值得好評。領航員搭載了一台3。5T雙渦輪增壓發動機,最大功率能夠達到415馬力,最大扭矩更是高達670牛米,而且這台發動機還能燒92號汽油哦。

喜歡看美國大片的朋友一定不會對美系大型SUV感到陌生,特工們開着尺寸碩大的全尺寸SUV執行各種各樣的任務時,簡直帥呆了;其實這些美系大型SUV離我們並不算遙遠,比如說如今我們能夠買到的美系大型SUV就有許多,其中國人最熟悉的還是凱迪拉克凱雷德和林肯領航員。

正因為碩大的尺寸帶來的龐大氣場,加上領航員敦實穩重的設計,這款車的霸氣指數堪稱SUV界之最,也正因為超級霸氣的設計,領航員也一直是電影里的汽車明星,《黑金殺機》《巫山歷險記》等諸多電影里都有領航員的身影,而今天我們也把這個美系大塊頭做了一個詳細測試,這台車由於是我們自己購買的車,所以測試時間里程都會更為詳細客觀。測試車型:2018款領航員3.5T尊耀版,指導售價:111.80萬

在測試中,領航員給我留下了深刻的印象,雖然領航員的重量達到了2.8噸,但是在3.5T發動機的加持下百公里加速十分快,實際行駛中底盤的支撐性也還不錯,懸挂的濾震表現和舒適性相當不錯。

和以往的美式大塊頭那種粗獷不一樣,林肯領航員從上至下从里到外都透露着濃濃的精緻感,大量的直線條配合這個龐大的車身,營造出來的氣場非常不錯,巨大的尺寸和穩重的設計使得這款車看起來咄咄逼人,即使是和路虎攬勝這樣的車停在一起,氣場也能完全碾壓。

內飾上領航員也是十分精緻,立體感十足的中控搭配上大面積的真皮和實木,豪華感十分充足;而且豐富的配置也很顯檔次,全液晶儀錶盤的立體感和動畫效果都值得稱讚,相比奔馳奧迪的全液晶儀錶有過之而無不及。

做工上領航員也显示出了非常高的水準,車身的漆面厚度適中,每個面的噴漆都相當均勻,噴漆的工藝水準比較高,而且在縫隙控制上領航員也做得相當到位,各個鈑金之間的接縫都十分小。

在內飾的用料上領航員可以說十分奢華,觸手可及的真皮用料和搪塑材質都使得內飾的檔次感大幅度提升,車內觸手可及的地方几乎都是軟性材質,觸感非常好。而且在拼裝緊緻度上也比較不錯,除了空調控制按鈕上方略顯鬆散之外,再無其它可以挑剔的地方了。

座椅的皮革比較細膩,觸感很好,寬大的座椅也能夠提供更好的舒適性。

對於領航員這種大塊頭來說,最不用擔心的其實就是空間了,從實測數據來看領航員的空間表現確實十分恐怖,三排座乘客都有很寬敞的空間,中排的獨立座椅也是VIp級別待遇,第三排座椅除了在坐墊長度上略有欠缺之外,其它地方基本和普通轎車的後排無異,要知道對於SUV來說想做到這個水平是非常難的。

即使不放倒第三排座位,領航員的後備箱空間也比一般的7座SUV大得多;放倒第三排座椅后領航員的後備箱空間十分寬敞,而且第三排座椅還是電動收放,用起來十分得心應手,即使是女性使用也是比較方便的,因此對於領航員的尾箱空間確實值得好評。

領航員搭載了一台3.5T雙渦輪增壓發動機,最大功率能夠達到415馬力,最大扭矩更是高達670牛米,而且這台發動機還能燒92號汽油哦。

變速箱則是一台10AT變速箱,可以說是目前市面上擋位數最多的變速箱了,雖然變速箱擋位比較多,但是這台變速箱的換擋平順性相當出色,日常駕駛幾乎感受不到變速箱換擋。

林肯領航員的百公里加速十分恐怖,測試路麵灰塵泥沙較多,抓地力會打折扣,對於這樣一台重量達到2.8噸的大傢伙來說,依然能跑到7.1秒的百公里加速確實十分厲害;當然,剎車距離也受到了一定的影響,對於這麼一個慣性如此大的SUV來說,在這種路況下,44.3米的剎車成績也算不錯了。

對於這麼一個龐然大物來說,對操縱顯然不能有太多要求,它的定位更加偏向於舒適性,不過在實際駕駛中這台車卻也不顯得笨拙,響應迅速的動力以及不錯的底盤支撐確實能夠讓你稍微開快一點。

第一次開領航員的話對於它的轉向系統需要有一定時間適應,從左到右要打3.8圈左右,這轉向圈數比一般的車都要多,不過除此之外領航員的駕駛真的值得好評,變速箱換擋平順且快速,油門響應十分积極,懸挂支撐性也比較好,並沒有忽忽悠悠開船的感覺。

因此稍微開快點會覺得領航員還應付得過來,不過如果真的要飆車的話還是算了吧,對於這麼一個龐然大物來說,較高的重心決定了它的操控極限並不高。發動機的動力表現確實值得稱讚,這台發動機不僅動力強勁,渦輪遲滯也不大,因此行駛中動力不會有拖泥帶水的感覺。

領航員的車身尺寸十分龐大,重量也很重,這正是我們印象中的大美系車,而這樣的車確實舒適性出色,開着領航員行駛在路上,路面上大大小小的顛簸都能被很好的過濾,即使是面對減速帶也不用擔心,領航員的懸挂系統會很好地吸收振動,保證車內最好的舒適性。

同樣出色的還有隔音,雖然領航員的迎風面積很大,方正的外觀也意味着風阻大,但是實際上車內噪音卻比較小,可以用靜謐來形容,唯一不足的是發動機的聲音有些滋擾。

如果說德系豪車是以操控聞名,那麼領航員則是靠龐大的氣場和精緻的細節,對於喜歡大尺寸SUV的人來說,領航員顯然十分有吸引力,而且使用了3.5T+10AT變速箱之後這個龐然大物的油耗也不會特別恐怖,所以說硬漢在換了一套高科技的動力系統之後,好處還是十分明顯的。

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

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

一起玩轉微服務(2)——框架與工具

一、微服務架構有哪些優勢?

  • 獨立開發 – 所有微服務都可以根據各自的功能輕鬆開發·
  • 獨立部署 – 基於其服務,可以在任何應用程序中單獨部署它們·
  • 故障隔離 – 即使應用程序的一項服務不起作用,系統仍可繼續運行· 混合技術堆棧 – 可以使用不同的語言和技術來構建同一應用程序的不同服務·
  • 粒度縮放 – 單個組件可根據需要進行縮放,無需將所有組件縮放在一起

二、微服務有哪些特點?

  • 解耦 – 系統內的服務很大程度上是分離的。因此,整個應用程序可以輕鬆構建,更改和擴展·
  • 組件化 – 微服務被視為可以輕鬆更換和升級的獨立組件·
  • 業務能力 – 微服務非常簡單,專註於單一功能·
  • 自治 – 開發人員和團隊可以彼此獨立工作,從而提高速度·
  • 持續交付 – 通過軟件創建,測試和批準的系統自動化,允許頻繁發布軟件·
  • 責任 – 微服務不關注應用程序作為項目。相反,他們將應用程序視為他們負責的產品·
  • 分散治理 – 重點是使用正確的工具來做正確的工作。這意味着沒有標準化模式或任何技術模式。開發人員可以自由選擇最有用的工具來解決他們的問題·
  • 敏捷 – 微服務支持敏捷開發。任何新功能都可以快速開發並再次丟棄

三、微服務架構如何運作?

微服務架構具有以下組件:

 

 

 

  • 客戶端 – 來自不同設備的不同用戶發送請求。·
  • 身份提供商 – 驗證用戶或客戶身份並頒發安全令牌。·
  • API 網關 – 處理客戶端請求。·
  • 靜態內容 – 容納系統的所有內容。·
  • 管理 – 在節點上平衡服務並識別故障。·
  • 服務發現 – 查找微服務之間通信路徑的指南。·
  • 內容交付網絡 – 代理服務器及其數據中心的分佈式網絡。·
  • 遠程服務 – 啟用駐留在 IT 設備網絡上的遠程訪問信息。

四、什麼是 REST / RESTful 以及它的用途是什麼?

Representational State Transfer(REST)/ RESTful Web 服務是一種幫助計算機系統通過 Internet 進行通信的架構風格。這使得微服務更容易理解和實現。微服務可以使用或不使用 RESTful API 實現,但使用 RESTful API 構建鬆散耦合的微服務總是更容易。

五、什麼是不同類型的微服務測試?

在使用微服務時,由於有多個微服務協同工作,測試變得非常複雜。因此,測試分為不同的級別。·

  • 在底層,我們有面向技術的測試,如單元測試和性能測試。這些是完全自動化的。·
  • 在中間層面,我們進行了諸如壓力測試和可用性測試之類的探索性測試。·
  • 在頂層, 我們的 驗收測試數量很少。這些驗收測試有助於利益相關者理解和驗證軟件功能。

六、常用的微服務框架

各種語言都有自己的微服務框架,主要包括:

  • 主流微服務框架:SpringCloud、Dubbo、spring cloud alibaba
  • 新銳微服務框架:Istio

下面一起看一下常用的微服務框架體系。

1. Spring 頂級項目

首先,我們一起來看一下Spring的頂級項目都包括哪些:

  • Spring IO platform:用於系統部署,是可集成的,構建現代化應用的版本平台,具體來說當你使用maven dependency引入Spring jar包時它就在工作了。
  • Spring Boot:旨在簡化創建產品級的 Spring 應用和服務,簡化了配置文件,使用嵌入式web服務器,含有諸多開箱即用微服務功能,可以和Spring Cloud聯合部署。
  • Spring Framework:即通常所說的Spring 框架,是一個開源的Java/Java EE全功能棧應用程序框架,其它Spring項目如Spring Boot也依賴於此框架。
  • Spring Cloud:微服務工具包,為開發者提供了在分佈式系統的配置管理、服務發現、斷路器、智能路由、微代理、控制總線等開發工具包。
  • Spring XD:是一種運行時環境(服務器軟件,非開發框架),組合Spring技術,如Spring batch、Spring Boot、Spring data,採集大數據並處理。
  • Spring Data:是一個數據訪問及操作的工具包,封裝了很多種數據及數據庫的訪問相關技術,包括:jdbc、Redis、MongoDB、Neo4j等。
  • Spring Batch:批處理框架,或說是批量任務執行管理器,功能包括任務調度、日誌記錄/跟蹤等。
  • Spring Security:是一個能夠為基於Spring的企業應用系統提供聲明式的安全訪問控制解決方案的安全框架。
  • Spring Integration:面向企業應用集成(EAI/ESB)的編程框架,支持的通信方式包括HTTP、FTP、TCP/UDP、JMS、RabbitMQ、Email等。
  • Spring Social:一組工具包,一組連接社交服務API,如Twitter、Facebook、LinkedIn、GitHub等,有幾十個。
  • Spring AMQP:消息隊列操作的工具包,主要是封裝了RabbitMQ的操作。
  • Spring HATEOAS:是一個用於支持實現超文本驅動的 REST Web 服務的開發庫。
  • Spring Mobile:是Spring MVC的擴展,用來簡化手機上的Web應用開發。
  • Spring for Android:是Spring框架的一個擴展,其主要目的在乎簡化Android本地應用的開發,提供RestTemplate來訪問Rest服務。
  • Spring Web Flow:目標是成為管理Web應用頁面流程的最佳方案,將頁面跳轉流程單獨管理,並可配置。
  • Spring LDAP:是一個用於操作LDAP的Java工具包,基於Spring的JdbcTemplate模式,簡化LDAP訪問。
  • Spring Session:session管理的開發工具包,讓你可以把session保存到redis等,進行集群化session管理。
  • Spring Web Services:是基於Spring的Web服務框架,提供SOAP服務開發,允許通過多種方式創建Web服務。
  • Spring Shell:提供交互式的Shell可讓你使用簡單的基於Spring的編程模型來開發命令,比如Spring Roo命令。
  • Spring Roo:是一種Spring開發的輔助工具,使用命令行操作來生成自動化項目,操作非常類似於Rails。
  • Spring Scala:為Scala語言編程提供的Spring框架的封裝(新的編程語言,Java平台的Scala於2003年底/2004年初發布)。
  • Spring BlazeDS Integration:一個開發RIA工具包,可以集成Adobe Flex、BlazeDS、Spring以及Java技術創建RIA。
  • Spring Loaded:用於實現java程序和web應用的熱部署的開源工具。
  • Spring REST Shell:可以調用Rest服務的命令行工具,敲命令行操作Rest服務。

2. 背景

對於使用java技術更多的團隊來說,dubbo,Spring Cloud的知名度相對較高,而且在業界的影響力非常高。那麼我們就來一起討論一下這兩種框架,做一個綜合的評比。
Dubbo
Dubbo是阿里出品的服務化組件,應用於多個部門,後來開源之後,也衍生出像dubbox這樣的框架,對其進行了進一步的增強。Dubbo 是一個分佈式服務框架,是國內互聯網公司開源做的比較不錯的阿里開放的微服務化治理框架,致力於提供高性能和透明化的RPC遠程服務調用方案,以及SOA服務治理方案。目前Dubbo已經正式進入Apache孵化器。

現在可以再apache 下面找到dubbo

http://dubbo.apache.org/zh-cn/

 

其核心部分包含:

  • 遠程通訊: 提供對多種基於長連接的NIO框架抽象封裝,包括多種線程模型,序列化,以及“請求-響應”模式的信息交換方式;
  • 集群容錯: 提供基於接口方法的透明遠程過程調用,包括多協議支持,以及軟負載均衡,失敗容錯,地址路由,動態配置等集群支持;
  • 自動發現: 基於註冊中心目錄服務,使服務消費方能動態的查找服務提供方,使地址透明,使服務提供方可以平滑增加或減少機器。

 

Spring Cloud
Spring Cloud從其命名中就可以知道,是Spring Source的產物,可以說Spring的出現整個改變了企業級開發,Spring Cloud,除了具有 Spring 社區的強大背景外,還有 Netflix 強大的後盾與技術輸出。Netflix 作為一家成功實踐微服務架構的互聯網公司,在幾年前就把幾乎整個微服務框架棧開源貢獻給了社區,這些框架開源的整套微服務架構套件是 Spring Cloud 的核心。

  • Eureka: 服務註冊發現框架;
  • Zuul: 服務網關;
  • Karyon: 服務端框架;
  • Ribbon: 客戶端框架;
  • Hystrix: 服務容錯組件;
  • Archaius: 服務配置組件;
  • Servo: Metrics組件;
  • Blitz4j: 日誌組件。

從背景上來看,前者在國內的影響力比較大,後者在國外的影響力比較大,使用的公司也更多一些。

spring-cloud-alibaba

地址:

https://spring.io/projects/spring-cloud-alibaba

服務限流降級:默認支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降級功能的接入,可以在運行時通過控制台實時修改限流降級規則,還支持查看限流降級 Metrics 監控。
服務註冊與發現:適配 Spring Cloud 服務註冊與發現標準,默認集成了 Ribbon 的支持。
分佈式配置管理:支持分佈式系統中的外部化配置,配置更改時自動刷新。
消息驅動能力:基於 Spring Cloud Stream 為微服務應用構建消息驅動能力。
分佈式事務:使用 @GlobalTransactional 註解, 高效並且對業務零侵入地解決分佈式事務問題。。
阿里雲對象存儲:阿里雲提供的海量、安全、低成本、高可靠的雲存儲服務。支持在任何應用、任何時間、任何地點存儲和訪問任意類型的數據。
分佈式任務調度:提供秒級、精準、高可靠、高可用的定時(基於 Cron 表達式)任務調度服務。同時提供分佈式的任務執行模型,如網格任務。網格任務支持海量子任務均勻分配到所有 Worker(schedulerx-client)上執行。
阿里雲短信服務:覆蓋全球的短信服務,友好、高效、智能的互聯化通訊能力,幫助企業迅速搭建客戶觸達通道。

Istio
Istio 作為用於微服務服務聚合層管理的新銳項目,是 Google、IBM、Lyft(海外共享出行公司、Uber勁敵) 首個共同聯合開源的項S目,提供了統一的連接,安全,管理和監控微服務的方案。
目前首個測試版是針對 Kubernetes 環境的,社區宣稱在未來幾個月內會為虛擬機和 Cloud Foundry 等其他環境增加支持。 Istio 將流量管理添加到微服務中,併為增值功能(如安全性,監控,路由,連接管理和策略)創造了基礎。

  • HTTP、gRPC 和 TCP 網絡流量的自動負載均衡;
  • 提供了豐富的路由規則,實現細粒度的網絡流量行為控制;
  • 流量加密、服務間認證,以及強身份聲明;
  • 全範圍(Fleet-wide)的策略執行;
  • 深度遙測和報告。

3. 社區活躍度

我們選擇一個開源框架,社區的活躍度是我們極為關注的一個要點。社區越活躍,解決問題的速度越快,框架也會越來越完善,不然當我們碰到問題,就不得不自己解決。
先看一下dubbo和dubbox,
下圖是dubbo目前的情況,在2017年,阿里已經宣布有團隊重新維護dubbo項目,可以看到dubbo項目已經重新開始修復裡邊的一些bug,並且進入不斷的完善的過程中了。Dubbo 目前在 GitHub 上有超過 16000 個 star 和超過 12000 的 fork 數,是國內影響力最大的開源項目之一。

 

 

反倒是繼承而來的dubbox,已經有很長時間沒有進行維護了。

 

 

再看Spring Cloud,沿襲着Spring一慣的風格,更新非常及時,響應的速度也非常快,社區非常活躍。

4. 架構完整度

根據微服務架構在各方面的要素,看看Spring Cloud和Dubbo都提供了哪些支持。表N展示了Dubbo和Spring Cloud的區別。

 

其實單純從提供的數量上來比較,有一些不公平,因為dubbo是幾年前出來的,極大的改變了人們對於分佈式系統的認知。而上邊寫無的部分其實也可以整合其它的框架,所以,對於這一點的評比上來說,選用何種框架取決於團隊目前的情況,而不是一下子就全部轉型,要階梯式的實現整個應用架構。

5. 總結

使用Dubbo構建的微服務架構就像組裝電腦,各環節我們的選擇自由度很高,但是最終結果很有可能因為一條內存質量不行就點不亮了,總是讓人不怎麼放心,但是如果你是一名高手,那這些都不是問題;而Spring Cloud就像品牌機,在Spring Source的整合下,做了大量的兼容性測試,保證了機器擁有更高的穩定性,但是如果要在使用非原裝組件外的東西,就需要對其基礎有足夠的了解。

最後呢,說一下工具,個人偏好是eclipse派!
https://spring.io/tools

 

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

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

東芝挑戰綠能新趨勢 本月內建立亞洲首座虛擬電廠

摘錄自2020年11月5日中國時報報導

日本東芝集團旗下能源系統公司宣布,本月內將與德國公司次世代電廠(Next Kraftwerke)成立合資新公司東芝次世代電廠(東芝 Next Kraftwerke),建立亞洲首座虛擬電廠(virtual power plant,縮寫VPP),透過通信技術和物聯網技術,雲端中央控制全國數個太陽能發電設備,使其像傳統電廠一樣運作。

VPP是在IT技術及物聯網技術更加成熟後,最近10年才興起的分散式電廠概念,有別於傳統集中式電廠,聚集不同類型技術電力,透過電網來調度電力,因此能解決風力、太陽能等再生能源發電量與運動時間不穩定問題,例如在發電後將多餘電量送往其他需要地點或者加以儲存備用,也可以客製化不斷變換負載狀況,更加靈活而有效率,同時也能節省能源。

東芝次世代電廠初步資本額是1.8億日圓(約4991萬台幣),公司就設在東芝能源系統公司本部、神奈川縣川崎市,東芝持股51%,次世代電廠持股49%。東芝已有準確預測發電量的技術,內部人士指出透過中央統一依據需求量來控制各發電設備發電量和調度,可將電力價格波動控制到較低程度。

能源轉型
國際新聞
日本
綠能

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

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

「虹之松原」原來是海岸保安林 國寶級景點靠公私協力經營

環境資訊中心特約記者 廖靜蕙報導

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

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

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

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

從字符串到常量池,一文看懂String類設計

從一道面試題開始

看到這個標題,你肯定以為我又要講這道面試題了

//  這行代碼創建了幾個對象?
String s3 = new String("1");

是的,沒錯,我確實要從這裏開始

這道題就算你沒做過也肯定看到,總所周知,它創建了兩個對象,一個位於堆上,一個位於常量池中。

這個答案粗看起來是沒有任何問題的,但是仔細思考確經不起推敲。

如果你覺得我說的不對的話,那麼可以思考下面這兩個問題

  1. 你說它創建了兩個對象,那麼這兩個對象分別是怎樣創建的呢?我們回顧下Java創建對象的方式,一共就這麼幾種

    • 使用new關鍵字創建對象
    • 使用反射創建對象(包括Class類的newInstance方法,以及Constructor類的newInstance方法)
    • 使用clone複製一個對象
    • 反序列化得到一個對象

    你說它創建了兩個對象,那你告訴我除了new出來那個對象外,另外一個對象怎麼創建出來的?

  2. 堆跟常量池到底什麼關係?不是說在JDK1.7之後(含1.7版本)常量池已經移到了堆中了嗎?如果說常量池本身就位於堆中的話,那麼這種一個對象在堆中,一個對象在常量池的說法還準確嗎?

如果你也產生過這些疑問的話,那麼請耐心看完這篇文章!要解釋上面的問題首先我們得對常量池有個準確的認知。

常量池

通常來說,我們提到的常量池分為三種

  • class文件中的常量池
  • 運行時常量池
  • 字符串常量池

對於這三種常量池,我們需要搞懂下面幾個問題?

  1. 這個常量池在哪裡?
  2. 這個常量池用來干什麼呢?
  3. 這三者有什麼關係?

接下來,我們帶着這些問題往下看

class文件中的常量池

位置在哪?

顧名思義,class文件中的常量池當然是位於class文件中,而class文件又是位於磁盤上

用來干什麼的?

在學習class文件中的常量池前,我們首選需要對class文件的結構有一定了解

Class文件是一組以8個字節為基礎單位的二進制流,各個數據項目嚴格按照順序緊湊地排列在文

件之中,中間沒有添加任何分隔符,這使得整個Class文件中存儲的內容幾乎全部是程序運行的必要數

據,沒有空隙存在。

​ ————《深入理解Java虛擬機》

整個class文件的組成可以用下圖來表示

對本文而言,我們只關注其中的常量池部分,常量池可以理解為class文件中資源倉庫,它是class文件結構中與其它項目關聯最多的數據類型,主要用於存放編譯器生成的各種字面量(Literal)和符號引用(Symbolic References)
字面量就是我們所說的常量概念,如文本字符串、被聲明為final的常量值等
符號引用是一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可(它與直接引用區分一下,直接引用一般是指向方法區的本地指針,相對偏移量或是一個能間接定位到目標的句柄)。一般包括下面三類常量:

  • 類和接口的全限定名
  • 字段的名稱和描述符
  • 方法的名稱和描述符

現在我們知道了class文件中常量池的作用:存放編譯器生成的各種字面量(Literal)和符號引用(Symbolic References)。很多時候知道了一個東西的概念並不能說你會了,對於程序員而言,如果你說你已經會了,那麼最好的證明是你能夠通過代碼將其描述出來,所以,接下來,我想以一種直觀的方式讓大家感受到常量池的存在。通過分析一段簡單代碼的字節碼,讓大家能更好感知常量池的作用。

talk is cheap ,show me code

我們以下面這段代碼為例,通過javap來查看class文件中的具體內容,代碼如下:

/**
 * @author 程序員DMZ
 * @Date Create in 22:59 2020/6/15
 * @公眾號 微信搜索:程序員DMZ
 */
public class Main {
    public static void main(String[] args) {
        String name = "dmz";
    }
}

進入Main.java文件所在目錄,執行命令:javac Main.java ,那麼此時會在當前目錄下生成對應的Main.class文件。再執行命令:javap -v -c Main.class,此時會得到如下的解析后的字節碼信息

public class com.dmz.jvm.Main
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
// 這裏就是常量池了
Constant pool:
   #1 = Methodref          #4.#20         // java/lang/Object."<init>":()V
   #2 = String             #21            // dmz
   #3 = Class              #22            // com/dmz/jvm/Main
   #4 = Class              #23            // java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Utf8               LineNumberTable
   #9 = Utf8               LocalVariableTable
  #10 = Utf8               this
  #11 = Utf8               Lcom/dmz/jvm/Main;
  #12 = Utf8               main
  #13 = Utf8               ([Ljava/lang/String;)V
  #14 = Utf8               args
  #15 = Utf8               [Ljava/lang/String;
  #16 = Utf8               name
  #17 = Utf8               Ljava/lang/String;
  #18 = Utf8               SourceFile
  #19 = Utf8               Main.java
  #20 = NameAndType        #5:#6          // "<init>":()V
  #21 = Utf8               dmz
  #22 = Utf8               com/dmz/jvm/Main
  #23 = Utf8               java/lang/Object
 // 下面是方法表                           
{
  public com.dmz.jvm.Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/dmz/jvm/Main;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=2, args_size=1
         // 可以看到方法表中的指令引用了常量池中的常量,這也是為什麼說常量池是資源倉庫的原因
         // 因為它會被class文件中的其它結構引用         
         0: ldc           #2                  // String dmz
         2: astore_1
         3: return
      LineNumberTable:
        line 9: 0
        line 10: 3
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  args   [Ljava/lang/String;
            3       1     1  name   Ljava/lang/String;
}
SourceFile: "Main.java"

在上面的字節碼中,我們暫且關注常量池中的內容即可。主要看這兩行

#2 = String             #14            // dmz
#14 = Utf8               dmz

如果要看懂這兩行代碼,我們需要對常量池中String類型常量的結構有一定了解,其結構如下:

CONSTANT_String_info tag 標誌常量類型的標籤
index 指向字符串字面量的索引

對應到我們上面的字節碼中,tag=String,index=#14,所以我們可以知道,#2是一個字面量為#14的字符串類型常量。而#14對應的字面量信息(一個Utf8類型的常量)就是dmz

常量池作為資源倉庫,最大的用處在於被class文件中的其它結構所引用,這個時候我們再將注意力放到main方法上來,對應的就是這三條指令

0: ldc           #2                  // String dmz
2: astore_1
3: return

ldc:這個指令的作用是將對應的常量的引用壓入操作數棧,在執行ldc指令時會觸發對它的符號引用進行解析,在上面例子中對應的符號引用就是#2,也就是常量池中的第二個元素(這裏就能看出方法表中就引用了常量池中的資源)

astore_1:將操作數棧底元素彈出,存儲到局部變量表中的1號元素

return:方法返回值為void,標誌方法執行完成,將方法對應棧幀從棧中彈出

下面我用畫圖的方式來畫出整個流程,主要分為四步

  1. 解析ldc指令的符號引用(#2

  2. #2對應的常量的引用壓入到操作數棧頂

  3. 將操作數棧的元素彈出並存儲到局部變量表中

  4. 執行return指令,方法執行結束,彈出棧區該方法對應的棧幀

第一步:

在解析#2這個符號引用時,會先到字符串常量池中查找是否存在對應字符串實例的引用,如果有的話,那麼直接返回這個字符串實例的引用,如果沒有的話,會創建一個字符串實例,那麼將其添加到字符串常量池中(實際上是將其引用放入到一個哈希表中),之後再返回這個字符串實例對象的引用。

到這裏也能回答我們之前提出的那個問題了,一個對象是new出來的,另外一個是在解析常量池的時候JVM自動創建的

第二步:

將第一步得到的引用壓入到操作數棧,此時這個字符串實例同時被操作數棧以及字符串常量池引用。

第三步:

操作數棧中的引用彈出,並賦值給局部變量表中的1號位置元素,到這一步其實執行完了String name = "dmz"這行代碼。此時局部變量表中儲存着一個指向堆中字符串實例的引用,並且這個字符串實例同時也被字符串常量池引用。

第四步:

這一步我就不畫圖了,就是方法執行完成,棧幀彈出,非常簡單。

在上文中,我多次提到了字符串常量池,它到底是個什麼東西呢?我們還是分為兩部分討論

  1. 位置在哪?
  2. 用來干什麼的?

字符串常量池

位置在哪?

字符串常量池比較特殊,在JDK1.7之前,其存在於永久代中,到JDK1.7及之後,已經中永久代移到了堆中。當然,如果你非要說永久代也是堆的一部分那我也沒辦法。

另外還要說明一點,經常有同學會將方法區元空間永久代(permgen space)的概念混淆。請注意

  1. 方法區JVM在內存分配時需要遵守的規範,是一個理論,具體的實現可以因人而異
  2. 永久代hotspot jdk1.8以前對方法區的實現,使用jdk1.7的老司機肯定以前經常遇到過java.lang.OutOfMemoryError: PremGen space異常。這裏的PermGen space其實指的就是方法區。不過方法區和PermGen space又有着本質的區別。前者是JVM的規範,而後者則是JVM規範的一種實現,並且只有HotSpot才有PermGen space
  3. 元空間jdk1.8對方法區的實現,jdk1.8徹底移除了永久代,其實,移除永久代的工作從JDK 1.7就開始了。JDK 1.7中,存儲在永久代的部分數據就已經轉移到Java Heap或者Native Heap。但永久代仍存在於JDK 1.7中,並沒有完全移除,譬如符號引用(Symbols)轉移到了native heap;字面量(interned strings)轉移到了Java heap;類的靜態變量(class statics)轉移到了Java heap。到jdk1.8徹底移除了永久代,將JDK7中還剩餘的永久代信息全部移到元空間,元空間相比對永久代最大的差別是,元空間使用的是本地內存(Native Memory)

用來干什麼的?

字符串常量池,顧名思義,肯定就是用來存儲字符串的嘛,準確來說存儲的是字符串實例對象的引用。我查閱了很多博客、資料,它們都會說,字符串常量池中存儲的就是字符串對象。其實我們可以類比下面這段代碼:

HashSet<Person> persons = new HashSet<Person>;

persons這個集合中,存儲的是Person對象還是Person對象對應的引用呢?

所以,請大聲跟我念三遍

字符串常量池存儲的是字符串實例對象的引用!

字符串常量池存儲的是字符串實例對象的引用!

字符串常量池存儲的是字符串實例對象的引用!

下面我們來看R大博文下評論的一段話:

簡單來說,HotSpot VM里StringTable是個哈希表,裏面存的是駐留字符串的引用(而不是駐留字符串實例自身)。也就是說某些普通的字符串實例被這個StringTable引用之後就等同被賦予了“駐留字符串”的身份。這個StringTable在每個HotSpot VM的實例里只有一份,被所有的類共享。類的運行時常量池裡的CONSTANT_String類型的常量,經過解析(resolve)之後,同樣存的是字符串的引用;解析的過程會去查詢StringTable,以保證運行時常量池所引用的字符串與StringTable所引用的是一致的。

​ ——R大博客

從上面我們可以知道

  1. 字符串常量池本質就是一個哈希表
  2. 字符串常量池中存儲的是字符串實例的引用
  3. 字符串常量池在被整個JVM共享
  4. 在解析運行時常量池中的符號引用時,會去查詢字符串常量池,確保運行時常量池中解析后的直接引用跟字符串常量池中的引用是一致的

為了更好理解上面的內容,我們需要去分析String中的一個方法—–intern()

intern方法分析

/** 
 * Returns a canonical representation for the string object. 
 * <p> 
 * A pool of strings, initially empty, is maintained privately by the 
 * class <code>String</code>. 
 * <p> 
 * When the intern method is invoked, if the pool already contains a 
 * string equal to this <code>String</code> object as determined by 
 * the {@link #equals(Object)} method, then the string from the pool is 
 * returned. Otherwise, this <code>String</code> object is added to the 
 * pool and a reference to this <code>String</code> object is returned. 
 * <p> 
 * It follows that for any two strings <code>s</code> and <code>t</code>, 
 * <code>s.intern()&nbsp;==&nbsp;t.intern()</code> is <code>true</code> 
 * if and only if <code>s.equals(t)</code> is <code>true</code>. 
 * <p> 
 * All literal strings and string-valued constant expressions are 
 * interned. String literals are defined in section 3.10.5 of the 
 * <cite>The Java&trade; Language Specification</cite>. 
 * 
 * @return  a string that has the same contents as this string, but is 
 *          guaranteed to be from a pool of unique strings. 
 */  
public native String intern();  

String#intern方法中看到,這個方法是一個 native 的方法,但註釋寫的非常明了。“如果常量池中存在當前字符串, 就會直接返回當前字符串. 如果常量池中沒有此字符串, 會將此字符串放入常量池中后, 再返回”。

關於其詳細的分析可以參考:美團:深入解析String#intern

珠玉在前,所以本文着重就分析下intern方法在JDK不同版本下的差異,首先我們要知道引起差異的原因是因為JDK1.7及之後將字符串常量池從永久代挪到了堆中。

我這裏就以美團文章中的示例代碼來進行分析,代碼如下:

public static void main(String[] args) {
    String s = new String("1");
    s.intern();
    String s2 = "1";
    System.out.println(s == s2);

    String s3 = new String("1") + new String("1");
    s3.intern();
    String s4 = "11";
    System.out.println(s3 == s4);
}

打印結果是

  • jdk6 下false false
  • jdk7 下false true

在美團的文章中已經對這個結果做了詳細的解釋,接下來我就用我的圖解方式再分析一波這個過程

jdk6 執行流程

第一步:執行 String s = new String("1"),要清楚這行代碼的執行過程,我們還是得從字節碼入手,這行代碼對應的字節碼如下:

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/String
       3: dup
       4: ldc           #3                  // String 1
       6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
       9: astore_1
      10: return

new :創建了一個類的實例(還沒有調用構造器函數),並將其引用壓入操作數棧頂

dup:複製棧頂數值並將複製值壓入棧頂,這是因為invokespecialastore_1各需要消耗一個引用

ldc:解析常量池符號引用,將實際的直接引用壓入操作數棧頂

invokespecial:彈出此時棧頂的常量引用及對象引用,執行invokespecial指令,調用構造函數

astore_1:將此時操作數棧頂的元素彈出,賦值給局部變量表中1號元素(0號元素存的是main函數的參數)

我們可以將上面整個過程分為兩個階段

  1. 解析常量
  2. 調用構造函數創建對象並返回引用

在解析常量的過程中,因為該字符串常量是第一次解析,所以會先在永久代中創建一個字符串實例對象,並將其引用添加到字符串常量池中。此時內存狀態如下:

當真正通過new方式創建對象完成后,對應的內存狀態如下,因為在分析class文件中的常量池的時候已經對棧區做了詳細的分析,所以這裏就省略一些細節了,在執行完這行代碼后,棧區存在一個引用,指向 了堆區的一個字符串實例內存狀態對應如下:

第二步:緊接着,我們調用了s的intern方法,對應代碼就是 s.intern()

當intern方法執行時,因為此時字符串常量池中已經存在了一個字面量信息跟s相同的字符串的引用,所以此時內存狀態不會發生任何改變。

第三步:執行String s2 = "1",此時因為常量池中已經存在了字面量1的對應字符串實例的引用,所以,這裏就直接返回了這個引用並且賦值給了局部變量s2。對應的內存狀態如下:

到這裏就很清晰了,s跟s2指向兩個不同的對象,所以s==s2肯定是false嘛~

如果看過美團那篇文章的同學可能會有些疑惑,我在圖中對常量池的描述跟美團文章圖中略有差異,在美團那篇文章中,直接將具體的字符串實例放到了字符串常量池中,而在我上面的圖中,字符串常量池存的永遠時引用,它的圖是這樣畫的

就我查閱的資料而言,我個人不贊同這種說法,常量池中應該保存的僅僅是引用。關於這個問題,我已經向美團的團隊進行了留言,也請大佬出來糾錯!

接着我們分析s3跟s4,對應的就是這幾行代碼:

String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);

我們一行行分析,看看執行完后,內存的狀態是什麼樣的

第一步:String s3 = new String("1") + new String("1"),執行完成后,堆區多了兩個匿名對象,這個我們不用多關注,另外堆區還多了一個字面量為11的字符串實例,並且棧中存在一個引用指向這個實例

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NVeeWKoO-1592334452491)(upload\image-20200617020742618.png)]

實際上上圖中還少了一個匿名的StringBuilder的對象,這是因為當我們在進行字符串拼接時,編譯器默認會創建一個StringBuilder對象並調用其append方法來進行拼接,最後再調用其toString方法來轉換成一個字符串,StringBuildertoString方法其實就是new一個字符串

public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}

這也是為什麼在圖中會說在堆上多了一個字面量為11的字符串實例的原因,因為實際上就是new出來的嘛!

第二步:s3.intern()

調用intern方法后,因為字符串常量池中目前沒有11這個字面量對應的字符串實例的應用,所以JVM會先從堆區複製一個字符串實例到永久代中,再將其引用添加到字符串常量池中,最終的內存狀態就如下所示

第三步:String s4 = "11"

這應該沒啥好說的了吧,常量池中有了,直接指向對應的字符串實例

到這裏可以發現,s3跟s4指向的根本就是兩個不同的對象,所以也返回false

jdk7 執行流程

在jdk1.7中,s跟s2的執行結果還是一樣的,這是因為 String s = new String("1")這行代碼本身就創建了兩個字符串對象,一個屬於被常量池引用的駐留字符串,而另外一個只是堆上的一個普通字符串對象。跟1.6的區別在於,1.7中的駐留字符串位於堆上,而1.6中的位於方法區中,但是本質上它們還是兩個不同的對象,在下面代碼執行完后

    String s = new String("1");
    s.intern();
    String s2 = "1";
    System.out.println(s == s2);

內存狀態為:

但是對於s3跟s4確不同了,因為在jdk1.7中不會再去複製字符串實例了,在intern方法執行時在發現堆上有對應的對象之後,直接將這個對應的引用添加到字符串常量池中,所以代碼執行完,內存狀態對應如下:

看到了吧,s3跟s4指向的同一個對象,這是因為intern方法執行時,直接s3這個引用複製到了常量池,之後執行String s4= "11"的時候,直接再將常量池中的引用複製給了s4,所以s3==s4肯定為true啦。

在理解了它們之間的差異之後,我們再來思考一個問題,假設我現在將代碼改成這個樣子,那麼運行結果是什麼樣的呢?

public static void main(String[] args) {
    String s = new String("1");
    String sintern = s.intern();
    String s2 = "1";
    System.out.println(sintern == s2);

    String s3 = new String("1") + new String("1");
    String s3intern = s3.intern();
    String s4 = "11";
    System.out.println(s3intern == s4);
}

上面這段代碼運行起來結果會有差異嗎?大家可以自行思考~

在我們對字符串常量池有了一定理解之後會發現,其實通過String name = "dmz"這行代碼申明一個字符串,實際的執行邏輯就像下面這段偽代碼所示

/**
  * 這段代碼邏輯類比於
  * <code>String s = "字面量"</code>;這種方式申明一個字符串
  * 其中字面量就是在""中的值
  *
  */
public String declareString(字面量) {
    String s;
    // 這是一個偽方法,標明會根據字面量的值到字符串值中查找是否存在對應String實例的引用
    s = findInStringTable(字面量);
    // 說明字符串池中已經存在了這個引用,那麼直接返回
    if (s != null) {
        return s;
    }
    // 不存在這個引用,需要新建一個字符串實例,然後調用其intern方法將其拘留到字符串池中,
    // 最後返回這個新建字符串的引用
    s = new String(字面量);
    // 調用intern方法,將創建好的字符串放入到StringTable中,
    // 類似就是調用StringTable.add(s)這也的一個偽方法
    s.intern();
    return s;
}

按照這個邏輯,我們將我們將上面思考題中的所有字面量進行替換,會發現不管在哪個版本中結果都應該返回true。

運行時常量池

位置在哪?

位於方法區中,1.6在永久代,1.7在元空間中,永久代跟元空間都是對方法區的實現

用來干什麼?

jvm在執行某個類的時候,必須經過加載、連接、初始化,而連接又包括驗證# 位置在哪?

位於方法區中,1.6在永久代,1.7在元空間中,永久代跟元空間都是對方法區的實現

用來干什麼?

jvm在執行某個類的時候,必須經過加載、連接、初始化,而連接又包括驗證、準備、解析三個階段。而當類加載到內存中后,jvm就會將class常量池中的內容存放到運行時常量池中,由此可知,運行時常量池也是每個類都有一個。在上面我也說了,class常量池中存的是字面量和符號引用,也就是說他們存的並不是對象的實例,而是對象的符號引用值。而經過解析(resolve)之後,也就是把符號引用替換為直接引用,解析的過程會去查詢全局字符串池,也就是我們上面所說的StringTable,以保證運行時常量池所引用的字符串與全局字符串池中所引用的是一致的

所以簡單來說,運行時常量池就是用來存放class常量池中的內容的。

總結

我們將三者進行一個比較

以一道測試題結束

// 環境1.7及以上
public class Clazz {
    public static void main(String[] args) {
        String s1 = new StringBuilder().append("ja").append("va1").toString();
        String s2 = s1.intern();
        System.out.println(s1==s2);
        
        String s5 = "dmz";
        String s3 = new StringBuilder().append("d").append("mz").toString();
        String s4 = s3.intern();
        System.out.println(s3 == s4);

        String s7 = new StringBuilder().append("s").append("pring").toString();
        String s8 = s7.intern();
        String s6 = "spring";
        System.out.println(s7 == s8);
    }
}

答案是true,false,true。大家可以仔細思考為什麼,如有疑惑可以給我留言,或者進群交流!

如果本文對你有幫助的話,記得點個贊吧!也歡迎關注我的公眾號,微信搜索:程序員DMZ,或者掃描下方二維碼,跟着我一起認認真真學Java,踏踏實實做一個coder。

我叫DMZ,一個在學習路上匍匐前行的小菜鳥!

參考文章:

R大博文:請別再拿“String s = new String(“xyz”);創建了多少個String實例”來面試了吧

R大知乎回答:JVM 常量池中存儲的是對象還是引用呢?

Java中幾種常量池的區分

方法區,永久代和元空間

美團:深入解析String#intern

參考書籍:

《深入理解Java虛擬機》第二版

《深入理解Java虛擬機》第三版

《Java虛擬機規範》

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

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

深入理解 EF Core:EF Core 讀取數據時發生了什麼?

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

原文:https://bit.ly/2UMiDLb
作者:Jon P Smith
翻譯:王亮
聲明:我翻譯技術文章不是逐句翻譯的,而是根據我自己的理解來表述的。其中可能會去除一些本人實在不知道如何組織但又不影響理解的句子。

本文將為你詳細描繪 EF Core 從數據庫中讀取數據的“幕後”視圖。我將揭開兩種數據庫讀取方式的面紗:一個是普通的查詢,另一個是使用 AsNoTracking 方法的非跟蹤查詢。我還將通過一個實驗來演示我是如何解決我的一個客戶遇到的性能問題。

我假設你對 EF Core 已經有了一定的認識,但在深入學習之前,我們先來了解一下如何使用 EF Core,以確保我們已經掌握了一些基本知識。這是一個“深入研究”的課題,所以我準備大量的技術細節,希望我的描述方式你能理解。

本文是“深入理解 EF Core”系列中的第一篇。以下是本系列文章列表:

  • 當 EF Core 從數據庫讀取數據時發生了什麼?(本文)
  • 當 EF Core 寫入數據到數據庫時發生了什麼?(敬請期待)

概要

  • EF Core 有兩種方法從數據庫中讀取數據(也稱為查詢):普通 LINQ 查詢和包含 AsNoTracking 方法的非跟蹤 LINQ 查詢。
  • 這兩種方法查詢的返回類(被稱為實體類),它連接的其它的實體類(即所謂的導航屬性)也被同時加載,但這兩種法如何連接及連接的內容是不一樣的。
  • 普通查詢接受的是 DbContext 執行讀取時所有數據的副本——此時的實體類稱為被跟蹤。這允許加載的實體類參与數據庫的更新操作。
  • 普通查詢還會有一些其它的複雜底層實現,稱為關係修補(fixup),用於描述讀入的實體類和其他被跟蹤實體之間的連接關係。
  • AsNoTracked 非跟蹤查詢沒有副本,所以它沒有被跟蹤——這意味着它比普通查詢更快。這也意味着它不會用於數據庫的寫操作。
  • 最後,我將展示 EF Core 普通查詢中一個鮮為人知的特性,以此作為示例,說明通過導航屬性連接實體類的關係是多麼智能。

EF Core 如何讀取數據庫數據

提示:如果你已經對 EF Core 有一定的認識,那麼你可以跳過這一節,這部分只是一個如何讀取數據庫的例子。

為了能讓你更好地理解,我先描述一個數據庫結構,然後再給出一個簡單的數據庫讀取示例。下面是一些基本表的結構和它們之間的關係。

這些表被映射到具有類似名稱的類,例如 Book、BookAuthor、Author,這些類的屬性名稱與表的字段名稱相同。由於篇幅有限,我不打算展開來講這些類,但您可以在我的 GitHub 倉庫[1]中查看這些類。

EF Core 讀取數據庫需要下面五部分:

  1. 數據庫服務器,如 SQL server, Sqlite, PostgreSQL 等。
  2. 具有數據的數據庫。
  3. 映射到數據表的類(稱為實體類)。
  4. 一個繼承 DbContext 的類,該類包含 EF Core 的配置。
  5. 最後,從數據庫讀取數據的命令。

下面的單元測試代碼來自我的 GitHub 創庫[2],展示了一個簡單的示例,它從現有數據庫中讀取 4 個 Book 實體及其關聯的 BookAuthor 和 Authors 實體。

倉庫地址:https://bit.ly/2Yza7QQ

[Fact]
public void TestBookCountAuthorsOk()
{
    //SETUP
    var options = SqliteInMemory.CreateOptions<EfCoreContext>();
    //code to set up the database with four books, two with the same Author
    using (var context = new EfCoreContext(options))
    {
        //ATTEMPT
        var books = context.Books
            .Include(r => r.AuthorsLink)
            .ThenInclude(r => r.Author)
            .ToList();

        //VERIFY
        books.Count.ShouldEqual(4);
        books.SelectMany(x => x.AuthorsLink.Select(y => y.Author))
            .Distinct().Count().ShouldEqual(3);
    }
}

現在,如果我們將單元測試代碼對應到上面的 5 部分,結果是這樣的:

  1. 數據庫服務器——第 5 行:我選擇了一個 Sqlite 數據庫服務器,在本例中是 SqliteInMemory.CreateOptions 方法,它使用我的一個 NuGet 包 EfCore.TestSupport 創建了一個內存數據庫(內存中的數據庫對於單元測試非常有用,因為你可以為這個測試建立一個新的空數據庫)。
  2. 具有數據的數據庫——第 6 行:我將在下一篇文章介紹數據是如何寫入數據庫的,現在假設有一個數據庫包含 4 本書信息,其中兩本書的作者是同一個人。
  3. 實體類——代碼里這裏沒有展示,但是你可以在這裏查看這些類[1]。其中有一個 Books 實體類,通過一個名為 BookAuhor 的實體類多對多關聯 Authors 實體類。
  4. 一個繼承 DbContext 的類——第 7 行:EfCoreContext 類繼承了 DbContext 類並配置了從類到數據庫的映射關係(你可以在我的 GitHub 倉庫[3] 中查看該類)。
  5. 從數據庫讀取數據的命令——第 10 到 13 行,這是一個查詢:
    • 第 10 行 — context 為 EfCoreContext 的實例,通過它訪問你的數據庫,.Books 表示您希望訪問 Books 表。
    • 第 11 行 — Include 被稱為貪婪加載,它告訴 EF Core 當它加載 Books 時,也應該加載關聯到的所有 BookAuthor 實體類。
    • 第 12 行 — ThenInclude 是繼續貪婪加載,它告訴 EF Core 當它加載一個 BookAuthor 時,它也應該加載關聯到該 BookAuthor 的 Author 實體類。

所有這一切查詢出來是一個結果集,其中有普通屬性,像 Books 的 Title 屬性;有關聯實體類的導航屬性,像 Books 的 AuthorsLink 屬性。

這個示例稱為查詢或讀取,也是四種數據庫訪問類型之一,即 CRUD(新增、讀取、更新和刪除)。我將在下一篇文章中介紹新增和更新。

EF Core 如何表示讀取的數據

當你查詢數據庫時,EF Core 會將數據庫返回的數據轉換為實體類並填充導航屬性的值。在本節中,我們將研究兩種類型的查詢步驟——普通查詢(即沒有 AsNoTracking 方法,也稱為讀寫查詢)和添加了 AsNoTracking 方法的非跟蹤查詢(稱為只讀查詢)。

我們先來看一下最初 LINQ 語句是如何轉換成數據庫相應的查詢命令然後返回數據的。對於我們將要看到的兩種類型的查詢來說,這是很常見的操作。關於查詢的第一部分,請參見下圖。

有一些非常複雜的代碼將你的 LINQ 轉換為數據庫查詢命令,但這些內部細節我們不必關心。如果你的 LINQ 不能被翻譯,你會從 EF Core 得到一個異常消息,其中包含類似“不能被翻譯”的描述詞語。此外,當數據返回時,像 Value Converters[4] 這樣的特性可能會調整數據。

本節展示了查詢的第一部分,其中 LINQ 被轉換為數據庫命令並返回所有正確的值。現在我們來看查詢的第二部分,在這裏 EF Core 獲取返回值並將它們轉換為實體類的實例,並填充導航屬性。我們將分別看看兩種類型的查詢。

1. 普通查詢(讀寫查詢)

普通查詢讀取數據的方式可以修改數據並更新到數據庫,這就是我將其稱為讀寫查詢的原因。它不會自動更新數據(請參閱下一篇文章,了解如何寫入數據庫)。如果你要更新數據,你的查詢必須是讀寫查詢。

我在介紹中給出的示例執行的是一個普通讀寫查詢,讀取帶有 AuthorsLink 實例的示例。下面是該示例的查詢部分的代碼:

var books = context.Books
    .Include(r => r.AuthorsLink)
    .ThenInclude(r => r.Author)
    .ToList();

然後 EF Core 通過三個步驟將這些值轉換並填充含有導航屬性的實體類。下圖显示了這三個步驟以及生成的實體類及其導航屬性的實體類。

讓我們來分析一下這三個步驟:

  1. 創建類並填充數據。它接受數據庫返回的值,並填充非導航(稱為標量)屬性、字段等。在 Book 實體類中,是 BookId(主鍵)、Title 等屬性——參見上圖左下角淺藍色矩形。
  2. 修補關聯關係。首先是填入主鍵和外鍵的信息,它們定義如何相互關聯數據。然後,EF Core 使用這些鍵設置實體類之間的導航屬性(如圖中藍色粗線所示)。這個關係的修補所需的信息不僅是查詢讀入的實體類,它還會查看 DbContext 中跟蹤的每個實體,並填充導航屬性。這是一個強大的功能,但你的被跟蹤實體越多,所需消耗時間也越多——這就是為什麼需要 AsNoTracking 來實現更快的查詢。
  3. 創建跟蹤快照。跟蹤快照是返回給用戶的實體類的一個副本,加上它所隱藏的與每個實體類的關聯關係——若一個實體處於被跟蹤狀態,這意味着它將會發生修改並會寫入到數據庫中。

2. 非跟蹤查詢(只讀查詢)

非跟蹤查詢,即使用 AsNoTracking 方法的查詢,是一個只讀查詢。這意味着,當 SaveChanges 方法被調用時,你讀取的任何內容都不會被寫入數據庫。非跟蹤查詢的查詢效率更高,在下一節中,我將介紹非跟蹤查詢以及與普通查詢的其他區別。

在前文的示例之後,我修改了查詢代碼,添加了下面的 AsNoTracking 方法(請看第 2 行):

var books = context.Books
    .AsNoTracking()
    .Include(r => r.AuthorsLink)
    .ThenInclude(r => r.Author)
    .ToList();

這裏的 LINQ 查詢只有上面的普通查詢的前兩個步驟(沒有第三個步驟)。下圖显示了 AsNoTracking 查詢的步驟。

步驟如下:

  1. 創建類並填充數據。它接受數據庫返回的值,並填充非導航(稱為標量)屬性、字段等。在 Book 實體類中,是 BookId(主鍵)、Title 等屬性——參見上圖左下角淺藍色矩形。
  2. 修補關聯關係。首先是填入主鍵和外鍵的信息,它們定義如何相互關聯數據。然後,EF Core 使用這些鍵設置實體類之間的導航屬性(如圖中藍色粗線所示)。這個關係的修補所需的信息不僅是查詢讀入的實體類,它還會查看 DbContext 中跟蹤的每個實體,並填充導航屬性。這是一個強大的功能,但你的被跟蹤實體越多,所需消耗時間也越多——這就是為什麼需要 AsNoTracking 來實現更快的查詢。

普通查詢和非跟蹤查詢的區別

現在讓我們比較這兩種查詢比較明顯的區別。

  1. 非跟蹤查詢查詢的性能更好。使用非跟蹤查詢查詢的主要原因是性能。非跟蹤查詢查詢表現為:

    • 稍微快一點,使用的內存稍微少一點,因為它不需要創建跟蹤快照。
    • 避免沒有必要的跟蹤快照可以提高 SaveChanges 的性能,因為它不必檢查跟蹤快照以查找更改。
    • 稍微快一點,因為修補關聯關係時沒有所謂的身份解析。這就是為什麼你會得到兩個具有相同數據的 Author 實例。
  2. 非跟蹤查詢修補關聯關係時只鏈接查詢中的實體。在普通查詢中,我已經說過修補關聯關係時連接的是查詢中的實體和當前跟蹤的實體,但是非跟蹤查詢只修補查詢中的實體關係。

  3. 非跟蹤查詢並不總是代表數據庫關係。這兩種類型查詢之間的關係修補的另一個區別是,非跟蹤查詢關係修補更快,它不需要標識的解析。這可以為數據庫中的同一行生成多個實例——見上圖右下角藍色的 Author 實體和註釋。如果只是向用戶显示數據,那麼這種差異並不重要,但是如果具有業務邏輯,那麼多個實例不能正確反映數據的結構,就可能會有問題。

對層級數據有用的關係修補特性

關聯關係修補的步驟是非常智能的,特別是在普通查詢中。下面我想向你展示我是如何利用關係修補的特性來解決一個客戶項目中的性能問題的。

我曾在一家公司工作,那裡的許多數據處理都是層次化結構的,即數據具有一系列深度不確定的關聯關係。問題是我必須先解析整個層次結構,然後才能呈現這些數據。我最初是通過貪婪的方式加載前兩個層級,然後顯式地加載更深的層級來實現這一點的。它可以工作,但是性能非常慢,並且數據庫因大量單數據庫訪問而超載。

這不得不讓我思考解決辦法,如果普通查詢的關係修補那麼智能的話,它能幫助我提高查詢的性能嗎?它可以!讓我給你舉一個公司員工的例子。下圖显示了我們想要加載的公司的層次結構。

你可以接龍式地使用 .Include(x => x.WorksForMe).ThenInclude(x => x.WorksForMe)… 等等來加載所需的層級信息,但結果是一個 .Include(x => x.WorksForMe) 就夠了。因為 EF Core 的關係修補為你做了剩下的事情,這一點很驚奇,但也很有用。

例如,如果我想查詢角色為 Development 的所有員工(每個員工都有一個名為 WhatTheyDo 的屬性和名為 Role 的屬性,該 Role 包含他們工作的部門),我可以這樣編寫代碼:

var devDept = context.Employees
    .Include(x => x.WorksFromMe)
    .Where(x => x.WhatTheyDo.HasFlag(Roles.Development))
    .ToList();

這將創建一個查詢,用於加載角色為 Development 的所有員工,並且在員工實體類上修補與 WorksFoMe 導航屬性(集合)和 Manager 導航屬性(單個)的關係。通過只執行一個查詢,既提高了查詢花費的時間,又減少了數據庫服務器上的負載。

總結

你已經看到了兩種類型的查詢,我稱之為 a)普通的讀寫查詢,和 b) 非跟蹤的只讀查詢。對於每一種查詢類型,我都向你展示了 EF Core “幕後”是如何讀取數據並展示的。他們工作方式的不同也表現出他們的優勢和劣勢。

非跟蹤查詢是只讀查詢的解決方案,因為它比普通讀寫查詢更快。但是您應該記住關係修補的機制,它可以在數據庫只有一個關係的情況下創建類的多個實例。

普通的讀寫查詢是查詢跟蹤實體的解決方案,這意味着你可以在創建、更新和刪除數據時使用它們。普通的讀寫查詢確實會佔用更多的時間和內存資源,但是有一些有用的特性,比如自動鏈接到其他被跟蹤的實體類實例。

我希望這篇文章對您有用。祝你編程快樂!

[1]. https://bit.ly/2MXK3ZY
[2]. https://bit.ly/2Yza7QQ
[3]. https://bit.ly/2Y0UORO
[4]. https://bit.ly/2YEyg8j

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

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

美環保組織:燕麥片含致癌除草劑 美國桂格、家樂氏上榜

摘錄自2018年08月18日蘋果日報美國報導

美國環保組織「環境工作小組」(Environmental Working Group,EWG)近日選取45種燕麥食物送化驗,結果發現其中43種含有除草劑「年年春」(Roundup)的主要化學物質嘉磷塞(glyphosate),且超過2/3產品的嘉磷塞含量,超出兒童攝取的安全份量。而讓人驚訝的是,美國的桂格燕麥(Quaker Oats)、家樂氏(Kellogg’s)和Cheerios等的化學物含量最高。對此,美國桂格燕麥及家樂氏均強調,旗下食品合乎安全標準。

EWG指,兒童對嘉磷塞的安全攝取量,不應超過160 ppb,但桂格燕麥兩個樣本,均被驗出嘉磷塞的濃度超過1000 ppb。此外,桂格的另外兩種產品「恐龍蛋即食燕麥片」(Quaker’s dinosaur eggs instant oatmeal)和「鋼切燕麥粒」(Steel Cut Oats),嘉磷塞含量也高達500 ppb。

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

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

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

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