Mariadb之顯式使用表鎖和行級鎖

  首先我們來看看mariadb的鎖定概念,所謂鎖就是當一個進程或事務在操作某一資源時,為了防止其他用戶或者進程或事務對其進行資源操作,導致資源搶佔而發生衝突,通常在A進程操作該資源時,會對該資源進行加鎖,實現多進程或多用戶操作同一資源時,不會發生衝突;通常情況鎖的類型分讀鎖和寫鎖,所謂讀鎖就是共享鎖,它可以實現多個讀操作共享;而寫鎖就是排它鎖,獨佔鎖,一旦加了寫鎖,其他用戶的讀寫操作將被阻塞,直到該寫鎖被釋放或者因超時而被釋放,在其他用戶進行的讀寫操作,此時就會被執行;對於鎖定範圍來講,它又可以分為,表鎖和行鎖,從字面意思就可以理解到,表鎖就是針對整張表所施加的鎖,而這種鎖定力度相當粗糙,併發相對就比較低,但是維持鎖狀態鎖消耗的成本資源就較小;對於行鎖來說,它針對的範圍就是行級別所施加的鎖,這種鎖的粒度就相對要精細,同時併發相對較高,但是維護鎖狀態消耗的成本資源就相對要大;對於mysql來講又鎖分為存儲引擎級別的鎖和mysql server級別的鎖,存儲引擎級別的鎖指的是對於何時施加鎖或者釋放鎖由存儲引擎自行決定;mysql server級別鎖指的是用戶使用命令可自行決定施加鎖或釋放鎖;簡單講就是允許用戶顯式請求加鎖或釋放鎖;顯式鎖就是用戶手動用命令施加的鎖,隱式鎖指的是由存儲引擎根據需要自行施加的鎖;對於innodb存儲引擎來講,它支持事務,行級鎖;而早期的MyISAM存儲引擎它不支持事務,對鎖的粒度是表級鎖,不支持行級鎖;

  显示鎖的使用

  1)LOCK TABLES

    指令使用語法:

    LOCK TABLES  tbl_name  read|write, tbl_name read|write, …

  示例:

  提示:以上語句表示對test_tb這張表施加讀鎖操作,這意味着其他用戶或進程都不能對該表進行寫的操作,只能讀,因為讀鎖是共享鎖;

  測試:對test_tb表進行寫操作,看看是否能夠寫進去?

  提示:從上面的提示,它告訴我們test_tb這張表施加了讀鎖,不允許更新;這說明施加讀鎖,對於寫的操作就不能進行;

  測試:對test_tb表進行讀操作,看看是否能夠進行呢?

  提示:可以看到加了讀鎖的表,對於讀操作上可以繼續進行的;

  測試:對test_tb表施加寫鎖

  提示:釋放鎖用unlock tables即可釋放剛才的讀鎖;

  測試:對test_tb進行寫操作,看看是否能夠進行?

  提示:在當前終端(加寫鎖所在終端)上是可以進行讀寫操作的;

  測試:在其他終端看看是否能夠對test_tb表進行讀寫操作呢?

  提示:從上面的截圖可以看到當我們重新啟動一個終端對test_tb進行寫操作,它一直處於阻塞狀態;

  提示:對於對操作也是同樣的效果;一直阻塞着;

  總結:對於施加讀鎖的表,是可以進行讀操作的,但是不能進行寫操作,包括當前終端也不能寫操作;對於施加寫鎖的表,在當前施加鎖的終端上是可以對其進行讀寫操作的,但是在別的終端讀寫操作都將阻塞;

  除了以上指令來對錶進行加鎖外,還可以使用 flush tables指令來加讀鎖,具體語法請看下面;

  FLUSH TABLES tbl_name,… [WITH READ LOCK];

  測試:加讀鎖

  提示:以上flush tables 只能加讀鎖,不能加寫鎖;

  行級鎖:SELECT cluase [FOR UPDATE | LOCK IN SHARE MODE]

  行級排它鎖

  提示:以上紅框中的內容就是給第一行加了一個排它鎖,這意味着該事務沒有提交,其他事務就不能再獲取該行的其他鎖,包括共享鎖和排它鎖,但是獲取排它鎖的事務是可以對數據就行讀取和修改。

  提示:可以看到我們重新啟動一個事務,然後對第一行進行更新操作,語句就阻塞在哪裡了;說明行級排它鎖對其他事務來講是不允許對加鎖的行進行寫操作;默認情況updeate更新會默認加上排它鎖,因為對於第一行來講,已經有一個排它鎖了,所以其他事務就不能對其在加其他鎖;而對於select語句來講,它執行時默認會加任何鎖的,所以我們執行select語句是可以正常的查看第一行數據;如果我們在select後面手動加鎖,它也會阻塞的;如下

  提示:從上面的截圖信息可以看到,我們手動加上排它鎖,查詢語句也不會順利執行;從上面信息還可以了解到,我們對第二行也沒法進行操作,這又是為什麼呢?

  提示:我們查看test_tb這張表上的索引信息,發現沒有索引,然後我們在上面創建了一個索引;創建索引時,需要把前面的事務提交了,才可創建成功,否則一直鎖在哪裡的;接下來我們在創建一個事務,把第一行加上排它鎖,然後在對第二行操作看看是否還會一直阻塞呢?

  提示:可以看到當我們創建就了索引后,再對第一行加鎖,然後更新第二行就可以正常更新了 ,對第一行還是處於阻塞狀態;這說明innodb存儲引擎的行級鎖的實現其實是依靠其對應的索引,所以如果操作的行並沒有用到索引,那麼用的還是表級鎖。施加行級排它鎖后,其他事務將不能對其在施加任何鎖;那麼對於獲取到排它鎖的是否能夠正常操作呢?

  提示:對於獲取到排它鎖的事務,是可以正常更新修改的;也可以給對應行施加其他鎖;

  行級共享鎖

  提示:以上紅框中的內容表示給第一行施加共享鎖,這意味着在其他事務鎖可以共享這把鎖看到數據,但是不能更新修改數據;

  測試:在當前事務中更新數據,看看是否可更新?

  提示:在當前事務中是可以正常修改數據的;也能正常查看數據;

  在其他事務中修改數據,看看是否可修改?

  提示:可以看到在其他事務中,就不能對有共享鎖的行進行修改操作,但是可以正常讀;

  總結:innodb存儲引擎的行級鎖依賴索引,如果沒有索引,就相當於表級鎖;對於排它鎖來講,獲取到排它鎖的事務是可以正常修改更新以及加共享鎖,對於沒有獲取到排它鎖的事務,是不能夠對有鎖的行進行修改更新以及加鎖的操作;對於共享鎖來講,對於當前事務(加鎖操作的事務)是可以正常修改更新有鎖的行,對於其他事務,是不可修改和更新有鎖的行;

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

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

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

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

※超省錢租車方案

LG Chem 開發新技術 電動車充電後可行駛 500 公里

電動車續航力有望加倍,南韓電池巨擘 LG Chem 宣布開發出新技術,電動車充電一次可行駛 400 至 500 公里,預計不久後就能量產。   韓國先驅報(Korea Herald)1 日報導,目前一般電動車充電後僅能行駛不到 200 公里。LG Chem 副會長兼執行長朴鎮洙表示,已研發出新技術,電動車行駛里程能增至 400至 500 公里。他說,產品不久就能投產,但拒絕透露更多細節。   LG Chem 砸錢投入開發,宣稱 2018 年研發費用將由當前的 6,000 億韓圜,提高到 9,000 億韓圜;未來 3 年研發人員也將增至 4,100 人。  

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

【其他文章推薦】

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

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

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

福特攜兩款新智慧電動車 亮相MWC2015

在巴賽隆納舉辦的2015世界移動通信大會上,福特汽車公司宣佈了「智慧騎行」的試驗專案,首推智慧電動車,這也是一項通過在車載連接技術、移動出行、自動駕駛汽車、消費者體驗以及大資料方面的創新,説明改變世界出行方式的計畫。   首推的倆款智能電動車為MoDe:Pro與MoDe:Me。MoDe:Pro由福特團隊打造完成,用於商業用途。MoDe:Me在自行車製造商Dahon的協助下打造完成,便於摺疊存放。    

(圖片來源:騰訊)

    這兩款電動自行車都配備了200瓦的馬達和9安培小時的電池,提供電動踏板輔助,最高時速可達2萬5千公尺。原型電動自行車的技術可實現行車預警系統,在汽車超車時,透過車把手的震動向騎士們發出警告,還可通過開啟把手上的警示燈,提醒汽車司機注意電動自行車的存在。   MoDe:Me和MoDe:Pro可與一款名為MoDe:Link的原型應用程式配合使用,此款應用程式可與iPhone 6手機相容。通過應用程式中的即時資訊,電動自行車可實現導航、線路選擇、速度和舒適度調整等功能。

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

