BTA 常問的 Java基礎40道常見面試題及詳細答案

原文:http://www.ymq.io/2018/03/10/java/html

 

  1. 八種基本數據類型的大小,以及他們的封裝類
  2. 引用數據類型
  3. Switch可否用string作參數
  4. equals與==的區別
  5. 自動裝箱,常量池
  6. Object有哪些公用方法
  7. Java的四種引用,強弱軟虛,用到的場景
  8. Hashcode的做用
  9. HashMap的hashcode的做用
  10. 爲何重載hashCode方法?
  11. ArrayList、LinkedList、Vector的區別
  12. String、StringBuffer與StringBuilder的區別
  13. Map、Set、List、Queue、Stack的特色與用法
  14. HashMap和HashTable的區別
  15. JDK7與JDK8中HashMap的實現
  16. HashMap和ConcurrentHashMap的區別,HashMap的底層源碼
  17. ConcurrentHashMap能徹底替代HashTable嗎
  18. 爲何HashMap是線程不安全的
  19. 如何線程安全的使用HashMap
  20. 多併發狀況下HashMap是否還會產生死循環
  21. TreeMap、HashMap、LindedHashMap的區別
  22. Collection包結構,與Collections的區別
  23. try?catch?finally,try裏有return,finally還執行麼
  24. Excption與Error包結構,OOM你遇到過哪些狀況,SOF你遇到過哪些狀況
  25. Java(OOP)面向對象的三個特徵與含義
  26. Override和Overload的含義去區別
  27. Interface與abstract類的區別
  28. Static?class?與non?static?class的區別
  29. java多態的實現原理
  30. foreach與正常for循環效率對比
  31. Java?IO與NIO
  32. java反射的做用於原理
  33. 泛型經常使用特色
  34. 解析XML的幾種方式的原理與特色:DOM、SAX
  35. Java1.7與1.8,1.9,10 新特性
  36. 設計模式:單例、工廠、適配器、責任鏈、觀察者等等
  37. JNI的使用
  38. AOP是什麼
  39. OOP是什麼
  40. AOP與OOP的區別

八種基本數據類型的大小,以及他們的封裝類

八種基本數據類型:int、short、float、double、long、boolean、byte、char;它們的java

封裝類分別是:Integer、Short、Float、Double、Long、Boolean、Byte、Character。node

引用數據類型

引用數據類型是由類的編輯器定義的,他們是用於訪問對象的。這些變量被定義爲不可更改的特定類型算法

例如:Employee, Puppy 等等spring

  • 類對象和數組變量就是這種引用數據類型
  • 任何引用數據類型的默認值都爲空
  • 一個引用數據類型能夠被用於任何聲明類型和兼容類型的對象

Switch可否用string作參數

jdk7以前 switch 只能支持 byte、short、char、int 這幾個基本數據類型和其對應的封裝類型。數據庫

switch後面的括號裏面只能放int類型的值,但因爲byte,short,char類型,它們會 自動 轉換爲int類型(精精度小的向大的轉化),因此它們也支持express

jdk1.7後 整形,枚舉類型,boolean,字符串均可以。編程

原理segmentfault

switch (expression) // 括號裏是一個表達式,結果是個整數{ case constant1: // case 後面的標號,也是個整數 group of statements 1; break; case constant2: group of statements 2; break; ... default: default group of statements } 

jdk1.7後,整形,枚舉類型,boolean,字符串均可以。設計模式

public class TestString { static String string = "123"; public static void main(String[] args) { switch (string) { case "123": System.out.println("123"); break; case "abc": System.out.println("abc"); break; default: System.out.println("defauls"); break; } } } 

爲何jdk1.7後又能夠用string類型做爲switch參數呢?

其實,jdk1.7並無新的指令來處理switch string,而是經過調用switch中string.hashCode,將string轉換爲int從而進行判斷

equals與==的區別

使用==比較原生類型如:boolean、int、char等等,使用equals()比較對象

一、==是判斷兩個變量或實例是否是指向同一個內存空間
equals是判斷兩個變量或實例所指向的內存空間的值是否是相同

二、==是指對內存地址進行比較
equals()是對字符串的內容進行比較

三、==指引用是否相同 equals()指的是值是否相同

