java 面試題總結(一)

從網上找了些面試題,本身手工總結了理解了一下,若有理解錯誤,還請指正。javascript

java基礎html

1.String 爲何是final的?
    String做爲引用類型,類設計成final的,不讓任何子類有機會繼承它,目的時保證String能夠做爲一個不可變的對象存儲起來。
    String使用很是頻繁,所以設計成一種相似於int那樣的值傳遞,須要有直接的值,並且值不能被改變,若是String的值可以被輕易改變將是很是不安全的。
    String做爲HashMap的key以及其餘場合的時候,須要保持不可變性,不然會發生錯誤,所以須要聲明成final的。
    String底層使用char數組實現,char數組雖然是final的,可是數組的內容能夠被改變(引用不變),所以String設計成final的,保證不會被其餘程序修改String的行爲,致使錯誤。
    final方法編譯器會採起內聯方式實現,JVM執行效率獲得提升。
 
2.HashMap的源碼,底層結構和實現原理
    底層結構是一個Entry數組+鏈表。
    HashMap存放元素時,獲取key的hashCode而後調用hash方法,得到Entry數組的下標。而後指定的這個value就會存儲到這個數組下標位置。若是這個數組下標的位置已經有元素存在了,就將value插入數組下標的頭元素前,造成一個鏈表。Entry做爲一個HashMap一個靜態內部類,內部持有了key,hash,value和下一個Entry的引用以及。每一個Entry都是一個鏈表。
get元素時,根據傳入的key,獲取hash值,找到數組的下標。而後在這個鏈表中不停地迭代下一個Entry,根據key的字面值,獲取對應的value。
源碼中還包括了key值爲null的put和get狀況,數組初始化,超過負載因子時的再哈希算法等等內容。
    實際使用中要儘可能避免哈希表屢次擴容,由於擴容再哈希是十分消耗性能的作法;要注意key值必定要是不可變的元素並且符合equals約束和hashCode約束;儘可能不要用null值做爲key等。貼上兩篇經典文章:
 
3.說說你知道的java集合及其實現類,如list,set,map等
    java集合類主要是實現了Collection,Map接口。其中Collection實現了Iterable,這個頂級的接口提供了元素的迭代器,凡是須要遍歷類的值,都須要這個迭代器(list雖然能夠經過下標遍歷,可是不能在遍歷時作插入和刪除操做)。
    此外還有一個ListIteraotr,能夠用來在迭代時插入刪除並支持雙向遍歷。
    List和Set實現了Collection接口,其中List是有序的,容許元素重複的相似於數組的集合。Set是無序的不容許重複的底層是HashMap實現的用於模擬數學意義上集合的集合
    List和Set依然是接口,他們向下生成了AbstractList和AbstractSet兩個抽象類,抽象類定義了一些基本的約束條件,而後再向下延伸出具體平常使用中的工具類。
    List下面有ArrayList,LinkedList和Vector幾種集合類型,其中ArrayList平常使用最多,LinkedList是使用鏈表實現的List,提供快速的插入和刪除,可是遍歷較慢。Vector是線程安全的List,可是設計上有一些問題,用得很少,就算是須要同步功能,也能夠經過Collections來獲取一個同步的ArrayList。
    Set下面有HashSet,EnumSet,TreeSet,LinkedHashSet等,其中HashSet底層是使用沒有value的Map來實現的。TreeSet使用了一個排序的平衡二叉樹,所以沒有HashMap的性能困擾,須要時能夠從HashSet直接轉換爲TreeSet。LinkedHashSet提供了爲Set排序和保持順序的功能。
    Map接口下面是AbstractMap,抽象類,接下來是Map的其餘具體實現。
    HashMap,EnumMap,HashTable,LinkedHashMap,WeakHashMap等
    HashMap是標準實現,用得最多,上面已經解釋過了。EnumMap是使用枚舉類型做爲key的map,HashTable是同步的Map,不容許null值,由於同步因此沒有HashMap的快速失敗機制。HashTable不方便轉換爲有序Map。LinkedHashMap只是對HashMap作了一層簡單的包裝,保證放進去和拿出來的順序是同樣的。TreeMap使用樹實現,支持排序等操做。WeakHashMap內部使用弱引用來管理數據,能夠被垃圾回收器回收,所以是做爲緩存實現的極好的方式。Properties是繼承自Map的實現鍵值對數據管理的一個簡單的工具類。
     Collection的另外一個分支是隊列,提供了ArrayDeque,PriorityQueue等實現,這些單線程的使用很少,而相似於ArrayBlockingQueue,ConcurrentLinkedDeque,LinkedBlockingDeque,SynchronousQueue等可用於併發環境下的隊列在平常工做中使用最多!
     最後是關於線程安全的問題,雖然Vector和HashTable提供了這個功能,可是由於種種緣由實際工做中使用的並很少。就算是要對List,Set,Map作同步操做,也能夠經過:Collections.synchronizedCollection / synchronizedList / synchronizedMap / synchronizedSet / synchronizedSortedMap / synchronizedSortedSet來獲得一個安全的集合。另外concurrent包下的一些包裝集合類,也能夠實現同步的功能,內部機制暫時還不清楚,可是不管在何時,多線程的操做都是須要很是注意的,並非用一個class就可以一勞永逸不用管的。由於這些類也只能保證在讀取的時候不修改,刪除的時候不迭代,而沒法阻止兩個線程前後修改數據(業務上)。
     參考資料
    【java集合類詳解 】

