天天看點

J2EE基礎知識點

J2SE基礎

1.     九種基本資料類型的大小,以及他們的封裝類。

基本類型 大小(位元組)      預設值    封裝類

byte         1           (byte)0     Byte

short        2           (short)0   Short

int           4               0        Integer

long         8              0L      Long

float        4              0.0f   Float

double         8               0.0d   Double

boolean       -           false  Boolean

char         2         \u0000(null)         Character

void         -            -       Void

基本類型所占的存儲空間是不變的:這種不變性也是Java具有可移植性的原因之一。

基本類型是放在棧中,直接存儲值。

所有數值類型都有正負号,沒有無符号的數值類型。

注意:

1.int是基本資料類型,Integer是int的封裝類,是引用類型。int預設值是0,而Integer預設值是null,是以Integer能區分出0和null的情況。一旦java看到null,就知道這個引用還沒有指向某個對象,再任何引用使用前,必須為其指定一個對象,否則會報錯。

2.基本資料類型在聲明時系統會自動給它配置設定空間,而引用類型聲明時隻是配置設定了引用空間,必須通過執行個體化開辟資料空間之後才可以指派

3.數組對象也是一個引用對象,将一個數組指派給另一個數組時隻是複制了一個引用,是以通過某一個數組所做的修改在另一個數組中也看的見。

2. Switch能否用string做參數?

   在 Java 7之前,switch 隻能支援 byte、short、char、int或者其對應的封裝類以及Enum 類型。在 Java 7中,String支援被加上了。

3. equals與==的差別。

    1.基本資料類型,也稱原始資料類型。byte,short,char,int,long,float,double,boolean。他們之間的比較,應用雙等号(==),比較的是他們的值。

2.複合資料類型(類),當他們用(==)進行比較的時候,比較的是他們在記憶體中的存放位址,是以,除非是同一個new出來的對象,他們的比較後的結果為true,否則比較後結果為false。JAVA當中所有的類都是繼承于Object這個基類的,在Object中的基類中定義了一個equals的方法,這個方法的初始行為是比較對象的記憶體位址,但在一些類庫當中這個方法被覆寫掉了,如String,Integer,Date在這些類當中equals有其自身的實作,而不再是比較類在堆記憶體中的存放位址了。   對于複合資料類型之間進行equals比較,在沒有覆寫equals方法的情況下,他們之間的比較還是基于他們在記憶體中的存放位置的位址值的,因為Object的equals方法也是用雙等号(==)進行比較的,是以比較後的結果跟雙等号(==)的結果相同。

3.String比較特殊:new的作為複合資料類型,直接雙引号的作為基本類型。

4. Object有哪些公用方法?

    1).clone方法

保護方法,實作對象的淺複制,隻有實作了Cloneable接口才可以調用該方法,否則抛出CloneNotSupportedException異常。

主要是JAVA裡除了8種基本類型傳參數是值傳遞,其他的類對象傳參數都是引用傳遞,我們有時候不希望在方法裡講參數改變,這是就需要在類中複寫clone方法。

2).getClass方法

final方法,獲得運作時類型。

3).toString方法

該方法用得比較多,一般子類都有覆寫。

4).finalize方法

該方法用于釋放資源。因為無法确定該方法什麼時候被調用,很少使用。

5).equals方法

該方法是非常重要的一個方法。一般equals和==是不一樣的,但是在Object中兩者是一樣的。子類一般都要重寫這個方法。

6).hashCode方法

該方法用于哈希查找,可以減少在查找中使用equals的次數,重寫了equals方法一般都要重寫hashCode方法。這個方法在一些具有哈希功能的Collection中用到。

一般必須滿足obj1.equals(obj2)==true。可以推出obj1.hash-Code()==obj2.hashCode(),但是hashCode相等不一定就滿足equals。不過為了提高效率,應該盡量使上面兩個條件接近等價。

如果不重寫hashcode(),在HashSet中添加兩個equals的對象,會将兩個對象都加入進去。

7).wait方法

wait方法就是使目前線程等待該對象的鎖,目前線程必須是該對象的擁有者,也就是具有該對象的鎖。wait()方法一直等待,直到獲得鎖或者被中斷。wait(long timeout)設定一個逾時間隔,如果在規定時間内沒有獲得鎖就傳回。

調用該方法後目前線程進入睡眠狀态,直到以下事件發生。

(1)其他線程調用了該對象的notify方法。

(2)其他線程調用了該對象的notifyAll方法。

(3)其他線程調用了interrupt中斷該線程。

(4)時間間隔到了。

此時該線程就可以被排程了,如果是被中斷的話就抛出一個InterruptedException異常。

8).notify方法

該方法喚醒在該對象上等待的某個線程。

9).notifyAll方法

該方法喚醒在該對象上等待的所有線程。

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

    1、強引用

強引用不會被GC回收,并且在java.lang.ref裡也沒有實際的對應類型,平時工作接觸的最多的就是強引用。

  Object obj = new Object();這裡的obj引用便是一個強引用。如果一個對象具有強引用,那就類似于必不可少的生活用品,垃圾回收器絕不會回收它。當記憶體空間不足,Java虛拟機甯願抛出OutOfMemoryError錯誤,使程式異常終止,也不會靠随意回收具有強引用的對象來解決記憶體不足問題。

2、軟引用

如果一個對象隻具有軟引用,那就類似于可有可物的生活用品。如果記憶體空間足夠,垃圾回收器就不會回收它,如果記憶體空間不足了,就會回收這些對象的記憶體。隻要垃圾回收器沒有回收它,該對象就可以被程式使用。軟引用可用來實作記憶體敏感的高速緩存。軟引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用的對象被垃圾回收,Java虛拟機就會把這個軟引用加入到與之關聯的引用隊列中。

這裡有幾點需要說明:

1、System.gc()告訴JVM這是一個執行GC的好時機,但具體執不執行由JVM決定(事實上這段代碼一般都會執行GC)

2、Thread.sleep(200); 這是因為從對象被回收到JVM将引用加入refQueue隊列,需要一定的時間。而且poll并不是一個阻塞方法,如果沒有資料會傳回null,是以我們選擇等待一段時間。

3、弱引用

如果一個對象隻具有弱引用,那就類似于可有可物的生活用品。弱引用與軟引用的差別在于:隻具有弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的記憶體區域的過程中,一旦發現了隻具有弱引用的對象,不管目前記憶體空間足夠與否,都會回收它的記憶體。不過,由于垃圾回收器是一個優先級很低的線程,是以不一定會很快發現那些隻具有弱引用的對象。弱引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果弱引用所引用的對象被垃圾回收,Java虛拟機就會把這個弱引用加入到與之關聯的引用隊列中。這裡需要注意下:

1、remove這是一個阻塞方法,類似于J.U.C并發包下的阻塞隊列,如果沒有隊列沒有資料,那麼目前線程一直等待。

2、如果隊列有資料,那麼remove和pool都會将第一個元素出隊。

4、幽靈引用(虛引用)

虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個差別在于:虛引用必須和引用隊列(ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的記憶體之前,把這個虛引用加入到與之關聯的引用隊列中。程式可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否将要被垃圾回收。如果程式發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的記憶體被回收之前采取必要的行動。由于Object.finalize()方法的不安全性、低效性,常常使用虛引用完成對象回收前的資源釋放工作。參考我的另一篇部落格:解釋為什麼finalize是不安全的,不建議使用

這裡特别需要注意:當JVM将虛引用插入到引用隊列的時候,虛引用執行的對象記憶體還是存在的。但是PhantomReference并沒有暴露API傳回對象。是以如果我想做清理工作,需要繼承PhantomReference類,以便通路它指向的對象。如NIO直接記憶體的自動回收,就使用到了sun.misc.Cleaner。

6. Hashcode的作用。

    1、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用來在散列存儲結構中确定對象的存儲位址的;

2、如果兩個對象相同,就是适用于equals(Java.lang.Object) 方法,那麼這兩個對象的hashCode一定要相同;

3、如果對象的equals方法被重寫,那麼對象的hashCode也盡量重寫,并且産生hashCode使用的對象,一定要和equals方法中使用的一緻,否則就會違反上面提到的第2點;

4、兩個對象的hashCode相同,并不一定表示兩個對象就相同,也就是不一定适用于equals(java.lang.Object) 方法,隻能夠說明這兩個對象在散列存儲結構中,如Hashtable,他們“存放在同一個籃子裡”。

7. ArrayList、LinkedList、Vector的差別。

   ArrayList 是一個可改變大小的數組.當更多的元素加入到ArrayList中時,其大小将會動态地增長.内部的元素可以直接通過get與set方法進行通路,因為ArrayList本質上就是一個數組.

LinkedList 是一個雙連結清單,在添加和删除元素時具有比ArrayList更好的性能.但在get與set方面弱于ArrayList.

當然,這些對比都是指資料量很大或者操作很頻繁的情況下的對比,如果資料和運算量很小,那麼對比将失去意義.

Vector 和ArrayList類似,但屬于強同步類。如果你的程式本身是線程安全的(thread-safe,沒有在多個線程之間共享同一個集合/對象),那麼使用ArrayList是更好的選擇。

Vector和ArrayList在更多元素添加進來時會請求更大的空間。Vector每次請求其大小的雙倍空間,而ArrayList每次對size增長50%.

而 LinkedList 還實作了 Queue 接口,該接口比List提供了更多的方法,包括offer(),peek(),poll()等.

注意: 預設情況下ArrayList的初始容量非常小,是以如果可以預估資料量的話,配置設定一個較大的初始值屬于最佳實踐,這樣可以減少調整大小的開銷。

8. String、StringBuffer與StringBuilder的差別。

   1.可變與不可變

  String類中使用字元數組儲存字元串,如下就是,因為有“final”修飾符,是以可以知道string對象是不可變的。

  private final char value[];

  StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在AbstractStringBuilder中也是使用字元數組儲存字元串,如下就是,可知這兩種對象都是可變的。

  char[] value;

2.是否多線程安全

  String中的對象是不可變的,也就可以了解為常量,顯然線程安全。

  AbstractStringBuilder是StringBuilder與StringBuffer的公共父類,定義了一些字元串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。

StringBuffer對方法加了同步鎖或者對調用的方法加了同步鎖,是以是線程安全的。

StringBuilder并沒有對方法進行加同步鎖,是以是非線程安全的。

 3.StringBuilder與StringBuffer共同點

StringBuilder與StringBuffer有公共父類AbstractStringBuilder(抽象類)。

抽象類與接口的其中一個差別是:抽象類中可以定義一些子類的公共方法,子類隻需要增加新的功能,不需要重複寫已經存在的方法;而接口中隻是對方法的申明和常量的定義。

StringBuilder、StringBuffer的方法都會調用AbstractStringBuilder中的公共方法,如super.append(...)。隻是StringBuffer會在方法上加synchronized關鍵字,進行同步。

最後,如果程式不是多線程的,那麼使用StringBuilder效率高于StringBuffer。

9. Map、Set、List、Queue、Stack的特點與用法。

Set集合類似于一個罐子,"丢進"Set集合裡的多個對象之間沒有明顯的順序。

List集合代表元素有序、可重複的集合,集合中每個元素都有其對應的順序索引。

Stack是Vector提供的一個子類,用于模拟"棧"這種資料結構(LIFO後進先出)

Queue用于模拟"隊列"這種資料結構(先進先出 FIFO)。 

Map用于儲存具有"映射關系"的資料,是以Map集合裡儲存着兩組值。

10. HashMap和HashTable的差別。

Hashtable是基于陳舊的Dictionary類的,HashMap是Map接口的一個實作 。

Hashtable的方法是線程同步的,而HashMap的方法不是。

隻有HashMap可以讓你将空值作為一個表的條目的key或value。

11. HashMap和ConcurrentHashMap的差別,HashMap的底層源碼。

   Hashmap本質是數組加連結清單。根據key取得hash值,然後計算出數組下标,如果多個key對應到同一個下标,就用連結清單串起來,新插入的在前面。

ConcurrentHashMap:在hashMap的基礎上,ConcurrentHashMap将資料分為多個segment,預設16個(concurrencylevel),然後每次操作對一個segment加鎖,避免多線程鎖的幾率,提高并發效率。

12. TreeMap、HashMap、LindedHashMap的差別。

Hashmap 是一個最常用的Map,它根據鍵的HashCode值存儲資料,根據鍵可以直接擷取它的值,具有很快的通路速度,周遊時,取得資料的順序是完全随機的。  LinkedHashMap儲存了記錄的插入順序,在用Iterator周遊LinkedHashMap時,先得到的記錄肯定是先插入的.也可以在構造時用帶參數,按照應用次數排序  TreeMap取出來的是排序後的鍵值對。但如果您要按自然順序或自定義順序周遊鍵,那麼TreeMap會更好。

13. Collection包結構,與Collections的差別。

   Collection  是單列集合

List   元素是有序的、可重複

有序的collection,可以對清單中每個元素的插入位置進行精确地控制。

可以根據元素的整數索引(在清單中的位置)通路元素,并搜尋清單中的元素。

可存放重複元素,元素存取是有序的。

List接口中常用類

l Vector:線程安全,但速度慢,已被ArrayList替代。

底層資料結構是數組結構

l ArrayList:線程不安全,查詢速度快。

           底層資料結構是數組結構

l LinkedList:線程不安全。增删速度快。

            底層資料結構是清單結構

Set(集) 元素無序的、不可重複。

取出元素的方法隻有疊代器。不可以存放重複元素,元素存取是無序的。

Set接口中常用的類

l HashSet:線程不安全,存取速度快。

       它是如何保證元素唯一性的呢?依賴的是元素的hashCode方法和euqals方法。

l TreeSet:線程不安全,可以對Set集合中的元素進行排序。

 它的排序是如何進行的呢?通過compareTo或者compare方法中的來保證元素的唯一性。元素是以二叉樹的形式存放的。

Map  是一個雙列集合

|--Hashtable:線程安全,速度快。底層是哈希表資料結構。是同步的。

不允許null作為鍵,null作為值。

     |--Properties:用于配置檔案的定義和操作,使用頻率非常高,同時鍵和值都是字元串。

是集合中可以和IO技術相結合的對象。(到了IO在學習它的特有和io相關的功能。)

|--HashMap:線程不安全,速度慢。底層也是哈希表資料結構。是不同步的。

允許null作為鍵,null作為值。替代了Hashtable.

    |--LinkedHashMap: 可以保證HashMap集合有序。存入的順序和取出的順序一緻。

|--TreeMap:可以用來對Map集合中的鍵進行排序。

Collection 和 Collections的差別

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

Collections是針對集合類的一個幫助類,提供了操作集合的工具方法:一系列靜态方法實作對各種集合的搜尋、排序、線程安全化等操作。

14. try catch finally,try裡有return,finally還執行麼?

    在try中沒有異常的情況下try、catch、finally的執行順序 try ---finally

如果try中有異常,執行順序是try--- catch --- finally

如果try中沒有異常并且try中有return這時候正常執行順序是try---- finally --- return

如果try中有異常并且try中有return這時候正常執行順序是try----catch---finally---return

總之 finally 永遠執行!

try-catch-finally裡都沒有return ,finally 之後有個return ,如果try中有異常,finally執行完後,還能執行return嗎?那是不可能執行的了,try中有異常以後,根據java的異常機制先執行catch後執行finally,此時錯誤異常已經抛出,程式因異常而終止,是以你的return是不會執行的

    在存在try-catch-finally的方法中,return可能出現的位置有4個,在try中,在catch中,在finally中,在finally後(try-catch-finally外的語句塊)。

    在這4個位置都出現return的情況下(事實上應該是不可能的,如果前面3個位置都存在return,那麼最後一個位置的return就成了unreachable code,編譯不會通過),最終會執行的return應該是finally中的return。也就是finally中的return會覆寫掉其它位置的return。

try中有return語句

-------- try catch finally順序:

1.try {}.

2.如果有Error Exception則,執行catch(){}中的代碼。

3.無論有沒有 Error Exception都要執行finally{}中的代碼。

4.執行 try 中的 return

catch中有return

  但當finally中不存在return,而catch中存在return,但finally中的語句又會對catch中的return的值産生影響時,情況就有點複雜。

int ret = 0;

try{

throw new Exception();

}

catch(Exception e){

ret = 1;

return ret;

}

finally{

ret = 2;

}

這裡finally中沒有return,但是将catch中要return的ret指派為2.那麼最後傳回的值是1. 為什麼?

從調試中可以知道Finally中的指派語句的确被執行了,而執行完這條finally語句後的下一條語句就是catch中的return,那麼為啥傳回的是1呢?

catch中有return,finally中沒有return,return的值在執行finally之前已經确定下來了。

另一個值得注意的是最後的return(位于try-catch-finally外)并沒有被執行。

15. Excption與Error包結構。OOM你遇到過哪些情況,SOF你遇到過哪些情況。

OOM:

1,   OutOfMemoryError異常

除了程式計數器外,虛拟機記憶體的其他幾個運作時區域都有發生OutOfMemoryError(OOM)異常的可能,

Java Heap 溢出

一般的異常資訊:java.lang.OutOfMemoryError:Javaheap spacess

java堆用于存儲對象執行個體,我們隻要不斷的建立對象,并且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,就會在對象數量達到最大堆容量限制後産生記憶體溢出異常。

出現這種異常,一般手段是先通過記憶體映像分析工具(如Eclipse Memory Analyzer)對dump出來的堆轉存快照進行分析,重點是确認記憶體中的對象是否是必要的,先厘清是因為記憶體洩漏(Memory Leak)還是記憶體溢出(Memory Overflow)。

如果是記憶體洩漏,可進一步通過工具檢視洩漏對象到GC Roots的引用鍊。于是就能找到洩漏對象時通過怎樣的路徑與GC Roots相關聯并導緻垃圾收集器無法自動回收。

如果不存在洩漏,那就應該檢查虛拟機的參數(-Xmx與-Xms)的設定是否适當。

2,   虛拟機棧和本地方法棧溢出

如果線程請求的棧深度大于虛拟機所允許的最大深度,将抛出StackOverflowError異常。

如果虛拟機在擴充棧時無法申請到足夠的記憶體空間,則抛出OutOfMemoryError異常

這裡需要注意當棧的大小越大可配置設定的線程數就越少。

3,   運作時常量池溢出

異常資訊:java.lang.OutOfMemoryError:PermGenspace

如果要向運作時常量池中添加内容,最簡單的做法就是使用String.intern()這個Native方法。該方法的作用是:如果池中已經包含一個等于此String的字元串,則傳回代表池中這個字元串的String對象;否則,将此String對象包含的字元串添加到常量池中,并且傳回此String對象的引用。由于常量池配置設定在方法區内,我們可以通過-XX:PermSize和-XX:MaxPermSize限制方法區的大小,進而間接限制其中常量池的容量。

4,   方法區溢出

方法區用于存放Class的相關資訊,如類名、通路修飾符、常量池、字段描述、方法描述等。

異常資訊:java.lang.OutOfMemoryError:PermGenspace

方法區溢出也是一種常見的記憶體溢出異常,一個類如果要被垃圾收集器回收,判定條件是很苛刻的。在經常動态生成大量Class的應用中,要特别注意這點。

SOF:

StackOverFlow。函數棧溢出,一般無限遞歸的時候會出現。

16. Java面向對象的三個特征與含義。

    封裝,也就是把客觀事物封裝成抽象的類,并且類可以把自己的資料和方法隻讓可信的類或者對象操作,對不可信的進行資訊隐藏。封裝是面向對象的特征之一,是對象和類概念的主要特性。簡單的說,一個類就是一個封裝了資料以及操作這些資料的代碼的邏輯實體。在一個對象内部,某些代碼或某些資料可以是私有的,不能被外界通路。通過這種方式,對象對内部資料提供了不同級别的保護,以防止程式中無關的部分意外的改變或錯誤的使用了對象的私有部分。

繼承是指可以讓某個類型的對象獲得另一個類型的對象的屬性的方法。它支援按級分類的概念。繼承是指這樣一種能力:它可以使用現有類的所有功能,并在無需重新編寫原來的類的情況下對這些功能進行擴充。通過繼承建立的新類稱為“子類”或“派生類”,被繼承的類稱為“基類”、“父類”或“超類”。繼承的過程,就是從一般到特殊的過程。要實作繼承,可以通過“繼承”(Inheritance)和“組合”(Composition)來實作。繼承概念的實作方式有二類:實作繼承與接口繼承。實作繼承是指直接使用基類的屬性和方法而無需額外編碼的能力;接口繼承是指僅使用屬性和方法的名稱、但是子類必須提供實作的能力。

多态就是指一個類執行個體的相同方法在不同情形有不同表現形式。多态機制使具有不同内部結構的對象可以共享相同的外部接口。這意味着,雖然針對不同對象的具體操作不同,但通過一個公共的類,它們(那些操作)可以通過相同的方式予以調用。父對象就可以根據目前指派給它的子對象的特性以不同的方式運作。

多态是面向對象三大特征裡相對難了解和表述的一個特征。

多态的定義:指允許不同類的對象對同一消息做出響應。即同一消息可以根據發送對象的不同而采用多種不同的行為方式。(發送消息就是函數調用)

多态存在的三個必要條件

一、要有繼承;

二、要有重寫;

三、父類引用指向子類對象。

17. Override和Overload的含義與差別。

    方法的重寫(Overriding)和重載(Overloading)是Java多态性的不同表現。  

重寫(Overriding)是父類與子類之間多态性的一種表現,而重載(Overloading)是一個類中多态性的一種表現。如果在子類中定義某方法與其父類有相同的名稱和參數,我們說該方法被重寫 (Overriding) 。子類的對象使用這個方法時,将調用子類中的定義,對它而言,父類中的定義如同被"屏蔽"了。如果在一個類中定義了多個同名的方法,它們或有不同的參數個數或有不同的參數類型或有不同的參數次序,則稱為方法的重載(Overloading)。不能通過通路權限、傳回類型、抛出的異常進行重載。

1. Override 特點  

1、覆寫的方法的标志必須要和被覆寫的方法的标志完全比對,才能達到覆寫的效果;  

2、覆寫的方法的傳回值必須和被覆寫的方法的傳回一緻;  

3、覆寫的方法所抛出的異常必須和被覆寫方法的所抛出的異常一緻,或者是其子類;

4、方法被定義為final不能被重寫。 

5、對于繼承來說,如果某一方法在父類中是通路權限是private,那麼就不能在子類對其進行重寫覆寫,如果定義的話,也隻是定義了一個新方法,而不會達到重寫覆寫的效果。(通常存在于父類和子類之間。)

2.Overload 特點  

1、在使用重載時隻能通過不同的參數樣式。例如,不同的參數類型,不同的參數個數,不同的參數順序(當然,同一方法内的幾個參數類型必須不一樣,例如可以是fun(int, float),但是不能為fun(int, int));  

2、不能通過通路權限、傳回類型、抛出的異常進行重載;  

3、方法的異常類型和數目不會對重載造成影響;  

4、重載事件通常發生在同一個類中,不同方法之間的現象。

5、存在于同一類中,但是隻有虛方法和抽象方法才能被覆寫。

18. Interface與abstract類的差別。

   1.abstract class 在Java中表示的是一種繼承關系,一個類隻能使用一次繼承關系。但是,一個類卻可以實作多個interface。

2.在abstract class 中可以有自己的資料成員,也可以有非abstarct的方法,而在interface中,隻能夠有靜态的不能被修改的資料成員(也就是必須是static final的,不過在 interface中一般不定義資料成員),所有的方法都是public abstract的。

3.抽象類中的變量預設是 friendly 型,其值可以在子類中重新定義,也可以重新指派。接口中定義的變量預設是public static final 型,且必須給其賦初值,是以實作類中不能重新定義,也不能改變其值。

4.abstract class和interface所反映出的設計理念不同。其實abstract class表示的是"is-a"關系,interface表示的是"like-a"關系。

5.實作抽象類和接口的類必須實作其中的所有方法。抽象類中可以有非抽象方法。接口中則不能有實作方法。

abstract class 和 interface 是 Java語言中的兩種定義抽象類的方式,它們之間有很大的相似性。但是對于它們的選擇卻又往往反映出對于問題領域中的概念本質的了解、對于設計意圖的反映是否正确、合理,因為它們表現了概念間的不同的關系。

19. Static class 與non staticclass的差別。

   java允許我們在一個類裡面定義靜态類。比如内部類(nested class)。把nested class封閉起來的類叫外部類。在java中,我們不能用static修飾頂級類(top level class)。隻有内部類可以為static。

     靜态内部類和非靜态内部類之間到底有什麼不同呢?下面是兩者間主要的不同。

    (1)内部靜态類不需要有指向外部類的引用。但非靜态内部類需要持有對外部類的引用。

    (2)非靜态内部類能夠通路外部類的靜态和非靜态成員。靜态類不能通路外部類的非靜态成員。他隻能通路外部類的靜态成員。

(3)一個非靜态内部類不能脫離外部類實體被建立,一個非靜态内部類可以通路外部類的資料和方法,因為他就在外部類裡面。

20. java多态的實作原理。

    衆所周知,多态是面向對象程式設計語言的重要特性,它允許基類的指針或引用指向派生類的對象,而在具體通路時實作方法的動态綁定。C++ 和 Java 作為目前最為流行的兩種面向對象程式設計語言,其内部對于多态的支援到底是如何實作的呢,本文對此做了全面的介紹。

注意到在本文中,指針和引用會互換使用,它們僅是一個抽象概念,表示和另一個對象的連接配接關系,無須在意其具體的實作。

Java 的實作方式

Java 對于方法調用動态綁定的實作主要依賴于方法表,但通過類引用調用和接口引用調用的實作則有所不同。總體而言,當某個方法被調用時,JVM 首先要查找相應的常量池,得到方法的符号引用,并查找調用類的方法表以确定該方法的直接引用,最後才真正調用該方法。以下分别對該過程中涉及到的相關部分做詳細介紹。

JVM 的結構

此結構中,我們隻探讨和本文密切相關的方法區 (method area)。當程式運作需要某個類的定義時,載入子系統 (class loader subsystem) 裝入所需的 class 檔案,并在内部建立該類的類型資訊,這個類型資訊就存貯在方法區。類型資訊一般包括該類的方法代碼、類變量、成員變量的定義等等。可以說,類型資訊就是類的 Java 檔案在運作時的内部結構,包含了改類的所有在 Java 檔案中定義的資訊。

注意到,該類型資訊和 class 對象是不同的。class 對象是 JVM 在載入某個類後于堆 (heap) 中建立的代表該類的對象,可以通過該 class 對象通路到該類型資訊。比如最典型的應用,在 Java 反射中應用 class 對象通路到該類支援的所有方法,定義的成員變量等等。可以想象,JVM 在類型資訊和 class 對象中維護着它們彼此的引用以便互相通路。兩者的關系可以類比于程序對象與真正的程序之間的關系。

Java 的方法調用方式

Java 的方法調用有兩類,動态方法調用與靜态方法調用。靜态方法調用是指對于類的靜态方法的調用方式,是靜态綁定的;而動态方法調用需要有方法調用所作用的對象,是動态綁定的。類調用 (invokestatic) 是在編譯時刻就已經确定好具體調用方法的情況,而執行個體調用 (invokevirtual) 則是在調用的時候才确定具體的調用方法,這就是動态綁定,也是多态要解決的核心問題。

JVM 的方法調用指令有四個,分别是invokestatic,invokespecial,invokesvirtual 和 invokeinterface。前兩個是靜态綁定,後兩個是動态綁定的。本文也可以說是對于 JVM 後兩種調用實作的考察。

21. 實作多線程的兩種方法:Thread與Runable。

     實作Runnable接口,繼承Thread類來說實作Runnable接口好處如下:

(1)适合多個相同程式代碼的線程去處理同一資源的情況,把虛拟CPU(線程)同程式的代碼,資料有效的分離,較好地展現了面向對象的設計思想。

(2)可以避免由于Java的單繼承特性帶來的局限。我們經常碰到這樣一種情況,即當我們要将已經繼承了某一個類的子類放入多線程中,由于一個類不能同時有兩個父類,是以不能用繼承Thread類的方式,那麼,這個類就隻能采用實作Runnable接口的方式了。

(3)有利于程式的健壯性,代碼能夠被多個線程共享,代碼與資料是獨立的。當多個線程的執行代碼來自同一個類的執行個體時,即稱它們共享相同的代碼。多個線程操作相同的資料,與它們的代碼無關。當共享通路相同的對象是,即它們共享相同的資料。當線程被構造時,需要的代碼和資料通過一個對象作為構造函數實參傳遞進去,這個對象就是一個實作了Runnable接口的類的執行個體。

22. 線程同步的方法:sychronized、lock、reentrantLock等。

    在并發量比較小的情況下,使用synchronized是個不錯的選擇,但是在并發量比較高的情況下,其性能下降很嚴重,此時ReentrantLock是個不錯的方案。

1、ReentrantLock 擁有Synchronized相同的并發性和記憶體語義,此外還多了 鎖投票,定時鎖等候和中斷鎖等候

     線程A和B都要擷取對象O的鎖定,假設A擷取了對象O鎖,B将等待A釋放對O的鎖定,

     如果使用synchronized ,如果A不釋放,B将一直等下去,不能被中斷

     如果 使用ReentrantLock,如果A不釋放,可以使B在等待了足夠長的時間以後,中斷等待,而幹别的事情

   ReentrantLock擷取鎖定與三種方式:

   a)  lock(), 如果擷取了鎖立即傳回,如果别的線程持有鎖,目前線程則一直處于休眠狀态,直到擷取鎖

   b) tryLock(), 如果擷取了鎖立即傳回true,如果别的線程正持有鎖,立即傳回false;

   c)tryLock(long timeout,TimeUnit unit),   如果擷取了鎖定立即傳回true,如果别的線程正持有鎖,會等待參數給定的時間,在等待的過程中,如果擷取了鎖定,就傳回true,如果等待逾時,傳回false;

   d) lockInterruptibly:如果擷取了鎖定立即傳回,如果沒有擷取鎖定,目前線程處于休眠狀态,直到或者鎖定,或者目前線程被别的線程中斷