兩會進行時:關於新能源汽車的提案,都有什麼?

  3月3日,來自汽車行業的人大代表和政協委員再次聚首北京,為當今的汽車產業建言獻策,而新能源汽車仍將是2015年全國“兩會”汽車界熱議的焦點,政協委員和人大代表等也紛紛為新能源汽車的發展建言獻策。  
柳崇禧:加大基礎設施補貼力度 推動新能源車發展   全國政協委員、中國汽車工業工程公司副總經理柳崇禧帶來了《關於加大充換電基礎設施建設,推動新能源汽車市場應用的提案》。   針對電動汽車的發展受動力電池技術制約,充電設施網路不完善、充電不方便等問題,柳崇禧委員建議,加大充換電基礎設施建設,包括支援國家電網、南方電網等傳統電力供應央企加大充換電基礎設施建設,鼓勵中石化、中石油等傳統汽車能源供應央企參與充換電基礎設施建設。同時,還應鼓勵民營資本進入基礎設施建設,制定法規保障其權益,加大充換電基礎設施建設財政補貼力度,減免稅收。  
李書福:電動汽車不一定環保 呼籲放開汽車限購   全國政協委員、吉利集團董事長李書福共帶來”關於推動計程車體制改革”和”加強資訊化技術下經營交易稅收管理”兩個提案,就推進和深化計程車體制改革提出建議:關鍵要打破計程車牌照的壟斷式管理;建議根據不同城市規模,設立相應計程車體制改革試點城市,對各項改革措施,進行分類指導、科學評估。   另外李書福在接受採訪時呼籲放開汽車限購。他認為,目前某些地方實行的限購政策只是權宜之計。李書福表示,吉利今年會發力新能源汽車。但他認為電動汽車也不一定有利於環境,因為發電要耗費大量的非清潔能源。  
景柱:五大因素致使新能源汽車推廣艱難   全國人大代表、海馬集團董事長景柱認為,電動汽車商業化推廣存在五大癥結:一是以電池為靈魂的四大技術障礙,即功率密度、轉化效率、充電時間、系統安全。二是難以克服的兩大社會障礙:使用習慣、使用環境。三是非規模生產造成的高成本、高故障。四是以高成本為前提的兩大市場因素:特定環境和特定用戶。五是政策導向的四個誤區。   景柱認為,目前我國發展新能源汽車的最佳路徑,是汽車輕量化、節能減排和新能源技術齊頭並進。首先經過深混和插混技術,最終過渡到電動汽車或燃料電池汽車。  
鐘發平:建議國家大力發展混合動力汽車減少汽車污染   全國人大代表、科力遠董事長鐘發平建議國家通過大力發展混合動力汽車來減少汽車尾氣污染,解決當下的霧霾問題。鐘發平認為,相較動力鋰電池,鎳氫動力電池雖然能量密度不如,卻足夠穩定和安全。  
劉義發:小型電動車應成為國民車 實行分類管理   全國人大代表、山東時風集團董事長劉義發建議,綜合小型電動車的種種優勢,順應市場趨勢,將小型電動車作為“國民車”,實行分類管理,引導和規範行業科學發展。  
張天任:建議對微型電動車適當放開政策   全國人大代表、天能集團董事長張天任:建議國家對微型電動車適當放開政策,實行“產品准入從嚴,企業准入從寬”的管理原則,鼓勵和支援多種電池路線、不同電池搭配的新能源電動汽車相容發展,並針對微型電動車建立起科學的技術標準體系。他建議國家將微型電動車納入我國道路交通規劃之中,列入法定交通工具,允許符合標準的微型電動車上牌上路。  
王麒:推廣新能源汽車是治霾重要途徑之一   全國人大代表、啟陽(成都)投資管理有限公司董事長王麒認為推廣新能源汽車是汽車行業對治理霧霾做出貢獻的重要途徑之一,新能源汽車的使用推廣環節應當與生產研發的進度相匹配,要加快新能源汽車的推廣力度,完善新能源汽車相關配套設施建設。  
民進中央:可在有條件的省份率先開展低速電動車試點   民進中央提案呼籲,工信部、發改委和公安部應儘快出臺行業准入辦法,對具有汽車生產資質、產品通過檢測的低速電動車企業實行准入管理。建議將低速電動汽車和燃油汽車、新能源汽車進行分類管理。針對低速電動汽車制定一套嚴格的管理辦法,包括車輛生產、檢測、保險、上路管理等多個環節。在道路資源和安全可控範圍內,將低速電動車納入現行的道路交通管理體系,除高速公路和限行道路外,所有鄉村公路和城鎮的部分道路應向低速電動汽車開放。車輛可採用專用明顯標誌的車牌便於識別,駕照可考慮使用C4/C5駕照。   同時,民進中央提案建議,在有條件的省份率先開展低速電動車試點工作,探索一種以市場需求為拉動,政府不投資和補貼,技術路線由低到高、先易後難、自主研發,在發展中規範、在規範中提高的發展模式。  
全國工商聯:建議儘快廢除計程車專營制度   全國工商聯向大會遞交提案,建議儘快廢除計程車專營制度,讓計程車行業回歸市場經濟的軌道。政府應明確職責定位,徹底斬斷與行業之間的利益關聯,從行業中完全退場。此外,計程車市場應對社會開放,破除“數量管制”、建立“品質管制”體系。  
丁金宏:國家應出臺統一的城市限車標準   全國政協委員、人口學家丁金宏教授此次帶來了“限制大城市汽車消費”的提案,建議國家出臺統一標準,“城市規模達到多大,汽車擁有量達到多高時,就採取不同的措施,包括限購、重點區域限行、擁擠收費等,綜合採取多項措施,而不是多個城市單獨去做。”他說,限車如果僅僅從消費者權利和汽車發展利益出發,不能保證城市健康發展。  
施傑:應嚴管佔用應急車道 加大處罰   全國政協委員、國浩律師(成都)事務所律師施傑在提案中建議,對違反交規的駕駛行為,除了加大處罰力度以外,還應搭建一個公共監督平臺,讓公眾在履行自己監督義務時有暢道的管道。   施傑認為,對佔用應急車道行為除了加大罰款、扣分力度以外,同時還可實行“責任形式多元化”,用法律的“籠子”把佔用高速應急車道的行為給牢牢關住。 他認為,相關交通管理部門可以通過公眾微信帳號、微博平臺,收集公眾提供的照片、錄影等證據,一經核實便納入交管系統。對違規車輛進行處罰。同時對舉報者給予一定獎勵,當然,若發生虛報謊報情形也會加以懲處。讓公眾成為“移動電子眼”,以便做到無縫監督。  
宗慶後:建議儘快取消汽車限購限行   全國人大代表、娃哈哈集團董事長宗慶後:建議取消限購限行,加強對尾氣排放的檢測監管和交通基礎設施改善,從而減輕汽車帶來的環境壓力和交通壓力。   在宗慶後看來,現在限行限購都存在不合理的地方。“有些地方想用限購限行解決堵車問題、空氣污染問題,但限了購也限了行,路依舊在堵,空氣依舊污染,只是給老百姓造成了一些困惑而已。”他指出,目前汽車尾氣排放存在三個問題,一個是汽油的品質問題;二是噴油嘴的問題,噴油嘴不好不能噴得很均勻,不能充分燃燒,尾氣就大了;三是尾氣過濾問題。  
蔡繼明:計程車行業可試點放開平等競爭   全國人大代表、清華大學政治經濟學研究中心主任蔡繼明指出,專車和計程車一樣是平等競爭,而打破計程車行業壟斷的改革,需要改變現行法規,至少應試點在某個區域、一定時間內暫時停止執行相關法律檔。  
王鳳英:應加強交通綜合治理法制建設   全國人大代表、長城汽車總裁王鳳英提出要加強城市交通綜合治理法制建設。她建議站在國家法律、制度層面進行具有前瞻性的頂層設計,建立健全具有中國特色的、符合國情的相關法律和配套制度體系,保障各項交通治理政策措施的推出有法可依、有法必依;並在符合法律框架的前提下,積極採用最新的資訊技術、智慧交通等科技手段,對城市發展規劃、道路路網建設、市政交通管理等進行系統性創新管理,從而全面、高效地從根本上解決城市交通擁堵難題。     文章來源:第一電動網

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

【其他文章推薦】

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

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

法國將提高純電動車補貼 增至一萬歐元