4. 描述一下ArrayList和LinkedList各自實現和區別  java

     ArrayList是線性表 ,隨機取數快,數據刪除插入較慢,佔用內存小面試

     LinkedList是鏈表  插入刪除快,查找遍歷較慢佔用內存稍大算法

5. Java中的隊列都有哪些,有什麼區別。編程

    隊列是實現生產者消費者的重要工具,java中的隊列通常氛圍阻塞的和非阻塞的,或者是根據須要有其餘多重的實現方式:api

    ArrayBlockingQueue——帶邊界的阻塞式隊列數組

    ConcurrentLinkedDeque / ConcurrentLinkedQueue——無邊界的鏈表隊列(CAS)緩存

    DelayQueue——元素帶有延遲的隊列安全

    LinkedBlockingDeque / LinkedBlockingQueue——鏈表隊列(帶鎖),可設定是否帶邊界

    LinkedTransferQueue——可將元素`transfer`進行w/o存儲

    PriorityBlockingQueue——併發PriorityQueue

    SynchronousQueue——使用Queue接口進行Exchanger

    Queue在設計之處就考慮了多線程的安全性,表明性的Queue的兩種實現,表示實現線程安全的兩套方案:•BlockingQueue,•ConcurrentLinkedQueue

    前者是阻塞的同步的,後者是併發的。

    這裏提一下多線程安全問題的含義,以前有些搞混了。線程安全的類 ,指的是類內共享的全局變量的訪問必須保證是不受多線程形式影響的。若是因爲多線程的訪問(好比修改、遍歷、查看)而使這些變量結構被破壞或者針對這些變量操做的原子性被破壞,則這個類就不是線程安全的。實現線程安全又有兩種方式,一種是同步的,將多線程的請求排個隊,A線程進來了關上廁所門,其餘線程在外面等待。A線程執行結束,B線程進入一樣關上門。這樣的操做保證了多線程下共享變量不會被修改,可是效率比較差,若是線程太多會致使嚴重的鎖爭用。還有一種方式是併發的,多個線程能夠同時進入一段代碼,如何保證線程安全呢?就是經過各個線程之間對共享變量的修改和操做都是可見的,你改了什麼我必須第一時間知道,而後針對性地作一些操做。這樣極大地提升了處理速度,只是算法的實現會很是的複雜。另外,類自身實現線程安全,只是表明類自身的api保持原子性,可是外部使用多個api的狀況下,仍是要手動進行加鎖。concurrent包下的集合類實現,都是採用的併發方式,而Collections工具類包裝的集合都是使用同步鎖方式。其中CurrentHashMap應該是使用了分段鎖和volatile。java併發:同步容器&併發容器 - 煙雨暗千家 - 博客園 

    LinkedList實現了Queue接口,也能夠做爲隊列使用,可是不能夠訪問非Queue接口的方法,以避免發生錯亂。

    BlocingQueue:ArrayBlockingQueue,LinkedBlockingQueue(入隊和出隊兩把鎖),DelayQueue,PriorityBlockingQueue(公平鎖),SynchronousQueue。

    ReentrantLock     只鎖部分代碼塊

    ConcurrentLinkedQueue:使用volatile關鍵字使共享變量可見,具體細節頗爲複雜。

   【Java多線程總結之聊一聊Queue - I'm Sure - ITeye技術網站 】【java中線程隊列BlockingQueue的用法-shwenwen-ITPUB博客 】

    這個坑很深,目前只能研究到這裏了……

