更新時間:2020-03-20 10:07:08 來源:動力節(jié)點 瀏覽2267次
談談接口和抽象類有什么區(qū)別?
接口是對行為的抽象,它是抽象方法的集合,利用接口可以達到API定義和實現(xiàn)分離的目的。接口,不能實例化;不能包含任何非常量成員,同時,沒有非靜態(tài)方法實現(xiàn),也就是說要么是抽象方法,要么是靜態(tài)方法。Java標準類庫中,定義了非常多的接口,比如java.util.List。
抽象類是不能實例化的類,用abstract關鍵字修飾class,其目的主要是代碼重用。除了不能實例化,形式上和一般的Java類并沒有太大區(qū)別,可以有一個或者多個抽象方法,也可
以沒有抽象方法。抽象類大多用于抽取相關Java類的共用方法實現(xiàn)或者是共同成員變量,然后通過繼承的方式達到代碼復用的目的。Java標準庫中,比如collection框架,很多通用
部分就被抽取成為抽象類,例如java.util.AbstractList。
進行面向?qū)ο缶幊蹋莆栈镜脑O計原則是必須的,我今天介紹最通用的部分,也就是所謂的S.O.L.I.D原則。
單一職責類或者對象最好是只有單一職責,在程序設計中如果發(fā)現(xiàn)某個類承擔著多種義務,可以考慮進行拆分。
開關原則設計要對擴展開放,對修改關閉。換句話說,程序設計應保證平滑的擴展性,盡量避免因為新增同類功能而修改已有實現(xiàn),這樣可以少產(chǎn)出些回歸(regression)問題。
里氏替換這是面向?qū)ο蟮幕疽刂唬M行繼承關系抽象時,凡是可以用父類或者基類的地方,都可以用子類替換。
接口分離我們在進行類和接口設計時,如果在一個接口里定義了太多方法,其子類很可能面臨兩難,就是只有部分方法對它是有意義的,這就破壞了程序的內(nèi)聚性。對于這種情況,可以通過拆分成功能單一的多個接口,將行為進行解耦。在未來維護中,如果某個接口設計有變,不會對使用其他接口的子類構成影響
依賴反轉(zhuǎn)實體應該依賴于抽象而不是實現(xiàn)。也就是說高層次模塊,不應該依賴于低層次模塊,而是應該基于抽象。實踐這一原則是保證產(chǎn)品代碼之間適當耦合度的法寶
談談你知道的設計模式?請手動實現(xiàn)單例模式,Spring等框架中使用了哪些模式?
設計模式可以分為創(chuàng)建型模式、結(jié)構型模式和行為型模式。
創(chuàng)建型模式,是對對象創(chuàng)建過程的各種問題和解決方案的總結(jié),包括各種工廠模式(Factory、AbstractFactory)、單例模式(Singleton)、構建器模式(Builder)、原型模式(ProtoType)。
結(jié)構型模式,是針對軟件設計結(jié)構的總結(jié),關注于類、對象繼承、組合方式的實踐經(jīng)驗。常見的結(jié)構型模式,包括橋接模式(Bridge)、適配器模式(Adapter)、裝飾者模式(Decorator)、代理模式(Proxy)、組合模式(Composite)、外觀模式(Facade)、享元模式(Flyweight)等。
行為型模式,是從類或?qū)ο笾g交互、職責劃分等角度總結(jié)的模式。比較常見的行為型模式有策略模式(Strategy)、解釋器模式(Interpreter)、命令模式(Command)、觀察者模式(Observer)、迭代器模式(Iterator)、模板方法模式(TemplateMethod)、訪問者模式(Visitor)。更多相關內(nèi)容你可以參考:https://en.wikipedia.org/wiki/Design_Patterns
InputStream是一個抽象類,標準類庫中提供了FileInputStream、ByteArrayInputStream等各種不同的子類,分別從不同角度對InputStream進行了功能擴展,這是典型的裝飾器模式應用案例。識別裝飾器模式,可以通過識別類設計特征來進行判斷,也就是其類構造函數(shù)以相同的抽象類或者接口為輸入?yún)?shù)
創(chuàng)建型模式尤其是工廠模式,在我們的代碼中隨處可見,我舉個相對不同的API設計實踐。比如,JDK最新版本中HTTP/2ClientAPI,下面這個創(chuàng)建HttpRequest的過程,就是典型的構建器模式(Builder),通常會被實現(xiàn)成fuent風格的API,也有人叫它方法鏈。使用構建器模式,可以比較優(yōu)雅地解決構建復雜對象的麻煩,這里的“復雜”是指類似需要輸入的參數(shù)組合較多,如果用構造函數(shù),我們往往需要為每一種可能的輸入?yún)?shù)組合實現(xiàn)相應的構造函數(shù),一系列復雜的構造函數(shù)會讓代碼閱讀性和可維護性變得很差。
Spring等如何在API設計中使用設計模式
BeanFactory和ApplicationContext應用了工廠模式
在Bean的創(chuàng)建中,Spring也為不同scope定義的對象,提供了單例和原型等模式實現(xiàn)。
AOP領域則是使用了代理模式、裝飾器模式、適配器模式等。
各種事件監(jiān)聽器,是觀察者模式的典型應用。
類似JdbcTemplate等則是應用了模板模式。
synchronized和ReentrantLock有什么區(qū)別?
synchronized是Java內(nèi)建的同步機制,它提供了互斥的語義和可見性,當一個線程已經(jīng)獲取當前鎖時,其他試圖獲取的線程只能等待或者阻塞在那里。
ReentrantLock,通常翻譯為再入鎖,是Java5提供的鎖實現(xiàn),它的語義和synchronized基本相同。再入鎖通過代碼直接調(diào)用lock()方法獲取,代碼書寫也更加靈活。與此同時,ReentrantLock提供了很多實用的方法,能夠?qū)崿F(xiàn)很多synchronized無法做到的細節(jié)控制,比如可以控制fairness,也就是公平性,或者利用定義條件等。但是,編碼中也需要注意,必須要明確調(diào)用unlock()方法釋放,不然就會一直持有該鎖。
synchronized底層如何實現(xiàn)?什么是鎖的升級、降級?
synchronized代碼塊是由一對兒monitorenter/monitorexit指令實現(xiàn)的,Monitor對象是同步的基本實現(xiàn)單元。在Java6之前,Monitor的實現(xiàn)完全是依靠操作系統(tǒng)內(nèi)部的互斥鎖,因為需要進行用戶態(tài)到內(nèi)核態(tài)的切換,所以同步操作是一個無差別的重量級操作。現(xiàn)代的(Oracle)JDK中,JVM對此進行了大刀闊斧地改進,提供了三種不同的Monitor實現(xiàn),也就是常說的三種不同的鎖:偏向鎖(BiasedLocking)、輕量級鎖和重量級鎖,大大改進了其性能。
所謂鎖的升級、降級,就是JVM優(yōu)化synchronized運行的機制,當JVM檢測到不同的競爭狀況時,會自動切換到適合的鎖實現(xiàn),這種切換就是鎖的升級、降級。
當沒有競爭出現(xiàn)時,默認會使用偏斜鎖。JVM會利用CAS操作,在對象頭上的MarkWord部分設置線程ID,以表示這個對象偏向于當前線程,所以并不涉及真正的互斥鎖。這樣做的假設是基于在很多應用場景中,大部分對象生命周期中最多會被一個線程鎖定,使用偏斜鎖可以降低無競爭開銷。
如果有另外的線程試圖鎖定某個已經(jīng)被偏斜過的對象,JVM就需要撤銷(revoke)偏斜鎖,并切換到輕量級鎖實現(xiàn)。輕量級鎖依賴CAS操作MarkWord來試圖獲取鎖,如果重試成功,就使用普通的輕量級鎖;否則,進一步升級為重量級鎖。
你知道“自旋鎖”是做什么的嗎?它的使用場景是什么?
自旋鎖:競爭鎖的失敗的線程,并不會真實的在操作系統(tǒng)層面掛起等待,而是JVM會讓線程做幾個空循環(huán)(基于預測在不久的將來就能獲得),在經(jīng)過若干次循環(huán)后,如果可以獲得鎖,那么進入臨界區(qū),如果還不能獲得鎖,才會真實的將線程在操作系統(tǒng)層面進行掛起。
適用場景:自旋鎖可以減少線程的阻塞,這對于鎖競爭不激烈,且占用鎖時間非常短的代碼塊來說,有較大的性能提升,因為自旋的消耗會小于線程阻塞掛起操作的消耗。如果鎖的競爭激烈,或者持有鎖的線程需要長時間占用鎖執(zhí)行同步塊,就不適合使用自旋鎖了,因為自旋鎖在獲取鎖前一直都是占用cpu做無用功,線程自旋的消耗大于線程阻塞掛起操作的消耗,造成cpu的浪費
在單核CPU上,自旋鎖是無用,因為當自旋鎖嘗試獲取鎖不成功會一直嘗試,這會一直占用CPU,其他線程不可能運行,
同時由于其他線程無法運行,所以當前線程無法釋放鎖。
一個線程兩次調(diào)用start()方法會出現(xiàn)什么情況?談談線程的生命周期和狀態(tài)轉(zhuǎn)移。
Java的線程是不允許啟動兩次的,第二次調(diào)用必然會拋出IllegalThreadStateException,這是一種運行時異常,多次調(diào)用start被認為是編程錯誤。
新建(NEW),表示線程被創(chuàng)建出來還沒真正啟動的狀態(tài),可以認為它是個Java內(nèi)部狀態(tài)。
就緒(RUNNABLE),表示該線程已經(jīng)在JVM中執(zhí)行,當然由于執(zhí)行需要計算資源,它可能是正在運行,也可能還在等待系統(tǒng)分配給它CPU片段,在就緒隊列里面排隊。
阻塞(BLOCKED),這個狀態(tài)和我們前面兩講介紹的同步非常相關,阻塞表示線程在等待Monitorlock。比如,線程試圖通過synchronized去獲取某個鎖,但是其他線程已經(jīng)獨占了,那么當前線程就會處于阻塞狀態(tài)。
等待(WAITING),表示正在等待其他線程采取某些操作。一個常見的場景是類似生產(chǎn)者消費者模式,發(fā)現(xiàn)任務條件尚未滿足,就讓當前消費者線程等待(wait),另外的生產(chǎn)者線程去準備任務數(shù)據(jù),然后通過類似notify等動作,通知消費線程可以繼續(xù)工作了。Thread.join()也會令線程進入等待狀態(tài)。
計時等待(TIMED_WAIT),其進入條件和等待狀態(tài)類似,但是調(diào)用的是存在超時條件的方法,比如wait或join等方法的指定超時版本
以上就是動力節(jié)點Java培訓機構小編介紹的“熟知的Java常見筆試題及答案”的內(nèi)容,希望對大家有幫助,如有疑問,請在線咨詢,有專業(yè)老師隨時為你服務。