據京華時報報導,法國政府正在醞釀進一步提高電動汽車的購買補貼,計畫將純電動汽車的補貼從6300歐元增加到1萬歐元,插電式混合動力車的購買補貼從4000歐元增加到6500歐元。   如果消費者家中有開了13年或者時間更長的柴油車,還可以舊換新,在所有補貼的基礎上再額外獲得3700歐元或者2500歐元的獎勵。而依然購買新柴油車(歐6車輛排放低於110克/CO)的消費者,只能獲得500歐元的獎勵。目前相關補貼方案正在制定中,並有望於今年4月1日起實行。

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

【其他文章推薦】

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

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

深入理解JVM(③)虛擬機的類加載過程

前言

上一篇我們介紹到一個類的生命周期大概分7個階段:加載、驗證、準備、解析、初始化、使用、卸載。並且也介紹了類的加載時機,下面我們將介紹一下虛擬機中類的加載的全過程。主要是類生命周期的,加載、驗證、準備、解析和初始化這五個階段所執行的具體動作。

加載

類加載過程的第一個階段就是加載,在加載階段,Java虛擬機需要完成以下三件事情:

1. 通過一個類的全限定名來獲取定義此類的二進制字節流。
2. 將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。
3. 在內存中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口。
《Java虛擬機規範》對這三點要求其實並不是特別具體,這樣留給虛擬機實現和Java應用的靈活度都是相當大的。僅第一條,獲取二進制字節流,並沒有有指出從哪裡獲取,如何獲取。這樣就已經能被我們的Java開發人員玩出各種花樣了。
例如:

  • 從ZIP包中讀取(JAR、EAR、WAR)。
  • 從網絡中獲取(Web Applet)。
  • 運行時計算生成,最典型的就是動態代理技術,在java.lang.reflect.Proxy中,就是用了ProxyGenerator.generateProxyClass()來為特定接口生成形式為“$Proxy”的代理類的二進制字節流。
  • 由其他文件生成(JSP)。
  • 從數據庫中讀取。
  • 從加密文件中獲取(防止被反編譯獲取源碼)。
  • …….. …..

相對於類加載的其他階段,非數組類型的加載階段是開發人員可控性最強的階段。加載階段即可以使用Java虛擬機里內置的引導類加載器完成,也可以由用戶自定義的類加載器去完成。

驗證

驗證這一階段的目的是確保Class文件的字節流中包含的信息符合《Java虛擬機規則》的全部約束要求,保證這些信息被當作代碼運行后不會危害虛擬機自身安全。
驗證階段大致上會完成下面四個階段的檢驗動作:
文件格式驗證、元數據驗證、字節碼驗證和複合引用驗證

文件格式驗證

這是驗證的第一個階段,主要是驗證字節流是否符合Class文件格式的規範,並且能被當前版本的虛擬機處理。
這一階段的驗證點有:

  • 是否以魔數0xCAFEBABE開頭。
  • 主、次版本號是否在當前Java虛擬機接受範圍之內。
  • 常量池的常量中是否有不被支持的常量類型。
  • 指向常量的各種索引值是否有指向不錯在的常量或不符合類的常量。

這個階段的驗證是基於二進制字節流進行的,只有通過了這個階段的驗證之後,這段字節流才被允許進入Java虛擬機內存的方法區中進行存儲,後面的階段都是基於方法區的存儲結構進行的,不會再直接讀取、操作字節流了。

元數據驗證

第二階段是對字節碼描述的信息進行語義分析,以保證其描述的信息符合《Java虛擬機規則》的要求,這個階段主要有以下一些驗證點:

  • 當前類是否有父類(除java.lang.Object外,所有類都應當有父類)。
  • 當前類的父類是否繼承了不允許被繼承的類(被final修飾的類)。
  • 如果當前類非抽象類,是否實現了父類或接口要求實現的所有方法。
  • 類中的字段、方法是否與父類產生矛盾。
字節碼驗證

第三階段是整個驗證過程最複雜的一個階段,主要目的是通過數據流分析和孔劉分析,確定程序語義是合法的、符合邏輯的。
為了保證被校驗的方法在運行時不會做出危害虛擬機的安全的行為,主要做了如下一些校驗:

  • 保證任意時刻操作棧的數據類型與指令代碼序列都能配合工作,例如不會出現類似於“在操作放置了一個int類型數據,使用時卻按long類型來加載如本地變量表中”這樣的情況。
  • 保證任何跳轉指令都不會跳轉到方法體以外的字節碼指令上。
  • 保證方法體中的類型轉換總是有效的。例如:一個子類對象賦值給父類數據類型,這是安全的,但是把父類對象賦值為子類數據類型,獲取賦值給另外一個毫無關係的數據類型,則是不合法的。
  • … …

如果一個類型中有方法體的字節碼沒有通過字節碼驗證,那它肯定是有問題的;但如果一個方法體通過了字節碼驗證,也仍然不能保證它一定就是安全的。因為字節碼驗證也是在程序中進行的,即不能通過程序準確地檢查出程序是否能在有限時間之內結束運行。

符號引用驗證

最後一個階段的校驗行為發生在虛擬機將符號引用轉化為直接引用的時候,這個轉化動作將在連接的第三個階段——解析階段發生。
本階段通常需要校驗下列內容:

  • 符號引用中通過字符串描述的全限定名是否能找到對應的類。
  • 在指定類中是否存在符合方法的字段描述符及簡單名稱所描述的方法和字段。
  • 符合引用中的類、字段、方法的可訪問性,是否可被當前類訪問。

驗證階段對於虛擬機的類加載機制來說,是一個非常重要的、但卻不是必須要執行的階段,因為如果程序運行的全部代碼都已經被反覆使用和驗證過,在生成環境的實施階段就可以考慮使用-Xverify:none參數來關閉大部分的類驗證措施,來縮短類加載的時間。

準備

準備階段是正式為類中定義的變量分配內存並設置類變量的初始值的階段,這些變量所使用的內存都應當在方法區中進行分配,需要注意的是,這裏所說的方法區只是一個概念上的區域,在JDK7以及之前HotpSpot用永久代實現方法區,這個概念是正確的,但是在JDK8以及之後,類變量會隨着Class對象一起存放在Java堆中,這個時候類變量存在於方法區就僅僅是一個概念了。
在準備階段有兩點需要着重強調
1、在準備階段進行內存分配的僅包括類變量,而不包括實例變量,實例變量將會在對象實例化時隨着對象一起分配在Java堆中。
2、這裏所說的為類變量設置初始值,“通常情況”下是數據類的零值。
例如一個類變量定義為:

public static int value = 666;

那變量在準備階段過後的初始值為0而不為666,因為這個時候還未開始執行任何Java方法,而把value賦值為666的putstatic指令是程序被編譯后,存放於類構造器 ()方法之中,所以把value賦值為666的動作要到類的初始化階段才會被執行。

但是如果類字段的屬性表中存在ConstantValue屬性,那在準備階段變量值就會被初始化為ConstantValue屬性所指定的初始值,例如:

public static final int value = 666;

在編譯時Javac將會為value生成ConstantValue屬性,在準備階段虛擬機就會根據ConstantValue的設置將value賦值為666。

解析

解析階段是Java虛擬機將常量池內的符號引用替換為直接引用的過程,符號引用在Class文件中它以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等類型的常量出現。
先來解釋一下什麼是符號引用和直接引用。

  • 符號引用:符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。
  • 直接引用:直接引用是可以直接指向目標的指針、相對偏移量或者是一個能間接定位到目標的句柄。

解析動作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調用點限定這7類符號引用進行,分別對應於常量池的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info、CONSTANT_MehodType_info、CONSTANT_MethodHandle_info、CONSTANT_Dynamic_info和CONSTANT_InvokeDynamic_info
這8種常量類型。

初始化

初始化階段是類加載過程的最後一個步驟,之前介紹的幾個類加載的動作里,出了在加載階段用戶應用程序可以通過自定義類加載器的方式局部參与外,其餘動作都完全由Java虛擬機來主導控制。
簡單的來說,初始化階段就是執行類構造器<clinit>()方法的過程。那麼<clinit>()是如何執行的呢?

  • <clinit>()方法是由編譯器自動收集類中所有變量的複製動作和靜態語句塊(stataic{}塊)中的語句合併產生的,編譯器收集的順序是由語句在源文件中出現的的順序決定的,靜態語句塊中只能訪問到定義在靜態語句塊之前的變量,定義在它之後的變量,在前面的靜態語句塊可以賦值,但是不能訪問。
    例如:
  • <clinit>()方法與類的構造函數不同,它不需要顯式地調用父類構造器,Java虛擬機會保證在子類 ()方法執行前,父類 ()方法以及執行完畢。
  • 由於父類的<clinit>()方法先執行,即父類中定義的靜態語句塊要優先於子類的變量賦值操作。
    如下代碼運行結果會是 4
    父類