6. 反射中,Class.forName和classloader的區別。

    Class.forName()方法底層調用Class.forName(className,true,classloader);表示在加載類的時候,執行初始化步驟;

    ClassLoader.loadClass()方法底層調用ClassLoader.loadClass(className,false);表示不執行初始化步驟;

    初始化步驟會初始化靜態變量和靜態代碼塊,若是此處沒有初始化,那就須要在newInstance時,初始化了。根據不一樣的須要,調用相應的方法。

    可能會引伸到JVM裝載類的過程:編譯解釋java文件爲二進制class,裝載class(經過類的徹底限定名和類加載器實例),連接(對二進制字節碼校驗,初始化靜態變量,找到父類接口和類,肯定調用的方法和類都是存在的且具有權限),初始化(靜態代碼塊其餘)。 

7. Java七、Java8的新特性

    java 7 ,java8 新特徵 

    java7的更新多數是語法糖:

    try包含資源代碼塊,自動釋放;多個catch合併;更強的類型推測,new的時候再也不須要指定類型,直接<>便可;二進制字面量支持;經過[],{}初始化集合。

     java8

    Lambda表達式與函數接口,接口支持默認方法和靜態方法,可以使用相似於C++的方法引用 class::method,重複註解,更強的類型推測,可將參數名稱保存在字節碼中,Optional 支持對null的處理,Stream 相似於mapReduce的集合處理方式,api能夠直接連綴調用,Date/Time API 的更新,在兼容舊api分基礎上提供了許多新的api,傳聞java 9 將進一步支持貨幣api,base64直接支持,支持運行javascript以及js和java的相互調用(java8就有了?),jvm的permGen移除,類依賴分析器jdeps。

8. Java數組和鏈表兩種結構的操做效率,在哪些狀況下(從開頭開始,從結尾開始,從中間開始),哪些操做(插入,查找,刪除)的效率高。

    數組 不能動態擴容,佔內存小,添加刪除操做稍慢,查詢快。

    鏈表 能夠動態擴容,佔內存大,添加刪除操做快,查詢慢。

9. Java內存泄露的問題調查定位:jmap,jstack的使用等等。

    沒用過很差說

10. string、stringbuilder、stringbuffer區別

    String 是final的字符串,不可變。StringBuffer是線程安全的可變String,StringBuilder是簡化的可變字符串,不是線程安全的。

11. hashtable和hashmap的區別

    上面有所說起:

    1. HashTable不支持用null做爲key,而HashMap支持

    2. HashTable是線程安全的map集合,HashMap不是線程安全的。

    3. HashTable由於支持線程安全,因此沒有HashMap的快速失敗機制,可是HashTable不能保證多個api複合調用時保證安全。

    4. Map想要支持多線程安全能夠經過Collections工具類獲取同步的Map,或者經過concurrent包中的concurrentHashMap返回併發的Map,並且能夠與有序的TreeMap轉化,不少時候,即使是多線程環境下也並不必定須要HashTable。

13 .異常的結構,運行時異常和非運行時異常,各舉個例子。

    單獨再說

14. String 類的經常使用方法

     length() , subString , indexOf , lastIndexOf , charAt , equals ,toUpperCase , equalsIgnoreCase , trim , toString , toLowerCase , replace , match(用於正則) , startsWith , compareTo. format(容許相似於C語言的輸出字符串格式控制)

