Gogoro擴點台中南投屏東,全台將有360個換電站

徹底改寫電動機車市場銷售紀錄的新創公司Gogoro,在短短兩年內已突破20,000 輛的銷售數字,並建置了300 多個電池交換站,充分展現了積極搶攻市場版圖的超高效率與決心。Gogoro 13 日宣布,將在4 月底開始投入在台中海線的3 個城鎮以及屏東地區首座換電站的設置;5 月底前則將進入南投。

Gogoro 能源服務副總經理潘璟倫表示,Gogoro 在第一季的布站目標主要著力在高雄及台南地區,共於該區新增了19 個換電站。第二季,則會強化在六都以外的建置計畫,除預計在屏東市、東港、南投市、草屯、員林等地設置首座換電站外,也會在清水,沙鹿,大甲與鹿港4 個城鎮分別設置換電站以串連、打通台中彰化的海線。Gogoro 最南端的換電站,也預期在第二季末於東港設立。這些都是依據大數據分析的結果所進行的計畫,後續,大家也可以很快地可以看到在不同的區域,看到GoStation 電池交換站。

在六都地區,Gogoro 仍以「一公里一座換電站」為能源網路目標,從北台灣一路往南延伸,透過與加油站、捷運站、學校、便利商店、賣場等單位異業結盟,合作布點的方式,積極擴大台灣綠色生活圈。

依照Gogoro 第二季的計畫,6 月底前,全台將會有360 個換電站。這讓Gogoro 更貼近其將在2017 年底完成西岸走廊串連,讓基隆到屏東的西部都會區得以北南縱走,暢騎無阻的承諾。

(合作媒體:。圖片出處:Gogoro)

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

鋰電池原料暴漲,電動車產業受影響

鋰電池關鍵材料鈷、鋰價格暴漲,令電池乃至電動車製造商成本控制面臨極大壓力。

鈷的國際報價四月中旬來到每磅27.5美元,寫下八年新高記錄。從2017年初以來,鈷價累計漲幅達90%,對照一年之前,更是跳增2.5倍。根據主要交易機構官員表示,鈷的身價節節升高,主因需求看漲,市場預先囤貨所致。(日經新聞)

另外,鋰價近兩年翻升約三倍,電池製造商的成本上漲壓力可以想見,這將進一步轉嫁至電動車製造商身上。據估計,鈷約佔電池製造成本的兩成。

特斯拉大眾化電動車Model 3預計在今年稍晚量產,目前特斯拉已收到40萬輛預購訂單。除了特斯拉之外,日產新一代Leaf電動車也即將上市,這些均為鈷與鋰等電池材料提供炒作題材。

(本文內容由授權使用。圖片出處:wikipedia)

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

【其他文章推薦】

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

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

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

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

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

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

英威騰:高效電驅動專家 攜多款新品亮相振威新能源汽車展

中國已經成為全球新能源汽車發展最快,保有量最大的國家。但是隨著國家補貼的逐漸退坡,新能源汽車企業的壓力也越來越大。那麼隨著中國新能源汽車行業由政策導向變為市場導向,如何能快速、有效的降低生產成本,成為了新能源車企亟待解決的問題。

目前來說,動力電池方面價格在逐步的下降,除此之外電機、電控等環節也應該成為降低成本的重要方向。市場的規模效應一直都是降低成本的最好辦法,也就是說,除了動力電池之外,其餘的環節也需要這樣的規模效應。

英威騰作為電機控制器領域的龍頭企業,在新能源汽車電控產品的創新和研發上取得了重大的進展。據英威騰負責人介紹,後補貼時代的到來,英威騰將通過創新設計和規模效應來降低成本,通過提升產品品質降低費用,讓更多的客戶認可其產品品質和產品性能,從而推動大批量應用。這樣才會使企業減少對補貼的依賴,使產業更健康發展。

目前,英威騰電機控制器系列產品主要有純電動客車主電機控制器、混合動力客車主電機控制器、環衛車主電機控制器、物流車主電機控制器、輔助控制器及各種集成方案。其主電機控制器主要用於新能源汽車發電機與電動機的控制,可用來控制新能源汽車的非同步電機和永磁同步電機,實現高效、節能,控制器效率可達98%。輔助控制器主要用於新能源汽車轉向、刹車、上裝等的電機控制、蓄電池或輔助電源的供電等。

為了應對新能源汽車行業新的發展態勢,英威騰於去年8月成立了深圳市英威騰電動汽車充電技術有限公司,專門從事與電動汽車相關的電機控制器、電機、輔助控制器、 DC-DC轉換器、車載充電機、地面充電樁/充電機及電池組件產品和整體解決方案的開發及銷售。至此,英威騰基本形成了以電機電控、電池和充電設施等完善的新能源汽車配套產業解決方案產品體系。

據悉,英威騰將攜最新研發的電機控制器及其自主研發的最新充電設備產品首度亮相2017中國國際新能源汽車產業博覽會(以下簡稱振威新能源汽車展)。

本次英威騰將展出四款充電設備最新的相關產品。涵蓋了掛壁式充電樁,充電模組、充電機、DCDC。此四款產品的特點在於對電流的保護,特別是在過壓、欠壓、短路等方面表現出色。

振威新能源汽車展是由廣東省充電設施協會、廣東省新能源汽車產業協會、充電設施線上網及振威展覽股份聯合舉辦。將於2017年6月16-18日、2017年8月23-25日分別在深圳會展中心及上海新國際博覽中心舉行。超過800家新能源汽車產業相關的企業將參與本次展會,累積展示面積超過6萬平方米。

本次展會振威展覽股份醞釀了多年,從最初的鋰電展到充電設備展,經過多年的籌備,在2017年整合了新能源汽車產業的整車製造、核心三電(電池、電機、電控)、充電設備、BMS、零部件等全產業鏈環節,致力於打造中國新能源汽車產業第一展。

組委會聯繫方式:+86-20-83953286
連絡人:黃俊鵬
官方微信公眾號:nevechina

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

【其他文章推薦】

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

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

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

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

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

工研院高速充電鋁電池獲愛迪生獎,未來望應用電動車

台灣研發於國際發光!擁有「創新界奧斯卡獎」美譽的愛迪生獎(Edison Awards)於21日美國紐約公布得獎名單,工研院在經濟部能源局的支持下,以「可高速充放電鋁電池」贏得評審團青睞,勇奪「能源與永續」類的銀牌,這次獲獎讓台灣的創意與研發能力持續在國際舞台上大放光芒,也突顯工研院的技術創新領先國際。