public static void main(String[] args) { String a = new String("ab"); // a 爲一個引用 String b = new String("ab"); // b爲另外一個引用,對象的內容同樣 String aa = "ab"; // 放在常量池中 String bb = "ab"; // 從常量池中查找 System.out.println(aa == bb); // true System.out.println(a == b); // false,非同一對象 System.out.println(a.equals(b)); // true System.out.println(42 == 42.0); // true } public static void main(String[] args) { Object obj1 = new Object(); Object obj2 = new Object(); System.out.println(obj1.equals(obj2));//false System.out.println(obj1==obj2);//false obj1=obj2; System.out.println(obj1==obj2);//true System.out.println(obj2==obj1);//true } 

自動裝箱,常量池

自動裝箱 在jdk 1.5以前,若是你想要定義一個value爲100的Integer對象,則須要以下定義:

Integer i = new Integer(100); int intNum1 = 100; //普通變量 Integer intNum2 = intNum1; //自動裝箱 int intNum3 = intNum2; //自動拆箱 Integer intNum4 = 100; //自動裝箱 

上面的代碼中,intNum2爲一個Integer類型的實例,intNum1爲Java中的基礎數據類型, 將intNum1賦值給intNum2即是自動裝箱;而將intNum2賦值給intNum3則是自動拆箱。

八種基本數據類型: boolean byte char shrot int long float double ,所生成的變量至關於常量;

基本類型包裝類:Boolean Byte Character Short Integer Long Float Double。

自動拆箱和自動裝箱定義:

自動裝箱是將一個java定義的基本數據類型賦值給相應封裝類的變量。 拆箱與裝箱是相反的操做,自動拆箱則是將一個封裝類的變量賦值給相應基本數據類型的變量。

Object有哪些公用方法

Object是全部類的父類,任何類都默認繼承Object

clone 保護方法,實現對象的淺複製,只有實現了Cloneable接口才能夠調用該方法,不然拋出CloneNotSupportedException異常

equals 在Object中與==是同樣的,子類通常須要重寫該方法

hashCode 該方法用於哈希查找,重寫了equals方法通常都要重寫hashCode方法。這個方法在一些具備哈希功能的Collection中用到

getClass final方法,得到運行時類型

wait 使當前線程等待該對象的鎖,當前線程必須是該對象的擁有者,也就是具備該對象的鎖。 
wait() 方法一直等待,直到得到鎖或者被中斷。 
wait(long timeout) 設定一個超時間隔,若是在規定時間內沒有得到鎖就返回。

調用該方法後當前線程進入睡眠狀態,直到如下事件發生

一、其餘線程調用了該對象的notify方法 
二、其餘線程調用了該對象的notifyAll方法 
三、其餘線程調用了interrupt中斷該線程 
四、時間間隔到了 
五、此時該線程就能夠被調度了,若是是被中斷的話就拋出一個InterruptedException異常

notify 喚醒在該對象上等待的某個線程

notifyAll 喚醒在該對象上等待的全部線程

toString 轉換成字符串,通常子類都有重寫,不然打印句柄

Java的四種引用,強弱軟虛,用到的場景

從JDK1.2版本開始,把對象的引用分爲四種級別,從而使程序能更加靈活的控制對象的生命週期。這四種級別由高到低依次爲:強引用、軟引用、弱引用和虛引用。

一、強引用

最廣泛的一種引用方式,如String s = 「abc」,變量s就是字符串「abc」的強引用,只要強引用存在,則垃圾回收器就不會回收這個對象。

二、軟引用(SoftReference)

用於描述還有用但非必須的對象,若是內存足夠,不回收,若是內存不足,則回收。通常用於實現內存敏感的高速緩存,軟引用能夠和引用隊列ReferenceQueue聯合使用,若是軟引用的對象被垃圾回收,JVM就會把這個軟引用加入到與之關聯的引用隊列中。

三、弱引用(WeakReference)

弱引用和軟引用大體相同,弱引用與軟引用的區別在於:只具備弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。

四、虛引用(PhantomReference)

就是形同虛設,與其餘幾種引用都不一樣,虛引用並不會決定對象的生命週期。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收器回收。 虛引用主要用來跟蹤對象被垃圾回收器回收的活動。

虛引用與軟引用和弱引用的一個區別在於:

虛引用必須和引用隊列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,若是發現它還有虛引,就會在回收對象的內存以前,把這個虛引用加入到與之關聯的引用隊列中。

Hashcode的做用

http://blog.csdn.net/seu_calvin/article/details/52094115

一、HashCode的特性

(1)HashCode的存在主要是用於查找的快捷性,如Hashtable,HashMap等,HashCode常常用於肯定對象的存儲地址

(2)若是兩個對象相同, equals方法必定返回true,而且這兩個對象的HashCode必定相同

(3)兩個對象的HashCode相同,並不必定表示兩個對象就相同,即equals()不必定爲true,只可以說明這兩個對象在一個散列存儲結構中

(4)若是對象的equals方法被重寫,那麼對象的HashCode也儘可能重寫

二、HashCode做用

Java中的集合有兩類,一類是List,再有一類是Set。前者集合內的元素是有序的,元素能夠重複;後者元素無序,但元素不可重複

equals方法可用於保證元素不重複,但若是每增長一個元素就檢查一次,若集合中如今已經有1000個元素,那麼第1001個元素加入集合時,就要調用1000次equals方法。這顯然會大大下降效率。 因而,Java採用了哈希表的原理

哈希算法也稱爲散列算法,是將數據依特定算法直接指定到一個地址上

這樣一來,當集合要添加新的元素時,先調用這個元素的HashCode方法,就一會兒能定位到它應該放置的物理位置上

(1)若是這個位置上沒有元素,它就能夠直接存儲在這個位置上,不用再進行任何比較了

(2)若是這個位置上已經有元素了,就調用它的equals方法與新元素進行比較,相同的話就不存了

(3)不相同的話,也就是發生了Hash key相同致使衝突的狀況,那麼就在這個Hash key的地方產生一個鏈表,將全部產生相同HashCode的對象放到這個單鏈表上去,串在一塊兒(不多出現)

這樣一來實際調用equals方法的次數就大大下降了,幾乎只須要一兩次

如何理解HashCode的做用:

從Object角度看,JVM每new一個Object,它都會將這個Object丟到一個Hash表中去,這樣的話,下次作Object的比較或者取這個對象的時候(讀取過程),它會根據對象的HashCode再從Hash表中取這個對象。這樣作的目的是提升取對象的效率。若HashCode相同再去調用equal。

三、HashCode實踐(如何用來查找)

HashCode是用於查找使用的,而equals是用於比較兩個對象是否相等的

(1)例如內存中有這樣的位置

0 1 2 3 4 5 6 7 

而我有個類,這個類有個字段叫ID,我要把這個類存放在以上8個位置之一,若是不用HashCode而任意存放,那麼當查找時就須要到這八個位置裏挨個去找,或者用二分法一類的算法

但以上問題若是用HashCode就會使效率提升不少 定義咱們的HashCode爲ID%8,好比咱們的ID爲9,9除8的餘數爲1,那麼咱們就把該類存在1這個位置,若是ID是13,求得的餘數是5,那麼咱們就把該類放在5這個位置。依此類推。  

(2)可是若是兩個類有相同的HashCode,例如9除以8和17除以8的餘數都是1,也就是說,咱們先經過 HashCode來判斷兩個類是否存放某個桶裏,但這個桶裏可能有不少類,那麼咱們就須要再經過equals在這個桶裏找到咱們要的類

請看下面這個例子

public class HashTest { private int i; public int getI() { return i; } public void setI(int i) { this.i = i; } public int hashCode() { return i % 10; } public final static void main(String[] args) { HashTest a = new HashTest(); HashTest b = new HashTest(); a.setI(1); b.setI(1); Set<HashTest> set = new HashSet<HashTest>(); set.add(a); set.add(b); System.out.println(a.hashCode() == b.hashCode()); System.out.println(a.equals(b)); System.out.println(set); } } 

輸出結果爲:

true
False
[HashTest@1, HashTest@1]

以上這個示例,咱們只是重寫了HashCode方法,從上面的結果能夠看出,雖然兩個對象的HashCode相等,可是實際上兩個對象並非相等由於咱們沒有重寫equals方法,那麼就會調用Object默認的equals方法,顯示這是兩個不一樣的對象。

這裏咱們將生成的對象放到了HashSet中,而HashSet中只可以存放惟一的對象,也就是相同的(適用於equals方法)的對象只會存放一個,可是這裏其實是兩個對象ab都被放到了HashSet中,這樣HashSet就失去了他自己的意義了。

下面咱們繼續重寫equals方法:

public class HashTest {
    private int i;

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }

    public boolean equals(Object object) {
        if (object == null) {
            return false;
        }
        if (object == this) {
            return true;
        }
        if (!(object instanceof HashTest)) {
            return false;
        }
        HashTest other = (HashTest) object;
        if (other.getI() == this.getI()) {
            return true;
        }
        return false;
    }

    public int hashCode() {
        return i % 10;
    }

    public final static void main(String[] args) {
        HashTest a = new HashTest();
        HashTest b = new HashTest();
        a.setI(1);
        b.setI(1);
        Set<HashTest> set = new HashSet<HashTest>();
        set.add(a);
        set.add(b);
        System.out.println(a.hashCode() == b.hashCode());
        System.out.println(a.equals(b));
        System.out.println(set);
    }
}

輸出結果以下所示。

從結果咱們能夠看出,如今兩個對象就徹底相等了,HashSet中也只存放了一份對象。

注意: hashCode()只是簡單示例寫的,真正的生產換將不是這樣的

true
true
[HashTest@1]

HashMap的hashcode的做用

hashCode的存在主要是用於查找的快捷性,如Hashtable,HashMap等,hashCode是用來在散列存儲結構中肯定對象的存儲地址的;

若是兩個對象相同,就是適用於equals(java.lang.Object) 方法,那麼這兩個對象的hashCode必定要相同;

若是對象的equals方法被重寫,那麼對象的hashCode也儘可能重寫,而且產生hashCode使用的對象,必定要和equals方法中使用的一致,不然就會違反上面提到的第2點;

兩個對象的hashCode相同,並不必定表示兩個對象就相同,也就是不必定適用於equals(java.lang.Object) 方法,只可以說明這兩個對象在散列存儲結構中,如Hashtable,他們「存放在同一個籃子裏」。

何時須要重寫?

通常的地方不須要重載hashCode,只有當類須要放在HashTable、HashMap、HashSet等等hash結構的集合時纔會重載hashCode,那麼爲何要重載hashCode呢?