2、synchronized是在JVM層面上實作的,不但可以通過一些監控工具監控synchronized的鎖定,而且在代碼執行時出現異常,JVM會自動釋放鎖定,但是使用Lock則不行,lock是通過代碼實作的,要保證鎖定一定會被釋放,就必須将unLock()放到finally{}中

3、在資源競争不是很激烈的情況下,Synchronized的性能要優于ReetrantLock,但是在資源競争很激烈的情況下,Synchronized的性能會下降幾十倍,但是ReetrantLock的性能能維持常态;

5.0的多線程任務包對于同步的性能方面有了很大的改進,在原有synchronized關鍵字的基礎上,又增加了ReentrantLock,以及各種Atomic類。了解其性能的優劣程度,有助與我們在特定的情形下做出正确的選擇。

總體的結論先擺出來: 

synchronized:

在資源競争不是很激烈的情況下,偶爾會有同步的情形下,synchronized是很合适的。原因在于,編譯程式通常會盡可能的進行優化synchronize,另外可讀性非常好,不管用沒用過5.0多線程包的程式員都能了解。

ReentrantLock:

ReentrantLock提供了多樣化的同步,比如有時間限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在資源競争不激烈的情形下,性能稍微比synchronized差點點。但是當同步非常激烈的時候,synchronized的性能一下子能下降好幾十倍。而ReentrantLock确還能維持常态。