愛迪生獎於1987 年設立,每年表彰全球創新產品與服務,紀念發明家愛迪生追求創意與卓越的精神,強調研發過程中的合作、試作、設計到產品應用開發,著重技術創新、差異化、商品化及影響力。歷屆獲獎者有iPad、3M 分子檢測系統、波音787 Dreamliner 等,今年入圍決選的國際大廠與研發機構包括:陶氏化學、MIT Media Lab、3M、美國國防部高等研究計劃署(DARPA)、Nokia、HP、默克藥廠等。

愛迪生獎資深遴選委員Steve Herring 在頒獎現場表示:「我們很榮幸2017 年愛迪生獎有工研院參賽,他們努力追求創新的精神正是我們想要表揚的。」愛迪生獎執行總監Frank Bonafilia 亦讚許工研院在電池技術上提供了前所未有的實質發展。

工研院綠能與環境研究所所長胡耀祖表示:「根據工研院產經中心(IEK)分析,2015 年全球商業電池市場約有791 億美元的規模。鋁電池突破過去30 年的技術瓶頸,不但可快速充放電,材料安全更不會引起爆炸,還可結合現有製程,大幅提升效率及降低成本,短期可望取代鉛酸電池,未來能將此應用擴大至電動機車、輕型電動車等交通載具,以及做為備用電力與儲能裝置。」

胡耀祖進一步指出:「鋁電池未來還可搭配再生能源,成為我國儲能的重要生力軍,開啟一個能源產業與應用的新時代。目前電池材料、組裝和終端應用等產業,都能切入未來鋁電池的廣大市場,可望帶動我國電池產業升級,迎接新能源產業商機。」

「可高速充放電鋁電池」為工研院與美國史丹佛大學共同合作,以地球上蘊藏豐富的石墨和鋁為原料,成功開發出全球第一個可以穩定充放電的鋁電池,只要一分鐘便可完成充電,且重複充放電逾萬次,仍可維持高蓄電量。此外,鋁電池所使用的離子液體在室溫下是液態的鹽類,若遇到高溫短路或是受到外力破壞,也不會爆炸燃燒。自2015 年登上英國《Nature》雜誌開始,「可高速充放電鋁電池」接連拿下2016 年百大研發獎(R&D 100 Awards)及2017 年愛迪生獎銀牌,在國際上備受肯定。工研院目前已布局包括核心電極材料、電解質材料、電芯與充電器等台灣及多國專利,且陸續獲證中,建立我國發展鋁電池產業的堅實根基。

(合作媒體:。圖片出處:TechNews)

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

【其他文章推薦】

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

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

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

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

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

Tesla擴大充電網絡規模,供卓越充電體驗

隨著Tesla 準備生產首款因應大眾市場的車型,Model S、Model X 的產量持續擴大,為客戶提供卓越的充電體驗成為Tesla 的首要目標。為現在和未來的車主提供便捷、豐富、可靠的充電服務對我們以及公司的使命至關重要。2017 年,Tesla 全球充電網絡規模將擴大一倍,並對現有的充電站進行擴建, 讓駕駛不必排隊等候充電;同時,我們還將擴大市中心區域的充電站 覆蓋。

一直以來,最便捷的充電方式都是夜間在停車位充電。為了更好的服務出門在外以及缺乏穩定家用充電設備的車主,我們將繼續大舉擴建Tesla 公共充電網絡。自2012 年Tesla 超級充電站網絡投入使用至今,Tesla 已經建造了5,400 多個超級充電座, 目的是讓全球20 多萬Tesla 車主都能方便地進行長途旅行。此外,我們還建立了一個已擁有9,000 多個目的地充電座的充電網絡,通過在酒店、度假村、餐廳等場所安裝Tesla 壁掛式充電座,為車主提供如同在家一般的便捷舒適的充電體驗。但我們知道, 要想真正推動電動車的普及,我們必須繼續投資建設充電基礎設施。

2017 年初,全球已經有5,000 多個Tesla 超級充電座;截至今年年底,Tesla 將把這個數量擴大一倍,超級充電座總數將突破1 萬個,目的地充電座總數增至約1.5 萬個。北美地區的超級充電座數量將增至現有的1.5 倍,僅加州就將增設超過1,000 個超級充電座。選址工作已經開始,新的充電站很快將投入建設,並於今年夏天旅行季節到來之前投入使用。

為此,我們將沿著最繁忙的行駛路線建設規模更大的充電站,這些道路沿線將同時出現數十座Tesla 超級充電站。此外,我們還將在距離公路較遠的地帶增設多個充電站,讓當地Tesla 駕駛可以隨需隨充,從而實現市中心隨處皆可充電的目標。

憑藉全球領先、便捷的充電技術,不斷打造獨一無二、充分利用此充電技術的電動車,Tesla 長期以來一直引領全球電動車產業的發展。充電網絡的不斷擴展將確保Tesla 駕駛在任何情況下都能快速、便捷地為車輛充電,而這正是Tesla 工作的首要目標。

針對台灣,目前已完成架構超過160 座目的地充電站,以及位於台北花博的6 座超級充電站。Tesla 預計於2017 年中完成超過200 座的目的地充電站,分佈於全台超過50 個地點。同時,車主高度期待位於中部、南部的超級充電站也預計同樣於年中完成建置。如此一來,Tesla 車主即可毫無顧慮的完成全島南北往返的長途旅行。針對如此的長途旅行,不僅是都會區,Tesla 也在各個熱門景點附近的飯店設立目的地充電站,以便車主旅途中隔夜充電使用。

(合作媒體:。圖片出處:Steve Jurvetson via Flickr CC2.0)  

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

效率思維模式與Zombie Scrum

Scrum是由Ken Schwaber和Jeff Sutherland在20世紀90年代提出的概念,並在1995年首次正式確定。起初Scrum是為了解決產品和軟件開發固有的複雜性,然而現在Scrum被成功地應用於市場營銷、組織變革和科學研究等多個領域的複雜問題。

Scrum主要建立在以下三個原則的基礎上:

  • 透明度:你需要收集數據(比如一些指標、團隊成員的反饋或其他團隊的經驗之談),從而找到你的目標。
  • 檢查:你需要和大家一起監督迭代的進度,並決定迭代完成的標準是什麼。
  • 適應:你需要做出改變,希望能更好更快地完成你的目標。

在實施Scrum之前首先要用一段時間來定義和調整這些規則,以發現工作中的問題,找到可以改善的方向,這裏說的問題不是那種一年一次或項目完成時才發生的問題,而是每天、每周或每月都在持續發生的問題。我們不是將我們的決策建立在對可能永遠不會發生的潛在風險的假設上,而是根據我們收集到的數據來做決策,這就是所謂的經驗主義。

Scrum的價值?

當你需要接受你並不了解和無法控制一切的時候,Scrum提供的經驗方法就會變得非常有用。也正因如此,你會改變之前的想法,雖然可能會犯錯,但也會有新的、有價值的想法出現,而這些是你從未考慮過的。與其在前期制定一個精確的計劃,然後無論如何都要堅持下去,不如把你的想法當作假設或假說,用Scrum的方式來驗證。

