1. 程式人生 > >Java程式設計師面試寶典筆記記錄-第4章Java基礎部分(下)

Java程式設計師面試寶典筆記記錄-第4章Java基礎部分(下)

導言

  本次博文對何昊出版的《java程式設計師面試寶典》的第四章關於Java一部分基礎知識(4.7-4.10)的概括筆記,刪除其中部分程式碼,試題和一部分相對簡單的內容題目。

相關題目

Java IO流實現機制是什麼?

  輸入輸出都被稱為抽象的流,流被看做一組有序位元組集合。其本質資料傳輸,根據資料型別分位元組流和字元流。位元組流以位元組為單位,包含抽象類:InputStream和OutputStream。字元流以字元(16bit)為單位,根據碼錶對映字元,一次課讀多個字元,包含兩抽象類:Reader和Writer。

位元組流和字元流區別:位元組流處理輸入輸出不用到快取,而字元流用到。

Java IO類使用裝飾者模式,可以在執行時動態地給物件新增額外職責。

Java幾種型別的流?

  兩種,位元組流和字元流。位元組流繼承於InputStream和OutputStream。字元流繼承Reader和Writer。流的作用是為了改善程式效能並且使用方便。

管理檔案和目錄的類是什麼?

  File類。可檢視檔案或目錄屬性,及實現檔案或目錄建立、刪除與重新命名。

  常用方法:File(String pathname),createNewFile(),delete().isFile(),isDirectory(),listFile()【若物件是目錄,返還所有檔案File物件】,mkdir(),exists()

Java的Socket是什麼?

  網路上兩個程式通過雙向通訊連線實現資料交換,該雙向鏈路一端被稱為一個Socket。Socket被稱為套接字,可以實現不同虛擬機器或計算機之間通訊。Java的Socket分兩類:面向連線的Socket通訊協議

(TCP,Transmission Control Protocol,傳輸控制協議)和面向無連線的Socket協議(UDP,User Datagram Protocol,使用者資料報協議)

  任何Socket都由IP地址和埠號唯一確定。

TCP通訊過程是什麼?

   (1)Server段Listen指定某個埠(建議大於1024)是否有連線請求    (2)Client發出Connect連線    (3)Server段向Client段發回Accept資訊。    (4)連線建立,會話產生。客戶端和服務端都可以通過Send,Write等方法與對方通訊。

Socket的生命週期有哪些?

  三個階段:開啟Socket,使用Socket收發資料和關閉Socket。

  ServerSocket作為伺服器端,Socket作為客戶端。

Java的NIO是什麼?

  在非阻塞IO(Nonblocking IO,NIO)出現之前,java通過Socket實現網路通訊。

  NIO通過Selector,Channel和Buffer實現費阻塞IO操作。

  NIO實現採用Reactor(反應器)設計模式,與觀察者模式,但觀察者模式只能處理一個事件源,而反應器模式可以處理多個事件源。

  Channel可被看做雙向非阻塞通道,通道兩邊都可進行資料讀寫。Selector實現用一個執行緒管理多個通道(採用複用與解複用方式使一個執行緒能夠管理多個通道,即可以把多個流合併成一個流,或把一個流分為多個流)。

  實現時,把需要處理的Channel的IO事件(connect,read或write等)註冊給Selector。Selector內部實現原理:對所有註冊的Channel進行輪訓訪問,一旦輪詢到一個Channe 1有註冊事件發生(資料來了),則傳回SelectionKey方式通知對Channe 1進行資料讀寫。Key(由SelectionKey類表示)封裝一個特定Channe 1和一個特定Selector之間關係。

  這種通過輪詢方式在處理多執行緒請求時不需要上下文切換。

  採用多執行緒實現方式線上程之間切換時需要上下文切換,也需要進行壓棧和彈棧。

  Buffer儲存資料,可以存放從Channe 1讀取的資料或其傳送的資料。   Buffer具有不同型別,ByteBuffer,CharBuffer等。通過Buffer大大簡化開發人員對資料流管理。

Java序列化是什麼?

  兩種方式:序列化和外部序列化

序列化(Serialization)

  分散式環境,遠端通訊時任何資料均以二進位制序列形式在網路上傳送。序列化是一種將物件以一連串位元組描述的過程,用以解決在物件流進行讀寫操作引發的問題。序列化可以將物件狀態寫在流裡進行網路傳輸,或者儲存到檔案、資料庫等,並按需從流讀取出重新構造一個相同物件。

  實現序列化需要實現Serializable介面【標誌介面】。使用輸出流(例FileOutputStream)構造ObjectOutputStream(物件流)物件。接著使用該物件的writeObject方法將obj物件寫出(即儲存其狀態),要恢復時可以使用其對應的輸入流。

  特點:如果一個類可被序列化,其子類也能。static(靜態)代表類的成員,transient(宣告序列,物件儲存時,其值不維持)代表物件臨時資料,這兩種不能被序列化。

  多個物件序列化介面:ObjectOutputStream,ObjectInput,ObjectOutput,ObjectInputStream。

  序列化使用會影響系統性能,若無必要,則別使用。   Java序列化使用情況:需要通過網路傳送物件或物件狀態持久化到資料庫或檔案;序列化能實現深複製,即可以複製引用的物件。

  反序列化:將流轉換成物件,SerialVesionUID起到重要作用。每個都有特定的SerialVesionUID,反序列通過SerialVesionUID判斷類相容性。如果待序列化的物件與目標物件的SerialVesionUID不同,那麼反序列化時會丟擲InvaildClassException異常。

  好習慣:在被序列化的類中顯式宣告SerialVesionUID(該欄位必須static final)。

  自定義SerialVesionUID的優點:提高執行效率,提高不同平臺程式相容性;增強程式各版本可相容性。