public class FatherClass {

    public static int fatherObject = 3;

    static {
        fatherObject = 4;
    }
}

子類

public class SonClass extends FatherClass{

    public static int sonObject = fatherObject;

}

測試

@Test
public void testClassLoad(){
    System.out.println(SonClass.sonObject);
}

運行結果

4
  • <clinit>()方法對於類或接口來說並不是必需的。
  • 接口中不能使用靜態語句塊,但仍然有變量初始化的賦值操作,因此接口與類一樣都會生成<clinit>()方法。
  • Java虛擬機必須保證一個類的<clinit>()方法在多線程環境中被正確地加鎖同步,若同時多個線程區初始化一個類,那麼只會有其中一個線程去執行這個類的<clinit>()方法,其他線程都需要阻塞等待,直到活動線程執行完畢<clinit>()方法。

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

【其他文章推薦】

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

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

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

【秒懂Java】【第1章_初識Java】01_編程語言

各位小夥伴們好哇!從今日起,我將開始更新《秒懂Java》系列文章,從0開始講解Java的方方面面,完全零基礎也可以看懂。後面也將推出配套的視頻版,歡迎大家保持關注!

  • 我會儘力辦到:在保證通俗易懂的同時,不丟失知識的嚴謹性和完整性
  • 不管你是否有編程經驗,只要你感興趣、細心閱讀,就能學會
  • 本文是《秒懂Java》系列的第1篇文章,主要認識幾個基礎概念

什麼是計算機?

一說到計算機(Computer),大家首先想到的可能是電腦(台式電腦、手提電腦)。

實際上,我們日常生活中使用的平板電腦智能手機也屬於計算機,它們的功能也跟電腦一樣非常強大。

還有KTV的點歌機、超市的自助收銀機等很多設備也都屬於計算機。

毋庸置疑,計算機是人類歷史上最偉大的發明之一,極大地提高了社會的生產力,目前它已經延伸到了生活、學習、工作等各個領域,無處不在。如今,我們會在計算機上安裝各種各樣的軟件(應用、遊戲),學習工作、衣食住行、吃喝玩樂一網打盡,極大地改變了我們的生活、學習、工作方式。

什麼是編程語言?

語言,是雙方進行溝通交流的主要表達方式。

  • 如果我要跟咱們中國人進行交流,應該用漢語
  • 如果我要跟美國人進行交流,應該用英語
  • 如果我要跟韓國人進行交流,應該用韓語

那如果我要跟計算機進行交流呢?那就應該用計算機編程語言

  • 是的,計算機編程語言,就是用來跟計算機進行溝通交流的語言
  • 一般把計算機編程語言簡稱為:編程語言(Programming Language)

注意:我們要學習的很多IT技術都源自西方國家(比如美國),因此,很多技術名詞,都是從英文翻譯過來的。為了保證描述的嚴謹性,當首次提及某個技術名詞時,我都會在它旁邊標註原本的英文單詞。

與計算機進行交流

那如何利用編程語言與計算機進行交流呢?

編程語言,顧名思義,就是可以用來“編寫程序”的語言。

  • 首先,利用編程語言編寫一段程序。例如上圖所示的程序,只包含了3行代碼,這裏簡單說一下代碼的大概意思(了解一下即可,不用去深究)
    • 第1行:創建了一個播放器
    • 第2行:設置播放器的音量為100
    • 第3行:開始播放某個mp4視頻
  • 最後,將程序運行到計算機上,計算機就會開始識別執行程序中的每一句代碼,完成相應的功能,最後成功播放視頻

我們平時使用的各種軟件(應用、遊戲)都是通過編程語言開發出來的,它們都由一大堆的代碼組成。當打開軟件時(將軟件運行到計算機上時),計算機就會開始識別執行軟件中包含的代碼,完成相應的功能。

所以,編程語言有一個非常重要的作用,那就是:開發軟件!

主流編程語言

世界上有上百種各式各樣的編程語言,目前比較常見的主流編程語言有

  • Java、C、C++、C#、PHP、Python、Go
  • JavaScript(簡稱JS)、TypeScript(簡稱TS)
  • Objective-C(簡稱OC)、Swift、Kotlin
  • Scala、Assembly Language(彙編語言) 等

每一門編程語言的作用都不太一樣,比如

  • Java、Kotlin:可以用來開發Android系統中的軟件
  • Objective-C、Swift:可以用來開發iOS、Mac系統中的軟件
  • C、C++、C#:可以用來開發Windows系統中的軟件

當然,上面列舉的僅僅是它們的部分功能,並沒有說完整。比如:

  • C、C++也可以開發Android、iOS、Mac、Linux等系統中的軟件
  • Java也可以開發Mac、Linux、Windows等系統中的軟件

其他的就不一一說明了,了解一下即可

語法

每當我們要學習一門新的語言時,都要先學習它的語法。

  • 比如,學習英語就要先學習它的語法,英語的語法規定了:英文句子可以怎麼寫、不可以怎麼寫
  • 同樣的,每一門編程語言都有自己的語法,編程語言的語法規定了:你的代碼可以怎麼寫、不可以怎麼寫
    • 比如,Java語言的語法規定:每一句代碼後面都必須以分號(;)結束
    • 代碼一旦出現了語法錯誤,就無法成功運行到計算機上

不同編程語言的語法是不一樣的,每一門編程語言都有自己特有的語法。比如,想讓計算機播放一個視頻,不同編程語言可能會有不同的寫法。

不難看出它們之間的一些差異(了解一下即可,不用去深究)

  • Java、OC都以分號(;)結束,而Python不需要以分號(;)結束
  • Java、Python中都使用了點(.)、小括號(()),而OC中使用了中括號([])、冒號(:)、@符號
  • Java、OC中都使用雙引號(“”),而Python中可以使用單引號(

雖然它們的寫法不一樣,但是都完成了一樣的功能:讓計算機播放一個視頻。如果想利用編程語言開發出強大優秀的軟件、控制計算機做更多的事情,首先要踏踏實實學好編程語言的語法。如果你連語法都不懂,怎麼可能寫出正確的代碼呢?

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

【其他文章推薦】

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

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

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

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

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

※超省錢租車方案

Spring Bean生命周期的各階段介紹

一.xml方式配置bean

二.Aware接口

  2.1 BeanNameAware

  2.2 BeanFactoryAware

  2.3 ApplicationContextAware

  2.4 Aware各接口的執行順序

  2.4 Aware接口總結

三.BeanPostProcessor接口

四.InitializingBean接口

五.init-method方法

六.DestructionAwareBeanPostProcessor接口

七.DisposableBean接口

八.destory-method方法

九.生命周期大雜燴

  9.1 實現多接口的Student類

  9.2 BeanPostProcessor前後置處理

  9.3 DestructionAwareBeanPostPrecessor接口

  9.4 配置xml文件

  9.5 測試代碼

  9.6 輸出結果

十.總結

 

 

 

 

  Spring Bean的生命周期是一個老生常談的問題了,網上一搜一大把,無非就是畫一幅流程圖(比如下面這幅圖),然後用語言介紹創建bean后執行各Aware接口,然後BeanPostProcessor…..最終Bean創建成功了,就可以使用這個Bean了,然後在容器銷毀的時候,又會執行一些操作。

  其實對於上面的提到的流程圖,注意上面的圖只是Spring Bean的大概流程(省略了一部分),主要涉及到了5個接口,分別是XxxAware、BeanPostProcessor、InitiailizingBean、Destruction、DisposableBean接口,本文將會對這幾個接口,以及init-method、destroy-method做相關的使用介紹,在明白怎麼使用后,再把他們串起來,這樣的話,對於Spring Bean的生命周期就差不多知道咋回事了,而不用死記硬背。

 

一. xml方式配置Bean

  在說Aware、BeanPostProcessor、InitiailizingBean、Destruction、DisposableBean這些接口前,先簡單回顧一下使用xml配置並獲取一個Student類的bean過程,後面介紹各個接口的使用方式時時,也是按照這個形式;

1.1 創建Student類

  平淡無奇的Student類:

package cn.ganlixin.entity;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Data
@Slf4j
public class Student {

    private Integer id;
    private String name;
}

  

1.2 創建配置文件

  平淡無奇的applicationContext.xml配置文件,創建一個student bean,利用setter方式設置初始值:

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

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>
</beans>

  

1.3 測試

  創建一個Main類,用於測試

package cn.ganlixin;

import cn.ganlixin.entity.Student;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

@Slf4j
public class Test {

    public static void main(String[] args) {
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

        Student student = beanFactory.getBean("student", Student.class);
        log.info("測試程序獲取到的student bean:{}", student);
    }
}

  下面是運行程序的輸出,可以看到和預期相符,創建一個Student的bean,id和name默認值為99、張三;

INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

   

二.Aware接口

  Aware接口有很多實現類,本文只介紹BeanNameAware、BeanFactoryAware、ApplicationContextAware,關係如下:

  

 

2.1 BeanNameAware

  創建一個Student類,讓該類實現BeanNameAware接口,並且重寫setBeanName方法

@Data
@Slf4j
public class Student implements BeanNameAware {

    private Integer id;
    private String name;

    /**
     * 實現了BeanNameAware接口后,需重寫setBeanName方法,接收的參數就是bean的id
     *
     * @param s bean的id
     */
    @Override
    public void setBeanName(String s) {
        log.info("beanName:{}, student bean:{}", s, this);
        this.id = 100;
        log.info("將beanName:{}的id改為100", s);
    }
}

  配置文件和測試程序都不改變,運行測試程序,輸出內容如下:

INFO  [main] cn.ganlixin.entity.Student - beanName:student, student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - 將beanName:student的id改為100
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=100, name=張三)

  可以看到,實現BeanNameAware接口后,重寫setBeanName的方法中,獲取到的student bean,是已經初始化的bean(屬性都已經有值了),並且setBeanName方法中可以對當前的bean進行各種操作,包括修改bean的某些屬性,最後獲取到的bean是已經修改后的bean。

  這裏只是簡單介紹了一下BeanNameAware接口的用法,使用BeanNameAware接口,可以對當前Bean進行操作

 