Scrum可以讓你快速了解自己是否偏離了軌道,是否需要做出調整,而不是簡單地按照計劃行事,你可以先解決你目前面臨的最大風險。當你在一個不確定的、不斷變化的環境中工作時,這一點尤為重要。你一開始的假設在當時可能是絕對正確的,但是當你在開發產品的時候,環境可能會發生很大的變化,以至於你的整個方案都失敗。在一個漫長的項目結束的時候,經驗主義的方法並不是災難性的失敗,而是將其降低為一個小的減速帶,需要你修正一下方向。

所以,實際上Scrum是降低了複雜的、適應性問題、固有的不可預測性和不確定性的風險。它允許你不斷地驗證你仍然在做正確的事情,並朝着解決你設定的目標前進。更好的是,你現在有了一個积極發現更好想法的過程,並將其納入到下一步的塑造中。現在,不確定性反而變成了一件好事,因為其中蘊含着所有的可能性。

“Scrum降低了複雜的、適應性問題固有的不可預測性和不確定性的風險。”

Zombie Scrum和效率思維模式

那麼,Zombie Scrum與這一切有什麼聯繫呢?我們發現一個現象:人們使用Scrum的起因很多都是錯誤的。當你問一個Zombie Scrum組織中的人,他們希望從Scrum中得到什麼時,你會聽到諸如 “更快”、”更多的大腦”、”更多的產出 “和 “更高的效率”。這與 “敏捷 “這個詞的實際含義是非常不同的。這與Scrum的設計目的也大相徑庭。這種矛盾從何而來?

傳統的組織管理和產品開發方式是為了實現與敏捷性相反的目標,這種心理模式通常被稱為 “效率思維模式”。它的目的是盡可能地減少不確定性,提高可預測性,推動效率的提高。這通常表現為會制定詳細的計劃,通過協議和程序使工作標準化,高度的任務專業化,以及衡量效率(如每天的工作量、出現的問題) 。這種思維模式當然可以在工作相當重複和簡單的環境中發揮作用,比如流水線化的工作或某些行政工作,但在人們處理複雜的、適應性強的問題的環境中肯定行不通,因為這些問題本身就具有不可預測性和不確定性。

“效率思維模式的目的是盡可能地減少不確定性,提高可預測性,推動效率的提高。”

Zombie Scrum與領導強烈關注績效和工作量是有很大關係的,但最終客戶是否滿意?是否交付了有價值的東西?卻無人問津。而且,這種思維模式在很多企業中是根深蒂固的,它已經成為一個我們不需要討論的 “真相”。這樣的企業是想試圖用Scrum來影響效率、速度和產出的角度來理解它是有道理的,只不過當發現Scrum似乎並沒有做到這一點時,人們就會感到失望。

從非常廣泛的意義上來說,Scrum關注的更多是效率,而不是高效。效率是為了盡可能多的完成工作(產出),而高效則是為了工作的價值和有用性(結果)。雖然完全有可能通過Scrum提高效率,但這既不是承諾也不是目標。

在充斥着 “Zombie Scrum”的環境中,大家是很看重“效率思維”的,以至於人們只看到Scrum的結構性元素:角色、事件和工件。他們沒有看到也沒有體會到這個過程的價值。這就是為什麼Zombie Scrum只是看起來像Scrum,但沒有其精髓。

“Scrum更關注的是有效(結果),而不是高效(產出)。”

在這篇文章中,我們提到了Scrum的三個原則,如何在必要的時候重複進行,以捕捉工作中出現的偏差、意外發現和潛在機會。Scrum中的所有內容都是圍繞這三個支柱設計的。這也是經驗主義發揮作用的原因。採用Zombie Scrum的組織,往往有一種效率思維,目標是盡可能減少不確定性,提高可預測性,推動效率。這與在複雜工作中學習和發現的經驗主義過程相矛盾。

原文作者:Barry Overeem

翻譯整理:Worktile

Worktile 官網:worktile.com

文章首發於「Worktile官方博客」,轉載請註明出處。

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

【其他文章推薦】

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

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

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

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

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

圖像處理中的valid卷積與same卷積

valid卷積

在full卷積的卷積過程中,會遇到\(K_{flip}\)靠近I的邊界(K矩陣與I矩陣),就會有部分延申到I之外,這時候忽略邊界,只考慮I完全覆蓋\(K_{flip}\)內的值情況,這個的過程就是valid卷積。一個高為H1,寬為W1的矩陣I與高為H2,寬為W2的矩陣K,在H1大於等於H2,W1大於等於W2的情況下,valid卷積的結果就是一個(H1-H2+1)*(W-W+1)的矩陣\(C_{valid}\)

\[C_{valid}與C_{full}的對應關係為: C_{valid} = C_{full}( Rect (W_{2}-1,H_{2}-1,W_{1}-W_{2}+1,H_{1}-H_{2}+1) ) \]

same卷積

無論是full卷積還是valid卷積都不會得到正好的尺寸,要麼比原尺寸大要麼比原尺寸小,這時就需要same卷積來解決這個問題。若想得到寬和高都正好的矩陣我們首先需要給\(K_{flip}\)一個錨點,將錨點放在(循環)圖像矩陣的(r,c)處,((r,c)在矩陣之內),將對應位置的元素逐個相乘,最終將所有的積進行求和作為輸出圖像矩陣在(r,c)處的輸出值。這個過程稱為same卷積。
OpenCv函數copyMakeBorder的參數表

參數 解釋
src 輸入矩陣
dst 輸出矩陣
top 上側擴充的行數
bottom 下側擴充的行數
left 左側擴充的行數
right 右側擴充的行數
borderType 邊界擴充的類型
value border Type= BORDER_CONSTANT事的常數

其中borderType有多種類型,比如:BORDER_REPLICATE(邊界複製)、BORDER_CONSTANT(常數擴充)、BORDER_REFLECT(反射擴充)等。
在使用Python進行卷積操作時用到包Scipy,其中有關的操作函數為convolve2d(in1,in2,mode=’full’,boundary=’fill’,fillvalue=0)

參數 解釋
in1 輸入數組
in2 輸入數組,代表K(卷積算子)
mode 卷積類型,也就是以上提到的三種類型:full,valid,same
boundary 邊界填充:fill\wrap\symm
fillvalue 當boundary=’fill’時,設置邊界填充的值,默認為0

在這裏需要注意的是當model為same時卷積算子的錨點位置由不同尺寸而不同,假設K(卷積算子)的寬和高分別為W、H。

W和H的值 錨點位置
均為奇數 默認為中心點
H為偶數、W為奇數 (H-1,(W-1)/2)
H為奇數,W為偶數 ((H-1)/2,W-1)
均為偶數 (H-1,W-1)

