1. 程式人生 > >Java面試(二)

Java面試(二)

它的 mod ica 克隆對象 機會 沒有 提前 new ble

1 同步方法 VS 同步代碼塊:

  java中,每一個對象都有一把鎖,線程用synchronized獲取對象上的鎖。

  非靜態同步方法:鎖是類的對象的鎖。

  靜態同步方法:鎖的是類本身。

  同步方法塊:鎖是可以選擇的。所以能更加精確的控制。粒度更細致,可以更精確的控制對象鎖。

2 確保N個線程可以訪問N個資源而不死鎖?

  指定資源獲取順序。所有線程都按照同樣的順序請求資源。

3 Java集合框架接口

  Collection: 代表一組對象。

  Set:

  List:

  Map:

4 為什麽集合類沒有實現Cloneable和Serializable接口?

  應該集合中的元素自己實現,定義自己的行為。

5 什麽是叠代器(Iterator)?

它可以使得對於序列類型的數據結構的遍歷行為與被遍歷的對象分離,即我們無需關心該序列的底層結構是什麽樣子的。只要拿到這個對象,使用叠代器就可以遍歷這個對象的內部.

主要方法: hasNext( ) / next( ) / remove(刪除後,指向下一個)

叠代器優點:提供統一的叠代方法;可以在對客戶透明的情況下,提供不同的叠代方法;快速失敗機制,防止多線程下叠代的不安全操作。

6 快速失敗&&安全失敗:

(註意:以下的集合的修改是指,集合結構的變化,增加、刪除節點,修改某一個節點的對象的內容不算是集合結構的變化。)

快速失敗(fail-fast):

  在用叠代器遍歷一個集合時,遍歷過程中,其他線程對集合內容進行修改(增,刪),則

會拋出異常ConCurrentModificationException。叠代器在遍歷的時候直接訪問集合的元素,並且在遍歷過程中使用一modCount變量。集合在被遍歷期間如果內容發生變化,就會改變modCount的值。每當叠代器用hasNext()/next()遍歷一個元素的之前,都會檢測modCount,如果改過,就拋出異常。Java.util包中的集合都是快速失敗的,不能在多線程下發生並發修改。

安全失敗(fail-safe):

  如果在叠代器遍歷concurrentHashMap的過程中,往map中增/刪/改,是不會拋出異常。但是會導致不一致的問題。采用安全失敗機制的集合容器,在遍歷時不是直接在集合內容上訪問,而是先復制原集合內容,在拷貝的集合上進行遍歷。缺點是:不能訪問修改後的內容,

java.util.concurrent包下的容器都是安全失敗的,可以在多線程下並發使用,並發修改。

安全失敗叠代器在叠代中被修改,不會拋出任何異常,因為它是在集合的克隆對象叠代的,所以任何對原集合對象的結構性修改都會被叠代器忽略,但是這類叠代器有一些缺點,其一是它不能保證你叠代時獲取的是最新數據,因為叠代器創建之後對集合的任何修改都不會在該叠代器中更新,還有一個缺點就是創建克隆對象在時間和內存上都會增加一些負擔。

7 Iterator和ListIterator區別

  Iterator可以用來遍歷Set,Map和List,但是ListItrator只能用來遍歷List。

  Iterator對集合只能向前遍歷(hasNext(), Next()).ListIterator是雙向都可以遍歷。

  ListIterator實現了Iterator接口,並包含了其他功能,比如:增加元素,替換元素,獲取前一個和後一個元素的索引。

8 HashMap的工作原理

  鍵值對形式存儲元素。需要一個hash函數,使用hashCode() 和equals()方法來向集合添加,檢索元素。

  Put(key, value) : 計算key的hash值,然後把鍵值對存儲在集合中合適對索引上。Entry存儲在LinkedList中,所以如果存在entry,它使用equals()方法來檢查傳遞的key是否已經存在,如果存在,它會覆蓋value,如果不存在,它會創建一個新的entry然後保存。

  Get(key): 首先是用hashcode找到桶,然後key,equals(),判斷是否相等。

  重要特性是:容量(默認初始值是32),負載因子(默認是0.75),閾值是負載因子乘以容量,如果大於閾值,就會擴容。擴容極限。建議如果在開始的時候知道元素的個數,設定初始值。

容量:桶的數量。

   負載因子:有多少桶被占用了。  

9 HashMap VS Hashtable

  都實現了Map接口,很相似。有如下不同點:

  hashMap的鍵和值是null,hasttable不允許鍵或值是null.

  HashMap不是同步的(適合單線程,是快速失敗的),hashtable(適合多線程)是同步的。

  HashMap提供了鍵集合;而Hashtable提供了對鍵的枚舉。

  Hashtable是遺留的類。