外部序列化

  外部序列化和序列化區別在於序列化是內建API,只需要實現Serializable介面。

  使用外部序列化,Externalizable介面【自己編寫繼承Serializable】的讀寫方法必須由開發人員實現,其編寫程式難度更大,但更具靈活性,可對需要持久化的那些屬性進行控制,或許會提高效能。

  在用介面Serializable實現序列化時,在這個類中的所有屬性都會被序列化,那麼怎樣才能實現只序列化部分屬性呢?

  方法一:實現Externalizable介面,根據需求實現readExternal與writeExternal方法來控制序列化與反序列化所使用的數學。缺點在於增加程式設計難度。

  另一方法使用關鍵幀transient控制序列化屬性。被修飾的屬性是臨時不會被序列化。

  注意:Java序列化時不會例項化static變數。

為什麼說Java是平臺獨立性語言?

  Java保證平臺獨立性機制是“中間碼”和“Java虛擬機器(JVM)“。Java程式被編譯後是生成中間碼。不同平臺具有不同JVM,JVM把中間碼翻譯成硬體平臺能執行的程式碼。JVM不具有平臺獨立性。

  解釋執行分三步:程式碼裝入,校驗和執行。裝入程式碼由類載入器完成。裝入程式碼由位元組碼校驗器進行檢查。

  位元組碼執行方式:即時編譯和解釋執行。即時編譯是直譯器將位元組碼翻譯成機器碼並執行該機器碼。解釋執行是編譯器每次解釋一小段程式碼完成Java位元組碼程式所有操作。通常採用解釋執行方式。

JVM載入class檔案原理機制是什麼?

  Java是動態性解釋型語言,類(class)只有被載入到JVM後才能執行。當執行指定程式時,JVM會將編譯生成的.class檔案按照需求和一定規則載入到記憶體中,並組織成一個完整的Java應用程式。這個載入過程由類載入器完成,即由ClassLoader和其子類實現。類載入器本身也是類,本質是把類檔案從硬碟讀取到記憶體中。

  載入方式分隱式和顯式。前者指使用new等方式建立物件,會隱式呼叫類的載入器把對應類載入到JVM中。後者是直接呼叫class.forName()方法把所需類載入到JVM中。

  把需要類載入入JVM,其他類被使用才被載入,這樣可以加快載入速度,可節約程式執行對記憶體開銷。Java中每個類或介面對應.class檔案,這些檔案可以被看成一個個可以被動態載入的單元,因此當只有部分類被修改時,只需要重新編譯變化的類即可,而不需要編譯所有檔案,因此加快了編譯速度。

  Java類是動態載入。不會一次性將所有都載入,而是載入保證程式執行的基礎類(基類)完全載入到JVM中,至於其他類則需要時載入。

  Java把類分三類:系統類,擴充套件類和自定義類。

  這三類有三種類型的載入器。關係如下

  三類的協調完成類的載入:通過委託的方式實現的。當有類被載入時,類載入器請求父類來完成這個載入工作,父類會使用其自己的搜尋路徑來搜尋需要被載入的類,如果搜尋不到,才會由子類按照其搜尋路徑來搜尋帶載入的類。

類載入主要步驟有哪些?

   (1)裝載。根據查詢路徑找到相應的class檔案。然後匯入。 (2)連結。連結又可以分為3個小步驟:    1). 檢查。檢查待載入的class檔案的正確性    2). 準備。給類中的靜態變數分配儲存空間。    3). 解析。將符號引用轉換成直接引用(這一步是可選的)解析。將符號引用轉換成直接引用(這一步是可選的)    (3)初始化。對靜態變數和靜態程式碼塊執行初始化工作。

什麼是GC?

  GC主要作用是回收程式中不再使用的記憶體。   GC自動檢測物件作用域,可自動地把不再被使用的儲存空間釋放掉。   GC要負責三項任務:分配記憶體、確保被引用物件的內容不被錯誤地回收以及回收不再被引用的物件的記憶體空間。   GC提高開發人員生產效率,遮蔽釋放記憶體方法,保證程式穩定性。   GC的問題:GC必須跟蹤記憶體的使用情況,釋放沒用物件,在完成記憶體的釋放後還需要處理堆中的碎片,這些操作增加JVM負擔,降低程式執行效率。   對物件而言,若沒有任何變數去引用則不可能被程式訪問,則認為是垃圾資訊,可被回收。只要有一個以上的變數引用該物件則不可被回收。   GC使用有向圖記錄和管理堆記憶體的所有物件。該有向圖識別哪些是“可達的“(有引用變數引用則是可達的)。哪些物件是不可達的。所有不可達物件都可被回收。