代碼實現:

import numpy as np
from scipy import signal

if __name__ == "__main__":

    I = np.array([[1,2],[3,4],np.float32])
    #I的高和寬
    H1,W1 = I.shape[:2]
    #卷積算子
    k = np.array([[-1,-2],[2,1],np.float32])
    #K的寬和高
    H2,W2 = k.shape[:2]
    #計算full卷積
    c_full = signal.convolve2d(I,k,mode='full')
    #設定錨點
    r,c = 0,0
    #根據錨點來從full卷積中截取same卷積
    c_same= c_full[H2-r-1:H1-r-1,W2-c-1:W1+W2-c-1]

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

編碼的道與禪

該文章發布在github中,如果您覺得寫的還不錯的話,可以 star 一下進行支持,傳送門:TechShare。

Bob 大叔在《代碼整潔之道》一書的前言打趣着說,當你寫的代碼在經受代碼審查時,如果審查者憤怒的吼道“What the fuck is this shit?”等言辭激烈的詞語時,那說明你寫的是 Bad Code;如果審查者只是漫不經心的吐出幾個“What the fuck?”,那說明你寫的是 Good Code。這就是衡量代碼質量的唯一標準——每分鐘罵出“What the fuck?”的頻率。

想寫出整潔的代碼很難,有一部分原因在於糟糕的代碼太容易編寫。想快點完成任務時,考慮不周全時,忽略安全時,隨意命名時,參數過多時,嵌套太深時,未及時更改註釋時,違反法則時,重複你自己時等等情形,我們有太多的機會來製造糟糕的代碼。只有嚴肅對待自己的代碼,了解哪些事情會使我們的代碼變味,才有可能寫出整潔的代碼。

寫代碼和寫文章在某種程度上有相似之處,好的文章一定有好的可讀性,寫代碼也一樣,只有優美乾淨的代碼才能具有良好的可讀性。編寫具有可讀性的代碼不光是保持有意義的命名就行,如果你想成為一名更好的程序員,寫代碼時你需要注意的有很多,比如:

  1. 規範本地變量的位置
  2. 使函數盡量短小
  3. 調用者盡可能放在被調用者上面
  4. 保持代碼擁有良好的格式
  5. 編寫只做一件事的函數
  6. 函數參數不要超過三個
  7. 暴露時序耦合
  8. 使用異常代替返回錯誤碼

除此之外,你還須牢記眾多設計原則,如:

  1. 開放封閉原則(OCP)
  2. 迪米特法則
  3. 依賴倒置原則(DIP)
  4. 單一職責原則(SRP)
  5. 里氏替換原則(LSP)
  6. 不要重複(DRY)
  7. 你不會需要它(YAGNI)

當然僅有這些是不夠的,這不是騎自行車,學寫整潔代碼得花許多功夫,必須不斷實踐,從失敗中提取代碼的壞味道並從中得到啟發。

編寫整潔代碼,你需要牢記並遵守很多東西,但這並不是循規蹈矩和刻板,而是對簡單之美、代碼之美的追求。代碼整潔之道,是編寫優秀代碼的一種方法,其核心是儘力使代碼保持簡單——Keep It Simple, Stupid。判斷一個人寫的代碼的好壞,不是看它的代碼寫的有多複雜,而是看他有沒有把複雜的事物抽象出來並用簡單的方式去描述它,此外這個人對代碼的態度也至關重要,大多數時候我們並不能從一開始就把代碼寫的很完美,當我們需要快速做出一個原型,或者一開始代碼看起來不錯,但新的需求使現有的設計無法滿足,如果不對設計進行改動的話,那麼代碼就會變的醜陋,如果你熱愛自己正在做的事情,崇尚代碼之美,那麼你就會有足夠的動力去重構它、完善它,而不是破壞結構使代碼腐爛。

保持簡單、追求簡單,我想這就是編碼之中的禪意,一種追求本真的境界。這種禪在 Python 的設計哲學中體現的淋漓盡致,讓我們在 Python 解釋器中輸入“import this”,來看看經典的 Python 之禪。

  • Beautiful is better than ugly.
    優美勝於醜陋。
  • Explicit is better than implicit.
    顯式勝於隱式。
  • Simple is better than complex.
    簡單勝於複雜。
  • Complex is better than complicated.
    複雜勝於難懂。
  • Flat is better than nested.
    扁平勝於嵌套。
  • Sparse is better than dense.
    分散勝於密集。
  • Readability counts.
    可讀性應當被重視。
  • Special cases aren’t special enough to break the rules. Although practicality beats purity.
    儘管實用性會打敗純粹性,特例也不能凌駕於規則之上。
  • Errors should never pass silently. Unless explicitly silenced.
    除非明確地使其沉默,錯誤永遠不應該默默地溜走。
  • In the face of ambiguity, refuse the temptation to guess.
    面對不明確的定義,拒絕猜測的誘惑。
  • There should be one– and preferably only one –obvious way to do it.
    用一種方法,最好只有一種方法來做一件事。
  • Although that way way not be obvious at first unless you’re Dutch.
    雖然一開始這種方法並不是顯而易見的,但誰叫你不是Python之父呢。
  • Now is better than never. Although never is often better than right now.
    做比不做好,但立馬去做有時還不如不做。
  • If the implementation is hard to explain, it’s a bad idea.
    如果實現很難說明,那它是個壞想法。
  • If the implementation is easy to explain, it may be a good idea.
    如果實現容易解釋,那它有可能是個好想法。
  • Namespaces are one honking great idea – let’s do more of those!
    命名空間是個絕妙的想法,讓我們多多地使用它們吧!

道着重於方法,禪着重於態度,讓我們把這兩者相結合,做一個有追求的程序員,為成為軟件匠人而奮鬥吧。

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

【其他文章推薦】

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

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

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

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

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

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

面試必問系列之JDK動態代理

掃描文末二維碼或者微信搜索公眾號小李不禿,即可關注微信公眾號,獲取到更多 Java 相關內容。

1. 帶着問題去學習

面試中經常會問到關於 Spring 的代理方式有哪兩種?大家異口同聲的回答:JDK 動態代理和 CGLIB 動態代理。

這兩種代理有什麼區別呢?JDK 動態代理的類通過接口實現,CGLIB 動態代理是通過子類來實現的。

那 JDK 動態代理你了到底了解多少呢?有去看過代理對象的 class 文件么?下面兩個關於 JDK 動態代理的問題你能回答上來么?

  • 問題1:為什麼 JDK 動態代理要基於接口實現?而不是基於繼承來實現?
  • 問題2:JDK 動態代理中,目標對象調用自己的另一個方法,會經過代理對象么