10 數組(Array)和列表(ArrayList)有什麽區別?各自的應用場合?

  數組:可以存放基本類型+對象類型;列表:only對象類型(因為LIst裏的元素必須繼承自object)。

  Array大小固定的; ArrayList是動態變化的。

  ArrayList提供了很多方法: addAll(), removeall(), iterator()等等。

  對於基本類型,集合使用自動裝箱來減少代碼量;但是,當處理固定大小的基本數據類型的時候,這種方式比較慢。

11 ArrayList VS LinkedList

  都是實現了List接口。

  ArrayList底層是數組。可以以o(1)時間復雜度對元素進行隨機訪問。

  LinkedList: 是以元素列表的形式存儲數據,鏈表。查找的時間復雜度是0(n),但是插入,添加,刪除更快。

  LinkedList更費內存,因為要存儲指針。

12 Comparable 和 Comparator?

  實現了Comparable,表明可以比較元素,可以用SORT排序。Comparator看做是算法的實現,將算法和數據分離。

用 Comparator 是策略模式(strategy design pattern),就是不改變對象自身,而用一個策略對象(strategy object)來改變它的行為。

Comparable接口:只包含了一個compareTo()方法,可以進行排序。

Comparator接口:包含compare()和 equals()兩個方法。

二者用法不同:比如在集合中的元素可以自己實現Comparable,這樣就可以用Collections.sort()調用進行排序;如果想定義格外的排序方法,可以實現Comparator接口,Collections.sort()另外一個重載方法可以接收一個compareator.

13 java集合類的最佳實踐

  根據需要正確的選擇要使用的。比如:元素大小固定,事先知道,用 Array 而不是ArrayList

  對於一些容器(hashMap):如果知道容量,可以提前給一個初始容量,避免擴容,再哈西的性能損耗。

  為了類型安全,可讀性和健壯性總是要使用泛型,泛型可以避免ClassCastException

  用JDK提供的不變類(immutable class)作為Map的鍵可以避免為我們的類實現hashCode() 和

  equals() 方法。

  編程的時候接口優於實現。

  底層的集合實際上是空的情況下,返回長度是0的集合或數組,不要返回null。

14 java優先級隊列(Priority Queue)

每次出隊的是優先級最高的。是用大頂堆實現的。要提供一個比較器comparator,否則按照自然順序排列。

Queue<Test> priQueue = new PriorityQueue<Test>(11,OrderIsdn); // 指定一個comparator

// 默認隊列大小是11

Test t1 = new Test("t1",1);

Test t3 = new Test("t3",3);

priQueue.add(t3);

基於優先級堆無界隊列,它的元素按照自然順序排序,在創建的時候可以提供一個比較器。不允許null值。不是線程安全的,入隊和出對的時間復雜度是log(n).

15 什麽是線程安全?

  如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的。

比如一個 ArrayList 類,在添加一個元素的時候,它可能會有兩步來完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。在單線程運行的情況下,如果 Size = 0,添加一個元素後,此元素在位置 0,而且 Size=1;

  而如果是在多線程情況下,比如有兩個線程,線程 A 先將元素存放在位置 0。但是此時 CPU 調度線程A暫停,線程 B 得到運行的機會。線程B也向此 ArrayList 添加元素,因為此時 Size 仍然等於 0 (註意哦,我們假設的是添加一個元素是要兩個步驟哦,而線程A僅僅完成了步驟1),所以線程B也將元素存放在位置0。然後線程A和線程B都繼續運行,都增加 Size 的值。

  那好,現在我們來看看 ArrayList 的情況,元素實際上只有一個,存放在位置 0,而 Size 卻等於 2。這就是“線程不安全”了。
如何做到線程安全:
四種方式 sychronized關鍵字

1. sychronized method(){}          // 同步方法         

2. sychronized (objectReference) {/*block*/} // 同步塊

3. static synchronized method(){}      // static 同步方法

4. sychronized(classname.class)      // 指定同步類

其中1和2是代表鎖當前對象,即一個對象就一個鎖,3和4代表鎖這個類,即這個類的鎖。要註意的是sychronized method()不是鎖這個函數,而是鎖對象,即:如果這個類中有兩個方法都是sychronized,那麽只要有兩個線程共享一個該類的reference,每個調用這兩個方法之一,不管是否同一個方法,都會用這個對象鎖進行同步。
註意:long 和double是簡單類型中兩個特殊的咚咚:java讀他們要讀兩次,所以需要同步。

16 如何權衡用無序數組還是有序數組

  有序數組查找的時間復雜度是O(logn),但是插入操作的時間復雜度是O(n).

  無序的是O(n),但是插入的時間復雜度是O(1).

17 Java中,int 是否是類型安全的。

  Int 的賦值是原子的,但是i++不是。

18 HashSet VS TreeSet

  HashSet 元素是無序的,最多放入一個NULL值,add(), remove(), contains(), 這些方法的復雜度都是O(1)

  TreeSet: 樹形結構實現,元素是有序的,不能放入NULL值。 Add(), remove(), contains()復雜度是log(n).

  

Java面試(二)