15. Java 的引用類型有哪幾種

      String , Object ,基本類型的包裝類型:Integer,Long , Boolean , Short , Float,Double,Byte,Character,Void。Enum枚舉類型

16. 抽象類和接口的區別

      從語法上來看:  

參數 抽象類 接口
默認的方法實現 它能夠有默認的方法實現 接口徹底是抽象的。它根本不存在方法的實現
實現 子類使用extends關鍵字來繼承抽象類。若是子類不是抽象類的話,它須要提供抽象類中全部聲明的方法的實現。 子類使用關鍵字implements來實現接口。它須要提供接口中全部聲明的方法的實現
構造器 抽象類能夠有構造器 接口不能有構造器
與正常Java類的區別 除了你不能實例化抽象類以外,它和普通Java類沒有任何區別 接口是徹底不一樣的類型
訪問修飾符 抽象方法能夠有publicprotecteddefault這些修飾符 接口方法默認修飾符是public。你不可使用其它修飾符。
main方法 抽象方法能夠有main方法而且咱們能夠運行它 接口沒有main方法,所以咱們不能運行它。
多繼承 抽象方法能夠繼承一個類和實現多個接口 接口只能夠繼承一個或多個其它接口
速度 它比接口速度要快 接口是稍微有點慢的,由於它須要時間去尋找在類中實現的方法。
添加新方法 若是你往抽象類中添加新的方法,你能夠給它提供默認的實現。所以你不須要改變你如今的代碼。 若是你往接口中添加方法,那麼你必須改變實現該接口的類。

       從本質上來看:

       抽象類是對子類的一個抽象,抽象類提供了子類的某些方法約束,做爲公共的你們都必須遵照的約束,有利於直接複用代碼。不能肯定的子類行爲能夠採用抽象方法定義,交給具體子類實現。抽象類更多的是在具體代碼編寫時的一種設計。

       接口是定義一組約束規則,做爲完全的抽象層存在。它只要求實現者實現全部的方法,而不關心具體的實現方式,它只爲子類指定功能,可是不存在對子類實現方式的要求,也就是說沒有公共的已經實現的方法。更多的是用於軟件架構設計中,對不一樣模塊的抽象。

       java8中支持了接口的默認方法,具體後面再研究。

18. java的基礎類型和字節大小

       java中基本數據類型(不是引用類型)有8種

 

byte Byte 1 -128 127
char Character 2 unicode-0 unicode 2^16-1
short Short 2 -2^15  2^15-1
int Integer 4 -2^31 2^31-1
float Float 4    
double Double 8    
long Long 8    
boolean Boolean - true false
       對char不太理解,由於char是2個字節,可是unicode中utf-8的中文是3個字節,產生了混亂。後來有人建議不要用char類型。由於char類型使用的老舊的utf16編碼,65535個字符根本不夠囊括全部語言的字符。這裏記錄如下防止被坑吧……

19. Hashtable,HashMap,ConcurrentHashMap底層實現原理與線程安全問題。

      HashMap沒有作多線程的控制,底層實現原理以前有提到過。HashTable是使用了重用鎖,在操做時鎖住了一段代碼塊,是同步的。ConcurrentHashMap,內部使用了volatile關鍵字,使得共享變量在多線程之間可見和同步,編程上考慮的比較多算法比較複雜。具體的上面的文章中有提到過。

20. Hash衝突怎麼辦?哪些解決散列衝突的方法?

      hash衝突應該是hash算法的問題,也有多是hashCode算法的問題。這個//todo