小李帶着大家更深入的了解一下 JDK 的動態代理。

2. JDK 動態代理的寫法

  • JDK 動態代理需要這幾部分內容:接口、實現類、代理對象。
  • 代理對象需要繼承 InvocationHandler,代理類調用方法時會調用 InvocationHandlerinvoke 方法。
  • Proxy 是所有代理類的父類,它提供了一個靜態方法 newProxyInstance 動態創建代理對象。
public interface IBuyService {
     void buyItem(int userId);
     void refund(int nums);
}

 

@Service
public class BuyServiceImpl implements IBuyService {
    @Override
    public void buyItem(int userId) {
        System.out.println("小李不禿要買東西!小李不禿的id是: " + userId);
    }
    @Override
    public void refund(int nums) {
        System.out.println("商品過保質期了,需要退款,退款數量 :" + nums);
    }
}

 

public class JdkProxy implements InvocationHandler {

    private Object target;
    public JdkProxy(Object target) {
        this.target = target;
    }
    // 方法增強
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before(args);
        Object result = method.invoke(target,args);
        after(args);
        return result;
    }
    private void after(Object result) { System.out.println("調用方法后執行!!!!" ); }
    private void before(Object[] args) { System.out.println("調用方法前執行!!!!" ); }

    // 獲取代理對象
    public <T> getProxy(){
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }
}

 

public class JdkProxyMain {
    public static void main(String[] args) {
        // 標明目標 target 是 BuyServiceImpl
        JdkProxy proxy = new JdkProxy(new BuyServiceImpl());
        // 獲取代理對象實例
        IBuyService buyItem = proxy.getProxy();
        // 調用方法
        buyItem.buyItem(12345);
    }
}

查看運行結果

調用方法前執行!!!!
小李不禿要買東西!小李不禿的id是: 12345
調用方法后執行!!!!

我們完成了對目標方法的增強,開始對代理對象進行一個更全面的分析。

3. 剖析代理對象並解答問題

剖析代理對象的前提得是有代理對象,動態代理的對象是在運行時期創建的,我們就沒辦法通過打斷點的方式進行分析了。但是我們可以通過反編譯 .class 文件進行分析。如何獲取到 .class 文件呢?

通過在代碼中添加:System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true") ,就能夠實現將動態代理對象的 class 文件寫入到磁盤中。代碼如下:

public class JdkProxyMain {
    public static void main(String[] args) {
        // 代理對象的 class 文件寫入到磁盤中
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles""true");
        // 標明目標 target 是 BuyServiceImpl
        JdkProxy proxy = new JdkProxy(new BuyServiceImpl());
        // 獲取代理對象實例
        IBuyService buyItem = proxy.getProxy();
        // 調用方法
        buyItem.buyItem(12345);
    }
}

在項目的根目錄下多了一個 $Proxy0.class 文件

看一下這個文件的內容

public final class $Proxy0 extends Proxy implements IBuyService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void buyItem(int var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void refund(int var1) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.example.springtest.service.IBuyService").getMethod("buyItem", Integer.TYPE);
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.example.springtest.service.IBuyService").getMethod("refund", Integer.TYPE);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

動態代理對象 $Proxy0 繼承了 Proxy 類並且實現了 IBuyService 接口。那問題 1 的答案就出來了:動態代理對象默認繼承了 Proxy 對象,而且 Java 不支持多繼承,所以 JDK 動態代理要基於接口來實現。

$Proxy0 重寫了 IBuyService 接口的方法,還有 Object 的方法。在重寫的方法中,統一調用 super.h.invoke 方法。super 指的是 Proxyh 代表 InvocationHandler,這裏就是 JdkProxy。所以這裏調用的是 JdkProxyinvoke 方法。

所以每次調用 buyItem 方法的時候,會先打印出 調用方法前執行!!!!

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before(args);
// 通過反射調用方法
Object result = method.invoke(target,args);
after(args);
return result;
}
private void after(Object result) { System.out.println("調用方法后執行!!!!" ); }
private void before(Object[] args) { System.out.println("調用方法前執行!!!!" ); }

問題 2 還沒解決呢,接着往下看

@Service
public class BuyServiceImpl implements IBuyService {
    @Override
    public void buyItem(int userId) {
        System.out.println("小李不禿要買東西!小李不禿的id是: " + userId);
        refund(100);
    }
    @Override
    public void refund(int nums) {
        System.out.println("商品過保質期了,需要退款,退款數量 :" + nums);
    }
}

上面這段代碼中,在 buyItem 調用內部的 refund 方法,那這個內部調用方法是否走代理對象呢?看一下執行結果:

調用方法前執行!!!!
小李不禿要買東西!小李不禿的id是: 12345
商品過保質期了,需要退款,退款數量 :100
調用方法后執行!!!!

確實是沒有走代理對象,其實我們期待的結果是下面這樣的

調用方法前執行!!!!
小李不禿要買東西!小李不禿的id是: 12345
調用方法前執行!!!!
商品過保質期了,需要退款,退款數量 :100
調用方法后執行!!!!
調用方法后執行!!!!

那為什麼會造成這種差異呢?

因為內部調用 refund 方法的調用,相當於 this.refund(100),而這個 this 指的是 BuyServiceImpl 對象,而不是代理對象,所以refund 方法沒有得到增強

4. 總結和延伸

  • 本篇文章了解了 JDK 動態代理的使用,通過分析 JDK 動態代理生成對象的 class 文件,解決了兩個問題:

    • 問題1:為什麼 JDK 動態代理要基於接口實現?而不是基於繼承來實現?
    • 解答:因為 JDK 動態代理生成的對象默認是繼承 Proxy ,Java 不支持多繼承,所以 JDK 動態代理要基於接口來實現。
    • 問題2:JDK 動態代理中,目標對象調用自己的另一個方法,會經過代理對象么
    • 解答:內部調用方法使用的對象是目標對象本身,被調用的方法不會經過代理對象。
  • 我們知道了 JDK 動態代理內部調用是不走代理對象的。那對於 @Transactional 和 @Async 等註解不起作用是不是就搞清楚為啥了?

  • 因為 @Transactional@Async 等註解是通過 Spring AOP 來進行實現的,如果動態代理使用的是 JDK 動態代理,那麼在方法的內部調用該方法中其它帶有該註解的方法,由於此時調用的不是動態代理對象,所以註解失效

  • 上面這些問題就是 JDK 動態代理的缺點,那 Spring 如何避免這個問題呢?就是另個一個動態代理:CGLIB 動態代理,我會在下篇文章進行分析。

5. 參考

  • https://juejin.im/post/5d8a0799f265da5b7a752e7c#heading-6
  • https://blog.csdn.net/varyall/article/details/102952365

6. 猜你喜歡

  • JSON的學習和使用

  • 學習反射看這一篇就夠了

  • 併發編程學習(一)Java 內存模型

