JAVA基礎試題集合1

反射

  • 用於在運行時動態獲取類的字節碼和方法(是獲取不是創建,類的字節碼在類加載階段加載到內存中)
  • 反射機制可以在運行時認識編譯期不瞭解的對象信息,並能調用相應方法修改屬性值
  • 動態代理就是基於反射機制實現的

1 解釋"static"和"final"關鍵字?

(1)static修飾變量稱靜態變量,被所有對象所共享,在內存中只有一份(區別於類的實例變量,實例變量每個實例都有一份),它僅在類初次加載時才被初始化,可作全局變量來用。

(2)static修飾方法稱靜態方法,不依賴於任何對象,因此沒有this。 因此static方法中不能訪問非static的變量和方法,因爲它們必須依賴具體對象才能被調用,而static方法使用時這些對象可能還未創建。

(3)static還有關鍵作用就是構造靜態代碼塊以優化程序性能。static塊可以置於類中任何地方,類中可以有多個static塊。在類初次加載時,會按static塊順序來執行每個static塊,並且只會執行一次。

(4)static方法不能被覆蓋,因爲方法覆蓋是基於運行時動態綁定,而static方法是編譯時靜態綁定的。

(5)java中的final語義:1. 修飾類表示不可繼承;2. 修飾屬性,表示不可變,不可變是指,對基本類型來說值不可變,對引用來說引用不可變,但引用指向的對象可變;3. 修飾方法,表示該方法不允許重寫。

(6)在JVM的ClassFIle文件結構中,有一個access_flag標誌位,有一個標記位爲ACC_FINAL,表明該類不允許有子類,也就是final對於類的語義了。每一個類都有一個父類索引super_class,除了Object以外,取值都不爲0,也就是含有一個指向constant_pool的有效索引值。JVM會檢查super_class指向的父類,若是父類同時設置了ACC_FINAL,則會出錯。這部分在編譯時會進行檢查。因爲final類不允許繼承,也就隱含該類的所有方法都不許覆蓋。從這個角度來說,抽象類和接口,顯然是不能聲明爲final的。
方法裏也有ACC_FINAL標記,該標記和ACC_ABSTRACT衝突。ACC_FINAL設置之後,不允許子類覆蓋該方法,這個檢查是在編譯器進行的。但是父類中的private由於對子類是透明的,因此子類也無法覆蓋該方法。
屬性中也有ACC_FINAL標記位。設置後就不能設置爲ACC_VOLATILE。當設置了ACC_VOLATILE後,虛擬機將保證所有線程看到一致的值。

2 抽象類(使用abstract修飾的類)和接口(interface)的區別

(1)抽象類可擁有任意成員變量,也可擁有非抽象方法;但接口僅能有static final的成員變量(static:所有的實現共用,可以使用A.name方式直接訪問,防止多出現中的變量重名問題),同時所有方法必須抽象。
(2)在某種程度說,接口是抽象類的特殊化。
(3)子類只能繼承一個抽象類,但可以實現多個接口。
(4)抽象類是對類抽象,而接口是對行爲抽象。抽象類是對整個類整體進行抽象,包括屬性、行爲,但是接口卻是對類局部(行爲)進行抽象。
(5)對於抽象類而言,它是自下而上來設計的,我們要先知道子類才能抽象出父類,而接口則不同,它根本就不需要知道子類的存在,只需要定義一個規則即可,至於什麼子類、什麼時候怎麼實現它一概不知。
(6)默認情況下,建議使用接口而不是抽象類

4 "=="和equals方法有什麼區別?

  • (1)操作符比較兩個變量的值是否相等,即比較變量對應內存中存儲數值是否相同。要比較兩個基本類型(int、long…)的數據是否相等,只能用
    如果一個變量是對象的引用,這就涉及兩塊內存,對象本身佔用一塊(堆內存),變量也佔用一塊內存(棧內存)。例如Objet obj = new Object();變量obj是一個內存,new Object()是另一個內存,此時變量obj對應內存中存儲的數值就是對象佔用內存的首地址。對於這種變量,如果要比較兩個變量是否指向同一個對象,就需要用==操作符比較。
  • (2)equals方法用於比較兩個對象的內容是否相同。例如:
    String a=new String("foo");  
    String b=new String("foo");  