要比較兩個類的內容屬性值,是否相同時候,根據hashCode 重寫規則,重寫類的 指定字段的hashCode(),equals()方法

例如

public class EmpWorkCondition{ /** * 員工ID */ private Integer empId; /** * 員工服務總單數 */ private Integer orderSum; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } EmpWorkCondition that = (EmpWorkCondition) o; return Objects.equals(empId, that.empId); } @Override public int hashCode() { return Objects.hash(empId); } // 省略 getter setter } 
public static void main(String[] args) { List<EmpWorkCondition> list1 = new ArrayList<EmpWorkCondition>(); EmpWorkCondition emp1 = new EmpWorkCondition(); emp1.setEmpId(100); emp1.setOrderSum(90000); list1.add(emp1); List<EmpWorkCondition> list2 = new ArrayList<EmpWorkCondition>(); EmpWorkCondition emp2 = new EmpWorkCondition(); emp2.setEmpId(100); list2.add(emp2); System.out.println(list1.contains(emp2)); } 

輸出結果:true

上面的方法,作的事情就是,比較兩個集合中的,實體類對象屬性值,是否一致

OrderSum 不在比較範圍內,由於沒有重寫它的,equals()和hashCode()方法

爲何要重載equal方法?

由於Object的equal方法默認是兩個對象的引用的比較,意思就是指向同一內存,地址則相等,不然不相等;若是你如今須要利用對象裏面的值來判斷是否相等,則重載equal方法

爲何重載hashCode方法?

通常的地方不須要重載hashCode,只有當類須要放在HashTable、HashMap、HashSet等等hash結構的集合時纔會重載hashCode,那麼爲何要重載hashCode呢?

若是你重寫了equals,好比說是基於對象的內容實現的,而保留hashCode的實現不變,那麼極可能某兩個對象明明是「相等」,而hashCode卻不同。

這樣,當你用其中的一個做爲鍵保存到hashMap、hasoTable或hashSet中,再以「相等的」找另外一個做爲鍵值去查找他們的時候,則根本找不到。

爲何equals()相等,hashCode就必定要相等,而hashCode相等,卻不要求equals相等?

一、由於是按照hashCode來訪問小內存塊,因此hashCode必須相等
二、HashMap獲取一個對象是比較key的hashCode相等和equal爲true

之因此hashCode相等,卻能夠equal不等,就好比ObjectA和ObjectB他們都有屬性name,那麼hashCode都以name計算,因此hashCode同樣,可是兩個對象屬於不一樣類型,因此equal爲false

爲何須要hashCode?

一、經過hashCode能夠很快的查到小內存塊
二、經過hashCode比較比equal方法快,當get時先比較hashCode,若是hashCode不一樣,直接返回false

ArrayList、LinkedList、Vector的區別

List的三個子類的特色

ArrayList:

  • 底層數據結構是數組,查詢快,增刪慢。
  • 線程不安全,效率高。

Vector:

  • 底層數據結構是數組,查詢快,增刪慢。
  • 線程安全,效率低。
  • Vector相對ArrayList查詢慢(線程安全的)
  • Vector相對LinkedList增刪慢(數組結構)

LinkedList

  • 底層數據結構是鏈表,查詢慢,增刪快。
  • 線程不安全,效率高。

Vector和ArrayList的區別

  • Vector是線程安全的,效率低
  • ArrayList是線程不安全的,效率高
  • 共同點:底層數據結構都是數組實現的,查詢快,增刪慢

ArrayList和LinkedList的區別

  • ArrayList底層是數組結果,查詢和修改快
  • LinkedList底層是鏈表結構的,增和刪比較快,查詢和修改比較慢

共同點:都是線程不安全的

List有三個子類使用

  • 查詢多用ArrayList
  • 增刪多用LinkedList
  • 若是都多ArrayList

String、StringBuffer與StringBuilder的區別

String:適用於少許的字符串操做的狀況
StringBuilder:適用於單線程下在字符緩衝區進行大量操做的狀況
StringBuffer:適用多線程下在字符緩衝區進行大量操做的狀況
StringBuilder:是線程不安全的,而StringBuffer是線程安全的

這三個類之間的區別主要是在兩個方面,即運行速度和線程安全這兩方面
首先說運行速度,或者說是執行速度,在這方面運行速度快慢爲:StringBuilder > StringBuffer > String

String最慢的緣由

String爲字符串常量,而StringBuilder和StringBuffer均爲字符串變量,即String對象一旦建立以後該對象是不可更改的,但後二者的對象是變量,是能夠更改的。

再來講線程安全

在線程安全上,StringBuilder是線程不安全的,而StringBuffer是線程安全的

若是一個StringBuffer對象在字符串緩衝區被多個線程使用時,StringBuffer中不少方法能夠帶有synchronized關鍵字,因此能夠保證線程是安全的,但StringBuilder的方法則沒有該關鍵字,因此不能保證線程安全,有可能會出現一些錯誤的操做。因此若是要進行的操做是多線程的,那麼就要使用StringBuffer,可是在單線程的狀況下,仍是建議使用速度比較快的StringBuilder。

Map、Set、List、Queue、Stack的特色與用法

Map

  • Map是鍵值對,鍵Key是惟一不能重複的,一個鍵對應一個值,值能夠重複。
  • TreeMap能夠保證順序
  • HashMap不保證順序,即爲無序的。
  • Map中能夠將Key和Value單獨抽取出來,其中KeySet()方法能夠將全部的keys抽取正一個Set。而Values()方法能夠將map中全部的values抽取成一個集合。

Set

  • 不包含重複元素的集合,set中最多包含一個null元素
  • 只能用Lterator實現單項遍歷,Set中沒有同步方法。

List

  • 有序的可重複集合。
  • 能夠在任意位置增長刪除元素。
  • 用Iterator實現單向遍歷,也可用ListIterator實現雙向遍歷

Queue

  • Queue聽從先進先出原則。
  • 使用時儘可能避免add()和remove()方法,而是使用offer()來添加元素,使用poll()來移除元素,它的優勢是能夠經過返回值來判斷是否成功。
  • LinkedList實現了Queue接口。
  • Queue一般不容許插入null元素。

Stack

  • Stack聽從後進先出原則。
  • Stack繼承自Vector。
  • 它經過五個操做對類Vector進行擴展,容許將向量視爲堆棧,它提供了一般的push和pop操做,以及取堆棧頂點的peek()方法、測試堆棧是否爲空的empty方法等

用法

  • 若是涉及堆棧,隊列等操做,建議使用List
  • 對於快速插入和刪除元素的,建議使用LinkedList
  • 若是須要快速隨機訪問元素的,建議使用ArrayList

更爲精煉的總結

Collection 是對象集合, Collection 有兩個子接口 List 和 Set

List 能夠經過下標 (1,2..) 來取得值,值能夠重複
Set 只能經過遊標來取值,而且值是不能重複的

ArrayList , Vector , LinkedList 是 List 的實現類

  • ArrayList 是線程不安全的, Vector 是線程安全的,這兩個類底層都是由數組實現的
  • LinkedList 是線程不安全的,底層是由鏈表實現的

Map 是鍵值對集合

  • HashTable 和 HashMap 是 Map 的實現類
  • HashTable 是線程安全的,不能存儲 null 值
  • HashMap 不是線程安全的,能夠存儲 null 值

Stack類:繼承自Vector,實現一個後進先出的棧。提供了幾個基本方法,push、pop、peak、empty、search等。