掃描下方二維碼即可關注微信公眾號小李不禿,一起高效學習 Java。

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

【其他文章推薦】

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

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

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

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

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

看了Java的Class的源碼,我自閉了

java源碼之Class

​ 源碼的重要性不言而喻,雖然枯燥,但是也有拍案叫絕。這是我的源碼系列第二彈,後續還會一直更新,歡迎交流。String源碼可以看我的Java源碼之String,如有不足,希望指正。

1.class這個類是什麼

Class的本質也是一個類,只不過它是將我們定義類的共同的部分進行抽象,比如我們常定義的類都含有構造方法,類變量,函數,而Class這個類就是來操作這些屬性和方法的。當然我們常定義的類包含的類型都可以通過Class間接的來操作。而類的類型包含一般的類,接口,枚舉類型,註解類型等等。這麼說可能有點太理論,我們看下面這個例子:

我們將生活中的一類事物抽象為一個類的時候,往往是因為他們具有相同的共性和不同的個性。定義一個類的作用就是將相同的共性抽離出來。一般的類都包含屬性和方法(行為),下面我們定義水果和汽車這兩個大類:

代碼如下:

汽車類:

class Car{

    // 定義屬性
    private String name;
    private String color;

    /**
     * 定義兩個構造方法
     */
    public Car(){

    }

    public Car(String name,String color){
        this.name = name;
        this.color = color;
    }

    /**
     * 定義兩個普通方法(行為)
     */
    public void use(){
        
    }
    
    public void run(){
        
    }

    /**
     * 屬性的get和set方法
     * @return
     */
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
         this.color = color;
    }
}


水果類:

class Fruit{

    // 定義屬性
    private String name;
    private int size;

    /**
     * 定義兩個構造方法
     */
    public Fruit(){

    }

    public Fruit(String name,int size){
        this.name = name;
        this.size =size;
    }

    /**
     * 定義兩個方法(行為)
     */
    public void use(){
        
    }
    
    public void doFruit(){
        
    }

    /**
     * 屬性的get和set方法
     * @return
     */
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }
}

可以看到水果和汽車這兩個類都有共同的部分,也就是一個類共同的部分,那就是屬性和方法,而Class就是來操作我們定義類的屬性和方法。

​小試牛刀:通過Class這個類來獲取Fruit這個類中定義的方法;

public static void main(String[] args) {

        Fruit fruit = new Fruit();
        Class fruitClass = fruit.getClass();

        Method[] fruitMethods = fruitClass.getMethods();
        System.out.println("方法個數:" + fruitMethods.length);

        for (Method method : fruitMethods) {
            //得到返回類型
            System.out.print("方法名稱和參數:" + method.getName() + "(");
            //取得某個方法對應的參數類型數組
            Class[] paramsType = method.getParameterTypes();
            for (Class paramType : paramsType) {
                System.out.print(paramType.getTypeName() + " ");
            }
            System.out.print(")");

            Class returnType = method.getReturnType();
            System.out.println("返回類型:" + returnType.getTypeName());
        }
    }

運行結果:

方法個數:15
方法名稱和參數:getName()返回類型:java.lang.String
方法名稱和參數:setName(java.lang.String )返回類型:void
方法名稱和參數:getSize()返回類型:int
方法名稱和參數:setSize(int )返回類型:void
方法名稱和參數:use()返回類型:void
方法名稱和參數:doFruit()返回類型:void
方法名稱和參數:wait()返回類型:void
方法名稱和參數:wait(long int )返回類型:void
方法名稱和參數:wait(long )返回類型:void
方法名稱和參數:equals(java.lang.Object )返回類型:boolean
方法名稱和參數:toString()返回類型:java.lang.String
方法名稱和參數:hashCode()返回類型:int
方法名稱和參數:getClass()返回類型:java.lang.Class
方法名稱和參數:notify()返回類型:void
方法名稱和參數:notifyAll()返回類型:void

這裏可能有人疑惑了,Fruit類並沒有定義的方法為什麼會出現,如wait(),equals()方法等。這裏就有必要說一下java的繼承和反射機制。在繼承時,java規定每個類默認繼承Object這個類,上述這些並沒有在Fruit中定義的方法,都是Object中的方法,我們看一下Object這個類的源碼就會一清二楚:

 public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

 public final native void wait(long timeout) throws InterruptedException;

 public final void wait() throws InterruptedException {
        wait(0);
    }

而Class類中的getMethods()方法默認會獲取父類中的公有方法,也就是public修飾的方法。所以Object中的公共方法也出現了。

注: 要想獲得父類的所有方法(public、protected、default、private),可以使用apache commons包下的FieldUtils.getAllFields()可以獲取類和父類的所有(public、protected、default、private)屬性。

是不是感覺非常的強大 ,當然,使用Class來獲取一些類的方法和屬性的核心思想就是利用了Java反射特性。萬物皆反射,可見反射的強大之處,至於反射的原理,期待我的下一個博客。

2.常用方法的使用以及源碼分析

2.1構造方法

源碼如下:

 private Class(ClassLoader loader) {
        // Initialize final field for classLoader.  The initialization value of non-null
        // prevents future JIT optimizations from assuming this final field is null.
        classLoader = loader;
    }

可以看到Class類只有一個構造函數,並且是私有的。也就是說不能通過new來創建這個類的實例。官方文檔的解釋:私有構造函數,僅Java虛擬機創建Class對象。我想可能就是為了安全,具體原因不是很了解。如果有了解的話,可以在評論區內共同的交流。

Class是怎麼獲取一個實例的。

那麼既然這個class構造器私有化,那我們該如何去構造一個class實例呢,一般採用下面三種方式:

1.運用.class的方式來獲取Class實例。對於基本數據類型的封裝類,還可以採用.TYPE來獲取相對應的基本數據類型的Class實例,如下的示例。

 // 普通類獲取Class的實例。接口,枚舉,註解,都可以通過這樣的方式進行獲得Class實例
Class fruitClass = Fruit.class;

// 基本類型和封裝類型獲得Class實例的方式,兩者等效的
Class intClass = int.class;
Class intClass1 = Integer.TYPE;

下面的表格兩邊等價:

boolean.class Boolean.TYPE
char.class Character.TYPE
byte.class Byte.TYPE
short.class Short.TYPE
int.class Integer.TYPE
long.class Long.TYPE
float.class Float.TYPE
double.class Double.TYPE
void.class Void.TYPE

但是這種方式有一個不足就是對於未知的類,或者說不可見的類是不能獲取到其Class對象的。

2.利用對象.getClass()方法獲取該對象的Class實例;

這是利用了Object提供的一個方法getClass() 來獲取當著實例的Class對象,這種方式是開發中用的最多的方式,同樣,它也不能獲取到未知的類,比如說某個接口的實現類的Class對象。