Atomic:

和上面的類似,不激烈情況下,性能比synchronized略遜,而激烈的時候,也能維持常态。激烈的時候,Atomic的性能會優于ReentrantLock一倍左右。但是其有一個缺點,就是隻能同步一個值,一段代碼中隻能出現一個Atomic的變量,多于一個同步無效。因為他不能在多個Atomic之間同步。

是以,我們寫同步的時候,優先考慮synchronized,如果有特殊需要,再進一步優化。ReentrantLock和Atomic如果用的不好,不僅不能提高性能,還可能帶來災難。

23. 鎖的等級:方法鎖、對象鎖、類鎖。

    首先介紹一下對象鎖(也叫方法鎖)與類鎖有那些不同。下文中使用對象鎖稱呼代替方法鎖。

  對于對象鎖,是針對一個對象的,它隻在該對象的某個記憶體位置聲明一個标志位辨別該對象是否擁有鎖,是以它隻會鎖住目前的對象。一般一個對象鎖是對一個非靜态成員變量進行syncronized修飾,或者對一個非靜态方法進行syncronized修飾。對于對象鎖,不同對象通路同一個被syncronized修飾的方法的時候不會阻塞住。

類鎖是鎖住整個類的,當有多個線程來聲明這個類的對象的時候将會被阻塞,直到擁有這個類鎖的對象被銷毀或者主動釋放了類鎖。這個時候在被阻塞住的線程被挑選出一個占有該類鎖,聲明該類的對象。其他線程繼續被阻塞住。

無論是類鎖還是對象鎖,父類和子類之間是否阻塞沒有直接關系。當對一個父類加了類鎖,子類是不會受到影響的,相反也是如此。因為synchronized關鍵字并不是方法簽名的一部分,它是對方法進行修飾的。當子類覆寫父類中的同步方法或是接口中聲明的同步方法的時候,synchronized修飾符是不會被自動繼承的,是以相應的阻塞問題不會出現。

注意:這裡的阻塞問題是指的按照正常情況下應該阻塞,而因為synchronized是父類與子類之間不可傳遞導緻不會阻塞。那正常情況下阻塞是什麼那,下面會詳細介紹。但是,當一個子類沒有覆寫父類的方法的時候,這時候通過子類通路方法則會産生阻塞。

對于類鎖,則會把整個類鎖住,也就說隻能有一個對象擁有目前類的鎖。當一個對象擁有了類鎖之後,另外一個對象還想競争鎖的話則會被阻塞。兩個對象A,B,如果A正在通路一個被類鎖修飾的方法function,那麼B則不能通路。因為類鎖隻能在同一時刻被一個對象擁有。相對于對象鎖,則是不同。還是A,B兩個對象,如果A正在通路對象鎖修飾的function,那麼這個時候B也可以同時通路。