2.2 BeanFactoryAware

  創建Student類,實現BeanFactoryAware接口,並且重寫setBeanFactory方法

@Data
@Slf4j
public class Student implements BeanFactoryAware {

    private Integer id;
    private String name;

    /**
     * 實現BeanFactoryAware接口后,需重寫setBeanFactroy方法
     *
     * @param beanFactory 創建該bean的beanFactory
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        // 可以在setBeanFactory方法中獲取、修改beanFactory中的所有bean
        
        log.info("student this bean:{}", this);
        Student student = beanFactory.getBean("student", Student.class);
        log.info("通過beanFactory獲取student bean:{}", student);

        // 將name設置為李四
        this.name = "李四";
    }
}

  運行輸出如下:

INFO  [main] cn.ganlixin.entity.Student - student this bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - 通過beanFactory獲取student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=李四)

  通過上面的代碼輸出結果可以看出,實現BeanFactoryAware接口后,可以在setBeanFactory方法中操作BeanFactory的所有bean,操作的範圍要比BeanNameAware要大。

 

2.3 ApplicationContextAware

  ApplicationContext,有多種稱呼,比如“應用容器”、“環境”、“上線文”…

  創建Student類,實現ApplicationContextAware接口,並且重寫setApplicationContext接口:

@Data
@Slf4j
public class Student implements ApplicationContextAware {

    private Integer id;
    private String name;

    /**
     * 實現ApplicationContextAware接口后,徐重寫setApplicationContext方法
     *
     * @param applicationContext 該bean所在的上下文(applicationContext、容器)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("Student this:{}", this);

        final Student student = applicationContext.getBean("student", Student.class);
        final Environment environment = applicationContext.getEnvironment();
        log.info("student bean:{}", student);
        log.info("env -> user.dir:{}", environment.getProperty("user.dir"));
    }
}

  需要修改一下測試程序,測試程序中加載配置時使用的XmlBeanFactory,而XmlBeanFactory不會回調ApplicationContextAware接口的setApplicationContext方法,下面使用ClassPathXmlApplicationContext類來加載配置:

@Slf4j
public class Test {

    public static void main(String[] args) {
        //BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);
        log.info("測試程序獲取到的student bean:{}", student);
    }
}

  運行測試程序:

INFO  [main] cn.ganlixin.entity.Student - Student this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - env -> user.dir:/Users/ganlixin/code/java-code-all/spring
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  實現ApplicationContextAware接口后,在setApplicationContext方法中,入參是當前的applicationContext,也就是說,可以在該方法中對Spring容器進行設置,操作的範圍又要比BeanFactoryAware的setBeanFactory要廣得多。

 

2.4 Aware各接口執行的先後順序

  既然有這幾個Aware接口,如果一個類同時實現了這3個接口,那麼執行順序是怎樣的呢?下面就來測試一下。

  創建Student類,分別實現BeanNameAware、BeanFactoryAware、ApplicationContextAware接口,並重寫其接口的方法:

@Data
@Slf4j
public class Student implements BeanNameAware, BeanFactoryAware, ApplicationContextAware {

    private Integer id;
    private String name;

    /**
     * 實現了BeanNameAware接口后,需重寫setBeanName方法,接收的參數就是bean的id
     *
     * @param s bean的id
     */
    @Override
    public void setBeanName(String s) {
        log.info("call BeanNameAware.setBeanName()");
    }

    /**
     * 實現BeanFactoryAware接口后,需重寫setBeanFactroy
     *
     * @param beanFactory 創建該bean的bean工廠
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("call BeanFactoryAware.setBeanFactory()");
    }

    /**
     * 實現ApplicationContextAware接口后,徐重寫setApplicationContext方法
     *
     * @param applicationContext 該bean所在的上下文(applicationContext、容器)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("call ApplicationContextAware.setApplicationContext()");
    }
}

  仍舊使用ClassPathXmlApplicationContext類來加載配置,運行輸出結果如下:

INFO  [main] cn.ganlixin.entity.Student - call BeanNameAware.setBeanName()
INFO  [main] cn.ganlixin.entity.Student - call BeanFactoryAware.setBeanFactory()
INFO  [main] cn.ganlixin.entity.Student - call ApplicationContextAware.setApplicationContext()
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  

2.4 Aware接口總結

  上面演示了Spring中幾個Aware接口的用法和特點,下面總結一下:

  1.實現BeanNameAware接口后,重寫setBeanName方法,可以對單個Bean進行擴展修改;

  2.實現BeanFactoryAware接口后,重寫setBeanFactory方法,可以對bean工廠中的所有Bean進行擴展修改;

  3.實現ApplicationContextAware接口后,重寫setApplicationContext方法后,可以對整個容器進行擴展修改;

  4.這幾個接口的執行順序分別是BeanNameAware->BeanFactoryAware->ApplicationContextAware;

 

三.BeanPostProcessor接口

  BeanPostProcessor和前面的Aware接口有些區別,通過下面的例子就能看出區別在哪裡!

  下面舉個例子,創建MyBeanPostProcessor類,實現BeanPostProcessor接口,注意,這裏沒有在Student類上實現BeanPostProcessor接口。

@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {

    /**
     * 實現了BeanPostProcessor接口后,重寫postProcessBeforeInitialization,在各種Aware接口執行完畢后執行該方法
     *
     * @param bean     本次處理的bean
     * @param beanName 本次處理的beanName(bean id)
     * @return 返回的是在本方法中處理后的bean
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessBeforeInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }

    /**
     * 實現了BeanPostProcessor接口后,重寫postProcessBeforeInitialization,在initMethod方法執行完畢后執行該方法
     *
     * @param bean     本次處理的bean
     * @param beanName 本次處理的beanName(bean id)
     * @return 返回的是在本方法中處理后的bean
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessAfterInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }
}

  創建兩個類,分別是Student和User類,其中Use類沒有實現Aware接口,Student類實現了前面提到的3個Aware接口

@Data
public class User {
    private Integer id;
    private String name;
}

  

@Data
@Slf4j
public class Student implements BeanNameAware, BeanFactoryAware, ApplicationContextAware {

    private Integer id;
    private String name;

    /**
     * 實現了BeanNameAware接口后,需重寫setBeanName方法,接收的參數就是bean的id
     *
     * @param s bean的id
     */
    @Override
    public void setBeanName(String s) {
        log.info("call BeanNameAware.setBeanName()");
    }