Object類中的getClass()的源碼如下:

public final native Class<?> getClass();

源碼說明:

可以看到,這是一個native方法(一個Native Method就是一個java調用非java代碼的接口),並且不允許子類重寫,所以理論上所有類型的實例都具有同一個 getClass 方法。

使用:

 Fruit fruit = new Fruit();
 Class fruitClass = fruit.getClass();

3.使用Class類的靜態方法forName(),用類的名字獲取一個Class實例(static Class forName(String className) ),這種方式靈活性最高,根據類的字符串全名即可獲取Class實例,可以動態加載類,框架設計經常用到;

源碼如下:

    /*
     由於方法區 Class 類型信息由類加載器和類全限定名唯一確定,所以參數name必須是全限定名,
     參數說明   name:class名,initialize是否加載static塊,loader 類加載器
     */
    public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        Class<?> caller = null;
        
        // 1.進行安全檢查
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
           ....
            }
        }
        // 2.調用本地的方法
        return forName0(name, initialize, loader, caller);
    }
   
    // 3.核心的方法
    private static native Class<?> forName0(String name, boolean initialize,
                                            ClassLoader loader,
                                            Class<?> caller)
      throws ClassNotFoundException;

   /* 
    這個 forName是上述方法的重載,平時一般都使用這個 方法默認使用調用者的類加載器,將類的.class文件加載     到 jvm中
    這裏傳入的initialize為true,會去執行類中的static塊
    */
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

源碼說明已在註釋中說明,有些人會疑惑, static native Class<?> forName0()這個方法的實現。

這就要說到java的不完美的地方了,Java的不足除了體現在運行速度上要比傳統的C++慢許多之外,Java無法直接訪問到操作系統底層(如系統硬件等),為此Java使用native方法來擴展Java程序的功能。有關native的方法請移步這裏。

基本使用:

 Class fruitClass = Class.forName("cn.chen.test.util.lang.Fruit");

: 這種方式必須使用類的全限定名,,這是因為由於方法區 Class 類型信息由類加載器和類全限定名唯一確定,否則會拋出ClassNotFoundException的異常。

2.2一般方法以及源碼分析:

Class類的一般的方法總共有六十多種,其實看到這麼多方法咱也不要慫,這裏面還有很多重載的方法,根據二八原則,我們平時用的也就那麼幾個方法,所以這裏只對以下幾個方法的使用和實現進行交流,其他的方法可以移步Java官方文檔:

2.2.1 獲得類的構造方法

這個方法主要是用來了解一個類的構造方法有哪些,包含那些參數,特別是在單例的模式下。一般包含的方法如下:

  • public Constructor[] getConstructors() :獲取類對象的所有可見的構造函數

  • public Constructor[] getDeclaredConstructors():獲取類對象的所有的構造函數

  • public Constructor getConstructor(Class… parameterTypes): 獲取指定的可見的構造函數,參數為:指定構造函數的參數類型數組,如果該構造函數不可見或不存在,會拋出 NoSuchMethodException 異常

  • public Constructor getDeclaredConstructor(Class… parameterTypes) :獲取指定的構造函數,參數為:指定構造函數的參數類型數組,無論構造函數可見性如何,均可獲取

基本使用:

Constructor[] constructors = fruitClass.getConstructors();
 for (Constructor constructor : constructors) {
            System.out.println("獲得共有的構造方法:"+constructor);
        }

輸出結果:

獲得共有的構造方法:public cn.chen.test.util.lang.Fruit()
獲得共有的構造方法:public cn.chen.test.util.lang.Fruit(java.lang.String,int)

可以看到我們前面定義的來個構造方法,都被打印出來了。注意getConstructors()只能獲得被public修飾的構造方法,如果要獲得被(protected,default,private)修飾的構造方法,就要使用的getDeclaredConstructors()這個方法了。接下來,修改Fruit中的一個構造方法為private:

 private  Fruit(String name,int size){
        this.name = name;
        this.size =size;
    }

使用getConstructors()和getDeclaredConstructors()着兩個方法進行測試:

       Class fruitClass = Fruit.class;       
       Constructor[] constructors = fruitClass.getConstructors();
       Constructor[] constructors1 = fruitClass.getDeclaredConstructors();

        for (Constructor constructor : constructors) {
            System.out.println("獲得共有的構造方法:"+constructor);
        }

        System.out.println("=================================================");
        for (Constructor constructor : constructors1) {
            System.out.println("獲得所有的構造方法:"+constructor);
        }

輸出結果:

獲得共有的構造方法:public cn.chen.test.util.lang.Fruit()
===================分隔線=============================
獲得所有的構造方法:public cn.chen.test.util.lang.Fruit()
獲得所有的構造方法:private cn.chen.test.util.lang.Fruit(java.lang.String,int)

可以看到兩者的區別。所以,反射在一定程度上破壞了java的封裝特性。畢竟人無完人,語言亦是一樣。

getConstructors()的源碼分析:

public Constructor<?>[] getConstructors() throws SecurityException {
          
        // 1.檢查是否允許訪問。如果訪問被拒絕,則拋出SecurityException。
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyConstructors(privateGetDeclaredConstructors(true));
    }
    
 private static <U> Constructor<U>[] copyConstructors(Constructor<U>[] arg) {
      // 2.使用克隆,得到當前類的所有構造函數   
      Constructor<U>[] out = arg.clone();
     // 3.使用ReflectionFactory構造一個對象,也是不使用構造方法構造對象的一種方式。
        ReflectionFactory fact = getReflectionFactory();
     // 4.遍歷,將構造函數進行拷貝返回,注意在調用fact.copyConstructor(out[i])這個方法的時候,還會進行安全檢查,用的就是下面的LangReflectAccess() 這個方法。
        for (int i = 0; i < out.length; i++) {
            out[i] = fact.copyConstructor(out[i]);
        }
        return out;
    }



 private static LangReflectAccess langReflectAccess() {
        if (langReflectAccess == null) {
            Modifier.isPublic(1);
        }

        return langReflectAccess;
    } 

通過打斷點調試,可以看到下面的信息:

代碼的調用邏輯在註釋里已進行說明。

2.2.2 獲得屬性

主要獲取類的屬性字段,了解這個類聲明了那些字段。

一般有四個方法:

  • public Field[] getFields():獲取所有可見的字段信息,Field數組為類中聲明的每一個字段保存一個Field 實例
  • public Field[] getDeclaredFields():獲取所有的字段信息
  • public Field getField(String name) :通過字段名稱獲取字符信息,該字段必須可見,否則拋出異常
  • public Field getDeclaredField(String name) :通過字段名稱獲取可見的字符信息

基本使用:

首先我們在Fruit的類中加入一個public修飾的屬性:

    public double weight;
Class fruitClass = Fruit.class; 
Field[] field2 = fruitClass.getFields();
        for (Field field : field2) {
            System.out.println("定義的公有屬性:"+field);
        }

        Field[] fields = fruitClass.getDeclaredFields();
        for (Field field : fields) {
            System.out.println("定義的所有屬性:"+field);
        }

輸出結果:

定義的公有屬性:public double cn.chen.test.util.lang.Fruit.weight
========================分隔線============================
定義的所有屬性:private java.lang.String cn.chen.test.util.lang.Fruit.name
定義的所有屬性:private int cn.chen.test.util.lang.Fruit.size
定義的所有屬性:public double cn.chen.test.util.lang.Fruit.weight

源碼分析,就以getFileds()這個方法為例,涉及以下幾個方法:

public Field[] getFields() throws SecurityException {
        // 1.檢查是否允許訪問。如果訪問被拒絕,則拋出SecurityException。
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyFields(privateGetPublicFields(null));
    }
 
 private static Field[] copyFields(Field[] arg) {
         // 2. 聲明一個Filed的數組,用來存儲類的字段 
        Field[] out = new Field[arg.length];
        //  3.使用ReflectionFactory構造一個對象,也是不使用構造方法構造對象的一種方式。
        ReflectionFactory fact = getReflectionFactory();
       // 4.遍歷,將字段複製后返回。
        for (int i = 0; i < arg.length; i++) {
            out[i] = fact.copyField(arg[i]);
        }
        return out;
    }
    
 public Field copyField(Field var1) {
        return langReflectAccess().copyField(var1);
    }
 
// 再次檢查屬性的訪問權限
  private static LangReflectAccess langReflectAccess() {
        if (langReflectAccess == null) {
            Modifier.isPublic(1);
        }

        return langReflectAccess;
    }

2.2.3 獲得一般方法

就是獲取一個類中的方法,一般有以下幾個方法:

  • public Method[] getMethods(): 獲取所有可見的方法

  • public Method[] getDeclaredMethods() :獲取所有的方法,無論是否可見

  • public Method getMethod(String name, Class… parameterTypes)

    參數說明:

  1. 通過方法名稱、參數類型獲取方法
  2. 如果你想訪問的方法不可見,會拋出異常
  3. 如果你想訪問的方法沒有參數,傳遞 null作為參數類型數組,或者不傳值)
  • public Method getDeclaredMethod(String name, Class… parameterTypes)
  1. 通過方法名稱、參數類型獲取方法
  2. 如果你想訪問的方法沒有參數,傳遞 null作為參數類型數組,或者不傳值)

基本使用:

//在fruit中定義一個這樣的方法
 private  void eat(String describe){
        System.out.println("通過getMethod()方法調用了eat()方法:  "+describe);
    }

調用這個方法:

        Class fruitClass = Fruit.class;
        Method method = fruitClass.getDeclaredMethod("eat",String.class);
        method.setAccessible(true);
        method.invoke(fruitClass.newInstance(),"我是該方法的參數值");

輸出結果:

  通過getMethod()方法調用了eat()方法:我是該方法的參數值

分析getDeclaredMethod()涉及的源碼:

public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        // 1.檢查方法的修飾符
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        // 2.searchMethods()方法的第一個參數確定這個方法是不是私有方法,第二個參數我們定義的方法名,第三個參數就是傳入的方法的參數類型
        Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }

// 這個方法就是通過傳入的方法名找到我們定義的方法,然後使用了Method的copy()方法返回一個Method的實例,我們通過操作mehtod這個實例就可以操作我們定義的方法。
 private static Method searchMethods(Method[] methods,
                                        String name,
                                        Class<?>[] parameterTypes)
    {
        Method res = null;
        String internedName = name.intern();
        for (int i = 0; i < methods.length; i++) {
            Method m = methods[i];
            if (m.getName() == internedName
                && arrayContentsEq(parameterTypes, m.getParameterTypes())
                && (res == null
                    || res.getReturnType().isAssignableFrom(m.getReturnType())))
                res = m;
        }

        return (res == null ? res : getReflectionFactory().copyMethod(res));
    }

 public Method copyMethod(Method var1) {
        return langReflectAccess().copyMethod(var1);
    }

 
// 檢查屬性的訪問權限
  private static LangReflectAccess langReflectAccess() {
        if (langReflectAccess == null) {
            Modifier.isPublic(1);
        }

        return langReflectAccess;
    }

2.2.4 判斷類的類型的方法

這類型的方法顧名思義,就是來判斷這個類是什麼類型,是接口,註解,枚舉,還是一般的類等等。部分方法如下錶

boolean isAnnotation()判斷是不是註解
boolean isArray() 判斷是否為數組
boolean isEnum()判斷是否為枚舉類型
boolean isInterface() 是否為接口類型
boolean isMemberClass()當且僅當基礎類是成員類時,返回“true”
boolean isPrimitive()確定指定的“類”對象是否表示原始類型。
boolean isSynthetic()如果這個類是合成類,則返回’ true ‘;否則返回“false”。

基本用法:

// 定義一個接口:
interface  Animal{
    public void run();
}

判斷是不是一個接口:

Class AnimalClass = Animal.class;
 boolean flag = AnimalClass.isInterface();
 System.out.println(flag);

輸出結果:

true

源碼分析isInterface():

 public native boolean isInterface();

這是一個native方法,大家都知道native方法是非Java語言實現的代碼,供Java程序調用的,因為Java程序是運行在JVM虛擬機上面的,要想訪問到比較底層的與操作系統相關的就沒辦法了,只能由靠近操作系統的語言來實現。

2.2.5 toString()方法

將對象轉換為字符串。字符串表示形式是字符串“類”或“接口”,後跟一個空格,然後是該類的全限定名。

基本使用:

// 這是前面定義的兩個類Fruit和Car,Car是一個接口
 Class fruitClass = Fruit.class;
 Class AnimalClass = Animal.class;
 System.out.println(AnimalClass.toString());
 System.out.println(fruitClass.toString());

輸出結果:

// 格式  字符串“類”或“接口”,後跟一個空格,然後是該類的全限定名
interface cn.chen.test.util.lang.Animal
class cn.chen.test.util.lang.Fruit

源碼如下:

 public String toString() {
       // 先是判斷是接口或者類,然後調用getName輸出類的全限定名
        return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
            + getName();
    }

  public native boolean isInterface();
  public native boolean isPrimitive();

追本溯源,方能闊步前行。

參考資料

​ https://blog.csdn.net/x_panda/article/details/17120479

​ https://juejin.im/post/5d4450fbe51d4561ce5a1be1

​ JavaSE的官方文檔

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

【其他文章推薦】

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

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

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

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

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