對于對象鎖,當一個對象擁有鎖之後,通路一個加了對象鎖的方法,而該方法中又調用了該類中其他加了對象鎖的方法,那麼這個時候是不會阻塞住的。這是java通過可重入鎖機制實作的。可重入鎖指的是當一個對象擁有對象鎖之後,可以重複擷取該鎖。因為synchronized塊是可重入的,是以當你通路一個對象鎖的方法的時候,在該方法中繼續通路其他對象鎖方法是不會被阻塞的。

24. 寫出生産者消費者模式。

   import java.util.LinkedList;

public class ProducerConsumer {

   private LinkedList<Object> storeHouse = newLinkedList<Object>();

   private int MAX = 10;

   public ProducerConsumer() {

    }

   public void start() {

       new Producer().start();

       new Comsumer().start();

    }

   class Producer extends Thread {

       public void run() {

           while (true) {

                synchronized (storeHouse) {

                    try {

                        while(storeHouse.size() == MAX) {

                            System.out.println("storeHouseis full , please wait");

                            storeHouse.wait();

                        }

                        Object newOb = newObject();

                        if(storeHouse.add(newOb)) {

                           System.out.println("Producer put a Object to storeHouse");

                            Thread.sleep((long)(Math.random() * 3000));

                           storeHouse.notify();

                        }

                    } catch(InterruptedException ie) {

                       System.out.println("producer is interrupted!");

                    }

                }

           }

       }

    }

   class Comsumer extends Thread {

       public void run() {

           while (true) {

                synchronized (storeHouse) {

                    try {

                        while(storeHouse.size() == 0) {

                           System.out.println("storeHouse is empty , please wait");

                           storeHouse.wait();

                        }

                       storeHouse.removeLast();

                       System.out.println("Comsumer get a Object from storeHouse");

                        Thread.sleep((long)(Math.random() * 3000));

                        storeHouse.notify();

                    } catch(InterruptedException ie) {

                       System.out.println("Consumer is interrupted");

                    }

                }

           }

       }

    }

   public static void main(String[] args) throws Exception {

       ProducerConsumer pc = new ProducerConsumer();

       pc.start();

    }

}

25. ThreadLocal的設計理念與作用。

    總之,ThreadLocal不是用來解決對象共享通路問題的,而主要是提供了保持對象的方法和避免參數傳遞的友善的對象通路方式。歸納了兩點:

1。每個線程中都有一個自己的ThreadLocalMap類對象,可以将線程自己的對象保持到其中,各管各的,線程可以正确的通路到自己的對象。

2。将一個共用的ThreadLocal靜态執行個體作為key,将不同對象的引用儲存到不同線程的ThreadLocalMap中,然後線上程執行的各處通過這個靜态ThreadLocal執行個體的get()方法取得自己線程儲存的那個對象,避免了将這個對象作為參數傳遞的麻煩。

ThreadLocal的應用場合,我覺得最适合的是多線程多執行個體(每個線程對應一個執行個體)的對象的通路,并且這個對象很多地方都要用到。

當然如果要把本來線程共享的對象通過ThreadLocal.set()放到線程中也可以,可以實作避免參數傳遞的通路方式,但是要注意get()到的是那同一個共享對象,并發通路問題要靠其他手段來解決。但一般來說線程共享的對象通過設定為某類的靜态變量就可以實作友善的通路了,似乎沒必要放到線程中。

26. ThreadPool用法與優勢。

   合理利用線程池能夠帶來三個好處。第一:降低資源消耗。通過重複利用已建立的線程降低線程建立和銷毀造成的消耗。第二:提高響應速度。當任務到達時,任務可以不需要等到線程建立就能立即執行。第三:提高線程的可管理性。線程是稀缺資源,如果無限制的建立,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的配置設定,調優和監控。但是要做到合理的利用線程池,必須對其原理了如指掌。

    線程池的處理流程如下:

首先線程池判斷基本線程池是否已滿?沒滿,建立一個工作線程來執行任務。滿了,則進入下個流程。

其次線程池判斷工作隊列是否已滿?沒滿,則将新送出的任務存儲在工作隊列裡。滿了,則進入下個流程。

最後線程池判斷整個線程池是否已滿?沒滿,則建立一個新的工作線程來執行任務,滿了,則交給飽和政策來處理這個任務。

27. Concurrent包裡的其他東西:ArrayBlockingQueue、CountDownLatch等等。

    我們都知道,在JDK1.5之前,Java中要進行業務并發時,通常需要有程式員獨立完成代碼實作,當然也有一些開源的架構提供了這些功能,但是這些依然沒有JDK自帶的功能使用起來友善。而當針對高品質Java多線程并發程式設計時,為防止死蹦等現象的出現,比如使用java之前的wait()、notify()和synchronized等,每每需要考慮性能、死鎖、公平性、資源管理以及如何避免線程安全性方面帶來的危害等諸多因素,往往會采用一些較為複雜的安全政策,加重了程式員的開發負擔.萬幸的是,在JDK1.5出現之後,Sun大神(Doug Lea)終于為我們這些可憐的小程式員推出了java.util.concurrent工具包以簡化并發完成。開發者們借助于此,将有效的減少競争條件(race conditions)和死鎖線程。concurrent包很好的解決了這些問題,為我們提供了更實用的并發程式模型。

Executor                  :具體Runnable任務的執行者。

ExecutorService           :一個線程池管理者,其實作類有多種,我會介紹一部分。我們能把Runnable,Callable送出到池中讓其排程。

Semaphore                 :一個計數信号量

ReentrantLock             :一個可重入的互斥鎖定 Lock,功能類似synchronized,但要強大的多。

Future                    :是與Runnable,Callable進行互動的接口,比如一個線程執行結束後取傳回的結果等等,還提供了cancel終止線程。

BlockingQueue             :阻塞隊列。

CompletionService         : ExecutorService的擴充,可以獲得線程執行結果的

CountDownLatch            :一個同步輔助類,在完成一組正在其他線程中執行的操作之前,它允許一個或多個線程一直等待。

CyclicBarrier             :一個同步輔助類,它允許一組線程互相等待,直到到達某個公共屏障點

Future                    :Future 表示異步計算的結果。

ScheduledExecutorService :一個ExecutorService,可安排在給定的延遲後運作或定期執行的指令。

Semaphore

一個計數信号量。從概念上講,信号量維護了一個許可集合。如有必要,在許可可用前會阻塞每一個 acquire(),然後再擷取該許可。每個 release() 添加一個許可,進而可能釋放一個正在阻塞的擷取者。但是,不使用實際的許可對象,Semaphore 隻對可用許可的号碼進行計數,并采取相應的行動。

ReentrantLock

一個可重入的互斥鎖定 Lock,它具有與使用 synchronized 方法和語句所通路的隐式螢幕鎖定相同的一些基本行為和語義,但功能更強大。

ReentrantLock 将由最近成功獲得鎖定,并且還沒有釋放該鎖定的線程所擁有。當鎖定沒有被另一個線程所擁有時,調用 lock 的線程将成功擷取該鎖定并傳回。如果目前線程已經擁有該鎖定,此方法将立即傳回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法來檢查此情況是否發生。

此類的構造方法接受一個可選的公平參數。

當設定為 true時,在多個線程的争用下,這些鎖定傾向于将通路權授予等待時間最長的線程。否則此鎖定将無法保證任何特定通路順序。

與采用預設設定(使用不公平鎖定)相比,使用公平鎖定的程式在許多線程通路時表現為很低的總體吞吐量(即速度很慢,常常極其慢),但是在獲得鎖定和保證鎖定配置設定的均衡性時差異較小。不過要注意的是,公平鎖定不能保證線程排程的公平性。是以,使用公平鎖定的衆多線程中的一員可能獲得多倍的成功機會,這種情況發生在其他活動線程沒有被處理并且目前并未持有鎖定時。還要注意的是,未定時的 tryLock 方法并沒有使用公平設定。因為即使其他線程正在等待,隻要該鎖定是可用的,此方法就可以獲得成功。

BlockingQueue

支援兩個附加操作的 Queue,這兩個操作是:檢索元素時等待隊列變為非空,以及存儲元素時等待空間變得可用。

BlockingQueue 不接受 null 元素。試圖 add、put 或 offer 一個 null 元素時,某些實作會抛出NullPointerException。null 被用作訓示 poll 操作失敗的警戒值。

BlockingQueue 可以是限定容量的。它在任意給定時間都可以有一個 remainingCapacity,超出此容量,便無法無阻塞地 put 額外的元素。

沒有任何内部容量限制的 BlockingQueue 總是報告 Integer.MAX_VALUE 的剩餘容量。

BlockingQueue 實作主要用于生産者-使用者隊列,但它另外還支援Collection 接口。是以,舉例來說,使用 remove(x) 從隊列中移除任意一個元素是有可能的。

然而,這種操作通常不會有效執行,隻能有計劃地偶爾使用,比如在取消排隊資訊時。