Gc怎麼做?

  超出了作用域或引用計數為空的物件;從gc root開始搜尋找不到的物件,而且經過一次標記、清理,仍然沒有復活的物件。

Gc做什麼?

  刪除不使用的物件,回收記憶體空間;執行預設的finalize,當然程式設計師想立刻呼叫就用dipose呼叫以釋放資源如檔案控制代碼,JVM用from survivor、to survivor對它進行標記清理,物件序列化後也可以使它復活。

垃圾回收演算法有哪些?

(1)引用計數演算法   在堆中對每個物件都有一個引用計數器。當物件被引用時,引用計數加1.當引用被置空或離開作用域時,引用計數減1。由於最終方法無法解決相互引用問題,因此JVM不採用。

(2)追蹤回收演算法

  利用JVM維護物件的引用圖,從根節點開始遍歷物件的引用圖,同時標記遍歷到的物件。當遍歷結束後,未被標記的物件就是目前已不被使用的物件,就可以被回收。

(3)壓縮回收演算法

  把堆中活動的物件移動到堆中一端,這樣會在堆中另外一端留出很大的一塊空閒區域,相當於對堆中的碎片進行了處理。雖然大大簡化消除堆碎片的工作,但是每次處理都會帶來效能的損失。

(4)複製回收演算法   把堆分成兩個大小相同的區域,在任何時刻,只有其中一個區域被使用,直到這個區域被消耗完為止。此時GC會中斷程式的執行,通過遍歷的方式把所有活動的物件複製到另外一個區域中,在複製過程中它們是緊挨著佈置的,從而可以消除記憶體碎片。當複製過程結束後程序會接著執行,直到這塊區域被使用完,然後再採用上面的方法繼續進行垃圾回收。   優點:進行垃圾回收的同時對物件的佈置也進行安排,從而消除了記憶體碎片。這也付出極高的代價,對於指定大小的堆,需要兩倍大小的記憶體空間,同時由於在記憶體調整的過程中要中斷當前執行的程式,從而降低了程式執行效率。 (5)按代回收演算法   複製回收演算法缺點在於每次演算法執行所有處於活動狀態的物件都要被複制,效率低下。由於程式有”程式建立的大部分物件的生命週期都很短,只有一部分物件有較長的生命週期“的特點,因此可以根據這個特點對演算法進行優化。按代回收演算法主要思想:把堆分成兩個或多個子集,每個子集被視為一代。演算法在執行過程中有限收集那些“年幼”物件。如果一個物件進過多次收集仍然“存活”,則將這個物件轉移到高一級的堆裡,減少對其的掃描次數。

PS:fnalize方法是在物件空間被回收前呼叫而不是之後。

可以主動通知JVM進行垃圾回收?

  可以通過呼叫System.gc()來通知GC執行。JVM並不保證GC馬上執行。System.gc()方法執行會停止所有響應,去檢查記憶體中是否有可回收的物件,這會對程式正常執行以及效能造成極大威脅。

Java是否存在記憶體洩漏問題?

  存在。Java判斷符合垃圾回收標準:1.物件賦予空值null,而後沒有被使用;2.物件賦予新值,重新分配記憶體空間。記憶體洩漏情況兩種:1.堆中申請空間沒有被釋放。2.物件已不再被使用,但記憶體依舊保留。

  GC可以解決第一種情況,第二種情況,無法保證不再使用的物件會被釋放。記憶體洩漏也指的是第二種情況。

Java容易引起記憶體洩漏的原因有哪些?

  1. 靜態集合類(HashMap,Vector等)。
  2. 各種連線(資料庫連線、網路連線、IO連線等)。連線物件需顯示關閉(close方法),否則造成大量物件無法回收。
  3. 監聽器。在釋放物件同時沒有相應刪除監聽器可能造成記憶體洩漏。
  4. 不合理作用域。物件定義作用範圍大於使用範圍可能會造成記憶體洩漏,未及時將物件設定為null,也可能造成記憶體洩漏。
  5. 單例模式可能會造成記憶體洩漏