21. HashMap衝突很厲害,最差性能,你會怎麼解決?從O(n)提高到log(n)。

      首先哈希表是經過哈希函數計算元素的哈希值,而後將次哈希值做爲要存放的數組地址的下標,以便達到用數組存放元素,同時可以實現較快的查找刪除操做。

      這裏哈希函數就很是重要,若是哈希函數計算的不一樣元素的哈希值容易重複,就會致使哈希衝突(不一樣元素映射到同一下標),哈希表不得不額外地解決這些衝突。

      好的哈希函數會盡可能避免衝突,能均勻的分佈。以及計算簡單。

      解決哈希衝突的辦法主要有下面幾種:

      開放地址法:若是發現哈希值下標已經被佔用,就向下查找下一個空元素存入,問題是很是容易產生集聚現象,使得衝突加重。其中也有一些變種辦法,例如不直接向下查找下一個元素,而是生成不一樣的查找步數,這個能夠根據隨機數或者二次哈希產生。

      再哈希法:衝突後再次執行另外一個哈希算法,獲得新的地址。

      鏈地址法:若是哈希衝突了,就直接將衝突的元素接在原佔有的元素後面,造成一個鏈表。

      而HashMap就是採用這種辦法作的。HashMap的性能,也是與其hash函數有關,當存放的key是用戶自定義的對象而不是String的時候,須要重寫這個對象的hashCode方法,這個hashCode方法的效率就會影響到HashMap的哈希函數的效率,若是HashCode不能作到計算簡單隨機分佈,hash函數就會容易衝突。

      HashMap衝突很嚴重的話,還能夠將HashMap轉化爲平衡樹實現的TreeMap,後者的性能是logn.

23. rehash

      看起來應該是HashMap的再哈希算法,主要用於當HashMap的容量超過負載因子(致使元素容易集聚成鏈表),HashMap就會擴容,全部的元素的哈希值就要從新計算,rehash大概就是用於此處的。rehash比較消耗性能,所以應該避免HashMap頻繁擴容。

24. hashCode() 與 equals() 生成算法、方法怎麼重寫。

      hashCode

      hashCode用於生成標記一個對象的最好是惟一的散列碼,它將被用在HashMap等散列表中,哈希函數會根據該散列碼計算數組下標從而存放起來。

      所以若是散列碼是同樣的話,在HashMap中認爲是同一個對象,若是不同,它就會認爲這個一個新的對象。

      你們說在重寫equals時重寫hashCode方法,是爲了不對象在用在哈希表的鍵時形成的問題。

      設想重寫了equals方法,兩個不一樣的對象被認爲是相等的,咱們把相等的對象存到map的key中,而後再根據這個相等的對象取value的時候,就會出錯。由於HashMap根據hashCode計算出來的兩個對象的值不一樣,HashMap認爲是不同的。因此就找不到當初存放的對象。

      因此hashCode須要重寫,而且保證兩個值相同的對象,它的hashCode必定是相同的。(若是固定地返回某個常量值,會致使系統會將全部的這種類型的對象都視爲相同。)

      重寫hashCode方法是一項技術活,不過一些簡單的重寫方案仍是有的。下面的代碼從《effective java》中摘錄

 

/**此處的變量都是int類型*/
@Override
public int hashCode(){
int result = 17;
result = 31*reuslt + areaCode;
result = 31*result + prefix;
result = 31*result + lineNumber;
return result;
}

 

  

 

      equals

/**
* equals方法須要知足自反性,對稱性,傳遞性,一致性和非空性
* equasl方法寫完了要進行相關的測試
* 過程是:
* 檢查被比較的對象是否是本身,若是是返回true
* 檢查被比較的對象是否是null,若是是返回false
* 檢查被比較的對象是否是和本身是同一個類型的,若是不是返回false
* 將被比較對象強制轉換成本身的類型,而後逐一比較值。
* 對於float,double,數組等格式的域,須要使用特殊的比較方法,而不能用==。
*
*/
@Override
public boolean equals(Object obj){
if(this == obj)return true;
if(obj == null)return false;
if(obj.getClass() != this.getClass())return false;//!(obj instanceof Te1)
Te1 t = (Te1)obj;
if(!t.name.equals(name))return false;
if(!(t.id==id))return false;
if(Double.compare(salary,t.salary)!=0)return false;
if(!Arrays.equals(strs,t.strs))return false;
return true;
}

  

----------------------------------------------------------------------
以上
相關文章
相關標籤/搜索