1. 程式人生 > >07-撩課-Java面試寶典-第七篇

07-撩課-Java面試寶典-第七篇

61.什麼是併發修改異常?
什麼是併發修改異常:
當我們在遍歷實現了collection介面
與iterator介面的集合時(List、Set、Map), 
我們可以通過遍歷索引
也可以通過迭代器進行遍歷。
在我們使用迭代器進行遍歷集合的時候,
會獲取到當前集合的迭代物件。
在裡面有封裝了迭代器的remove方法
與集合自帶的remove方法,
如果我們呼叫迭代器物件的remove方法
是沒問題的,
但是當我們呼叫集合自帶的remove方法時,
就會產生ConcurrentModificationException 
併發修改異常。
也就是說,
當我們通過迭代器進行遍歷集合的時候,
是不允許集合本身在結構上發生變化的。
62.什麼是CopyOnWriteArrayList,它與ArrayList有何不同?
CopyOnWriteArrayList:
CopyOnWriteArrayList這是一個
ArrayList的執行緒安全的變體,

其原理大概可以通俗的理解為:
初始化的時候只有一個容器,
很常一段時間,
這個容器資料、
數量等沒有發生變化的時候,
大家(多個執行緒),都是讀取
假設這段時間裡只發生讀取的操作
同一個容器中的資料,

所以這樣大家讀到的資料都是
唯一、一致、安全的,
但是後來有人往裡面增加了一個數據,
這個時候CopyOnWriteArrayList 底層實現
新增的原理是先copy出一個容器
可以簡稱副本,

再往新的容器裡新增這個新的資料,
最後把新的容器的引用地址
賦值給了之前那個舊的的容器地址,
但是在新增這個資料的期間,
其他執行緒如果要去讀取資料,
仍然是讀取到舊的容器裡的資料。

Vector 
ArrayList 
CopyOnWriteArrayList 
這三個集合類都繼承List介面
1、ArrayList是執行緒不安全的;
2、Vector是比較古老的執行緒安全的,
但效能不行;
3、CopyOnWriteArrayList在兼顧了
執行緒安全的同時,
又提高了併發性,
效能比Vector有不少提高

63.迭代器和列舉之間的區別?
在Java集合中,
我們通常都通過 “Iterator(迭代器)” 
或 “Enumeration(列舉類)” 去遍歷集合。
Enumeration是一個介面,它的原始碼如下:
package java.util;
public interface Enumeration<E> {
  boolean hasMoreElements()
  E nextElement();
}
Iterator也是一個介面,它的原始碼如下:
package java.util;
public interface Iterator<E> {
  boolean hasNext();
  E next();
  void remove();
}

區別:
1 函式介面不同
Enumeration只有2個函式介面。
通過Enumeration,
我們只能讀取集合的資料,
而不能對資料進行修改。
Iterator只有3個函式介面。
Iterator除了能讀取集合的資料之外,
也能資料進行刪除操作。
2.Iterator支援fail-fast機制,而Enumeration不支援。
Enumeration 是JDK 1.0新增的介面。 
使用到它的函式包括Vector、Hashtable等類,
這些類都是JDK 1.0中加入的,
Enumeration存在的目的
就是為它們提供遍歷介面。
Enumeration本身並沒有支援同步,
而在Vector、Hashtable實現Enumeration時,
添加了同步。
而Iterator 是JDK 1.2才新增的介面,
它也是為了HashMap、ArrayList等集合
提供遍歷介面。
Iterator是支援fail-fast機制的:
當多個執行緒對同一個集合的內容進行操作時,
就可能會產生fail-fast事件。
Java API規範建議,
對於較新的程式,
Iterator應優先於Enumeration,
因為“ Iterator在Java集合框架中
代替Enumeration。”