這裏創建兩個對象,用a,b這兩個變量分別指向一個對象,這是兩個不同的對象(兩個new,分配的內存肯定不同),內存首地址不同,即a和b中存儲的數值不同,所以a==b將返回false,而這兩個對象中的內容是相同的,所以a.equals(b)將返回true。
字符串的比較基本上都是使用equals方法。

  • 如果一個類沒有自己定義equals方法,那麼它將繼承Object類的equals方法,實現代碼如下:
    boolean equals(Object o){  
    	return this==o;  
    }
    
    這說明如果一個類沒有定義equals方法,默認equals方法就是用操作符,這時用equals和會得到同樣結果,如果比較兩個不同對象則總返回false。如果你的類希望能比較該類創建的兩個實例對象的內容是否相同,那麼必須覆蓋(自定義)equals方法。
    hashcode相等兩個類一定相等嗎?equals呢?相反呢?
    • ----使用equals判斷兩個類是否相等,因此equals則類一定相等,hashcode也相等
    • ----hashcode相等則不一定,因爲可能存在hash的衝突

5.Overload和Override的區別

  • Overload是重載,Override是覆蓋,也就是重寫。
  • 重載Overload表示同一個類中可以有多個名稱相同的方法,但這些方法的參數列表各不相同(即參數個數或類型不同)。
  • 重寫Override表示子類方法可與父類的方法名稱和參數完全相同,通過子類創建的實例對象調用這個方法時,將調用子類中的定義方法,這相當於把父類中定義的那個完全相同的方法給覆蓋了,這是面向對象(封裝、繼承、多態)多態的一種表現。
  • 子類覆蓋父類方法時,只能比父類拋出更少異常,或拋出父類異常的子異常,因爲子類可解決父類的一些問題,不能比父類有更多的問題。
  • 子類方法的訪問權限只能比父類的更大。如果父類方法是private類型,則等於子類增加一個新方法(子類不可見父類的private類型 public protected private)。(異常更少,訪問權限更大)
  • 從JVM角度看,Overload是在編譯期確定的(靜態綁定,編譯期確定調用的具體方法,運行期類加載階段做一次常量池解析即可調用),而Override是運行期確定(動態綁定)。因此Override不能用於私有或靜態方法(因爲這些方法都是靜態綁定)

6.HashMap的數據結構是什麼?如何實現的?和HashTable、ConcurrentHashMap的區別?

  • HashMap以鍵值對(KV)形式存儲元素。HashMap需要一個hash函數,它使用hashCode()和equals()方法執行添加和檢索。調用put(key,v)時HashMap會計算key的hash值,然後把鍵值對存儲在集合中合適的索引上。如果key已經存在,value會被更新成新值。
  • 在Java 8中,HashMap的數據結構是由Node<k,v>作爲元素組成的數組:(1)如果多個值hash到同一桶中則組成一個鏈表,當這個鏈表的節點個數超過一定值時,將這個鏈表重構爲一個二叉樹;(2)如果發現map中的元素個數超過了閾值,則進行空間擴容——空間倍增。
  • HashMap和HashTable數據結構和操作基本相同,區別是前者是非線程安全,並且HashMap接受value爲null。
  • ConcurrentHashMap和HashTable都是線程安全的,區別是:HashTable每次操作都會鎖住整個表結構,導致一次只能有一個線程訪問HashTable對象,而ConcurrentHashMap不會,只會鎖住某個節點,只有在涉及到size操作時纔會鎖整個表結構。
  • HashMap使用hashCode()和equals()方法確定鍵值對索引,當根據k獲取v時也會用到這兩個方法。如果沒有正確實現這兩個方法,兩個不同k可能有相同hash值,可能會被集合認爲相等。而且這兩個方法也用來發現重複元素。所以這兩個方法對HashMap的精確性和正確性是至關重要的。

8. 爲什麼要裝箱(int – Integer)

  • (1)把基本類型包裝成類,可以使這個類型具有很多方法和狀態,比如方法返回Boolean,對處理失敗可以直接返回null,而不應該是默認的初始值false(對基本類型來說)
  • (2)Java向面嚮對象語言的靠近。其實Java還不算純正的面嚮對象語言。真正的面向對象,沒有基本數據類型,只有對象。
  • (3)用int型的包裝類類型來解決基本類型不可以做泛型參數的問題,基本類型是不可做泛型參數的。
List <int> list = new ArrayList<int> (); //合法
List<Integer> list = new ArrayList<Integer> (); //OK

9. String和StringBuilder、StringBuffer的區別?

  • String和StringBuffer/StringBuilder,都可以儲存和操作字符串。
  • String是隻讀字符串,創建後不能更改(每次String變化都是新創建一個String,並把新String指向原來變量)。
  • StringBuffer:字符串變量(線程安全),可直接修改
  • StringBuilder:字符串變量(線程不安全)可直接修改。StringBuilder在單線程下使用,效率比StringBuffer高。
  • 字符串的+操作其本質是創建了StringBuilder對象進行append操作,然後將拼接後的StringBuilder對象用toString方法處理成String對象