    /**
     * 實現BeanFactoryAware接口后,需重寫setBeanFactroy
     *
     * @param beanFactory 創建該bean的bean工廠
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("call BeanFactoryAware.setBeanFactory()");
    }

    /**
     * 實現ApplicationContextAware接口后,徐重寫setApplicationContext方法
     *
     * @param applicationContext 該bean所在的上下文(applicationContext、容器)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("call ApplicationContextAware.setApplicationContext()");
    }
}

  

  xml配置文件:

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

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>

    <bean class="cn.ganlixin.entity.User" id="user">
        <property name="id" value="88"/>
        <property name="name" value="王五"/>
    </bean>

    <!-- 將實現了BeanPostProcessor接口的類也聲明為bean -->
    <bean class="cn.ganlixin.processor.MyBeanPostProcessor"/>
</beans>

  

  測試:

INFO  [main] cn.ganlixin.entity.Student - call BeanNameAware.setBeanName()
INFO  [main] cn.ganlixin.entity.Student - call BeanFactoryAware.setBeanFactory()
INFO  [main] cn.ganlixin.entity.Student - call ApplicationContextAware.setApplicationContext()
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessBeforeInitialization, beanName:student1, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessAfterInitialization, beanName:student1, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessBeforeInitialization, beanName:user, bean:User(id=88, name=王五)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessAfterInitialization, beanName:user, bean:User(id=88, name=王五)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  從上面的運行結果可以得出以下結論:

  1.因為只有Student實現了Aware接口,所以創建student bean的時候會調用對應的Aware接口方法,而User類沒有實現Aware接口,所以並沒有調用Aware接口方法;

  2.Student和User類都沒有繼承BeanPostProcessor接口,但是在創建student和user bean的時候,都掉用了MyBeanPostProcessor類中的前置和後置處理(繼承自BeanPostProcessor接口);

  3.BeanPostProcessor接口的前置和後置處理,是在Aware接口之後調用;

  4.很重要的一點,需要將BeanPostProcessor接口實現類聲明為bean,使用<bean>配置或者使用@Component註解,不然BeanPostProcessor不起作用。

 

四.InitializingBean接口

  創建Student類,實現InitializingBean接口,然後重寫afterPropertiesSet方法:

@Data
@Slf4j
public class Student implements InitializingBean {

    private Integer id;
    private String name;

    @Override
    public void afterPropertiesSet() throws Exception {
        // 同樣可以在這裏修改bean的屬性值
        log.info("InitialingBean.afterPropertiesSet, this:{}", this);
    }
}

  修改xml配置文件,創建student bean,測試:

INFO  [main] cn.ganlixin.entity.Student - InitialingBean.afterPropertiesSet, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  

五.init-method

  創建Student類,增加一個額外的方法display()

@Data
@Slf4j
public class Student {

    private Integer id;
    private String name;

    public void display() {
        log.info("Student.display call, this:{}", this);
    }
}

  修改配置文件,在<bean>標籤中增加init-method屬性,值為display,也就是Student的display方法名:

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

    <bean class="cn.ganlixin.entity.Student" id="student" init-method="display">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>
</beans>

  運行測試:

INFO  [main] cn.ganlixin.entity.Student - Student.display call, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  上面,輸出了display中的內容,這是在設置bean的時候調用的。

 

六.DestructionAwareBeanPostProcessor接口

  DestructionAwareBeanPostProcessor接口,從名稱上可以看出來是DestructionAware + BeanPostProcessor的組合,其實也的確是這樣,但是需要注意的就是,spring並沒有提供DestructionAware接口!!

  下面是DestructionAwareBeanPostProcessor接口的定義:

public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {

    /**
     * Destruction執行的操作
     *
     * @param bean     處理的bean
     * @param beanName bean的名稱
     * @throws BeansException
     */
    void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;

    /**
     * 是否需要執行postProcessBeforeDestruction方法
     *
     * @param bean 執行Destruction的bean
     * @return 是否需要執行postProcessBeforeDestruction方法
     */
    default boolean requiresDestruction(Object bean) {
        return true;
    }
}

  DestructionAwareBeanPostProceesor繼承自BeanPostProcessor接口,所以也可以重寫前值和後置處理。

  下面介紹使用示例,創建MyDestructionAwareBeanPostProceesor,繼承DestructionAwareBeanPostProceesor接口:

@Slf4j
public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor {

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        log.info("DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, \n\tbeanName:{}, bean:{}", beanName, bean);
    }
    
    @Override
    public boolean requiresDestruction(Object bean) {
        return true; // 返回true,一律執行postProcessBeforeDestruction方法
        // 如果返回false,則不執行postProcessBeforeDestruction方法
    }
}

  修改配置文件:

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

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>


    <bean class="cn.ganlixin.entity.User" id="user">
        <property name="id" value="88"/>
        <property name="name" value="王五"/>
    </bean>

    <!-- 將實現了DestructionAwareBeanPostProcessor接口的實現類聲明為bean> -->
    <bean class="cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor"/>
</beans>

  測試程序:

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);
        User user = context.getBean("user", User.class);

        log.info("測試程序獲取到的student bean:{}", student);

        // 獲取bean工廠,然後調用destroyBean銷毀bean
        AutowireCapableBeanFactory factory = context.getAutowireCapableBeanFactory();
        factory.destroyBean(student);
    }
}

  運行測試程序,輸出如下:

INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor - DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, 
	beanName:cn.ganlixin.entity.Student, bean:Student(id=99, name=張三)

  可以看到,在手動調用destroyBean方法來銷毀student bean的時候,調用了MyDestructionAwareBeanPostProcessor中定義的方法。

  需要注意的是,雖然這裏使用destroyBean來銷毀了student bean,如果又通過getBean來獲取student bean,則會重新創建student bean。

 

七.DisposableBean接口 

  前面介紹了DestructionAwareBeanPostProcessor接口,可以對所有的bean設置銷毀(destruction)后的處理操作。

  而這裏介紹的DisposableBean接口,就是對單獨的Bean進行destrction后的處理,也就是說不是應用到所有的bean上。

  簡單介紹一下用法,創建Student類和User類,User類正常(不實現任何接口),Student類實現DisposableBean接口,然後重寫destroy方法:

@Data
@Slf4j
public class Student implements DisposableBean {

    private Integer id;
    private String name;

    @Override
    public void destroy() throws Exception {
        log.info("DisposableBean.destroy, this:{}", this);
    }
}

@Data
public class User {
    private Integer id;
    private String name;
}

  創建配置文件:

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

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>

    <bean class="cn.ganlixin.entity.User" id="user">
        <property name="id" value="88"/>
        <property name="name" value="王五"/>
    </bean>
</beans>

  測試程序:

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);
        User user = context.getBean("user", User.class);

        log.info("測試程序獲取到的student bean:{}", student);
        log.info("測試程序獲取到的user bean:{}",user);

        // 獲取bean工廠,然後調用destroyBean銷毀bean
        AutowireCapableBeanFactory factory = context.getAutowireCapableBeanFactory();
        factory.destroyBean(student);
        factory.destroyBean(user);
    }
}

  運行輸出:

INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的user bean:User(id=88, name=王五)
INFO  [main] cn.ganlixin.entity.Student - DisposableBean.destroy, this:Student(id=99, name=張三)

  可以看到,雖然測試代碼中destroy了student和user兩個bean,但是只有student bean在銷毀時觸發了DisposableBean的destory方法。

 

八.destroy-method方法

  和init-method相對應的就是destory-method方法了,創建Student類,增加clean方法(自定義):

@Data
@Slf4j
public class Student {

    private Integer id;
    private String name;

    public void clean() {
        log.info("Student.clean, this:{}", this);
    }
}

  修改配置文件,<bean>標籤中使用destroy-method屬性,值為clean方法

<bean class="cn.ganlixin.entity.Student" id="student" destroy-method="clean">
    <property name="id" value="99"/>
    <property name="name" value="張三"/>
</bean>

  測試程序:

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);

        log.info("測試程序獲取到的student bean:{}", student);

        // 刪除bean
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getAutowireCapableBeanFactory();
        registry.removeBeanDefinition("student");
    }
}

  輸出:

INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - Student.clean, this:Student(id=99, name=張三)

  

九.聲明周期大雜燴

  上面對每一種接口都做了介紹,這裏就將所有接口都做一下整合,嘗試在一個測試程序中測試所有接口,這個過程中就會對Bean的生命周期有清晰的認識:

9.1 實現多接口的Student類

  創建Student類,實現Aware、InitializingBean、DisposableBean接口,並且增加display、clean方法,作為init-method和destory-method。

package cn.ganlixin.entity;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

@Data
@Slf4j
public class Student implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {

    private Integer id;
    private String name;

    @Override
    public void setBeanName(String s) {
        log.info("BeanNameAware.setBeanName, this:{}", this);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("BeanFactoryAware.setBeanFactory, this:{}", this);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("ApplicationContextAware.setApplicationContext, this:{}", this);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("InitialingBean.afterPropertiesSet, this:{}", this);
    }

    @Override
    public void destroy() throws Exception {
        log.info("DisposableBean.destory, this:{}", this);
    }

    public void display() {
        log.info("init-method, Student.display, this:{}", this);
    }

    public void clean() {
        log.info("destroy-method, Student.clean, this:{}", this);
    }
}

 