64.Hashmap如何同步?
1、使用 synchronized 關鍵字,
這也是最原始的方法。
synchronized(anObject)  
{  
    value = map.get(key);  
}  
2、使用 JDK1.5 提供的鎖
Java.util.concurrent.locks.Lock
lock.lock();  
value = map.get(key);  
lock.unlock();  

3.可以使用 JDK1.5 提供的讀寫鎖
java.util.concurrent.locks.ReadWriteLock
rwlock.readLock().lock();  
value = map.get(key);  
rwlock.readLock().unlock();  

4.使用 JDK1.5 提供的 
java.util.concurrent.ConcurrentHashMap 類
該類將 Map 的儲存空間分為若干塊,
每塊擁有自己的鎖,
大大減少了多個執行緒
爭奪同一個鎖的情況
value = map.get(key); 

1、不同步確實最快,與預期一致。 
2、四種同步方式中,
ConcurrentHashMap 是最快的,
接近不同步的情況。 
3、synchronized 關鍵字非常慢
4、使用讀寫鎖的讀鎖,比普通所稍慢。

1、如果 ConcurrentHashMap 夠用,
則使用 ConcurrentHashMap。 
2、如果需自己實現同步,
則使用 JDK1.5 提供的鎖機制,
避免使用 synchronized 關鍵字。 

65.IdentityHashMap和HashMap的區別?
前者比較key時是
“引用相等”
而後者是
“物件相等”,
即對於
k1和k2,當k1==k2時, 
IdentityHashMap認為兩個key相等,
而HashMap只有在k1.equals(k2) == true 時
才會認為兩個key相等。 

2.IdentityHashMap 允許使用null作為key和value. 
不保證任何Key-value對的之間的順序, 
更不能保證他們的順序
隨時間的推移不會發生變化。 

3.IdentityHashMap有其特殊用途,
比如序列化或者深度複製。或
者記錄物件代理。 
舉個例子,
jvm中的所有物件都是獨一無二的,
哪怕兩個物件是同一個class的物件 
而且兩個物件的資料完全相同,
對於jvm來說,
他們也是完全不同的, 
如果要用一個map來記錄這樣jvm中的物件,
你就需要用IdentityHashMap,
而不能使用其他Map實現 

66.如何獲取某個日期是當月的最後一天?
import java.util.Calendar;

public class Test {

public static void main(String[] args) {
  System.out.println(daysCount(2010, 2));
}

public static int daysCount(int year, int month) {
  Calendar cal = Calendar.getInstance();
  cal.set(Calendar.YEAR, year);
  cal.set(Calendar.MONTH, month);
  cal.set(Calendar.DATE, 0);
  return cal.get(Calendar.DATE);
}
}
67.java中會存在記憶體洩漏嗎,請簡單描述
所謂記憶體洩露就是指
一個不再被程式使用的物件或變數
一直被佔據在記憶體中。
Java中有垃圾回收機制,
它可以保證一物件不再被引用的時候,
即物件程式設計了孤兒的時候,
物件將自動被垃圾回收器
從記憶體中清除掉。
由於Java 使用有向圖的方式
進行垃圾回收管理,
可以消除引用迴圈的問題,
例如有兩個物件,相互引用,
只要它們和根程序不可達的,
那麼GC也是可以回收它們的。

java中的記憶體洩露的情況:
長生命週期的物件持有
短生命週期物件的引用
就很可能發生記憶體洩露,
儘管短生命週期物件已經不再需要,
但是因為長生命週期物件
持有它的引用而導致不能被回收,
這就是java中記憶體洩露的發生場景,
通俗地說,
就是程式設計師可能建立了一個物件,
以後一直不再使用這個物件,
這個物件卻一直被引用,
即這個物件無用
但是卻無法被垃圾回收器回收的,
這就是java中可能出現
記憶體洩露的情況,
例如,快取系統,
我們載入了一個物件放在快取中(例如放在一個全域性map物件中),
然後一直不再使用它,
這個物件一直被快取引用,
但卻不再被使用。 
檢查java中的記憶體洩露,
一定要讓程式將各種分支情況
都完整執行到程式結束,
然後看某個物件是否被使用過,
如果沒有,
則才能判定這個物件屬於記憶體洩露。