BlockingQueue 實作是線程安全的。所有排隊方法都可以使用内部鎖定或其他形式的并發控制來自動達到它們的目的。

然而,大量的 Collection 操作(addAll、containsAll、retainAll 和 removeAll)沒有必要自動執行,除非在實作中特别說明。

是以,舉例來說,在隻添加了 c 中的一些元素後,addAll(c) 有可能失敗(抛出一個異常)。

BlockingQueue 實質上不 支援使用任何一種“close”或“shutdown”操作來訓示不再添加任何項。

這種功能的需求和使用有依賴于實作的傾向。例如,一種常用的政策是:對于生産者,插入特殊的 end-of-stream 或 poison 對象,并根據使用者擷取這些對象的時間來對它們進行解釋。

CountDownLatch

一個同步輔助類,在完成一組正在其他線程中執行的操作之前,它允許一個或多個線程一直等待。

用給定的計數初始化 CountDownLatch。由于調用了 countDown() 方法,是以在目前計數到達零之前,await 方法會一直受阻塞。

之後,會釋放所有等待的線程,await 的所有後續調用都将立即傳回。這種現象隻出現一次——計數無法被重置。如果需要重置計數,請考慮使用 CyclicBarrier。

CountDownLatch 是一個通用同步工具,它有很多用途。将計數 1 初始化的 CountDownLatch 用作一個簡單的開/關鎖存器,

或入口:在通過調用 countDown() 的線程打開入口前,所有調用 await 的線程都一直在入口處等待。

用 N 初始化的 CountDownLatch 可以使一個線程在 N 個線程完成某項操作之前一直等待,或者使其在某項操作完成 N 次之前一直等待。

CountDownLatch 的一個有用特性是,它不要求調用 countDown 方法的線程等到計數到達零時才繼續,

而在所有線程都能通過之前,它隻是阻止任何線程繼續通過一個 await。

28. wait()和sleep()的差別。

    對于sleep()方法,我們首先要知道該方法是屬于Thread類中的。而wait()方法,則是屬于Object類中的。

sleep()方法導緻了程式暫停執行指定的時間,讓出cpu該其他線程,但是他的監控狀态依然保持者,當指定的時間到了又會自動恢複運作狀态。

在調用sleep()方法的過程中,線程不會釋放對象鎖。

而當調用wait()方法的時候,線程會放棄對象鎖,進入等待此對象的等待鎖定池,隻有針對此對象調用notify()方法後本線程才進入對象鎖定池準備擷取對象鎖進入運作狀态。

29. foreach與正常for循環效率對比。

   直接for循環效率最高,其次是疊代器和 ForEach操作。作為文法糖,其實 ForEach 編譯成位元組碼之後,使用的是疊代器實作的。

30. Java IO與NIO。

    就速度來說 CPU> 記憶體 > 硬碟

I- 就是從硬碟到記憶體

O- 就是從記憶體到硬碟

第一種方式:我從硬碟讀取資料,然後程式一直等,資料讀完後,繼續操作。這種方式是最簡單的,叫阻塞IO。

第二種方式:我從硬碟讀取資料,然後程式繼續向下執行,等資料讀取完後,通知目前程式(對硬體來說叫中斷,對程式來說叫回調),然後此程式可以立即處理資料,也可以執行完目前操作在讀取資料。

在以前的 Java IO 中,都是阻塞式 IO,NIO 引入了非阻塞式 IO。

還有一種就是同步 IO 和異步 IO。經常說的一個術語就是“異步非阻塞”,好象異步和非阻塞是同一回事,這大概是一個誤區吧。

至于 Java NIO 的 Selector,在舊的 Java IO 系統中,是基于 Stream 的,即“流”,流式 IO。

當程式從硬碟往記憶體讀取資料的時候,作業系統使用了 2 個“小伎倆”來提高性能,那就是預讀,如果我讀取了第一扇區的第三磁道的内容,那麼你很有可能也會使用第二磁道和第四磁道的内容,是以作業系統會把附近磁道的内容提前讀取出來,放在記憶體中,即緩存。

(PS:以上過程簡化了)

通過上面可以看到,作業系統是按塊 Block從硬碟拿資料,就如同一個大臉盆,一下子就放入了一盆水。但是,當 Java 使用的時候,舊的 IO 确實基于流 Stream的,也就是雖然作業系統給我了一臉盆水,但是我得用吸管慢慢喝。

于是,NIO 橫空出世。

31. 反射的作用與原理。

   Java的反射機制是Java特性之一,反射機制是建構架構技術的基礎所在。靈活掌握Java反射機制,對大家以後學習架構技術有很大的幫助。

那麼什麼是Java的反射呢?

大家都知道,要讓Java程式能夠運作,那麼就得讓Java類要被Java虛拟機加載。Java類如果不被Java虛拟機加載,是不能正常運作的。現在我們運作的所有的程式都是在編譯期的時候就已經知道了你所需要的那個類的已經被加載了。

Java的反射機制是在編譯并不确定是哪個類被加載了,而是在程式運作的時候才加載、探知、自審。使用在編譯期并不知道的類。這樣的特點就是反射。

那麼Java反射有什麼作用呢?

假如我們有兩個程式員,一個程式員在寫程式的時候,需要使用第二個程式員所寫的類,但第二個程式員并沒完成他所寫的類。那麼第一個程式員的代碼能否通過編譯呢?這是不能通過編譯的。利用Java反射的機制,就可以讓第一個程式員在沒有得到第二個程式員所寫的類的時候,來完成自身代碼的編譯。

Java的反射機制它知道類的基本結構,這種對Java類結構探知的能力,我們稱為Java類的“自審”。大家都用過Jcreator和eclipse。當我們建構出一個對象的時候,去調用該對象的方法和屬性的時候。一按點,編譯工具就會自動的把該對象能夠使用的所有的方法和屬性全部都列出來,供使用者進行選擇。這就是利用了Java反射的原理,是對我們建立對象的探知、自審。

Class類

要正确使用Java反射機制就得使用java.lang.Class這個類。它是Java反射機制的起源。當一個類被加載以後,Java虛拟機就會自動産生一個Class對象。通過這個Class對象我們就能獲得加載到虛拟機當中這個Class對象對應的方法、成員以及構造方法的聲明和定義等資訊。

反射API

反射API用于反應在目前Java虛拟機中的類、接口或者對象資訊

功能

—擷取一個對象的類資訊.

—擷取一個類的通路修飾符、成員、方法、構造方法以及超類的資訊.

—檢獲屬于一個接口的常量和方法聲明.

—建立一個直到程式運作期間才知道名字的類的執行個體.

—擷取并設定一個對象的成員,甚至這個成員的名字是

   在程式運作期間才知道.

—檢測一個在運作期間才知道名字的對象的方法

利用Java反射機制我們可以很靈活的對已經加載到Java虛拟機當中的類資訊進行檢測。當然這種檢測在對運作的性能上會有些減弱,是以什麼時候使用反射,就要靠業務的需求、大小,以及經驗的積累來決定。

32. 泛型常用特點,List<String>能否轉為List<Object>。

泛型,即“參數化類型”。一提到參數,最熟悉的就是定義方法時有形參,然後調用此方法時傳遞實參。那麼參數化類型怎麼了解呢?顧名思義,就是将類型由原來的具體的類型參數化,類似于方法中的變量參數,此時類型也定義成參數形式(可以稱之為類型形參),然後在使用/調用時傳入具體的類型(類型實參)。

由此,我們發現,在使用泛型類時,雖然傳入了不同的泛型實參,但并沒有真正意義上生成不同的類型,傳入不同泛型實參的泛型類在記憶體上隻有一個,即還是原來的最基本的類型(本執行個體中為Box),當然,在邏輯上我們可以了解成多個不同的泛型類型。

究其原因,在于Java中的泛型這一概念提出的目的,導緻其隻是作用于代碼編譯階段,在編譯過程中,對于正确檢驗泛型結果後,會将泛型的相關資訊擦出,也就是說,成功編譯過後的class檔案中是不包含任何泛型資訊的。泛型資訊不會進入到運作時階段。

對此總結成一句話:泛型類型在邏輯上看以看成是多個不同的類型,實際上都是相同的基本類型。

33. 解析XML的幾種方式的原理與特點:DOM、SAX、PULL。

   1.DOM生成和解析XML文檔

為 XML 文檔的已解析版本定義了一組接口。解析器讀入整個文檔,然後建構一個駐留記憶體的樹結構,然後代碼就可以使用 DOM 接口來操作這個樹結構。優點:整個文檔樹在記憶體中,便于操作;支援删除、修改、重新排列等多種功能;缺點:将整個文檔調入記憶體(包括無用的節點),浪費時間和空間;使用場合:一旦解析了文檔還需多次通路這些資料;硬體資源充足(記憶體、CPU)。