9.2 BeanPostProcessor前後置處理

  創建MyBeanPostProcessor接口實現類,並重寫前置和後置處理方法:

package cn.ganlixin.processor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessBeforeInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessAfterInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }
}

 

9.3 DestructionAwareBeanPostPrecessor接口

  創建MyDestructionAwareBeanPostProcessor類,並重寫其中的方法(不重寫BeanPostProcessor的前後置處理方法):

package cn.ganlixin.processor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;

@Slf4j
public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor {

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        log.info("DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, \n\tbeanName:{}, bean:{}", beanName, bean);
    }

    @Override
    public boolean requiresDestruction(Object bean) {
        return true; // 返回true,一律執行postProcessBeforeDestruction方法
        // 如果返回false,則不執行postProcessBeforeDestruction方法
    }
}

  

9.4 配置xml文件  

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

    <!-- 創建student bean,指定init-method和destroy-method -->
    <bean class="cn.ganlixin.entity.Student" id="student" init-method="display" destroy-method="clean">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>


    <!-- 將實現了DestructionAwareBeanPostProcessor接口的實現類聲明為bean-->
    <bean class="cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor"/>

    <!-- 將實現了BeanPostProcessor接口的類也聲明為bean-->
    <bean class="cn.ganlixin.processor.MyBeanPostProcessor"/>
</beans>

  

9.5 測試代碼

package cn.ganlixin;

import cn.ganlixin.entity.Student;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);

        log.info("測試程序獲取到的student bean:{}", student);

        // 刪除bean
        BeanDefinitionRegistry factory = (BeanDefinitionRegistry) context.getAutowireCapableBeanFactory();
        factory.removeBeanDefinition("student");
    }
}

  

9.6 輸出結果

INFO  [main] cn.ganlixin.entity.Student - BeanNameAware.setBeanName, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - BeanFactoryAware.setBeanFactory, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - ApplicationContextAware.setApplicationContext, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessBeforeInitialization, beanName:student, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - InitialingBean.afterPropertiesSet, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - init-method, Student.display, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessAfterInitialization, beanName:student, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor - DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, 
	beanName:student, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - DisposableBean.destory, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - destroy-method, Student.clean, this:Student(id=99, name=張三)

  

十.總結

  看了上面這個輸出結果,再結合下面這個圖,基本就能掌握Bean的大致生命周期了。

  

 

   原文地址:https://www.cnblogs.com/-beyond/p/13188675.html

   

 

  

 

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

【其他文章推薦】

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

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

線段樹(毒瘤)總結

我們在這篇博客里將具體介紹一種超級毒瘤超級高效的算法
線段樹

概念引入

首先來認識一下線段樹
什麼是線段樹呢:
線段樹是一種二叉樹,也就是對於一個線段,我們會用一個二叉樹來表示。比如說一個長度為6的線段,我們可以表示成這樣

這個圖是什麼意思呢?

  • 將這個做成一個樹的結構 每個根節點存儲左右兩個節點的權值之和
    舉個栗子:最上邊的線段表示1~6的和 而他的左兒子表示1~3的和 右兒子表示4~6的和
  • 然後他左兒子的左兒子又表示1~2的和 左兒子的右兒子表示3的權值
  • 因此 節點i的權值=i左兒子的權值+i右兒子的權值
  • 所以我們可以得到 tree[rt].sum = tree[l].sum + tree[r].sum
  • 根據這個原理 我們就可以進行遞歸建樹了
struct node{
      int l,r,sum;//l表示左兒子 r表示右兒子  sum表示當前節點存儲的權值
}tree[maxn*4];

void build(int i,int l,int r){
	tree[i].l = l;tree[i].r = r;
	if(l == r){
		tree[i].sum = a[l];//a數組存儲給出的數組初始值
		return;
	}
	int mid = (l+r)/2;
	build(i*2,l,mid);
	build(i*2+1,mid+1,r);
	tree[i].sum = tree[i*2].sum+tree[i*2+1].sum;
	return;
}

這就是線段樹的建樹方法 如果你要問為什麼我們要花好幾倍的內存去建樹來完成一個數組就能完成的事情 那就是因為我們需要讓這個超級大的數組去干一些比較困難的事情
那什麼是比較困難的事情呢 讓我們進入下個專題

簡單的操作

單點修改 區間查詢

  • 舉個例子 我們要求出區間1~5的和
  • 顯然可以 for(int i = 1;i<=5;++i){ans+=a[i]};
  • 但是我們仍然要使用線段樹來進行操作
  • 首先看區間的位置
    當前處在根節點 存儲的左邊界是1 右邊界是6
    根節點的左兒子的左邊界是1 右邊界是3 右兒子的左邊界是4 右邊界是6
    左兒子的區間完全被該區間包括 所以我們直接返回左兒子的權值
    右兒子的左邊界在目標區間右邊界的左邊 所以我們繼續遞歸搜索右邊界
    現在最新的左兒子為4~5 完全包括在目標區間之中 直接返回權值 右兒子6與該區間毫無關係 返回0
    現在我們就可以把返回的值加起來了 3+2=5
  • 有人可能會吐槽了 用一個數組能解決的問題 為什麼要搞的這麼複雜
  • 但是有的時候雖然數組很方便 但是他並不能滿足我們的需求 ,O(n)的效率 ,有時候是無法令出題人快樂的, 這個時候就需要用到線段樹了 O(\(log_n\))

因此用代碼怎麼實現呢 也很簡單
先讓我們總結一下線段樹的查詢方式:

  • 如果當前區間完全被包括在目標區間之中,直接返回當前區間的權值
  • 如果當前區間與目標區間毫無關係 直接返回 0
  • 如果當前區間與目標區間有交叉 繼續遞歸搜索左兒子和右兒子
    那我們就可以有這樣的代碼實現形式
int search(int rt,int l,int r){
	if(tree[rt].r < l ||tree[rt].l > r)return 0;
	if(tree[rt].l >= l && tree[rt].r <= r)return tree[rt].sum;
	int ans = 0;
	if(tree[rt*2].r >= l)ans += search(2*rt,l,r);
	if(tree[rt*2+1].l <= r)ans += search(2*rt+1,l,r);
	return ans;
}

 那單點修改呢  這個相對就簡單許多了 * 給出一個位置x 一個值k * 如果我們要修改x位置的數 讓他加上一個數k 我們就讓樹去遞歸尋找這個位置 “`cpp void add(int rt,int x,int k){ if(tree[rt].l == tree[rt].r){//到達恭弘=叶 恭弘子節點 說明找到該位置 tree[rt].sum += k; return; } if(x <= tree[rt*2].r)add(rt*2,x,k); // 遞歸搜索左兒子 else add(rt*2+1,x,k);//遞歸搜索右兒子 tree[rt].sum = tree[rt*2].sum + tree[rt*2+1].sum;//重新將權值加和 return; } “`

區間修改單點查詢

區間修改和單點查詢的方法有很多
為了一會對pushdown的講解 我們這裏說一種比較便於下面理解的方法

區間修改和區間查詢很像

  • 不過區間查詢的 ”如果當前區間完全包括在目標區間就返回當前區間的值“要改為將當前區間打上k標記
  • 舉個例子: 我們要把一個區間所有的數加上k
  • 那就去遞歸搜索線段樹 如果發現某個線段樹的區間完全包括在目標區間中 那就將這個區間打上k標記
  • 但是我們這裏的建樹就要有所不同了
  • 因為我們的所有節點的初始值都會為0(為了便於記錄標記k)
void build(int l,int r,int rt){
    tree[rt].num=0;
    tree[rt].l=l;
    tree[rt].r=r;
    if(l==r)
        return ;
    int mid=(r+l)/2;
    build(l,mid,rt*2);
    build(mid+1,r,rt*2+1);
}

void add(int rt,int l,int r,int k){
    if(tree[rt].l>=l && tree[rt].r<=r){
        tree[rt].num+=k;
        return ;
    }
    if(tree[rt*2].r>=l)
       add(rt*2,l,r,k);
    if(tree[rt*2+1].l<=r)
       add(rt*2+1,l,r,k);
}

單點查詢可以去尋找這個節點 路上遇到的所有標記都要累加起來 最後再加上這個節點的初始值 用代碼實現大概是這個樣子 “`cpp void search(int rt,int dis){ ans+=tree[rt].num; if(tree[rt].l==tree[rt].r) return ; if(dis<=tree[rt*2].r) search(rt*2,dis); if(dis>=tree[rt*2+1].l) search(rt*2+1,dis); } //主函數中 printf(“%d\n”,ans+a[x]);//a[x]為目標位置的初始值 “`

建議將上面的板子打熟再向下繼續觀看