Queue接口:提供了幾個基本方法,offer、poll、peek等。已知實現類有LinkedList、PriorityQueue等。

HashMap和HashTable的區別

https://segmentfault.com/a/1190000008101567

Hashtable是基於陳舊的Dictionary類的,HashMap是Java 1.2引進的Map接口的一個實現,它們都是集合中將數據無序存放的。

一、hashMap去掉了HashTable 的contains方法,可是加上了containsValue()和containsKey()方法

HashTable Synchronize同步的,線程安全,HashMap不容許空鍵值爲空 ,效率低
HashMap 非Synchronize線程同步的,線程不安全,HashMap容許空鍵值爲空 ,效率高
Hashtable是基於陳舊的Dictionary類的,HashMap是Java 1.2引進的Map接口的一個實現,它們都是集合中將數據無序存放的。

Hashtable的方法是同步的,HashMap未經同步,因此在多線程場合要手動同步HashMap這個區別就像Vector和ArrayList同樣

查看Hashtable的源代碼就能夠發現,除構造函數外,Hashtable的全部 public 方法聲明中都有 synchronized 關鍵字,而HashMap的源代碼中則連 synchronized 的影子都沒有,固然,註釋除外。

二、Hashtable不容許 null 值(key 和 value 都不能夠),HashMap容許 null 值(key和value均可以)

三、二者的遍歷方式大同小異,Hashtable僅僅比HashMap多一個elements方法

Hashtable table = new Hashtable(); table.put("key", "value"); Enumeration em = table.elements(); while (em.hasMoreElements()) { String obj = (String) em.nextElement(); System.out.println(obj); } 

四、HashTable使用Enumeration,HashMap使用Iterator

從內部機制實現上的區別以下:

  1. 哈希值的使用不一樣,Hashtable直接使用對象的hashCode
int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; 

而HashMap從新計算hash值,並且用與代替求模:

int hash = hash(k); int i = indexFor(hash, table.length); static int hash(Object x) {   int h = x.hashCode();   h += ~(h << 9);   h ^= (h >>> 14);   h += (h << 4);   h ^= (h >>> 10);   return h; } 
static int indexFor(int hint length) {   return h & (length-1); 
  1. Hashtable中hash數組默認大小是11,增長的方式是 old*2+1。HashMap中hash數組的默認大小是16,並且必定是2的指數。

JDK7與JDK8中HashMap的實現

JDK7中的HashMap

HashMap底層維護一個數組,數組中的每一項都是一個Entry

transient Entry<K,V>[] table; 

咱們向 HashMap 中所放置的對象其實是存儲在該數組當中

而Map中的key,value則以Entry的形式存放在數組中

static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; Entry<K,V> next; int hash; 

總結一下map.put後的過程

當向 HashMap 中 put 一對鍵值時,它會根據 key的 hashCode 值計算出一個位置, 該位置就是此對象準備往數組中存放的位置。

若是該位置沒有對象存在,就將此對象直接放進數組當中;若是該位置已經有對象存在了,則順着此存在的對象的鏈開始尋找(爲了判斷是不是否值相同,map不容許<key,value>鍵值對重複), 若是此鏈上有對象的話,再去使用 equals方法進行比較,若是對此鏈上的每一個對象的 equals 方法比較都爲 false,則將該對象放到數組當中,而後將數組中該位置之前存在的那個對象連接到此對象的後面。

JDK8中的HashMap

JDK8中採用的是位桶+鏈表/紅黑樹(有關紅黑樹請查看紅黑樹)的方式,也是非線程安全的。當某個位桶的鏈表的長度達到某個閥值的時候,這個鏈表就將轉換成紅黑樹。

JDK8中,當同一個hash值的節點數不小於8時,將再也不以單鏈表的形式存儲了,會被調整成一顆紅黑樹(上圖中null節點沒畫)。這就是JDK7與JDK8中HashMap實現的最大區別。

接下來,咱們來看下JDK8中HashMap的源碼實現。

JDK中Entry的名字變成了Node,緣由是和紅黑樹的實現TreeNode相關聯。

transient Node<K,V>[] table; 

當衝突節點數不小於8-1時,轉換成紅黑樹。

static final int TREEIFY_THRESHOLD = 8; 

HashMap和ConcurrentHashMap的區別,HashMap的底層源碼

爲了線程安全從ConcurrentHashMap代碼中能夠看出,它引入了一個「分段鎖」的概念,具體能夠理解爲把一個大的Map拆分紅N個小的HashTable,根據key.hashCode()來決定把key放到哪一個HashTable中。

Hashmap本質是數組加鏈表。根據key取得hash值,而後計算出數組下標,若是多個key對應到同一個下標,就用鏈表串起來,新插入的在前面。

ConcurrentHashMap:在hashMap的基礎上,ConcurrentHashMap將數據分爲多個segment,默認16個(concurrency level),而後每次操做對一個segment加鎖,避免多線程鎖的概率,提升併發效率

總結

JDK6,7中的ConcurrentHashmap主要使用Segment來實現減少鎖粒度,把HashMap分割成若干個Segment,在put的時候須要鎖住Segment,get時候不加鎖,使用volatile來保證可見性,當要統計全局時(好比size),首先會嘗試屢次計算modcount來肯定,這幾回嘗試中,是否有其餘線程進行了修改操做,若是沒有,則直接返回size。若是有,則須要依次鎖住全部的Segment來計算。

jdk7中ConcurrentHashmap中,當長度過長碰撞會很頻繁,鏈表的增改刪查操做都會消耗很長的時間,影響性能

jdk8 中徹底重寫了concurrentHashmap,代碼量從原來的1000多行變成了 6000多 行,實現上也和原來的分段式存儲有很大的區別。

JDK8中採用的是位桶+鏈表/紅黑樹(有關紅黑樹請查看紅黑樹)的方式,也是非線程安全的。當某個位桶的鏈表的長度達到某個閥值的時候,這個鏈表就將轉換成紅黑樹。

JDK8中,當同一個hash值的節點數不小於8時,將再也不以單鏈表的形式存儲了,會被調整成一顆紅黑樹(上圖中null節點沒畫)。這就是JDK7與JDK8中HashMap實現的最大區別。

主要設計上的變化有如下幾點

1.jdk8不採用segment而採用node,鎖住node來實現減少鎖粒度。 2.設計了MOVED狀態 當resize的中過程當中 線程2還在put數據,線程2會幫助resize。 3.使用3個CAS操做來確保node的一些操做的原子性,這種方式代替了鎖。 4.sizeCtl的不一樣值來表明不一樣含義,起到了控制的做用。

至於爲何JDK8中使用synchronized而不是ReentrantLock,我猜是由於JDK8中對synchronized有了足夠的優化吧。

ConcurrentHashMap能徹底替代HashTable嗎

hashTable雖然性能上不如ConcurrentHashMap,但並不能徹底被取代,二者的迭代器的一致性不一樣的,hash table的迭代器是強一致性的,而concurrenthashmap是弱一致的。

ConcurrentHashMap的get,clear,iterator 都是弱一致性的。 Doug Lea 也將這個判斷留給用戶本身決定是否使用ConcurrentHashMap。

ConcurrentHashMap與HashTable均可以用於多線程的環境,可是當Hashtable的大小增長到必定的時候,性能會急劇降低,由於迭代時須要被鎖定很長的時間。由於ConcurrentHashMap引入了分割(segmentation),不論它變得多麼大,僅僅須要鎖定map的某個部分,而其它的線程不須要等到迭代完成才能訪問map。簡而言之,在迭代的過程當中,ConcurrentHashMap僅僅鎖定map的某個部分,而Hashtable則會鎖定整個map。

那麼既然ConcurrentHashMap那麼優秀,爲何還要有Hashtable的存在呢?ConcurrentHashMap能徹底替代HashTable嗎?

HashTable雖然性能上不如ConcurrentHashMap,但並不能徹底被取代,二者的迭代器的一致性不一樣的,HashTable的迭代器是強一致性的,而ConcurrentHashMap是弱一致的。 ConcurrentHashMap的get,clear,iterator 都是弱一致性的。 Doug Lea 也將這個判斷留給用戶本身決定是否使用ConcurrentHashMap。

那麼什麼是強一致性和弱一致性呢?

get方法是弱一致的,是什麼含義?可能你指望往ConcurrentHashMap底層數據結構中加入一個元素後,立馬能對get可見,但ConcurrentHashMap並不能如你所願。換句話說,put操做將一個元素加入到底層數據結構後,get可能在某段時間內還看不到這個元素,若不考慮內存模型,單從代碼邏輯上來看,倒是應該能夠看獲得的。

下面將結合代碼和java內存模型相關內容來分析下put/get方法。put方法咱們只需關注Segment#put,get方法只需關注Segment#get,在繼續以前,先要說明一下Segment裏有兩個volatile變量:count和table;HashEntry裏有一個volatile變量:value。

總結

ConcurrentHashMap的弱一致性主要是爲了提高效率,是一致性與效率之間的一種權衡。要成爲強一致性,就獲得處使用鎖,甚至是全局鎖,這就與Hashtable和同步的HashMap同樣了。

爲何HashMap是線程不安全的

HashMap 在併發執行 put 操做時會引發死循環,致使 CPU 利用率接近100%。由於多線程會致使 HashMap 的 Node 鏈表造成環形數據結構,一旦造成環形數據結構,Node 的 next 節點永遠不爲空,就會在獲取 Node 時產生死循環。

如何線程安全的使用HashMap

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

Hashtable ConcurrentHashMap Synchronized Map

Hashtable

例子

//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 於 Java 7 的,和8有區別,在8中 CHM 摒棄了 Segment(鎖段)的概念,而是啓用了一種全新的方式實現,利用 CAS 算法,有時間會從新總結一下。

SynchronizedMap

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

性能對比

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

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 

ConcurrentHashMap 性能是明顯優於 Hashtable 和 SynchronizedMap 的,CHM 花費的時間比前兩個的一半還少。

多併發狀況下HashMap是否還會產生死循環

今天原本想看下了ConcurrentHashMap的源碼,ConcurrentHashMap是Java 5中支持高併發、高吞吐量的線程安全HashMap實現

在看不少博客在介紹ConcurrentHashMap以前,都說HashMap適用於單線程訪問,這是由於HashMap的全部方法都沒有進行鎖同步,所以是線程不安全的,不只如此,當多線程訪問的時候還容易產生死循環。

雖然本身在前幾天的時候看過HashMap的源碼,感受思路啥啥的都還清楚,對於多線程訪問只知道HashMap是線程不安全的,可是不知道HashMap在多線程併發的狀況下會產生死循環呢,爲何會產生,何種狀況下才會產生死循環呢???

《Java困惑》:多併發狀況下HashMap是否還會產生死循環

http://blog.csdn.net/u010412719/article/details/52049347

既然會產生死循環,爲何併發狀況下,仍是用ConcurrentHashMap。 jdk 好像有,可是Jdk8 已經修復了這個問題

TreeMap、HashMap、LindedHashMap的區別

LinkedHashMap能夠保證HashMap集合有序,存入的順序和取出的順序一致。

TreeMap實現SortMap接口,可以把它保存的記錄根據鍵排序,默認是按鍵值的升序排序,也能夠指定排序的比較器,當用Iterator遍歷TreeMap時,獲得的記錄是排過序的。

HashMap不保證順序,即爲無序的,具備很快的訪問速度
HashMap最多隻容許一條記錄的鍵爲Null;容許多條記錄的值爲 Null
HashMap不支持線程的同步

咱們在開發的過程當中使用HashMap比較多,在Map中在Map 中插入、刪除和定位元素,HashMap 是最好的選擇

但若是您要按天然順序或自定義順序遍歷鍵,那麼TreeMap會更好

若是須要輸出的順序和輸入的相同,那麼用LinkedHashMap 能夠實現,它還能夠按讀取順序來排列

Collection包結構,與Collections的區別

http://blog.sina.com.cn/s/blog_105817120102vzh6.html

Collection 是集合類的上級接口,子接口主要有Set、List 、Map。

Collecions 是針對集合類的一個幫助類, 提供了操做集合的工具方法,一系列靜態方法實現對各種集合的搜索、排序線性、線程安全化等操做。

例如

Map<String, Object> map4 = Collections.synchronizedMap(new HashMap<String, Object>()); 線程安全 HashMap Collections.sort(List<T> list, Comparator<? super T> c); 排序 List 

Collection

Collection 是單列集合

List

元素是有序的、可重複
有序的 collection,能夠對列表中每一個元素的插入位置進行精確地控制 
能夠根據元素的整數索引(在列表中的位置)訪問元素,並搜索列表中的元素
可存放重複元素,元素存取是有序的。

List接口中經常使用類

Vector:線程安全,但速度慢,已被ArrayList替代。底層數據結構是數組結構
ArrayList:線程不安全,查詢速度快。底層數據結構是數組結構
LinkedList:線程不安全。增刪速度快。底層數據結構是列表結構

Set

Set接口中經常使用的類

Set(集) 元素無序的、不可重複
取出元素的方法只有迭代器。不能夠存放重複元素,元素存取是無序的

HashSet:線程不安全,存取速度快。它是如何保證元素惟一性的呢?依賴的是元素的hashCode方法和euqals方法
TreeSet:線程不安全,能夠對Set集合中的元素進行排序。它的排序是如何進行的呢?經過compareTo或者compare方法中的來保證元素的惟一性。元素是以二叉樹的形式存放的

Map

map是一個雙列集合

Hashtable:線程安全,速度快。底層是哈希表數據結構。是同步的。不容許null做爲鍵,null做爲值

Properties:用於配置文件的定義和操做,使用頻率很是高,同時鍵和值都是字符串。是集合中能夠和IO技術相結合的對象。(到了IO在學習它的特有和io相關的功能

HashMap:線程不安全,速度慢。底層也是哈希表數據結構。是不一樣步的。容許null做爲鍵,null做爲值。替代了Hashtable

LinkedHashMap: 能夠保證HashMap集合有序。存入的順序和取出的順序一致

TreeMap:能夠用來對Map集合中的鍵進行排序

try catch finally,try裏有return,finally還執行麼

確定會執行。finally{}塊的代碼
只有在try{}塊中包含遇到System.exit(0)
之類的致使Java虛擬機直接退出的語句纔會不執行

當程序執行try{}遇到return時,程序會先執行return語句,但並不會當即返回——也就是把return語句要作的一切事情都準備好,也就是在將要返回、但並未返回的時候,程序把執行流程轉去執行finally塊,當finally塊執行完成後就直接返回剛纔return語句已經準備好的結果。

Excption與Error包結構。OOM你遇到過哪些狀況,SO F你遇到過哪些狀況

Throwable是 Java 語言中全部錯誤或異常的超類
Throwable包含兩個子類: Error 和 Exception 。它們一般用於指示發生了異常狀況
Throwable包含了其線程建立時線程執行堆棧的快照,它提供了printStackTrace()等接口用於獲取堆棧跟蹤數據等信息

Java將可拋出(Throwable)的結構分爲三種類型:

被檢查的異常(Checked Exception)
運行時異常(RuntimeException)
錯誤(Error)

運行時異常RuntimeException

定義 : RuntimeException及其子類都被稱爲運行時異常
特色 : Java編譯器不會檢查它 也就是說,當程序中可能出現這類異常時,假若既」沒有經過throws聲明拋出它」,也」沒有用try-catch語句捕獲它」,仍是會編譯經過

例如,除數爲零時產生的ArithmeticException異常,數組越界時產生的IndexOutOfBoundsException異常,fail-fail機制產生的ConcurrentModificationException異常等,都屬於運行時異常。

堆內存溢出 OutOfMemoryError(OOM)

除了程序計數器外,虛擬機內存的其餘幾個運行時區域都有發生OutOfMemoryError(OOM)異常的可能

Java Heap 溢出 通常的異常信息:java.lang.OutOfMemoryError:Java heap spacess
java堆用於存儲對象實例,咱們只要不斷的建立對象,而且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,就會在對象數量達到最大堆容量限制後產生內存溢出異常。

堆棧溢出 StackOverflow (SOF)

StackOverflowError 的定義:
當應用程序遞歸太深而發生堆棧溢出時,拋出該錯誤
由於棧通常默認爲1-2m,一旦出現死循環或者是大量的遞歸調用,在不斷的壓棧過程當中,形成棧容量超過1m而致使溢出

棧溢出的緣由:

遞歸調用
大量循環或死循環
全局變量是否過多
數組、List、map數據過大

Java(OOP)面向對象的三個特徵與含義

封裝(高內聚低耦合 –>解耦)

封裝是指將某事物的屬性和行爲包裝到對象中,這個對象只對外公佈須要公開的屬性和行爲,而這個公佈也是能夠有選擇性的公佈給其它對象。在java中能使用private、protected、public三種修飾符或不用(即默認defalut)對外部對象訪問該對象的屬性和行爲進行限制

java的繼承(重用父類的代碼)

繼承是子對象能夠繼承父對象的屬性和行爲,亦即父對象擁有的屬性和行爲,其子對象也就擁有了這些屬性和行爲

java中的多態(父類引用指向子類對象)

多態是指父對象中的同一個行爲能在其多個子對象中有不一樣的表現

有兩種多態的機制:編譯時多態、運行時多態

一、方法的重載:重載是指同一類中有多個同名的方法,但這些方法有着不一樣的參數。,所以在編譯時就能夠肯定到底調用哪一個方法,它是一種編譯時多態
二、方法的重寫:子類能夠覆蓋父類的方法,所以一樣的方法會在父類中與子類中有着不一樣的表現形式

Override和Overload的含義去區別

重載 Overload方法名相同,參數列表不一樣(個數、順序、類型不一樣)與返回類型無關
重寫 Override 覆蓋。 將父類的方法覆蓋 重寫方法重寫:方法名相同,訪問修飾符只能大於被重寫的方法訪問修飾符,方法簽名個數,順序個數類型相同

Override(重寫)

  • 方法名、參數、返回值相同
  • 子類方法不能縮小父類方法的訪問權限
  • 子類方法不能拋出比父類方法更多的異常(但子類方法能夠不拋出異常)
  • 存在於父類和子類之間
  • 方法被定義爲final不能被重寫

Overload(重載)

  • 參數類型、個數、順序至少有一個不相同
  • 不能重載只有返回值不一樣的方法名
  • 存在於父類和子類、同類中

而重載的規則

一、必須具備不一樣的參數列表 二、能夠有不一樣的返回類型,只要參數列表不一樣就能夠了 三、能夠有不一樣的訪問修飾符 四、能夠拋出不一樣的異常

重寫方法的規則

一、參數列表必須徹底與被重寫的方法相同,不然不能稱其爲重寫而是重載。
二、返回的類型必須一直與被重寫的方法的返回類型相同,不然不能稱其爲重寫而是重載。
三、訪問修飾符的限制必定要大於被重寫方法的訪問修飾符(public>protected>default>private)。 四、重寫方法必定不能拋出新的檢查異常或者比被重寫方法申明更加寬泛的檢查型異常。

例如: 父類的一個方法申明瞭一個檢查異常IOException,在重寫這個方法是就不能拋出Exception,只能拋出IOException的子類異常,能夠拋出非檢查異常。

Interface與abstract類的區別

Interface 只能有成員常量,只能是方法的聲明 Abstract class能夠有成員變量,能夠聲明普通方法和抽象方法

interface是接口,全部的方法都是抽象方法,成員變量是默認的public static final 類型。接口不能實例化本身

abstract class是抽象類,至少包含一個抽象方法的累叫抽象類,抽象類不能被自身實例化,並用abstract關鍵字來修飾

Static class 與non static class的區別

static class(內部靜態類)

一、用static修飾的是內部類,此時這個內部類變爲靜態內部類;對測試有用 二、內部靜態類不須要有指向外部類的引用 三、靜態類只能訪問外部類的靜態成員,不能訪問外部類的非靜態成員

non static class(非靜態內部類)

一、非靜態內部類須要持有對外部類的引用 二、非靜態內部類可以訪問外部類的靜態和非靜態成員 三、一個非靜態內部類不能脫離外部類實體被建立 四、一個非靜態內部類能夠訪問外部類的數據和方法

java多態的實現原理

http://www.javashuo.com/article/p-oyxrljng-kk.html

foreach與正常for循環效率對比

用for循環arrayList 10萬次花費時間:5毫秒
用foreach循環arrayList 10萬次花費時間:7毫秒
用for循環linkList 10萬次花費時間:4481毫秒
用foreach循環linkList 10萬次花費時間:5毫秒

循環ArrayList時,普通for循環比foreach循環花費的時間要少一點 循環LinkList時,普通for循環比foreach循環花費的時間要多不少

當我將循環次數提高到一百萬次的時候,循環ArrayList,普通for循環仍是比foreach要快一點;可是普通for循環在循環LinkList時,程序直接卡死。

ArrayList:ArrayList是採用數組的形式保存對象的,這種方式將對象放在連續的內存塊中,因此插入和刪除時比較麻煩,查詢比較方便。

LinkList:LinkList是將對象放在獨立的空間中,並且每一個空間中還保存下一個空間的索引,也就是數據結構中的鏈表結構,插入和刪除比較方便,可是查找很麻煩,要從第一個開始遍歷。

結論:

須要循環數組結構的數據時,建議使用普通for循環,由於for循環採用下標訪問,對於數組結構的數據來講,採用下標訪問比較好

須要循環鏈表結構的數據時,必定不要使用普通for循環,這種作法很糟糕,數據量大的時候有可能會致使系統崩潰

Java IO與NIO

NIO是爲了彌補IO操做的不足而誕生的,NIO的一些新特性有:非阻塞I/O,選擇器,緩衝以及管道。管道(Channel),緩衝(Buffer) ,選擇器( Selector)是其主要特徵

概念解釋

Channel——管道實際上就像傳統IO中的流,到任何目的地(或來自任何地方)的全部數據都必須經過一個 Channel 對象。一個 Buffer 實質上是一個容器對象。

每一種基本 Java 類型都有一種緩衝區類型:

ByteBuffer——byte CharBuffer——char ShortBuffer——short IntBuffer——int LongBuffer——long FloatBuffer——float DoubleBuffer——double 

Selector——選擇器用於監聽多個管道的事件,使用傳統的阻塞IO時咱們能夠方便的知道何時能夠進行讀寫,而使用非阻塞通道,咱們須要一些方法來知道何時通道準備好了,選擇器正是爲這個須要而誕生的。

NIO和傳統的IO有什麼區別呢?

IO是面向流的,NIO是面向塊(緩衝區)的。

IO面向流的操做一次一個字節地處理數據。一個輸入流產生一個字節的數據,一個輸出流消費一個字節的數據。,致使了數據的讀取和寫入效率不佳;

NIO面向塊的操做在一步中產生或者消費一個數據塊。按塊處理數據比按(流式的)字節處理數據要快得多,同時數據讀取到一個它稍後處理的緩衝區,須要時可在緩衝區中先後移動。這就增長了處理過程當中的靈活性。通俗來講,NIO採起了「預讀」的方式,當你讀取某一部分數據時,他就會猜想你下一步可能會讀取的數據而預先緩衝下來。

IO是阻塞的,NIO是非阻塞的

對於傳統的IO,當一個線程調用read() 或 write()時,該線程被阻塞,直到有一些數據被讀取,或數據徹底寫入。該線程在此期間不能再幹任何事情了。

而對於NIO,使用一個線程發送讀取數據請求,沒有獲得響應以前,線程是空閒的,此時線程能夠去執行別的任務,而不是像IO中那樣只能等待響應完成。

NIO和IO適用場景

NIO是爲彌補傳統IO的不足而誕生的,可是尺有所短寸有所長,NIO也有缺點,由於NIO是面向緩衝區的操做,每一次的數據處理都是對緩衝區進行的,那麼就會有一個問題,在數據處理以前必需要判斷緩衝區的數據是否完整或者已經讀取完畢,若是沒有,假設數據只讀取了一部分,那麼對不完整的數據處理沒有任何意義。因此每次數據處理以前都要檢測緩衝區數據。

那麼NIO和IO各適用的場景是什麼呢?

若是須要管理同時打開的成千上萬個鏈接,這些鏈接每次只是發送少許的數據,例如聊天服務器,這時候用NIO處理數據多是個很好的選擇。

而若是隻有少許的鏈接,而這些鏈接每次要發送大量的數據,這時候傳統的IO更合適。使用哪一種處理數據,須要在數據的響應等待時間和檢查緩衝區數據的時間上做比較來權衡選擇。

通俗解釋,最後,對於NIO和傳統IO

有一個網友講的生動的例子:

之前的流老是堵塞的,一個線程只要對它進行操做,其它操做就會被堵塞,也就至關於水管沒有閥門,你伸手接水的時候,無論水到了沒有,你就都只能耗在接水(流)上。

nio的Channel的加入,至關於增長了水龍頭(有閥門),雖然一個時刻也只能接一個水管的水,但依賴輪換策略,在水量不大的時候,各個水管裏流出來的水,均可以獲得妥

善接納,這個關鍵之處就是增長了一個接水工,也就是Selector,他負責協調,也就是看哪根水管有水了的話,在當前水管的水接到必定程度的時候,就切換一下:臨時關上當

前水龍頭,試着打開另外一個水龍頭(看看有沒有水)。

當其餘人須要用水的時候,不是直接去接水,而是事前提了一個水桶給接水工,這個水桶就是Buffer。也就是,其餘人雖然也可能要等,但不會在現場等,而是回家等,能夠作

其它事去,水接滿了,接水工會通知他們。

這其實也是很是接近當前社會分工細化的現實,也是統分利用現有資源達到併發效果的一種很經濟的手段,而不是動不動就來個並行處理,雖然那樣是最簡單的,但也是最浪費資源的方式。

java反射的做用於原理

什麼是Java的反射呢?

Java 反射是可讓咱們在運行時,經過一個類的Class對象來獲取它獲取類的方法、屬性、父類、接口等類的內部信息的機制

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

反射的做用?

反射就是:在任意一個方法裏:

1.若是我知道一個類的名稱/或者它的一個實例對象, 我就能把這個類的全部方法和變量的信息找出來(方法名,變量名,方法,修飾符,類型,方法參數等等全部信息)

2.若是我還明確知道這個類裏某個變量的名稱,我還能獲得這個變量當前的值。

3.固然,若是我明確知道這個類裏的某個方法名+參數個數類型,我還能經過傳遞參數來運行那個類裏的那個方法。

反射機制主要提供瞭如下功能:

  • 在運行時判斷任意一個對象所屬的類;
  • 在運行時構造任意一個類的對象;
  • 在運行時判斷任意一個類所具備的成員變量和方法;
  • 在運行時調用任意一個對象的方法;
  • 生成動態代理。

反射的原理?

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

反射的實現API有哪些?

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

Class:類的對象 Constructor:類的構造方法 Field:類中的屬性對象 Method:類中的方法對象 

反射的實例

http://blog.csdn.net/xuefeng_yang/article/details/49497415

http://www.cnblogs.com/zhaoyanjun/p/6074887.html

泛型經常使用特色

泛型經常使用特色,List可否轉爲List

不能夠強轉類型的

這個問題涉及到了,範型向上轉型 和 範型向下轉型問題。

List向上轉換至List(等價於List)會丟失String類的身份(String類型的特有接口)

當須要由List向下轉型時,你的程序必須明確的知道將對象轉換成何種具體類型,否則這將是不安全的操做!

若是要強轉類型,Json 序列化轉型

List<String> str = new ArrayList<String>(); List<Object> obj= JSONObject.parseArray(JSONObject.toJSONString(str)); 

或者遍歷,或者克隆,可是取出來就是(Object)了,須要強轉,String 由於類型丟了

解析XML的幾種方式的原理與特色:DOM、SAX

Android中三種經常使用解析XML的方式(DOM、SAX、PULL)簡介及區別

http://blog.csdn.net/cangchen/article/details/44034799

xml解析的兩種基本方式:DOM和SAX的區別是?

DOM: document object modelSAX: simple api for xml 

dom一次性把xml文件所有加載到內存中簡歷一個結構一摸同樣的樹, 效率低
SAX解析器的優勢是解析速度快,佔用內存少,效率高

DOM在內存中以樹形結構存放,所以檢索和更新效率會更高。可是對於特別大的文檔,解析和加載整個文檔將會很耗資源

DOM,它是生成一個樹,有了樹之後你搜索、查找均可以作
SAX,它是基於流的,就是解析器從頭至尾解析一遍xml文件,解析完了之後你不過想再查找從新解析
sax解析器核心是事件處理機制。例如解析器發現一個標記的開始標記時,將所發現的數據會封裝爲一個標記開始事件,並把這個報告給事件處理器,

平時工做中,xml解析你是使用什麼?

JDOM
DOM4J

Java1.7與1.8,1.9.10 新特性

1.5

  1. 自動裝箱與拆箱
  2. 枚舉(經常使用來設計單例模式)
  3. 靜態導入
  4. 可變參數
  5. 內省

1.6

  1. Web服務元數據
  2. 腳本語言支持
  3. JTable的排序和過濾
  4. 更簡單,更強大的JAX-WS
  5. 輕量級Http Server
  6. 嵌入式數據庫 Derby

1.7

  1. switch中可使用字串了
  2. 運用List tempList = new ArrayList<>(); 即泛型實例化類型自動推斷
  3. 語法上支持集合,而不必定是數組
  4. 新增一些取環境信息的工具方法
  5. Boolean類型反轉,空指針安全,參與位運算
  6. 兩個char間的equals
  7. 安全的加減乘除
  8. map集合支持併發請求,且能夠寫成 Map map = {name:」xxx」,age:18};

1.8

  1. 容許在接口中有默認方法實現
  2. Lambda表達式
  3. 函數式接口
  4. 方法和構造函數引用
  5. Lambda的範圍
  6. 內置函數式接口
  7. Streams
  8. Parallel Streams
  9. Map
  10. 時間日期API
  11. Annotations

1.9

  1. Jigsaw 項目;模塊化源碼
  2. 簡化進程API
  3. 輕量級 JSON API
  4. 錢和貨幣的API
  5. 改善鎖爭用機制
  6. 代碼分段緩存
  7. 智能Java編譯, 第二階段
  8. HTTP 2.0客戶端
  9. Kulla計劃: Java的REPL實現

10

  1. 本地變量類型推斷
  2. 統一JDK倉庫
  3. 垃圾回收器接口
  4. G1的並行Full GC
  5. 應用程序類數據共享
  6. ThreadLocal握手機制

設計模式:單例、工廠、適配器、責任鏈、觀察者等等

【示例】設計模式——單例模式、工廠模式、代理模式、觀察者模式、裝飾器模式

http://blog.csdn.net/learrrrrrrrn/article/details/66476691

菜鳥教程-設計模式

http://www.runoob.com/design-pattern/design-pattern-intro.html

什麼是設計模式

設計模式是一種解決方案,用於解決在軟件設計中廣泛存在的問題,是前輩們對以前軟件設計中反覆出現的問題的一個總結。

咱們學設計模式,是爲了學習如何合理的組織咱們的代碼,如何解耦,如何真正的達到對修改封閉對擴展開放的效果,而不是去背誦那些類的繼承模式,而後本身記不住,回過頭來就罵設計模式把你的代碼搞複雜了,要反設計模式。

設計模式的六大原則

  • 開閉原則:實現熱插拔,提升擴展性。
  • 里氏代換原則:實現抽象的規範,實現子父類互相替換;
  • 依賴倒轉原則:針對接口編程,實現開閉原則的基礎;
  • 接口隔離原則:下降耦合度,接口單獨設計,互相隔離;
  • 迪米特法則,又稱不知道原則:功能模塊儘可能獨立;
  • 合成複用原則:儘可能使用聚合,組合,而不是繼承;

一、開閉原則(Open Close Principle)

開閉原則的意思是:對擴展開放,對修改關閉。在程序須要進行拓展的時候,不能去修改原有的代碼,實現一個熱插拔的效果。簡言之,是爲了使程序的擴展性好,易於維護和升級。想要達到這樣的效果,咱們須要使用接口和抽象類,後面的具體設計中咱們會提到這點。

二、里氏代換原則(Liskov Substitution Principle)

里氏代換原則是面向對象設計的基本原則之一。 里氏代換原則中說,任何基類能夠出現的地方,子類必定能夠出現。LSP 是繼承複用的基石,只有當派生類能夠替換掉基類,且軟件單位的功能不受到影響時,基類才能真正被複用,而派生類也可以在基類的基礎上增長新的行爲。里氏代換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,因此里氏代換原則是對實現抽象化的具體步驟的規範。

三、依賴倒轉原則(Dependence Inversion Principle)

這個原則是開閉原則的基礎,具體內容:針對接口編程,依賴於抽象而不依賴於具體。

四、接口隔離原則(Interface Segregation Principle)

這個原則的意思是:使用多個隔離的接口,比使用單個接口要好。它還有另一個意思是:下降類之間的耦合度。因而可知,其實設計模式就是從大型軟件架構出發、便於升級和維護的軟件設計思想,它強調下降依賴,下降耦合。

五、迪米特法則,又稱最少知道原則(Demeter Principle)

最少知道原則是指:一個實體應當儘可能少地與其餘實體之間發生相互做用,使得系統功能模塊相對獨立。

六、合成複用原則(Composite Reuse Principle)

合成複用原則是指:儘可能使用合成/聚合的方式,而不是使用繼承。

JNI的使用

http://www.javashuo.com/article/p-aymiiush-gy.html

JNI是 Java Native Interface 的縮寫,它提供了若干的API實現了Java和其餘語言的通訊(主要是C&C++)。從Java1.1開始,JNI標準成爲java平臺的一部分,它容許Java代碼和其餘語言寫的代碼進行交互。JNI一開始是爲了本地已編譯語言,尤爲是C和C++而設計的,可是它並不妨礙你使用其餘編程語言,只要調用約定受支持就能夠了。使用java與本地已編譯的代碼交互,一般會喪失平臺可移植性。

JNI步驟

  1. java類中編寫帶有native 聲明的方法。
  2. 使用 javac 命令編譯所編寫的java類。
  3. 使用 javah 命令生成頭文件。
  4. 使用C/C++實現本地方法。
  5. 生成動態鏈接庫。
  6. 執行(java)。

JNI實例

public class HelloWorld { public native void displayHelloWorld();//全部native關鍵詞修飾的都是對本地的聲明 static { System.loadLibrary("hello");//載入本地庫 } public static void main(String[] args) { new HelloWorld().displayHelloWorld(); } } 

AOP是什麼

AOP(Aspect Oriented Programming) 面向切面編程,是目前軟件開發中的一個熱點,是Spring框架內容,利用AOP能夠對業務邏輯的各個部分隔離,從而使的業務邏輯各部分的耦合性下降,提升程序的可重用性,踢開開發效率,主要功能:日誌記錄,性能統計,安全控制,事務處理,異常處理等。

AOP實現原理是java動態代理,可是jdk的動態代理必須實現接口,因此spring的aop是用cglib這個庫實現的,cglis使用裏asm這個直接操縱字節碼的框架,因此能夠作到不使用接口的狀況下實現動態代理。

OOP是什麼

OOP面向對象編程,針對業務處理過程的實體及其屬性和行爲進行抽象封裝,以得到更加清晰高效的邏輯單元劃分

AOP與OOP的區別

OOP面向對象編程,針對業務處理過程的實體及其屬性和行爲進行抽象封裝,以得到更加清晰高效的邏輯單元劃分。而AOP則是針對業務處理過程當中的切面進行提取,它所面對的是處理過程的某個步驟或階段,以得到邏輯過程的中各部分之間低耦合的隔離效果。這兩種設計思想在目標上有着本質的差別。

舉例:

對於「僱員」這樣一個業務實體進行封裝,天然是OOP的任務,咱們能夠創建一個「Employee」類,並將「僱員」相關的屬性和行爲封裝其中。而用AOP 設計思想對「僱員」進行封裝則無從談起。

一樣,對於「權限檢查」這一動做片斷進行劃分,則是AOP的目標領域。

OOP面向名次領域,AOP面向動詞領域。

總之AOP能夠經過預編譯方式和運行期動態代理實如今不修改源碼的狀況下,給程序動態贊成添加功能的一項技術。

相關文章
相關標籤/搜索