如果一個外部類的例項物件的方法
返回了一個內部類的例項物件,
這個內部類物件被長期引用了,
即使那個外部類例項物件不再被使用,
但由於內部類持久外部類的例項物件,
這個外部類物件將不會被垃圾回收,
這也會造成記憶體洩露。

記憶體洩露的另外一種情況:
當一個物件被儲存進HashSet集合中以後,
就不能修改這個物件中的
那些參與計算雜湊值的欄位了,
否則,物件修改後的雜湊值
與最初儲存進HashSet集合中時的雜湊值
就不同了,
在這種情況下,
即使在contains方法使用該物件的
當前引用作為的引數去HashSet集合中
檢索物件,
也將返回找不到物件的結果,
這也會導致無法從HashSet集合中
單獨刪除當前物件,
造成記憶體洩露

68.java中實現多型的機制是什麼?
靠的是父類或介面的
引用指向子類或實現類的物件,
呼叫的方法是記憶體中
正在執行的那個物件的方法。

Java實現多型有三個必要條件:
繼承、
重寫、
向上轉型。

繼承:
在多型中必須存在
有繼承關係的子類和父類。

重寫:
子類對父類中某些方法進行重新定義,
在呼叫這些方法時
就會呼叫子類的方法。

向上轉型:
在多型中需要將子類的引用
賦給父類物件,
只有這樣該引用才能夠具備
技能呼叫父類的方法和子類的方法。

只有滿足了上述三個條件,
我們才能夠在同一個繼承結構中
使用統一的邏輯實現程式碼處理不同的物件,
從而達到執行不同的行為。

多型機制遵循的原則概括為
當超類物件引用變數引用子類物件時,
被引用物件的型別
而不是引用變數的型別
決定了呼叫誰的成員方法,
但是這個被呼叫的方法
必須是在超類中定義過的,
也就是說被子類覆蓋的方法,
但是它仍然要根據繼承鏈中
方法呼叫的優先順序來確認方法,
該優先順序為:
this.method(O)、
super.method(O)、
this.method((super)O)、
super.method((super)O)。


69.區域性變數和成員變數的區別?
成員變數與區域性變數的區別
1、在類中的位置不同
成員變數:
在類中方法外面
區域性變數:
在方法或者程式碼塊中,
或者方法的宣告上

2、在記憶體中的位置不同,
成員變數:在堆中(方法區中的靜態區)
區域性變數:在棧中

3、生命週期不同
成員變數:
隨著物件的建立而存在,
隨著物件的消失而消失

區域性變數:
隨著方法的呼叫或者程式碼塊的執行
而存在,
隨著方法的呼叫完畢或者
程式碼塊的執行完畢而消失

4、初始值

成員變數:
有預設初始值

區域性變數:
沒有預設初始值,
使用之前需要賦值,
否則編譯器會報錯

70.什麼是匿名類,有什麼好處?
簡單地說:
匿名內部類就是沒有名字的內部類。

什麼情況下需要使用匿名內部類?
如果滿足下面的一些條件,
使用匿名內部類是比較合適的:
 
只用到類的一個例項。 
類在定義後馬上用到。 
類非常小(SUN推薦是在4行程式碼以下) 
給類命名並不會導致你的程式碼更容易被理解。 

在使用匿名內部類時,要記住以下幾個原則: 
匿名內部類不能有構造方法。 
匿名內部類不能定義任何靜態成員、方法和類。 
匿名內部類不能是
public,protected,private,static。 
只能建立匿名內部類的一個例項。 
一個匿名內部類一定是在new的後面,
用其隱含實現一個介面或實現一個類。 
因匿名內部類為區域性內部類,
所以區域性內部類的所有限制都對其生效。