2.SAX生成和解析XML文檔

為解決DOM的問題,出現了SAX。SAX ,事件驅動。當解析器發現元素開始、元素結束、文本、文檔的開始或結束等時,發送事件,程式員編寫響應這些事件的代碼,儲存資料。優點:不用事先調入整個文檔,占用資源少;SAX解析器代碼比DOM解析器代碼小,适于Applet,下載下傳。缺點:不是持久的;事件過後,若沒儲存資料,那麼資料就丢了;無狀态性;從事件中隻能得到文本,但不知該文本屬于哪個元素;使用場合:Applet;隻需XML文檔的少量内容,很少回頭通路;機器記憶體少。

3.DOM4J生成和解析XML文檔

DOM4J 是一個非常非常優秀的Java XMLAPI,具有性能優異、功能強大和極端易用使用的特點,同時它也是一個開放源代碼的軟體。如今你可以看到越來越多的 Java 軟體都在使用 DOM4J 來讀寫 XML,特别值得一提的是連 Sun 的 JAXM 也在用 DOM4J。

4.PULL解析器:

PULL解析器的運作方式和SAX類似,都是基于事件的模式。不同的是,在PULL解析過程中,我們需要自己擷取産生的事件然後做相應的操作,而不像SAX那樣由處理器觸發一種事件的方法,執行我們的代碼。PULL解析器小巧輕便,解析速度快,簡單易用,非常适合在Android移動裝置中使用,Android系統内部在解析各種XML時也是用PULL解析器。

