BAT常問問題總結以及回答(java基礎回答一)

java 基礎

  1. 八種基本數據類型的大小,以及他們的封裝類
     答:八種數據類型分別是byte(1字節)-128~12七、short(2字節)-32768~3276七、char(2字節)、int(4字節)、long(8字節)、float(4字節)、double(8字節)、boolean(原本是1bit的,可是計算機處理最小的單位是1字節),參數傳遞時候用的是參數傳遞,方法的修改並不能改變它。
    1.  
  2. 引用數據類型
      答:類、接口類型、數組類型、枚舉類型、註解類型、String    使用的是引用傳遞(傳遞的是地址),方法的修改能改變原來的值。
            使用String時候也是引用傳遞,傳的是地址,方法修改卻並不能修改他
        其餘的引用類能夠經過引用傳遞修改內容。
     
  3. Switch可否用string作參數
    答:jdk1.7以前不能夠,可是以後能夠了,將string類型轉化爲hashcode的int類型,從而能夠switch
     
  4. equals與==的區別
    equals比較的是內容是否相同,能夠經過重寫修改其具體含義;
    ==比較的是棧裏的地址,除了內容要同樣之外還要讓他們的類型一致,地址也一致。
     
     
  5. static的用途
    答:static是沒有this的,在類加載時進行加載的。分爲三種:靜態變量,靜態方法,靜態代碼塊
    1. 因爲靜態的在類加載以前就開始加載,而動態的是當new實例化的時候纔開始加載,因此在動態中方法中可使用靜態方法,而在靜態方法中沒法使用動態變量和動態方法。
    2. 一、靜態變量:
        靜態變量是供全部對象享有,只有一個副本,因此在改變他的值的時候,改變以後值就肯定爲修改後的了。而動態變量在每一個對象中有他的對應副本,修改單個對象的動態變量的時候不會修改其餘對象的動態變量。
      二、靜態方法:
            靜態方法不存在this,因此靜態方法能夠不依賴對象而去訪問。動態方法能夠調用靜態方法,相反不行。
    3. 三、靜態代碼塊:加載順序:父類靜態代碼塊,子類靜態代碼塊,其餘靜態代碼塊,父類構造器,子類構造器。靜態代碼塊只會被加載一次,因此對於屢次須要的變量能夠放到靜態代碼塊當中。使用場景:須要初始化的方法和變量,常用的變量。
    4. 優勢:能夠不實例化對象直接使用static,使用方便,效率更高不會隨對象清理自動清理。
    5. 缺點:若是建立了該類的任何實例,不能使用實例來訪問靜態成員,由於靜態成員管理的是整個類的信息,須要的是對象的成員變量。另外加載了靜態,長時間不適用會耗費內存。
  6. 自動裝箱,常量池
      自動裝箱和自動拆箱相對應。自動裝箱的過程是自動將基本類型轉變成包裝器類型;而自動拆箱過程是自動將包裝器類型轉變成基本類型。
      基本類型對應的包裝類型:object——>Number、Boolean->boolean、Character->char
    1. number:byte->Byte   short->Short   int->Integer  long->Long   float->Float  double ->Double裝箱:Integer num = 99;   Integer num = Integer.valueOf(99);
    2. 拆箱:int number = num;  int number = num.intValue();
    3.  
  7. Object有哪些公用方法
    hashcode()  equals()  finalized()  wait() notify()  notifyAll() toString()  clone()  getclass()
     
  8. clone的兩種類型及其原理
      clone分爲深克隆和淺克隆,深淺克隆都須要重寫方法clone,clone()方法須要類實現clonable接口 ,這個接口至關於一個許可,他自己不帶有clone方法,可是沒有實現這個接口的類調用clone方法的話會出現clonenotsupportexception異常。
          單純的object1=object1;  實際上是兩個變量同時指向同一個地址而已,並無生成新的對象。
         
    package test;
    import java.util.ArrayList;
    
    public class A implements Cloneable {
        public ArrayList b = new ArrayList();
    
    
    //    淺克隆
    //    @Override
    //    protected Object clone() throws CloneNotSupportedException {
    //        A a = (A) super.clone();
    //        return a;
    //    }
    //
    //   深克隆
    //    public Object clone() throws CloneNotSupportedException {
    //        A a = (A) super.clone();
    //        a.b = (ArrayList) b.clone();
    //        return a;
    //    }
    
        public static void main(String[] args) throws CloneNotSupportedException {
            A a = new A();
            a.b.add("s");
            A b = (A) a.clone();
            a.b.add("b");
            System.out.println(b.b.size());
        }
    }
  9. Java的四種引用,強弱軟虛,用到的場景
    強引用:強引用的使用就是咱們經常使用到的實例化,A a = new A(),垃圾回收器不能隨意清理強引用的對象,即便是拋出oom異常也不能清理(生活必需品,不能清理)
    軟引用:軟引用通常配合一個引用隊列使用
           Object obj=new Object();
            ReferenceQueue refQueue=new ReferenceQueue();
            SoftReference softRef=new SoftReference(obj,refQueue);
     String str=new String("abc");                                     // 強引用
    
     SoftReference<String> softRef=new SoftReference<String>(str);     // 軟引用

                軟引用通常是當內存不足的時候纔會清除,內存充足的時候調用system.gc()不會清除掉軟引用。(生活無關緊要品,家滿了的時候清除,平時不動)html

    弱引用:也是配合一個引用隊列使用,當被清理的時候,將其放入引用隊列中。當gc掃描到該對象的時候,不管內存是否已滿,都會回收。是一種比軟引用生命力更弱的引用。可是因爲gc的優先級較低,因此被清理的
    String str=new String("abc");    
    
    WeakReference<String> abcWeakRef = new WeakReference<String>(str);
    虛引用:也叫幽靈引用,用來跟蹤對象被垃圾回收器回收的活動。與強引用和軟引用的區別在與他必須和引用隊列配合使用,在虛引用指向的對象要被清除的時候,先將虛引用放入到引用隊列中去,纔是清理所要清理的對象指向。
     
     
  10. Hashcode的做用
    Java中的hashCode方法就是根據必定的規則將與對象相關的信息(好比對象的存儲地址,對象的 字段等)映射成一個數值,這個數值稱做爲散列值。用來快速查找對應對象的,若是兩個對象equals相等(地址和內容都相等),那麼他們的hashcode也必定相同。當重寫對象的equals方法時候,儘可能重寫對象的hashcode方法(在hashmap等須要處理衝突問題的地方是必須重寫hashcode方法的)。若是兩個對象的hashcode同樣,那麼只能證實他們在一個存儲位置,可是不能證實他們徹底相同,由於可能會出現碰撞。
     
  11. HashMap的hashcode的做用
    public V put(K key, V value) {
            if (key == null)
                return putForNullKey(value);
            int hash = hash(key.hashCode());
            int i = indexFor(hash, table.length);
            for (Entry<K,V> e = table[i]; e != null; e = e.next) {
                Object k;
                if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                    V oldValue = e.value;
                    e.value = value;
                    e.recordAccess(this);
                    return oldValue;
                }
            }
     
            modCount++;
            addEntry(hash, key, value, i);
            return null;
        }

     

    做用:一、爲了保證查找的快捷性;二、在使用equals時必定要重寫hashcode方法,由於hashmap採起桶機制來解決map衝突問題,當兩個對象的hashcode相同的時候,他們可能同時處於同一個bucket中的一個隊列上。因此hashmap中hashcode相同的,其值不必定相同。
     
  12. 爲何重載hashCode方法?

    一、重寫equals方法時須要重寫hashCode方法,主要是針對Map、Set等集合類型的使用;java

    a: Map、Set等集合類型存放的對象必須是惟一的;node

    b: 集合類判斷兩個對象是否相等,是先判斷equals是否相等,若是equals返回TRUE,還要再判斷HashCode返回值是否ture,只有二者都返回ture,才認爲該兩個對象是相等的。算法

    二、因爲Object的hashCode返回的是對象的hash值,因此即便equals返回TRUE,集合也可能斷定兩個對象不等,因此必須重寫hashCode方法,以保證當equals返回TRUE時,hashCode也返回Ture,這樣才能使得集合中存放的對象惟一。spring

     
  13. ArrayList、LinkedList、Vector的區別
    答:Arraylist和Vector底層都是數組實現的,可是Arraylist是線程不安全的,而Vector是線程安全的;性能上因爲Vector使用了synchronized方法來保證同步,因此性能上要比arraylist差。
             兩者的插入速度: 從內部實現機制來說ArrayList和Vector都是使用數組(Array)來控制集合中的對象。當你向這兩種類型中增長元素的時候,若是元素的數目超出了內部數組目前的長度它們都須要擴展內部數組的長度,Vector缺省狀況下自動增加原來一倍的數組長度,ArrayList是原來的50%,因此最後你得到的這個集合所佔的空間老是比你實際須要的要大。因此若是你要在集合中保存大量的數據那麼使用Vector有一些優點,由於你能夠經過設置集合的初始化大小來避免沒必要要的資源開銷。
          LinkedList也是線程不安全的,底層是鏈表。當向插入和刪除的時間複雜度是O(1),可是查找是很慢的,須要沿着鏈表去找。
         當在Arraylist和linkedlist末尾插入一個數據,哪一個時間快?  
           答:若是Arraylist已滿,則須要擴充容量(擴充以前的一半容量),則linkedlist速度快
                 正常狀況下應該是arraylist更快,由於他只須要設置數組下標,而linkedlist還須要加指針進行鏈接。
          但若是是在列表的中間插入的話,那麼linkedlist就比arraylist速度要快了,由於arraylist須要將數組中元素位置移動,而linkedlist直接指針操做。
     
  14. String、StringBuffer與StringBuilder的區別
    1、執行速度比較:StringBuilder>StringBuffer>String
           String最慢的緣由是,String存的是不可變的字符串,而咱們使用String str = "www"; str=str+"asd";時候看起來他是可變的,可是其實是新建立了一個名爲str的String變量,將以前的那個gc掉了。因此每次都須要進行產生副本才能進行String擴充,因此速度慢。而當咱們使用str=「www"+"asd";的時候實際上是wwwasd一塊兒的。
     2、線程安全:
       StringBuilder是線程不安全的,StringBuffer是線程安全的(使用Sychonized來保證線程安全)。   
    3、總結:
    1. String用來表示字符不是不少的字符串操做
    2. StringBuffer當多線程的時候使用
    3. StirngBuilder當單線程的時候使用
    4.  
  15. Map、Set、List、Queue、Stack的特色與用法 
    1. Interface Iterable 迭代器接口,這是Collection類的父接口。實現這個Iterable接口的對象容許使用foreach進行遍歷,也就是說,全部的Collection集合對象都具備"foreach可遍歷性"。這個Iterable接口只
    有一個方法: iterator()。它返回一個表明當前集合對象的泛型<T>迭代器,用於以後的遍歷操做 1.1 Collection Collection是最基本的集合接口,一個Collection表明一組Object的集合,這些Object被稱做Collection的元素。Collection是一個接口,用以提供規範定義,不能被實例化使用 1) Set Set集合相似於一個罐子,"丟進"Set集合裏的多個對象之間沒有明顯的順序。Set繼承自Collection接口,不能包含有重複元素(記住,這是整個Set類層次的共有屬性)。 Set判斷兩個對象相同不是使用"=="運算符,而是根據equals方法。也就是說,咱們在加入一個新元素的時候,若是這個新元素對象和Set中已有對象進行注意equals比較都返回false,  
      則Set就會接受這個新元素對象,不然拒絕。 由於Set的這個制約,在使用Set集合的時候,應該注意兩點:1) 爲Set集合裏的元素的實現類實現一個有效的equals(Object)方法、2) 對Set的構造函數,傳入的Collection參數不能包
      含重複的元素 1.1) HashSet HashSet是Set接口的典型實現,HashSet使用HASH算法來存儲集合中的元素,所以具備良好的存取和查找性能。當向HashSet集合中存入一個元素時,HashSet會調用該對象的
         hashCode()方法來獲得該對象的hashCode值,而後根據該HashCode值決定該對象在HashSet中的存儲位置。 值得主要的是,HashSet集合判斷兩個元素相等的標準是兩個對象經過equals()方法比較相等,而且兩個對象的hashCode()方法的返回值相等 1.1.1) LinkedHashSet LinkedHashSet集合也是根據元素的hashCode值來決定元素的存儲位置,但和HashSet不一樣的是,它同時使用鏈表維護元素的次序,這樣使得元素看起來是以插入的順序保存的。
           當遍歷LinkedHashSet集合裏的元素時,LinkedHashSet將會按元素的添加順序來訪問集合裏的元素。 LinkedHashSet須要維護元素的插入順序,所以性能略低於HashSet的性能,但在迭代訪問Set裏的所有元素時(遍歷)將有很好的性能(鏈表很適合進行遍歷) 1.2) SortedSet 此接口主要用於排序操做,即實現此接口的子類都屬於排序的子類 1.2.1) TreeSet TreeSet是SortedSet接口的實現類,TreeSet能夠確保集合元素處於排序狀態 1.3) EnumSet EnumSet是一個專門爲枚舉類設計的集合類,EnumSet中全部元素都必須是指定枚舉類型的枚舉值,該枚舉類型在建立EnumSet時顯式、或隱式地指定。EnumSet的集合元素也是有序的,
         它們以枚舉值在Enum類內的定義順序來決定集合元素的順序 2) List List集合表明一個元素有序、可重複的集合,集合中每一個元素都有其對應的順序索引。List集合容許加入重複元素,由於它能夠經過索引來訪問指定位置的集合元素。List集合默認按元素
       的添加順序設置元素的索引 2.1) ArrayList ArrayList是基於數組實現的List類,它封裝了一個動態的增加的、容許再分配的Object[]數組。 2.2) Vector Vector和ArrayList在用法上幾乎徹底相同,但因爲Vector是一個古老的集合,因此Vector提供了一些方法名很長的方法,但隨着JDK1.2之後,java提供了系統的集合框架,就將
         Vector改成實現List接口,統一納入集合框架體系中 2.2.1) Stack Stack是Vector提供的一個子類,用於模擬""這種數據結構(LIFO後進先出) 2.3) LinkedList implements List<E>, Deque<E>。實現List接口,能對它進行隊列操做,便可以根據索引來隨機訪問集合中的元素。同時它還實現Deque接口,即能將LinkedList看成雙端隊列
         使用。天然也能夠被看成"棧來使用" 3) Queue Queue用於模擬"隊列"這種數據結構(先進先出 FIFO)。隊列的頭部保存着隊列中存放時間最長的元素,隊列的尾部保存着隊列中存放時間最短的元素。新元素插入(offer)到隊列的尾部,
       訪問元素(poll)操做會返回隊列頭部的元素,隊列不容許隨機訪問隊列中的元素。結合生活中常見的排隊就會很好理解這個概念 3.1) PriorityQueue PriorityQueue並非一個比較標準的隊列實現,PriorityQueue保存隊列元素的順序並非按照加入隊列的順序,而是按照隊列元素的大小進行從新排序,這點從它的類名也能夠
         看出來 3.2) Deque Deque接口表明一個"雙端隊列",雙端隊列能夠同時從兩端來添加、刪除元素,所以Deque的實現類既能夠當成隊列使用、也能夠當成棧使用 3.2.1) ArrayDeque 是一個基於數組的雙端隊列,和ArrayList相似,它們的底層都採用一個動態的、可重分配的Object[]數組來存儲集合元素,當集合元素超出該數組的容量時,系統會在底層重
           新分配一個Object[]數組來存儲集合元素 3.2.2) LinkedList 1.2 Map Map用於保存具備"映射關係"的數據,所以Map集合裏保存着兩組值,一組值用於保存Map裏的key,另一組值用於保存Map裏的value。key和value均可以是任何引用類型的數據。Map的key不允
    許重複,即同一個Map對象的任何兩個key經過equals方法比較結果老是返回false。 關於Map,咱們要從代碼複用的角度去理解,java是先實現了Map,而後經過包裝了一個全部value都爲null的Map就實現了Set集合 Map的這些實現類和子接口中key集的存儲形式和Set集合徹底相同(即key不能重複) Map的這些實現類和子接口中value集的存儲形式和List很是相似(即value能夠重複、根據索引來查找) 1) HashMap 和HashSet集合不能保證元素的順序同樣,HashMap也不能保證key-value對的順序。而且相似於HashSet判斷兩個key是否相等的標準也是: 兩個key經過equals()方法比較返回true、
       同時兩個key的hashCode值也必須相等 1.1) LinkedHashMap LinkedHashMap也使用雙向鏈表來維護key-value對的次序,該鏈表負責維護Map的迭代順序,與key-value對的插入順序一致(注意和TreeMap對全部的key-value進行排序進行區
    分) 2) Hashtable 是一個古老的Map實現類 2.1) Properties Properties對象在處理屬性文件時特別方便(windows平臺上的.ini文件),Properties類能夠把Map對象和屬性文件關聯起來,從而能夠把Map對象中的key-value對寫入到屬性文
         件中,也能夠把屬性文件中的"屬性名-屬性值"加載到Map對象中 3) SortedMap 正如Set接口派生出SortedSet子接口,SortedSet接口有一個TreeSet實現類同樣,Map接口也派生出一個SortedMap子接口,SortedMap接口也有一個TreeMap實現類 3.1) TreeMap TreeMap就是一個紅黑樹數據結構,每一個key-value對即做爲紅黑樹的一個節點。TreeMap存儲key-value對(節點)時,須要根據key對節點進行排序。TreeMap能夠保證全部的
         key-value對處於有序狀態。一樣,TreeMap也有兩種排序方式: 天然排序、定製排序 4) WeakHashMap WeakHashMap與HashMap的用法基本類似。區別在於,HashMap的key保留了對實際對象的"強引用",這意味着只要該HashMap對象不被銷燬,該HashMap所引用的對象就不會被垃圾回收。
      但WeakHashMap的key只保留了對實際對象的弱引用,這意味着若是WeakHashMap對象的key所引用的對象沒有被其餘強引用變量所引用,則這些key所引用的對象可能被垃圾回收,當垃
      圾回收了該key所對應的實際對象以後,WeakHashMap也可能自動刪除這些key所對應的key-value對 5) IdentityHashMap IdentityHashMap的實現機制與HashMap基本類似,在IdentityHashMap中,當且僅當兩個key嚴格相等(key1 == key2)時,IdentityHashMap才認爲兩個key相等 6) EnumMap EnumMap是一個與枚舉類一塊兒使用的Map實現,EnumMap中的全部key都必須是單個枚舉類的枚舉值。建立EnumMap時必須顯式或隱式指定它對應的枚舉類。EnumMap根據key的天然順序
      (即枚舉值在枚舉類中的定義順序)
     
  16. HashMap和HashTable的區別
    區別一:出現時間不一樣,HashMap比HashTable上線較晚。HashMap和HashTable都是基於HashCode來實現的,因此從他們暴露在外的api看起
    區別二:接口不一樣,HashMap繼承了Abstract Map抽象類,而Abstract Map實現了Map接口;HashTable以前是繼承了Dictionary抽象類,可是該抽象類已通過時,因此如今是實現了Map接口。
    他倆都實現了Map、Cloneable、Serializable接口。
    區別三:支持空不一樣,HashMap支持null值和null鍵(將null的hashcode設置爲0),而HashTable傳入null值和null鍵的時候會拋出NullPointerException.
    區別四:數據結構相同,HashMap和HashTable都實現了entry接口,entry對象在內部保存一個鍵值對。

    Entry對象惟一表示一個鍵值對,有四個屬性:數據庫

    -K key 鍵對象
    -V value 值對象
    -int hash 鍵對象的hash值
    -Entry entry 指向鏈表中下一個Entry對象,可爲null,表示當前Entry對象在鏈表尾部編程

    內部有一個指向entry的數組,數組的每一個元素都是一個entry,每一個entry結構又是個鏈表結構,指向下一個entry。windows

    1.8以前當發生hash衝突的時候可使用entry的鏈表來解決衝突;可是很難保證hashmap的均勻性,很容易出現很長的鏈表,影響查詢效率。設計模式

    1.8以後使用了紅黑樹解決hash衝突,具體的能夠看:http://www.javashuo.com/article/p-majqhclb-n.htmlapi

    以上兩者是相同的
    可是兩者在實現數據結構時的算法不一樣:
          須要有算法在哈希桶內的鍵值對多到必定程度時,擴充哈希表的大小(數組的大小)。
          HashTable的初始大小爲11,他每次擴充爲原來的2*n+1,第二次爲23;HashMap的初始大小爲16,他每次擴充爲原來的2*n,第二次爲32.
           若是在建立時給定了初始化大小(5),那麼HashTable會直接使用你給定的大小(5),而HashMap會將其擴充爲2的冪次方大小(32)
          能夠看出hashtable取得的大小通常爲素數,由於使用素數做爲大小的話能夠取模hash更均勻;可是若是是2的冪次方的話,能夠直接使用位運算,比除法效率更高;因此整體說仍是hashmap的效率更高。
    區別五:hashtable是線程安全的而hashmap是線程不安全的。hashtable使用了synchronized描述符。
     
    若是你不須要線程安全,那麼使用HashMap,若是須要線程安全,那麼使用ConcurrentHashMap。HashTable已經被淘汰了,不要在新的代碼中再使用它。
     
     
     
     
  17. JDK7與JDK8中HashMap的實現
     JDK7就是個數組和entry鏈表的結合,在數組中的位置就是經過hashcode算法得出,而若是發生了hash衝突,就將舊值放到鏈表,將新值放到數組的entry中;當鍵爲null時,全都保存到table[0]上,當數組不夠用的時候會以原先值的2倍擴充它。
    JDK8則是利用紅黑樹和鏈表結合解決的hash衝突問題,當同數組節點上的鏈表元素小於8個的話就用鏈表表示,大於等於8個時才轉化爲紅黑樹;鏈表的查找時間複雜度最壞狀況下爲O(n),而紅黑樹的時間複雜度一直是O(lgn);同時entry也變成了node,由於紅黑樹和node有關。
     
     
  18. HashMap和ConcurrentHashMap的區別,HashMap的底層源碼
    HashMap是線程不安全的,而ConcurrentHashMap是線程安全的;HashMap能夠存null值而ConcurrentHashMap不能夠存放null鍵和null值。
    ConcurrentHashMap的實現原理是:引入一個"分段鎖"機制,能夠理解爲把一個Map拆分紅n個小的table, 根據key.hashCode()來決定把key放到哪一個HashTable中                       每一個segement是一個數組單元,使用ReentrantLock來保證每一個segement是線程安全的,以保證全局的線程安全。每一個segement中的結構相似於hashmap。
    一個重要指標是concurrencyLevel能夠翻譯成並行級別,並行數,segement數等,默認爲16,表示默認有16個segement字段,不一樣線程的寫操做放在16個不一樣的segement中就能夠實現多線程。這個指標是能夠初始化的,初始化後的值不能夠更改。
     
     
  19. ConcurrentHashMap能徹底替代HashTable嗎?????????(這道題保留)
    HashTable能夠當作是Hashmap的線程安全整合版,當HashTable大小增長到必定程度的時候,性能會急劇降低,由於迭代時須要長時間鎖定,因此效率很低
     
     
  20. 爲何HashMap是線程不安全的
    由於Hashmap中的put方法並非線程同步的(他所調用的addentry()entry鏈表增加方法也不是線程同步的),容易出現兩個線程分別同時加入一個元素,讓後hash衝突,進入同一個鏈進行addEntry可能會出現線程競技,致使一個元素丟失。
    另外一方面,resize()方法(增長entry方法)也不是線程安全的,多個線程同時到達entry數量閾值的時候就會致使只有一個線程的entry擴容成功,其餘的所有新增數據丟失。
     
     
  21. 如何線程安全的使用HashMap

    瞭解了HashMap爲何線程不安全,那如今看看如何線程安全的使用HashMap。這個無非就是如下三種方式:

    • Hashtable
    • ConcurrentHashMap
    • Synchronized Map

    例子:

    //Hashtable
    Map<String, String> hashtable = new Hashtable<>();
     
    //synchronizedMap
    Map<String, String> synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>());
     
    //ConcurrentHashMap
    Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();
     

    依次來看看。

    Hashtable

    先稍微吐槽一下,爲啥命名不是HashTable啊,看着好難受,無論了就裝做它叫HashTable吧。這貨已經不經常使用了,就簡單說說吧。HashTable源碼中是使用synchronized來保證線程安全的,好比下面的get方法和put方法:

    public synchronized V get(Object key) {
           // 省略實現
        }
    public synchronized V put(K key, V value) {
        // 省略實現
        }
     

    因此當一個線程訪問HashTable的同步方法時,其餘線程若是也要訪問同步方法,會被阻塞住。舉個例子,當一個線程使用put方法時,另外一個線程不但不可使用put方法,連get方法都不能夠,好霸道啊!!!so~~,效率很低,如今基本不會選擇它了。

    ConcurrentHashMap

    ConcurrentHashMap(如下簡稱CHM)是JUC包中的一個類,Spring的源碼中有不少使用CHM的地方。以前已經翻譯過一篇關於ConcurrentHashMap的博客,如何在java中使用ConcurrentHashMap,裏面介紹了CHM在Java中的實現,CHM的一些重要特性和什麼狀況下應該使用CHM。須要注意的是,上面博客是基於Java 7的,和8有區別,在8中CHM摒棄了Segment(鎖段)的概念,而是啓用了一種全新的方式實現,利用CAS算法,有時間會從新總結一下。

    SynchronizedMap

    看了一下源碼,SynchronizedMap的實現仍是很簡單的。

    // synchronizedMap方法
    public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
           return new SynchronizedMap<>(m);
       }
    // SynchronizedMap類
    private static class SynchronizedMap<K,V>
           implements Map<K,V>, Serializable {
           private static final long serialVersionUID = 1978198479659022715L;
     
           private final Map<K,V> m;     // Backing Map
           final Object      mutex;        // Object on which to synchronize
     
           SynchronizedMap(Map<K,V> m) {
               this.m = Objects.requireNonNull(m);
               mutex = this;
           }
     
           SynchronizedMap(Map<K,V> m, Object mutex) {
               this.m = m;
               this.mutex = mutex;
           }
     
           public int size() {
               synchronized (mutex) {return m.size();}
           }
           public boolean isEmpty() {
               synchronized (mutex) {return m.isEmpty();}
           }
           public boolean containsKey(Object key) {
               synchronized (mutex) {return m.containsKey(key);}
           }
           public boolean containsValue(Object value) {
               synchronized (mutex) {return m.containsValue(value);}
           }
           public V get(Object key) {
               synchronized (mutex) {return m.get(key);}
           }
     
           public V put(K key, V value) {
               synchronized (mutex) {return m.put(key, value);}
           }
           public V remove(Object key) {
               synchronized (mutex) {return m.remove(key);}
           }
           // 省略其餘方法
       }

    從源碼中能夠看出調用synchronizedMap()方法後會返回一個SynchronizedMap類的對象,而在SynchronizedMap類中使用了synchronized同步關鍵字來保證對Map的操做是線程安全的。

    性能對比

    這是要靠數聽說話的時代,因此不能只靠嘴說CHM快,它就快了。寫個測試用例,實際的比較一下這三種方式的效率(源碼來源),下面的代碼分別經過三種方式建立Map對象,使用ExecutorService來併發運行5個線程,每一個線程添加/獲取500K個元素。

    public class CrunchifyConcurrentHashMapVsSynchronizedMap {
     
        public final static int THREAD_POOL_SIZE = 5;
     
        public static Map<String, Integer> crunchifyHashTableObject = null;
        public static Map<String, Integer> crunchifySynchronizedMapObject = null;
        public static Map<String, Integer> crunchifyConcurrentHashMapObject = null;
     
        public static void main(String[] args) throws InterruptedException {
     
            // Test with Hashtable Object
            crunchifyHashTableObject = new Hashtable<>();
            crunchifyPerformTest(crunchifyHashTableObject);
     
            // Test with synchronizedMap Object
            crunchifySynchronizedMapObject = Collections.synchronizedMap(new HashMap<String, Integer>());
            crunchifyPerformTest(crunchifySynchronizedMapObject);
     
            // Test with ConcurrentHashMap Object
            crunchifyConcurrentHashMapObject = new ConcurrentHashMap<>();
            crunchifyPerformTest(crunchifyConcurrentHashMapObject);
     
        }
     
        public static void crunchifyPerformTest(final Map<String, Integer> crunchifyThreads) throws InterruptedException {
     
            System.out.println("Test started for: " + crunchifyThreads.getClass());
            long averageTime = 0;
            for (int i = 0; i < 5; i++) {
     
                long startTime = System.nanoTime();
                ExecutorService crunchifyExServer = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
     
                for (int j = 0; j < THREAD_POOL_SIZE; j++) {
                    crunchifyExServer.execute(new Runnable() {
                        @SuppressWarnings("unused")
                        @Override
                        public void run() {
     
                            for (int i = 0; i < 500000; i++) {
                                Integer crunchifyRandomNumber = (int) Math.ceil(Math.random() * 550000);
     
                                // Retrieve value. We are not using it anywhere
                                Integer crunchifyValue = crunchifyThreads.get(String.valueOf(crunchifyRandomNumber));
     
                                // Put value
                                crunchifyThreads.put(String.valueOf(crunchifyRandomNumber), crunchifyRandomNumber);
                            }
                        }
                    });
                }
     
                // Make sure executor stops
                crunchifyExServer.shutdown();
     
                // Blocks until all tasks have completed execution after a shutdown request
                crunchifyExServer.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
     
                long entTime = System.nanoTime();
                long totalTime = (entTime - startTime) / 1000000L;
                averageTime += totalTime;
                System.out.println("2500K entried added/retrieved in " + totalTime + " ms");
            }
            System.out.println("For " + crunchifyThreads.getClass() + " the average time is " + averageTime / 5 + " ms\n");
        }
    }

     

    測試結果:

     

    這個就不用廢話了,CHM性能是明顯優於Hashtable和SynchronizedMap的,CHM花費的時間比前兩個的一半還少,哈哈,之後再有人問就能夠甩數據了。

    Test started for: class java.util.Hashtable
    2500K entried added/retrieved in 2018 ms
    2500K entried added/retrieved in 1746 ms
    2500K entried added/retrieved in 1806 ms
    2500K entried added/retrieved in 1801 ms
    2500K entried added/retrieved in 1804 ms
    For class java.util.Hashtable the average time is 1835 ms
     
    Test started for: class java.util.Collections$SynchronizedMap
    2500K entried added/retrieved in 3041 ms
    2500K entried added/retrieved in 1690 ms
    2500K entried added/retrieved in 1740 ms
    2500K entried added/retrieved in 1649 ms
    2500K entried added/retrieved in 1696 ms
    For class java.util.Collections$SynchronizedMap the average time is 1963 ms
     
    Test started for: class java.util.concurrent.ConcurrentHashMap
    2500K entried added/retrieved in 738 ms
    2500K entried added/retrieved in 696 ms
    2500K entried added/retrieved in 548 ms
    2500K entried added/retrieved in 1447 ms
    2500K entried added/retrieved in 531 ms
    For class java.util.concurrent.ConcurrentHashMap the average time is 792 ms
  22. 多併發狀況下HashMap是否還會產生死循環
     由於在hash衝突時鏈表添加新的entry的時候會使用頭插法插入新節點,因此可能會出現多線程的時候產生環。
     具體緣由,當size已滿,兩個線程put新元素時,一個線程讀取完當前鏈表的順序後,就另外一個線程進行擴容,擴容須要調用transfer函數,由於頭插法因此致使剛纔的鏈表順序在rehash後順序反轉,而後切回第一個線程,從新rehash的時候,因爲第二個線程致使的順序反轉而產生環。
    1.  
  23. TreeMap、HashMap、LindedHashMap的區別
    Map主要用於存儲健值對,根據鍵獲得值,所以不容許鍵重複(重複了覆蓋了),但容許值重複。
    Hashmap 是一個最經常使用的Map,它根據鍵的HashCode 值存儲數據,根據鍵能夠直接獲取它的值,具備很快的訪問速度,遍歷時,取得數據的順序是徹底隨機的。HashMap最多隻容許一條記錄的鍵爲Null;容許多條記錄的值爲 Null;HashMap不支持線程的同步,即任一時刻能夠有多個線程同時寫HashMap;可能會致使數據的不一致。若是須要同步,能夠用 Collections的synchronizedMap方法使HashMap具備同步的能力,或者使用ConcurrentHashMap。
    LinkedHashMap保存了記錄的插入順序,在用Iterator遍歷LinkedHashMap時,先獲得的記錄確定是先插入的.也能夠在構造時用帶參數,按照應用次數排序。在遍歷的時候會比HashMap慢,不過有種狀況例外,當HashMap容量很大,實際數據較少時,遍歷起來可能會比LinkedHashMap慢,由於LinkedHashMap的遍歷速度只和實際數據有關,和容量無關,而HashMap的遍歷速度和他的容量有關。
    TreeMap實現SortMap接口,可以把它保存的記錄根據鍵排序,默認是按鍵值的升序排序,也能夠指定排序的比較器,當用Iterator 遍歷TreeMap時,獲得的記錄是排過序的。
     

    通常狀況下,咱們用的最多的是HashMap,HashMap裏面存入的鍵值對在取出的時候是隨機的,它根據鍵的HashCode值存儲數據,根據鍵能夠直接獲取它的值,具備很快的訪問速度。在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。
    TreeMap取出來的是排序後的鍵值對。但若是您要按天然順序或自定義順序遍歷鍵,那麼TreeMap會更好。
    LinkedHashMap 是HashMap的一個子類,若是須要輸出的順序和輸入的相同,那麼用LinkedHashMap能夠實現,它還能夠按讀取順序來排列,像鏈接池中能夠應用。

    LinkedHashMap使用after  before指針來控制插入的順序。
     
  24. Collection包結構,與Collections的區別
     
     
    Collection是單列集合,是集合類的上級接口,子接口主要有Set和List、Map
    Collections是針對集合類的一個幫助類,提供了操做集合的工具方法:一系列靜態方法實現對各類集合的搜索、排序、替換和線程安全化等操做。
     
  25. try?catch?finally,try裏有return,finally還執行麼
    還執行。
     
  26. Excption與Error包結構,OOM你遇到過哪些狀況,SOF你遇到過哪些狀況
    他倆都實現throwable接口,Exception下還繼承了RuntimeException。java可拋出的分爲三種可檢查異常,運行時異常,錯誤error
    第一種:運行時異常:若是異常沒有人爲的被throws和try catch捕獲的話就直接被編譯器經過,例如byzeroexception
    第二種:可拋出異常:java編譯器會檢查到他,除非使用異常捕獲進行捕獲不然將不能經過編譯。例如ClassNotFoundException。被檢查異常通常都是能夠恢復的
    第三種:錯誤: 編譯器也不會對錯誤進行檢查。 當資源不足、約束失敗、或是其它程序沒法繼續運行的條件發生時,就產生錯誤。程序自己沒法修復這些錯誤的。例如,VirtualMachineError就屬於錯誤。
    OOM異常是內存溢出異常:??????????????????
     
     
  27. Java(OOP)面向對象的三個特徵與含義
    三個特徵是:封裝,繼承,多態
      封裝:是指某個類經過權限控制來限制其餘類直接讀取該類的成員變量和成員方法,使用getset有選擇的公佈部分紅員。
      繼承: 繼承是子對象能夠繼承父對象的屬性和行爲
          多態:多態是指父對象中的同一個行爲能在其多個子對象中有不一樣的表現
  28. Override和Overload的含義去區別
     
     
     
  29. Interface與abstract類的區別
  30. Static?class?與non?static?class的區別
  31. java多態的實現原理
  32. foreach與正常for循環效率對比
  33. Java?IO與NIO
  34. java反射的做用於原理

    什麼是反射:

    一、在運行狀態中,對於任意一個類,都可以知道這個類的屬性和方法。

    二、對於任意一個對象,都可以調用它的任何方法和屬性。

    這種動態獲取信息以及動態調用對象的方法的功能稱爲JAVA的反射。

     

    反射的做用:

    在JAVA中,只有給定類的名字,就能夠經過反射機制來獲取類的全部信息,能夠動態的建立對象和編譯。

     

    實現原理:

    JAVA語言編譯以後會生成一個.class文件,反射就是經過字節碼文件找到某一個類、類中的方法以及屬性等。

    反射的實現主要藉助如下四個類:

    Class:類的對象

    Constructor:類的構造方法

    Field:類中的屬性對象

    Method:類中的方法對象

    根據類的名字獲取類:Class<T> c  = Class.forName("類名");

    根據類c獲取對象:Object o = c.newInstance();

    根據類c獲取實現的接口:Class<?> interfaces = c.getInterfaces();

    獲取有參構造器:Constructor<?> con = c.getConstructor(String.class,int.class); 

    獲取屬性:Field f2 = c.getField("age"); 

    方法調用:

    public class ReflectCase {
     
        public static void main(String[] args) throws Exception {
            Proxy target = new Proxy();
            Method method = Proxy.class.getDeclaredMethod("run");
            method.invoke(target);
        }
     
        static class Proxy {
            public void run() {
                System.out.println("run");
            }
        }
    }

     

     
  35. 泛型經常使用特色
  36. 解析XML的幾種方式的原理與特色:DOM、SAX
  37. Java1.7與1.8,1.9,10 新特性
  38. 設計模式:單例、工廠、適配器、責任鏈、觀察者等等
  39. JNI的使用
  40. AOP是什麼
  41. OOP是什麼
  42. AOP與OOP的區別

           AOP是面向切面編程,是spring中的一個特性,能夠增長業務的重用性,一旦某一功能出現問題直接修改該功能所在模塊就好。隔離業務,下降耦合度,提升開發效率。經常使用來處理日誌記錄,性能統計,安全分析等。

    OOP是java的基本編程思想,面向對象編程。針對業務處理過程當中的實體以及屬性和行爲進行封裝,以得到更加清晰的邏輯劃分。

   區別:對於某一類物體,其所具備的屬性和動做方法可使用oop來進行封裝;而對於一個行爲片斷咱們只能使用aop進行隔離,最經常使用的AOP應用在數據庫鏈接以及事務處理上,實現模式爲代理模式和工廠模式。

相關文章
相關標籤/搜索