10. String s = new String(“xyz”);創建了幾個字符串對象?

答:兩個對象,一個是常量區的"xyz",一個是用new創建在堆上的對象。

12. 什麼時候用斷言(assert)?

斷言是常用的調試方式,一般用於保證程序最基本、關鍵的正確性。斷言通常在開發和測試時開啓,軟件發佈後通常關閉。斷言是一個包含布爾表達式的語句,在執行這個語句時假定該表達式爲true;如果表達式的值爲false,那麼系統會報告一個AssertionError。

要在運行時啓用斷言,可以在啓動JVM時使用-enableassertions或者-ea標記。要在運行時選擇禁用斷言,可以在啓動JVM時使用-da或者-disableassertions標記。要在系統類中啓用或禁用斷言,可使用-esa或-dsa標記。還可以在包的基礎上啓用或者禁用斷言。

13. Java語言如何進行異常處理,關鍵字:throws、throw、try、catch、finally如何使用?

  • Java把各種異常進行分類,並提供良好的接口。
  • 每個異常都是一個對象,它是Throwable類或其子類的實例。當一個方法出現異常後便拋出一個異常對象,該對象中包含有異常信息,調用這個對象的方法可以捕獲到這個異常並可以對其進行處理。
  • Java異常處理通過5個關鍵詞實現:try、catch、throw、throws和finally。
  • 一般用try來執行一段程序,如果系統會拋出(throw)一個異常對象,可以通過它的類型來捕獲(catch)它,或通過總是執行代碼塊(finally)來處理;
  • try用來指定一塊預防所有異常的程序;catch子句緊跟在try塊後面,用來指定你想要捕獲的異常的類型;
  • throw語句用來明確地拋出一個異常;throws用來聲明一個方法可能拋出的各種異常;
  • finally確保範圍內的代碼無論如何(及時拋了異常)都要被執行;
  • try語句可以嵌套。

14.(容器)List、Map、Set三個接口存取元素時,各有什麼特點?

  • List以特定索引來存取元素,可以有重複元素 List。
  • Set不能存放重複元素(可用對象的equals()方法區分元素是否重複) Set。
  • Map保存鍵值對(KV)映射,映射關係可以是一對一或多對一。
  • Set和Map都有基於哈希和樹的兩種實現版本,基於哈希的理論插入/讀取時間複雜度都爲O(1),而基於排序樹版本在插入/刪除元素時會按照元素的鍵(key)構成排序樹從而達到排序和去重的效果(HashMap–TreeMap)(HashSet–TreeSet)。

18. 用Java寫一個單例類

單例:該對象的實例只創建一次,可避免對象重複創建,優化程序性能。

 public class Singleton {  
      private Singleton(){}  
      private static Singleton instance = new Singleton();  
      public static Singleton getInstance(){  
      return instance;
      }
   }

19. Exception和Error的區別

  • Error一般指系統問題,如系統崩潰,虛擬機錯誤,內存不足,方法調用棧溢出等。對這類錯誤導致的程序中斷,僅靠程序本身無法恢復和預防,建議讓程序終止。
  • Exception表示程序可處理的異常,可捕獲且可能恢復。這類異常應該儘可能處理,使程序恢復運行,不應該隨意終止異常。
  • Exception又分運行時異常(RuntimeException)和受檢異常(CheckedException )。運行時異常(NullPointerException等),編譯能通過,但一運行就終止,出現這類異常時程序會終止。而受檢異常(強制必須處理,否則編譯不通過)要麼用try–catch捕獲,要麼用throws拋出給父類處理,否則編譯不通過。

20. 範型是什麼?有什麼作用?

  • 泛型本質是參數化類型,就是說所操作的數據類型被指定爲一個參數,泛型在編譯時候檢查類型安全,並且所有的強制轉換都是自動和隱式的,可以提高代碼重用率(安全、簡單、便於複用)。

爲什麼說JAVA的泛型是僞泛型?

  • 真實泛型(如C#)在源碼、編譯器、運行期都是真實存在的,List與List是切實不同的類型
  • 僞泛型(JAVA,又稱類型擦除)只在源碼存在,編譯後字節碼中已經替換爲原生類型,並在相應地方插入強制轉換代碼。ArrayList與ArrayList是同一個類。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章