Java堆和棧區別有哪些?

  都是記憶體存放資料地方。變數分基本資料型別和引用型別。基本資料型別以及物件的引用變數,其記憶體分配在棧上。變量出了作用域就會被自動釋放,而引用型別的變數,記憶體分配在堆上或者常量池(字串常量和基本資料型別常量)中,需要通過new等方式建立。

  棧記憶體儲存基本資料型別與引用變數。棧記憶體管理是通過壓棧和彈棧操作完成,以棧幀為基本單位來管理程式呼叫關係。每當有函式呼叫,就會通過壓棧創新的棧幀,每當函式呼叫結束後都會通過彈棧的方式釋放棧幀。

  堆記憶體原來存放執行建立的物件。一般來講,通過new關鍵字創建出來的物件都存放在堆記憶體中。由於JVM是基於堆疊的虛擬機器,而每個Java程式都執行在一個單獨的JVM例項上,每一個例項唯一對應一個堆,一個Java程式內的多個執行緒也就執行在同一個JVM例項上,因此這些執行緒會共享堆記憶體。因此,多執行緒在訪問堆中資料需要對資料進行同步。

  在堆中產生一個數據或物件後,還可以在棧中定義一個特殊變數,讓棧中該變數的取值等於陣列或物件在堆記憶體中的首地址,棧中這個變數就成了陣列或物件的引用變數。引用變數就相當於為陣列或物件起的一個名稱,以後就可以在程式中使用棧中的引用變數來訪問堆中的陣列或物件,這就是Java引用的用法。

  從棧和堆功能和作用比較。堆主要存放物件,棧用來執行程式。相對於堆,棧讀取資料更快。但棧大小和生存期必須確定,缺乏靈活性。堆可以執行時動態分配記憶體,生存期不需提前告知編譯器,這也導致其存取速度的緩慢。

Set,List和Map介面區別有哪些?

   1)Set集合元素不可重複,存入Set的每個元素必須定義equals來確保物件唯一性。實現:HashSet和TreeSet【實現SortedSet介面,故有序】    2)List稱有序Collection介面。按照物件進入順序儲存,所以能對列表元素插入和刪除位置精確控制,可以儲存重複物件。實現:LinkedList,ArrayList,Vector。    3)Map提供鍵值對映資料結構,值可重複,但鍵唯一,不能重複。實現:HashMap【基於散列表實現,採用物件HashCode快速查詢】,LinkedHashMap【採用列表維護內部順序】,TreeMap【採用紅黑樹資料結構實現,內部按需排序】,WeekHashMap和IdentityHashMap。

迭代器什麼情況會遇到ConcurrentModificationException異常?

  這是由於使用Iterator遍歷容器同時做增加或刪除操作導致,或由於多執行緒操作導致。當一個執行緒使用迭代器遍歷容器,另一個執行緒對該容器進行增刪操作。

  單執行緒中在遍歷過程中對集合增刪物件會產生ConcurrentModificationException異常【使用迭代器,用一個變數expectedModCount儲存物件個數,每次呼叫next方法會比較expectedModCount和實際個數modCount是否相等。若不相等丟擲該異常】。可以採取把刪除物件儲存在集合中,遍歷結束呼叫removeAll方法刪除,或使用iter.remove()方法。

  多執行緒中的異常解決:用執行緒安全容器(ConcurrentHashMap,CopyOnWriteArrayList)代替非執行緒安全容器。使用迭代器遍歷對容器操作放在synchronized程式碼塊。但引用程式併發程式較高會影響程式效能。

Iterator和ListIterator的區別?

  前者只能正向遍歷,適用於移除元素。後者繼承前者,針對List,可以兩個方向遍歷同時支援元素修改。

ArrayList、Vector和LinkedList的區別?

  均包含在java.util包,均為可伸縮陣列,即可動態改變長度。

  ArrayList和Vector都是基於儲存Object[] array實現。它們會在記憶體中開闢一塊連續空間儲存,由於資料儲存是連續的,所以支援序號(下標)來訪問元素,同時索引資料的速度比較快。但插入元素需要移動容器中的元素,對資料插入操作執行比較慢。ArrayList和Vector都有一個初始化容量。當裡面儲存的元素超過這個大小,就需要動態擴充套件它們的儲存空間。Vector預設擴充為原來2倍(每次擴充大小可以設定),而ArrayList預設擴充為原來1.5倍(沒用提供設定空間擴充方法)。

  兩者最大區別是synchronization的使用。ArrayList的方法都是非同步。Vector絕大多數方法(add,insert,remove,set,equal,hashcode等)都是直接或間接同步的。使用Vector是執行緒安全的,而ArrayList不是。正因為Vector是執行緒安全的,所以效能略遜於ArrayList

  LinkedList是採用雙向列表實現,對資料的索引需要從列表頭開始遍歷,因此用於隨機訪問效率比較低,但插入元素時不需要對資料進行移動,所以插入效率高,但其是非執行緒安全容器。   實際運用如何抉擇?對資料主要是索引或只在集合末端增加刪除,採用ArrayList和Vector。對資料操作主要為指定位置刪除或插入,用LinkedList效率高。多執行緒中使用,選用Vector。

HashMap、HashTable、TreeMap和WeakHashMap有什麼區別?

  HashMap是根據鍵的HashCode值儲存資料,根據鍵可以直接獲取其值,具備很快訪問速度。HashMap和HashTable都採用hash法進行索引,兩者具備許多相似處,但也有區別。