34. Java與C++對比。

    然而,C++和Java之間仍存在一些顯著的差異。可以這樣說,這些差異代表着技術的極大進步。一旦我們弄清楚了這些差異,就會了解為什麼說Java是一種優秀的程式設計語言。本附錄将引導大家認識用于區分Java和C++的一些重要特征。

  (1) 最大的障礙在于速度:解釋過的Java要比C的執行速度慢上約20倍。無論什麼都不能阻止Java語言進行編譯。寫作本書的時候,剛剛出現了一些準實時編譯器,它們能顯著加快速度。當然,我們完全有理由認為會出現适用于更多流行平台的純固有編譯器,但假若沒有那些編譯器,由于速度的限制,必須有些問題是Java不能解決的。

  (2) 和C++一樣,Java也提供了兩種類型的注釋。

  (3) 所有東西都必須置入一個類。不存在全局函數或者全局資料。如果想獲得與全局函數等價的功能,可考慮将static方法和static資料置入一個類裡。注意沒有象結構、枚舉或者聯合這一類的東西,一切隻有“類”(Class)!

  (4) 所有方法都是在類的主體定義的。是以用C++的眼光看,似乎所有函數都已嵌入,但實情并非如何(嵌入的問題在後面講述)。

  (5) 在Java中,類定義采取幾乎和C++一樣的形式。但沒有标志結束的分号。沒有class foo這種形式的類聲明,隻有類定義。

  class aType()

  void aMethod() {}

  }

  (6) Java中沒有作用域範圍運算符“::”。Java利用點号做所有的事情,但可以不用考慮它,因為隻能在一個類裡定義元素。即使那些方法定義,也必須在一個類的内部,是以根本沒有必要指定作用域的範圍。我們注意到的一項差異是對static方法的調用:使用ClassName.methodName()。除此以外,package(包)的名字是用點号建立的,并能用import關鍵字實作C++的“#include”的一部分功能。例如下面這個語句:

  import java.awt.*;

  (#include并不直接映射成import,但在使用時有類似的感覺。)

  (7) 與C++類似,Java含有一系列“主類型”(Primitive type),以實作更有效率的通路。在Java中,這些類型包括boolean,char,byte,short,int,long,float以及double。所有主類型的大小都是固有的,且與具體的機器無關(考慮到移植的問題)。這肯定會對性能造成一定的影響,具體取決于不同的機器。對類型的檢查和要求在Java裡變得更苛刻。例如:

  ■條件表達式隻能是boolean(布爾)類型,不可使用整數。

  ■必須使用象X+Y這樣的一個表達式的結果;不能僅僅用“X+Y”來實作“副作用”。

  (8) char(字元)類型使用國際通用的16位Unicode字元集,是以能自動表達大多數國家的字元。

  (9) 靜态引用的字串會自動轉換成String對象。和C及C++不同,沒有獨立的靜态字元數組字串可供使用。

  (10) Java增添了三個右移位運算符“>>>”,具有與“邏輯”右移位運算符類似的功用,可在最末尾插入零值。“>>”則會在移位的同時插入符号位(即“算術”移位)。

  (11) 盡管表面上類似,但與C++相比,Java數組采用的是一個頗為不同的結構,并具有獨特的行為。有一個隻讀的length成員,通過它可知道數組有多大。而且一旦超過數組邊界,運作期檢查會自動丢棄一個異常。所有數組都是在記憶體“堆”裡建立的,我們可将一個數組配置設定給另一個(隻是簡單地複制數組句柄)。數組辨別符屬于第一級對象,它的所有方法通常都适用于其他所有對象。

  (12) 對于所有不屬于主類型的對象,都隻能通過new指令建立。和C++不同,Java沒有相應的指令可以“在堆棧上”建立不屬于主類型的對象。所有主類型都隻能在堆棧上建立,同時不使用new指令。所有主要的類都有自己的“封裝(器)”類,是以能夠通過new建立等價的、以記憶體“堆”為基礎的對象(主類型數組是一個例外:它們可象C++那樣通過集合初始化進行配置設定,或者使用new)。

  (13) Java中不必進行提前聲明。若想在定義前使用一個類或方法,隻需直接使用它即可——編譯器會保證使用恰當的定義。是以和在C++中不同,我們不會碰到任何涉及提前引用的問題。

  (14) Java沒有預處理機。若想使用另一個庫裡的類,隻需使用import指令,并指定庫名即可。不存在類似于預處理機的宏。

  (15) Java用包代替了命名空間。由于将所有東西都置入一個類,而且由于采用了一種名為“封裝”的機制,它能針對類名進行類似于命名空間分解的操作,是以命名的問題不再進入我們的考慮之列。資料包也會在單獨一個庫名下收集庫的元件。我們隻需簡單地“import”(導入)一個包,剩下的工作會由編譯器自動完成。

  (16) 被定義成類成員的對象句柄會自動初始化成null。對基本類資料成員的初始化在Java裡得到了可靠的保障。若不明确地進行初始化,它們就會得到一個預設值(零或等價的值)。可對它們進行明确的初始化(顯式初始化):要麼在類内定義它們,要麼在建構器中定義。采用的文法比C++的文法更容易了解,而且對于static和非static成員來說都是固定不變的。我們不必從外部定義static成員的存儲方式,這和C++是不同的。

  (17) 在Java裡,沒有象C和C++那樣的指針。用new建立一個對象的時候,會獲得一個引用(本書一直将其稱作“句柄”)。例如:

  String s = new String("howdy");

  然而,C++引用在建立時必須進行初始化,而且不可重定義到一個不同的位置。但Java引用并不一定局限于建立時的位置。它們可根據情況任意定義,這便消除了對指針的部分需求。在C和C++裡大量采用指針的另一個原因是為了能指向任意一個記憶體位置(這同時會使它們變得不安全,也是Java不提供這一支援的原因)。指針通常被看作在基本變量數組中四處移動的一種有效手段。Java允許我們以更安全的形式達到相同的目标。解決指針問題的終極方法是“固有方法”(已在附錄A讨論)。将指針傳遞給方法時,通常不會帶來太大的問題,因為此時沒有全局函數,隻有類。而且我們可傳遞對對象的引用。Java語言最開始聲稱自己“完全不采用指針!”但随着許多程式員都質問沒有指針如何工作?于是後來又聲明“采用受到限制的指針”。大家可自行判斷它是否“真”的是一個指針。但不管在何種情況下,都不存在指針“算術”。

  (18) Java提供了與C++類似的“建構器”(Constructor)。如果不自己定義一個,就會獲得一個預設建構器。而如果定義了一個非預設的建構器,就不會為我們自動定義預設建構器。這和C++是一樣的。注意沒有複制建構器,因為所有自變量都是按引用傳遞的。

  (19) Java中沒有“破壞器”(Destructor)。變量不存在“作用域”的問題。一個對象的“存在時間”是由對象的存在時間決定的,并非由垃圾收集器決定。有個finalize()方法是每一個類的成員,它在某種程度上類似于C++的“破壞器”。但finalize()是由垃圾收集器調用的,而且隻負責釋放“資源”(如打開的檔案、套接字、端口、URL等等)。如需在一個特定的地點做某樣事情,必須建立一個特殊的方法,并調用它,不能依賴finalize()。而在另一方面,C++中的所有對象都會(或者說“應該”)破壞,但并非Java中的所有對象都會被當作“垃圾”收集掉。由于Java不支援破壞器的概念,是以在必要的時候,必須謹慎地建立一個清除方法。而且針對類内的基礎類以及成員對象,需要明确調用所有清除方法。

  (20) Java具有方法“過載”機制,它的工作原理與C++函數的過載幾乎是完全相同的。

  (21) Java不支援預設自變量。

  (22) Java中沒有goto。它采取的無條件跳轉機制是“break 标簽”或者“continue 标準”,用于跳出目前的多重嵌套循環。

  (23) Java采用了一種單根式的分級結構,是以所有對象都是從根類Object統一繼承的。而在C++中,我們可在任何地方啟動一個新的繼承樹,是以最後往往看到包含了大量樹的“一片森林”。在Java中,我們無論如何都隻有一個分級結構。盡管這表面上看似乎造成了限制,但由于我們知道每個對象肯定至少有一個Object接口,是以往往能獲得更強大的能力。C++目前似乎是唯一沒有強制單根結構的唯一一種OO語言。

  (24) Java沒有模闆或者參數化類型的其他形式。它提供了一系列集合:Vector(向量),Stack(堆棧)以及Hashtable(散清單),用于容納Object引用。利用這些集合,我們的一系列要求可得到滿足。但這些集合并非是為實作象C++“标準模闆庫”(STL)那樣的快速調用而設計的。Java 1.2中的新集合顯得更加完整,但仍不具備正宗模闆那樣的高效率使用手段。

  (25) “垃圾收集”意味着在Java中出現記憶體漏洞的情況會少得多,但也并非完全不可能(若調用一個用于配置設定存儲空間的固有方法,垃圾收集器就不能對其進行跟蹤監視)。然而,記憶體漏洞和資源漏洞多是由于編寫不當的finalize()造成的,或是由于在已配置設定的一個塊尾釋放一種資源造成的(“破壞器”在此時顯得特别友善)。垃圾收集器是在C++基礎上的一種極大進步,使許多程式設計問題消彌于無形之中。但對少數幾個垃圾收集器力有不逮的問題,它卻是不大适合的。但垃圾收集器的大量優點也使這一處缺點顯得微不足道。

  (26) Java内建了對多線程的支援。利用一個特殊的Thread類,我們可通過繼承建立一個新線程(放棄了run()方法)。若将synchronized(同步)關鍵字作為方法的一個類型限制符使用,互相排斥現象會在對象這一級發生。在任何給定的時間,隻有一個線程能使用一個對象的synchronized方法。在另一方面,一個synchronized方法進入以後,它首先會“鎖定”對象,防止其他任何synchronized方法再使用那個對象。隻有退出了這個方法,才會将對象“解鎖”。線上程之間,我們仍然要負責實作更複雜的同步機制,方法是建立自己的“螢幕”類。遞歸的synchronized方法可以正常運作。若線程的優先等級相同,則時間的“分片”不能得到保證。

  (27) 我們不是象C++那樣控制聲明代碼塊,而是将通路限定符(public,private和protected)置入每個類成員的定義裡。若未規定一個“顯式”(明确的)限定符,就會預設為“友好的”(friendly)。這意味着同一個包裡的其他元素也可以通路它(相當于它們都成為C++的“friends”——朋友),但不可由包外的任何元素通路。類——以及類内的每個方法——都有一個通路限定符,決定它是否能在檔案的外部“可見”。private關鍵字通常很少在Java中使用,因為與排斥同一個包内其他類的通路相比,“友好的”通路通常更加有用。然而,在多線程的環境中,對private的恰當運用是非常重要的。Java的protected關鍵字意味着“可由繼承者通路,亦可由包内其他元素通路”。注意Java沒有與C++的protected關鍵字等價的元素,後者意味着“隻能由繼承者通路”(以前可用“private protected”實作這個目的,但這一對關鍵字的組合已被取消了)。

  (28) 嵌套的類。在C++中,對類進行嵌套有助于隐藏名稱,并便于代碼的組織(但C++的“命名空間”已使名稱的隐藏顯得多餘)。Java的“封裝”或“打包”概念等價于C++的命名空間,是以不再是一個問題。Java 1.1引入了“内部類”的概念,它秘密保持指向外部類的一個句柄——建立内部類對象的時候需要用到。這意味着内部類對象也許能通路外部類對象的成員,毋需任何條件——就好象那些成員直接隸屬于内部類對象一樣。這樣便為回調問題提供了一個更優秀的方案——C++是用指向成員的指針解決的。

  (29) 由于存在前面介紹的那種内部類,是以Java裡沒有指向成員的指針。

  (30) Java不存在“嵌入”(inline)方法。Java編譯器也許會自行決定嵌入一個方法,但我們對此沒有更多的控制權力。在Java中,可為一個方法使用final關鍵字,進而“建議”進行嵌入操作。然而,嵌入函數對于C++的編譯器來說也隻是一種建議。

  (31) Java中的繼承具有與C++相同的效果,但采用的文法不同。Java用extends關鍵字标志從一個基礎類的繼承,并用super關鍵字指出準備在基礎類中調用的方法,它與我們目前所在的方法具有相同的名字(然而,Java中的super關鍵字隻允許我們通路父類的方法——亦即分級結構的上一級)。通過在C++中設定基礎類的作用域,我們可通路位于分級結構較深處的方法。亦可用super關鍵字調用基礎類建構器。正如早先指出的那樣,所有類最終都會從Object裡自動繼承。和C++不同,不存在明确的建構器初始化清單。但編譯器會強迫我們在建構器主體的開頭進行全部的基礎類初始化,而且不允許我們在主體的後面部分進行這一工作。通過組合運用自動初始化以及來自未初始化對象句柄的異常,成員的初始化可得到有效的保證。

  public class Foo extends Bar {

   public Foo(String msg) {

   super(msg); // Calls base constructor

   }

   public baz(int i) { // Override

   super.baz(i); // Calls base method

   }

  }

  (32) Java中的繼承不會改變基礎類成員的保護級别。我們不能在Java中指定public,private或者protected繼承,這一點與C++是相同的。此外,在衍生類中的優先方法不能減少對基礎類方法的通路。例如,假設一個成員在基礎類中屬于public,而我們用另一個方法代替了它,那麼用于替換的方法也必須屬于public(編譯器會自動檢查)。

  (33) Java提供了一個interface關鍵字,它的作用是建立抽象基礎類的一個等價物。在其中填充抽象方法,且沒有資料成員。這樣一來,對于僅僅設計成一個接口的東西,以及對于用extends關鍵字在現有功能基礎上的擴充,兩者之間便産生了一個明顯的差異。不值得用abstract關鍵字産生一種類似的效果,因為我們不能建立屬于那個類的一個對象。一個abstract(抽象)類可包含抽象方法(盡管并不要求在它裡面包含什麼東西),但它也能包含用于具體實作的代碼。是以,它被限制成一個單一的繼承。通過與接口聯合使用,這一方案避免了對類似于C++虛拟基礎類那樣的一些機制的需要。

  為建立可進行“例示”(即建立一個執行個體)的一個interface(接口)的版本,需使用implements關鍵字。它的文法類似于繼承的文法,如下所示:

  public interface Face {

   public void smile();

  }

  public class Baz extends Bar implements Face {

   public void smile( ) {

   System.out.println("a warm smile");

   }

  }

(34) Java中沒有virtual關鍵字,因為所有非static方法都肯定會用到動态綁定。在Java中,程式員不必自行決定是否使用。

35. Java1.7與1.8新特性。

Java7:

1.對集合類的語言支援;

2.自動資源管理;

3.改進的通用執行個體建立類型推斷;

4.數字字面量下劃線支援;

5.switch中使用string;

6.二進制字面量;

7.簡化可變參數方法調用。

Java8:

一、接口的預設方法

二、Lambda 表達式

三、函數式接口

四、方法與構造函數引用

五、Lambda 作用域

六、通路局部變量

七、通路對象字段與靜态變量

八、通路接口的預設方法

九、Date API

十、Annotation 注解

36. 設計模式:單例、工廠、擴充卡、責任鍊、觀察者等等。

   http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html

這篇博文介紹得很詳細。

37. JNI的使用。

   調用本地方法。

   了解JNI:JAVA因其跨平台特性而受人們喜愛,也正是以,使得它和本機各種内部聯系變得很少,是以JNI(Java Native Interface)就是用來解決JAVA本地操作的一種方式。JAVA通過JNI調用本地方法,而本地方法是以庫檔案的形式存放的(在WINDOWS平台上是DLL檔案形式,在UNIX機器上是SO檔案形式)。通過調用本地的庫檔案的内部方法,使JAVA可以實作和本地機器的緊密聯系,調用系統級的各接口方法。