區間修改與區間查詢(pushdown and lazy)

前方高難
看到這樣的題你或許會想 不就是上邊那兩种放在一起嗎
但是如果你真的這樣寫完了代碼你會發現 WA
為什麼呢

先來回想一下剛才的操作:將區間加上標記 最終查詢的時候去從上往下找 將標記累加最後再加上初始值

但是這樣真的可以嗎?

答案是否定的 原因很簡單:如果你要求1~3區間的和 而你剛剛將3~5的區間加上標記 因為1~3並不包含3~5的標記 所以我們計算后的結果並不是加k之後的和 而是初始值的和

那如何解決這個問題呢? 也很簡單:只要將我們的標記k下放到i的兒子不就好了嗎

所以我們的算法雛形就出來了(這也是線段樹最毒瘤而且難調最具有魅力的地方)

  • 首先我們在結構體中多定義一個變量lazy 用於記錄標記 每次有加的操作的時候我們就加到lazy上
  • 然後就是下放操作pushdown 用於將lazy下放到i的兒子節點中
  • 所以通過簡單的推理和歸納我們仍然有以下性質:
      1. 如果當前區間完全被包含在目標區間中 則這個區間的權值 tree[rt].sum += k*(tree[rt].r - tree[rt].l + 1)
      2. 如果當前區間與目標區間有交集但是並沒有被完全覆蓋 就下放懶惰標記
      3. 下放之後分別對左兒子和右兒子進行相同的操作
  • 最後仍然是按照tree[rt].sum = tree[rt2].sum + tree[rt2+1].sum向上更新
    因此代碼實現就是
void pushdown(long long rt){
	if(tree[rt].lazy != 0){//如果當前區間已經被標記
		tree[rt*2].lazy += tree[rt].lazy;//下放到左兒子
		tree[rt*2+1].lazy += tree[rt].lazy;//下放到右兒子
		long long mid = (tree[rt].l + tree[rt].r)/2;
		tree[rt*2].sum += tree[rt].lazy*(mid - tree[rt*2].l + 1);//更新左兒子的值
		tree[rt*2+1].sum += tree[rt].lazy*(tree[rt*2+1].r - mid);//更新右兒子的值
		tree[rt].lazy = 0;//清空當前節點的懶惰標記
	}
	return;
}

void add(long long rt,long long l,long long r,long long k){
	if(tree[rt].l >= l && tree[rt].r <= r){//如果當前區間完全包含在目標區間直接更新並且標記懶惰標記
		tree[rt].sum += k*(tree[rt].r-tree[rt].l+1);//更新當前區間的權值
		tree[rt].lazy += k;//增加懶惰標記
		return;
	}
	pushdown(rt);//下放懶惰標記
	if(tree[rt*2].r >= l)add(rt*2,l,r,k);//遞歸更新左兒子
	if(tree[rt*2+1].l <= r)add(rt*2+1,l,r,k);//遞歸更新右兒子
	tree[rt].sum = tree[rt*2].sum+tree[rt*2+1].sum;//更新當前節點的權值
	return;
}

區間查詢的時候和之前幾乎一樣 不同的是要進行懶惰標記的下放之後在累加

long long search(long long rt,long long l,long long r){
	if(tree[rt].l >= l && tree[rt].r <= r)return tree[rt].sum;//如果當前區間完全包含在目標區間內 直接返回當前區間的權值
	if(tree[rt].r < l || tree[rt].l > r)return 0;//如果當前區間和目標區間完全沒有關係 直接返回0
	pushdown(rt);//下放懶惰標記
	long long s = 0;
	if(tree[rt*2].r >= l)s += search(rt*2,l,r);
	if(tree[rt*2+1].l <= r)s += search(rt*2+1,l,r);
	return s;//最後返回這個區間的和
}

線段樹模型大概就是這個樣子(線段樹還是比較受出題人青睞的難道是因為難調??)
附上練習攻略:
簡單線段樹建議用洛谷P3374【模板】樹狀數組1
        洛谷P3368【模板】樹狀數組2練習板子
如果簡單線段樹沒有問題了
可以去嘗試一下:洛谷P3372【模板】線段樹1
        洛谷P3373【模板】線段樹2
        洛谷P6242【模板】線段樹3

謝謝觀看
點個關注>_<

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

【其他文章推薦】

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

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

電動車不只轎跑車市場,電動垃圾車可成利基

電動車熱潮可說成也特斯拉(Tesla)、敗也特斯拉,先是因特斯拉成功引起媒體追逐熱潮而一時暴紅,也因為特斯拉表現不如預期遭市場唱空而暫時沉寂,不過,電動車的商機並不只有特斯拉所主打的時尚轎、跑車,其實若論節能減碳與減少空污,每天固定行駛里程數更高,煞停又重新起步更頻繁,而且重量與油耗都遠大於轎車的垃圾車,更是利基市場。  
    電動車固然需要充電,而電力也可能來自於火力發電,但是火力發電廠的能源效率可達 60%,汽油引擎內燃機卻只有 30%,這是電動車節能的理論基礎之一;此外,電動車可應用煞車回電,甚至如輪胎大廠固特異正研發「燒胎回電」,在煞車時可將車體的動能部分回收為電能,內燃機引擎車煞車時所有動能都化為摩擦熱喪失,這是電動車較內燃機引擎車節能的理論基礎之二,因此,車體越重,煞停時消耗的動能越大、行駛時煞車次數越多,電動車較內燃機節省能源的幅度就更高。   以此觀之,沉重的垃圾車,每到定點就要停車,等裝完垃圾再重新啟動,顯然比開在高速公路的輕量跑車來得更有節能空間。   於是,原本也是特斯拉共同創辦人之一的伊恩萊特(Ian Wright),選擇鎖定垃圾車,成立新創事業「萊特速度」(Wrightspeed),伊恩萊特表示,一般標準垃圾車每天都要煞停、再啟動 1,000 次,導致每 3 個月就會磨光煞車皮,每年燒掉 1.4 加侖汽油。這是很有節能潛力的目標。   萊特速度改裝現有垃圾車,將原本內燃機動力總成改裝為電動動力總成,並且加裝煞車回電系統,除了可回收電力以外,也能減輕煞車皮的負擔,不再每三個月就磨光煞車皮,而為了延長行駛距離,動力總成上還加裝了一組汽油發電機,作為備用能源,這與油電混合動力車不同,汽油發電機並不是直接驅動車輛,而是供電給電池,透過電動動力總成來驅動車輛,維持全電動的動力總成架構。    
省成本又解決空污問題   有了汽油發電機,只要有加油站,就不擔心會停擺,也因此,萊特速度的電池容量也不大,標準垃圾車電池容量僅 78 度電,而中型垃圾車電池容量更只有 26 度電,相較之下,特斯拉 Model S 最大擁有 85 度電容量的電池。   但萊特速度在煞車回電上的能力則遠大於特斯拉轎車,煞車回電的發電容量最高達 730 千瓦,以提供強大的煞車力來煞停沉重的垃圾車,並將盡可能多的動能回收為電能。   而煞車回電不僅回收電池電量,減少煞車皮損耗,也減少了煞車時對車軸與整個車身造成的損耗,伊恩萊特表示,一般垃圾車行駛 20 萬英里就要報廢,大概只能開上 5 年,並且需要大量維修工作,轉換為萊特速度電動系統以後,可大為延長使用期限,每年除了減少 3.5 萬美元的燃料費用以外,還能再減少 2 萬美元的維修費用,這讓萊特速度的客戶可在 4 年內回本。   除了省錢以外,改裝為電動車也解決了老舊垃圾車的空污問題,以加州為例,新的空污標準讓老舊垃圾車瀕臨淘汰,使用老舊垃圾車的單位面臨必須重買新車以符合空污排放標準的難題,但如果以萊特速度的技術改裝為電動車,就解決了空污問題,其成本也比重買新車來得低。   而這不僅是空氣污染問題,垃圾車穿越社區時,引擎聲往往擾人清夢,伊恩萊特認為,改為電動車之後,安靜將是一大競爭力。由於美國有 11 萬輛垃圾車,除了垃圾車以外,送貨用車如快遞業者的送貨廂型車等也是可能的客戶,萊特速度的改裝生意大有可為。   伊恩萊特更自信滿滿地表示,雖然特斯拉做得很好,但在保護環境方面,萊特速度能減少的碳排放、空氣污染與噪音一定能超過特斯拉。萊特速度到 2015 年 3 月,募資總額達 3,200 萬美元,目前正在進行最新一輪募資,預期要雇用至 300 名員工,提升最大年產能至 5,000 輛規模。     本文全文授權轉載自《科技新報》─〈〉

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

【其他文章推薦】

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

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務