HashMap和HashTable的區別?

  1)HashMap是HashTable輕量級實現(非執行緒安全實現),都完成了Map介面。但HashMap允許空(null)鍵值(僅一條),而HashTable不允許。

  2)HashMap去掉HashTable的contains方法,改成containsvalue和containsKey,因為contains方法容易讓人引起誤解。HashTable繼承自Dictionary,而HashMap是Java1.2引入的Map interface的一個實現。

  3)HashTable是執行緒安全的,HashMap不支援執行緒同步,所以不是執行緒安全。效率上,HashMap可能高於HashTable。

  4)HashTable使用Enumeration,HashMap使用Iterator

  5)HashTable和HashMap採用的hash/rehash演算法幾乎一樣,效能差異性不大。

  6)HastTable中,hash陣列預設是11,增加方式是old*2+1。HashMap,hash陣列預設大小是16,一定是2的倍數。

  7)hash值使用不同,HastTable直接使用物件的hashCode

  使用最多的是HashMap,HashMap存入的鍵值對在取出時沒有固定順序,是隨機的。 一般來說,在Map中插入刪除和定位元素,最好的是HashMap.由於TreeMap實現SortedMap介面,能夠把它儲存的記錄根據鍵排序,因此取出來是排序的鍵值對。如果需要按自然順序或自定義順序遍歷鍵,則選TreeMap。

  LinkedHashMap是HashMap的子類,如果需要輸出順序和輸入相同,則用其實現,其還可以按讀取順序來排序。

  WeakHashMap與HashMap類似,不同是WeakHashMap的Key採用“弱引用”的方式,只要key不再被外部引用,就會被GC回收。而HashMap中的Key是強引用。當Key不被外部引用,只有該Key從HashMap刪除後才給GC回收。

HashTable上下文中,同步指的是?

  同步指一個時間點只能有一個執行緒修改hash表,任何執行緒在執行HashTable的更新操作都需要獲取物件鎖,其他執行緒則等待鎖的釋放。

HashMap同步如何實現?**

  HashMap通過Map m = Collections.synchronizedMap(new HashMap())達到同步。該方法返還同步Map,該Map封裝底層HashMap所有方法,使得底層的HashMap即使在多執行緒的環境中也是安全的。

自定義HashMap或HashTable的key需要注意的問題?

  兩者都不能儲存重複的鍵。   HashMap中新增鍵值對Key-Value,經過步驟:   呼叫key的hashCode()方法生成一個hash值h1,如果這個h1在HashMap中不存在,那麼直接將Key-Value新增到HashMap,反之則找出HashMap所有hash值為h1的key,分別呼叫key的equals方法判斷新增的key是否與已存在的key值相同。若equals方法返還true,則表明當前新增key已存在,則用新value代替舊value。反之,說明不存在,因此會在HashMap建立新的對映關係。當新增加的key的hash值已在HashMap中存在則產生衝突。一般對於不同key值得到相同hash值就要對衝突處理。解決衝突方法有開放地址法,再hash法,鏈地址法等。

  HashMap採用鏈地址法解決衝突   注意問題:若想根據物件相關屬性判斷物件是否相等邏輯,需要重寫equals和hashCode方法。最好把作為key的類設定為不可變類。若兩個物件相等,則有相同hashCode,反之不成立。

執行緒是什麼?與程序區別?為何使用多執行緒?

  執行緒是程式在執行過程中能夠執行程式程式碼的一個執行單元。擁有四種狀態:執行,就緒,掛起和結束。   程序是指一段正在執行的程式,而執行緒有時也被稱為輕量級程序,是程式執行的最小單元,一個程序可擁有多個執行緒,各執行緒間共享程式記憶體空間(程式碼段,資料段和堆空間)及一些程序級資源(如開啟的檔案)。但每個執行緒擁有自己棧空間。   作業系統級別,程式執行都以程序為單位,每個程序都有多個執行緒互不影響併發執行。   使用多執行緒原因:能減少程式響應時間。單執行緒(執行過程中僅有一個有效操作序列,不同操作有明確先後順序)情況下,某操作很耗時或陷入長時間等待(等待網路響應),程式不會響應滑鼠和鍵盤燈操作。多執行緒可以把耗時執行緒分配到單獨執行緒執行,使得程式具備良好互動性。與程序比,執行緒建立和切換開銷更少。多執行緒在資料共享方面效率高。多CPU或多核計算機本身具備執行多執行緒能力,多執行緒能提高CPU利用率。多執行緒能簡化程式結構,使得程式便於理解和維護。複製執行緒可以分多個執行緒執行。

同步和非同步的區別?

  實現同步方式:同步程式碼塊,同步方法   非同步與非阻塞類似,每個執行緒都包含執行時所需要資料或方法。     你喊我吃飯,若聽到一起吃,否則不聽喊直到我聽到才一起吃。【同步】     你喊我吃飯,你去吃了,我得到訊息可能立即走,也可下班才去吃。【非同步】

Java如何實現多執行緒?

  (1)繼承Thread類,重寫run方法。   (2)實現Runnable介面,實現run方法。【將自定義例項化作為引數例項化Thread物件,然後使用(1)的方式起動執行緒】

前兩種都是用Thread的start方法起動執行緒,start方法後並不立即執行多執行緒程式碼而是使該執行緒變成可執行態,何時執行由作業系統決定。

  (3)實現Callable介面【Executor框架中功能類】,重寫call()方法。

為什麼Callable介面比Runnable更強大?

  1)其在任務結束後提供一個返回值,Runnable不提供;   2)call方法可以丟擲異常,而Runnable的run方法不能;   3)Callable可以得到Future物件,該物件表示非同步計算結果,提供檢查計算是否完成方法,執行緒屬於非同步計算模型,無法從別的執行緒得到函式返回值,可以使用Future監視目標執行緒呼叫call方法情況,利用Future的get方法得到結果,當前執行緒會阻塞,直到call方法結束返還結果。

為什麼推薦Runnable方式?

  Thread類定義多種方法可被派生類使用或重寫。但只有run方法是必須重寫的。也是實現Runnable所需的方法。一個類僅在需要被加強或修改才被繼承。若無必要重寫Thread類其他方法,那麼繼承Thread和實現Runnable一樣,這種情況最好使用Runnable。

一個類是否可以同時繼承Thread實現Runnable

  可以。Thread類的run方法被認為是實現Runnable介面。

run方法和start方法區別?

  系統呼叫執行緒start方法,使執行緒處於就緒狀態,由JVM通過run方法排程。

  如果直接用執行緒run方法,僅為普通方法,程式僅有主執行緒一個執行緒。start方法可以非同步呼叫run方法,直接呼叫run方法是同步,無法達到多執行緒目的。

多執行緒同步實現方法有哪些?

(1)synchronized關鍵字   執行緒呼叫物件一段synchronized程式碼,先要獲取鎖,然後執行,結束後釋放鎖。   可以用於靜態方法,類或某個例項,對程式效率影響大。   【1】synchronized方法     該方法一個時刻只能被一個執行緒訪問。當方法體規模巨大,會影響效率。   【2】synchronized塊     可以指定上鎖物件。靈活性高。對任意程式碼段宣告為synchronized (2)wait方法和notify方法 (3)Lock     JDK5提供ReentrantLock(重入鎖),Lock可以用來實現多執行緒同步。提供以下方法:

  1. lock(),以阻塞方式獲取鎖。如果獲得則返回,若別的執行緒持有則等待直到獲得返還。
  2. trylock(),以非阻塞式獲得鎖(嘗試性)。如果獲得返還true,否則false。
  3. trylock(long timeout,TimeUit unit),獲得鎖返還true,否則會等待引數給定的時間單元,在等待過程中獲得鎖,則返回true,如果超時則false。
  4. lockInterruptibly(),獲得鎖則返還,沒有則處於休眠狀態直到獲得鎖,或當前執行緒被別的執行緒中斷(會收到InterruptedException異常)。

與lock方法區別在於,lock方法不獲得鎖則阻塞並忽略interrupt()方法。

sleep和wait方法的區別?

  sleep方法適執行緒暫停執行一段時間,wait也是如此。

  1)原理不同。sleep是Thread類靜態方法,是執行緒控制自身流程,它會使此執行緒暫停執行一段時間,而把其他執行機會讓給其他執行緒,等到計時時間一到,此執行緒會自動“甦醒”。wait是object方法,用於執行緒間通訊。該方法會使當前擁有對該物件鎖的程序等待,直到其他執行緒呼叫notify(notifyAll)方法才醒來。

  2)對鎖的處理機制。sleep主要作用是讓執行緒暫停執行一段時間,時間到了自動恢復,不設計執行緒間通訊,因此sleep不釋放鎖。而wait方法會釋放佔用所,從而使執行緒所在物件中其他synchronized資料可被別的執行緒使用。

  3)使用區域不同。由於wait,notify,notifyall方法不需要捕獲異常,sleep過程中有可能被其他物件呼叫其interrupt,產生InterruptedException異常。

  由於sleep不會釋放“鎖標誌”容易導致死鎖問題,因此推薦wait方法。

sleep和yield方法的區別?

  1)前者給其他執行緒執行機會不考慮執行緒優先順序。後者只會給相同優先順序或更高優先順序執行緒執行機會。   2)前者使得執行緒進入阻塞狀態,在指定時間內不會被執行。後者使得當前執行緒回到可執行狀態,所以該執行緒可能剛進入可執行狀態馬上又被執行。

終止執行緒方法有哪些?

  stop或suspend方法終止執行緒執行。前者會釋放所有已鎖定的所有監視資源。如果當前任何一個受這些監視資源保護的物件處於不一致的狀態,其他執行緒將會“看”到這個不一致的狀態,會導致程式執行不確定性。後者容易發生死鎖,該方法不會釋放鎖,導致,如果suspend一個有鎖的程序,鎖恢復之前不會被釋放,如果呼叫suspend方法,執行緒試圖取得相同鎖,就會發生死鎖。

  終止執行緒方法?建議方法是讓執行緒自行結束進入Dead狀態。可以通過設定flag標誌控制迴圈是否執行,讓執行緒離開run方法而終止執行緒。

synchronized與Lock有什麼異同?

  這兩個都是對某個共享資源同步。synchronized使用object物件本身的notify,wait,notifyAll排程機制。而Lock使用Condition進行執行緒之間的排程,完成synchronized實現的所有功能。

  區別   1)用法不一樣。在需要同步的物件中加入synchronized控制,synchronized即可以加在方法上,也可以加在特定程式碼塊中,括號中表示需要鎖的物件。而Lock需要顯示指定起始位置和終止位置。synchronized是託管給JVM執行,Lock的鎖定是由程式碼實現,比synchronized更精確的執行緒語義。

  2)效能差異。ReentrantLock不僅擁有和synchronized相同的併發性和記憶體語義,還多了鎖投票、定時鎖、等候和中斷鎖等。在資源競爭不是很激烈情況下,synchronized優於ReentrantLock,反之synchronized效能下降很快,而ReentrantLock效能基本保持不變。

  3)鎖機制不一樣。synchronized獲得和釋放的方式都是在塊結構,當獲取多個鎖,必須以相反順序自動解鎖釋放,不會因異常而導致鎖沒有被釋放引發死鎖問題。Lock需要手動釋放,必須在finally塊中釋放,否則會引發死鎖問題。Lock提供更強大的功能,其tryLock方法是以非阻塞式獲鎖。

  雖然都可以實現多執行緒同步,但不要同時使用這兩種。因為兩者機制不同,執行是獨立,相當於兩種不同所,使用時不影響。

  synchronized:靜態方法的同步鎖是當前類的位元組碼,與非靜態的方法不能同步(非靜態方法用的是this),【使用同步使用非靜態同步方法,不影響其他執行緒呼叫靜態同步方法】

什麼是守護執行緒?

 兩種執行緒:守護執行緒使用者執行緒。守護執行緒又稱為“服務(後臺)程序”,是指程式執行時在後臺提供一種通用服務執行緒,並非是程式不可或缺部分。任何一個守護執行緒都是整個JVM中所有非守護執行緒的“保姆”。  守護執行緒和使用者執行緒幾乎一樣,不同是若使用者執行緒全部退出執行,只剩下守護執行緒存在,JVM也就退出。  守護執行緒具有較低的優先順序,它並非由JVM內部提供,使用者可以自己設定守護執行緒。  設定為守護執行緒的方法就是呼叫start方法之前呼叫setDaemon(true),若是設定false,則是使用者執行緒。當守護執行緒中產生氣體執行緒,這些執行緒預設是守護執行緒,使用者執行緒同理。

join方法的作用?

 呼叫該方法的執行緒在執行玩run方法後再執行join方法後面的程式碼。即將兩個執行緒合併,用於實現同步。可以通過A的join方法等待執行緒A的結束,或呼叫執行緒A的join(2000)來等待執行緒A結束,但最多等待2s。

如何通過JDBC訪問資料庫?

JDBC訪問資料庫的步驟   載入JDBC驅動器,將資料庫的JDBC驅動載入到classpath中,在JavaEE的web開發中,目標資料庫產品的JDBC驅動複製到WEB-INF/lib下。載入JDBC驅動,並註冊到DriverManager中,一般使用Class.forName(String driveName)。建立資料庫連線,取得Connection物件。一般通過DriverManager.getConnection(url,username,passwd)方法實現。url是連結資料庫字串,username表示連線資料庫的使用者名稱,passwd表示連線資料庫的密碼。   建立Statement物件或PreparedStatement物件。執行SQL語句,返還結果集ResultSet物件。依次將ResultSet,PreparedStatement,Connection物件關閉,釋放所佔用的資源。   釋放原因是JDBC驅動在底層通常都通過網路IO實現SQL命令與資料傳輸。

JDBC處理事務採用什麼方法?

  一個事務是由一條或多條對資料庫操作的SQL語句組成的不可分割工作單元。

  JDBC通過commit和rollback方法結束事務操作。commit是完成提交,rollback完成事務回滾(用於在處理事務過程中出現異常的情況)。[位於java.sql.Connection]

  JDBC事務預設自動提交,setAutoCommit(false)設定禁止自動提交。

  JDBC 5種事務隔離級別

  1)TRANSACTION_NONE_JDB:不支援事務。

  2)TRANSACTION_READ_UNCOMMITTED:未提交讀。說明在提交前一個事務可以看到另一個事務的變化。這樣讀“髒”資料,不可重複讀和虛讀都是允許的。

  3)TRANSACTION_READ_COMMITTED:已提交讀。說明讀取未提交的資料是不允許的。這個級別仍然允許不可重複讀和虛讀產生。

  4)TRANSACTION_REPEATABLE_READ:可重複讀。說明事務保證能夠再次讀取相同的資料而不會失敗,但虛讀仍然會出現。

  5)TRANSACTION_SERIALIZABLE:可序列化。是最高的事務級別,它防止讀“髒”資料,不可重複讀和虛讀。

  讀“髒”資料:一個事務讀取了另一個事務尚未提交的資料。【A更新,B讀取A為提交的資料,A回滾,B的是髒資料】

  不可重複讀:一個事務操作導致另一個事務前後兩次讀取不同資料。【B讀資料,A更新操作更改事務B讀的資料,B再去讀取,發現前後兩次不一致。】

  虛讀:一個事務的操作導致另一個事務前後兩次前後查詢的資料量不同。【B讀取資料,A增刪滿足事務A的查詢條件記錄,B再次查詢,查詢到前次不存在記錄或前次記錄不見了。】

  Connection物件的conn.setTransactionLevel可以設定隔離級別,getTransactionIsolation方法確定當前事務級別。

Class.forName的作用?

  把類載入到JVM中,它會返回一個與帶有給定字串名的類或介面相關聯的Class物件,並且JVM會載入這個類,同時JVM會執行該類的靜態程式碼段。

Statement,PreparedStatement和CallableStatement有什麼區別?

  Statement執行不帶引數的簡單SQL,返還生成結果的物件。   PreparedStatement表示預編譯SQL語句的物件,用於執行帶引數的預編譯SQL語句。   CallableStatement提供用來呼叫資料庫中儲存過程的介面,如果有輸出引數要註冊,說明是輸出引數。   Statement和PreparedStatement能夠完成相同功能,但PreparedStatement更優秀:     1、效率更高。使用PreparedStatement執行SQL命令,會被資料庫編譯和解析,並放到命令緩衝區。然後每當執行同一個PreparedStatement物件,由於緩衝區發現預編譯的命令,它會被再解析一次,但不會被再次編譯,是可以重複使用,能有效提高系統性能。要執行插入,更新和刪除等操作,最好使用PreparedStatement。     2、程式碼可讀性和可維護性更好     3、安全性更好。PreparedStatement能防止SQL注入。   SQL注入:把SQL命令插入Web表單遞交或輸入域名或命令請求的查詢字串,最終達到期盼伺服器,達到執行惡意SQL命令的目的。   注入對SQL語句編譯過程有破壞作用,執行階段只是把輸入串作為資料處理,不需對SQL語句解析。   CallableStatement由prepareCall()方法建立,為所有DBMS提供以標準形式呼叫已儲存過程的方法。它從PreparedStatement繼承處理輸入引數方法,還增加呼叫資料庫過程中儲存過程和函式以及設定輸出型別引數的功能。   對儲存過程的呼叫有兩種形式:帶結果引數和不帶。結果引數是輸出引數,是儲存過程的返回值。兩種形式可帶有數量可變的輸入,輸出或輸入和輸出引數。

getString和getObject方法區別?

  JDBC提供getString(),getInt()和getData()方法從ResultSet中獲取資料,當查詢資料集中的資料量較小時,不用考慮效能,使用這些方法完全能夠滿足需求,但是當查詢結果集中的資料量非常大,就會丟擲異常。通常情況下,getObject()方法可以解決這個問題。

  getString(),getInt()等方法被呼叫,程式會一次性把資料放到記憶體中,然後通過呼叫ResultSet的next()和getString()方法獲取資料。當資料量大到記憶體放不下時就會丟擲異常,而使用getObject就不會發生該問題,因為資料不會一次性就被讀到記憶體,每次呼叫會直接從資料庫中獲取資料,因此不會因為資料量過大而出錯。

使用JDBC需要注意的問題?

  首先要建立連線才能訪問;保證釋放不再使用的連線。createStatement和prepareStatement最好放在迴圈外面,用完後需及時關閉。如果不需要執行後的結果集資料就馬上關閉。

JDO是什麼?

  Java資料物件(Java Data Object,JDO)是用於存取某種資料倉庫中的物件的標準化API,使得開發人員能間接訪問資料庫。

  JDO是JDBC補充,提供透明物件儲存,靈活且通用,提供了到任何資料底層的儲存功能。

JDBC和Hibernate區別?

  Hibernate是JDBC封裝,採用配置檔案形式把資料庫連線引數寫到XML檔案中,資料庫訪問還是通過JDBC完成。   Hibernate是持久層框架,將表資訊對映到XML,再從XML對映到持久化類。這樣就可以使用Hibernate獨特查詢語言HQL ,該語言返還List<Object[.]>類,而JDBC通過statement返還結果是ResultSet有時需要自己封裝到List中。   最重要區別,Hibernate具有訪問層(DAO類),該層是HQL查詢語句唯一出現的位置,再往上不會出現查詢語句。而JDBC可以隨時連線隨時訪問。   假設100個類都有SQL查詢語句,表明改變,JDBC要重寫所有語句,Hibernate只需改寫DAO層的類,因此Hibernate具有良好維護性和擴充套件性。

結語

  本次博文對何昊出版的《java程式設計師面試寶典》的第四章關於Java一部分基礎知識(4.7-4.10)的概括筆記,刪除其中部分程式碼,試題和一部分相對簡單的內容題目。如果出現有誤的地方,歡迎指正。希望對Java基礎部分的面試有幫助、