Java基礎專題

Java後端知識點彙總——Java基礎專題javascript

全套Java知識點彙總目錄,見http://www.javashuo.com/article/p-xqwvurer-eq.htmlhtml

一、解釋下什麼是面向對象?面向對象和麪向過程的區別?java

面向對象是一種基於面向過程的編程思想,是向現實世界模型的天然延伸,這是一種「萬物皆對象」的編程思想。由執行者變爲指揮者,在現實生活中的任何物體均可以歸爲一類事物,而每個個體都是一類事物的實例。面向對象的編程是以對象爲中心,以消息爲驅動。mysql

區別: (1)編程思路不一樣:面向過程以實現功能的函數開發爲主,而面向對象要首先抽象出類、屬性及其方法,而後經過實例化類、執行方法來完成功能。 sql

         (2)封裝性:都具備封裝性,可是面向過程是封裝的是功能,而面向對象封裝的是數據和功能。 typescript

         (3)面向對象具備繼承性和多態性,而面向過程沒有繼承性和多態性,因此面向對象優點很明顯。數據庫

二、面向對象的三大特性?分別解釋下?
(1)封裝:一般認爲封裝是把數據和操做數據的方法封裝起來,對數據的訪問只能經過已定義的接口。編程

(2)繼承:繼承是從已有類獲得繼承信息建立新類的過程。提供繼承信息的類被稱爲父類(超類/基類),獲得繼承信息的被稱爲子類(派生類)。後端

(3)多態:分爲編譯時多態(方法重載)和運行時多態(方法重寫)。要實現多態須要作兩件事:一是子類繼承父類並重寫父類中的方法,二是用父類型引用子類型對象,這樣一樣的引用調用一樣的方法  就會根據子類對象的不一樣而表現出不一樣的行爲。數組

  關於繼承的幾點補充:

(1)子類擁有父類對象全部的屬性和方法(包括私有屬性和私有方法),可是父類中的私有屬性和方法子類是沒法訪問,只是擁有。由於在一個子類被建立的時候,首先會在內存中建立一個父類對象,而後在父類對象外部放上子類獨有的屬性,二者合起來造成一個子類的對象;

(2)子類能夠擁有本身屬性和方法;

(3)子類能夠用本身的方式實現父類的方法。(重寫)

三、JDK、JRE、JVM 三者之間的關係?JDK(Java Development Kit):是 Java 開發工具包,是整個 Java 的核心,包括了 Java 運行環境 JRE、Java 工具和 Java 基礎類庫。JRE( Java Runtime Environment):是 Java 的運行環境,包含 JVM 標準實現及 Java 核心類庫。JVM(Java Virtual Machine):是 Java 虛擬機,是整個 Java 實現跨平臺的最核心的部分,可以運行以 Java 語言寫做的軟件程序。全部的 Java 程序會首先被編譯爲 .class 的類文件,這種類文件能夠在虛擬機上執行。

四、重載和重寫的區別?
(1)重載:編譯時多態、同一個類中同名的方法具備不一樣的參數列表、不能根據返回類型進行區分【由於:函數調用時不能指定類型信息,編譯器不知道你要調哪一個函數】;

(2)重寫(又名覆蓋):運行時多態、子類與父類之間、子類重寫父類的方法具備相同的返回類型、更好的訪問權限。

五、Java 中是否能夠重寫一個 private 或者 static 方法?
Java 中 static 方法不能被覆蓋,由於方法覆蓋是基於運行時動態綁定的,而 static 方法是編譯時靜態綁定的。static 方法跟類的任何實例都不相關,因此概念上不適用。 Java 中也不能夠覆蓋 private 的方法,由於 private 修飾的變量和方法只能在當前類中使用, 若是是其餘的類繼承當前類是不能訪問到 private 變量或方法的,固然也不能覆蓋。

靜態方法的補充

靜態的方法能夠被繼承,可是不能重寫。若是父類和子類中存在一樣名稱和參數的靜態方法,那麼該子類的方法會把原來繼承過來的父類的方法隱藏,而不是重寫。通俗的講就是父類的方法和子類的方法是兩個沒有關係的方法,具體調用哪個方法是看是哪一個對象的引用;這種父子類方法也不在存在多態的性質。

六、構造器是否能夠被重寫?

在講繼承的時候咱們就知道父類的私有屬性和構造方法並不能被繼承,因此 Constructor 也就不能被 Override(重寫),可是能夠 Overload(重載),因此你能夠看到一個類中有多個構造函數的狀況。

七、構造方法有哪些特性?

(1)名字與類名相同;(2)沒有返回值,但不能用 void 聲明構造函數;

(3)成類的對象時自動執行,無需調用。

八、在 Java 中定義一個不作事且沒有參數的構造方法有什麼做用?

Java 程序在執行子類的構造方法以前,若是沒有用 super() 來調用父類特定的構造方法,則會調用父類中「沒有參數的構造方法」。所以,若是父類中只定義了有參數的構造方法,而在子類的構造方法中又沒有用 super() 來調用父類中特定的構造方法,則編譯時將發生錯誤,由於 Java 程序在父類中找不到沒有參數的構造方法可供執行。解決辦法是:在父類里加上一個不作事且沒有參數的構造方法。

九、Java 中建立對象的幾種方式?一、使用 new 關鍵字;二、使用 Class 類的 newInstance 方法,該方法調用無參的構造器建立對象(反射):Class.forName.newInstance();三、使用 clone() 方法;四、反序列化,好比調用 ObjectInputStream 類的 readObject() 方法。

十、抽象類和接口有什麼區別?
(1)抽象類中能夠定義構造函數,接口不能定義構造函數;

(2)抽象類中能夠有抽象方法和具體方法,而接口中只能有抽象方法(public abstract);

(3)抽象類中的成員權限能夠是 public、默認、protected(抽象類中抽象方法就是爲了重寫,因此不能被 private 修飾),而接口中的成員只能夠是 public(方法默認:public abstrat、成員變量默認:public static final);

(4)抽象類中能夠包含靜態方法,而接口中不能夠包含靜態方法;

JDK8中的改變:

一、在 JDK1.8中,容許在接口中包含帶有具體實現的方法,使用 default 修飾,這類方法就是默認方法。

二、抽象類中能夠包含靜態方法,在 JDK1.8 以前接口中不能包含靜態方法,JDK1.8 之後能夠包含。以前不能包含是由於,接口不能夠實現方法,只能夠定義方法,因此不能使用靜態方法(由於靜態方法必須實現)。如今能夠包含了,只能直接用接口調用靜態方法。JDK1.8 仍然不能夠包含靜態代碼塊。

十一、靜態變量和實例變量的區別?
靜態變量:是被 static 修飾的變量,也稱爲類變量,它屬於類,所以無論建立多少個對象,靜態變量在內存中有且僅有一個拷貝;靜態變量能夠實現讓多個對象共享內存。

實例變量:屬於某一實例,須要先建立對象,而後經過對象才能訪問到它。

十二、short s1 = 1;s1 = s1 + 1;有什麼錯?那麼 short s1 = 1; s1 += 1;呢?有沒有錯誤?
對於 short s1 = 1; s1 = s1 + 1; 來講,在 s1 + 1 運算時會自動提高表達式的類型爲 int ,那麼將 int 型值賦值給 short 型變量,s1 會出現類型轉換錯誤。對於 short s1 = 1; s1 += 1; 來講,+= 是 Java 語言規定的運算符,Java 編譯器會對它進行特殊處理,所以能夠正確編譯。

1三、Integer 和 int 的區別?

(1)int 是 Java 的八種基本數據類型之一,而 Integer 是 Java 爲 int 類型提供的封裝類;

(2)int 型變量的默認值是 0,Integer 變量的默認值是 null,這一點說明 Integer 能夠區分出未賦值和值爲 0 的區分;

(3)Integer 變量必須實例化後纔可使用,而 int 不須要。

關於 Integer 和 int 的比較的延伸:

一、因爲 Integer 變量其實是對一個 Integer 對象的引用,因此兩個經過 new 生成的 Integer 變量永遠是不相等的,由於其內存地址是不一樣的;

二、Integer 變量和 int 變量比較時,只要兩個變量的值是相等的,則結果爲  true。由於包裝類 Integer 和基本數據類型 int 類型進行比較時,Java 會自動拆包裝類爲 int,而後進行比較,實際上就是兩個 int 型變量在進行比較;

三、非 new 生成的 Integer 變量和 new Integer() 生成的變量進行比較時,結果爲 false。由於非 new 生成的 Integer 變量指向的是 Java 常量池中的對象,而 new Integer() 生成的變量指向堆中新建的對象,二者在內存中的地址不一樣;

四、對於兩個非 new 生成的 Integer 對象進行比較時,若是兩個變量的值在區間 [-128, 127] 之間,則比較結果爲 true,不然爲 false。Java 在編譯 Integer i = 100 時,會編譯成 Integer i = Integer.valueOf(100),而 Integer 類型的 valueOf 的源碼以下所示:

public static Integer valueOf(int var0) {           
return var0 >= -128 && var0 <= Integer.IntegerCache.high ? Integer.IntegerCache.cache[var0 + 128] : new Integer(var0);
}

 從上面的代碼中能夠看出:Java 對於 [-128, 127] 之間的數會進行緩存,好比:Integer i = 127,會將 127 進行緩存,下次再寫 Integer j = 127 的時候,就會直接從緩存中取出,而對於這個區間以外的數就須要 new 了。

  • 包裝類的緩存:

Boolean:所有緩存

Byte:所有緩存

Character:<= 127 緩存

Short:-128 — 127 緩存

Long:-128 — 127 緩存

Integer:-128 — 127 緩存

Float:沒有緩存

Doulbe:沒有緩存

1四、裝箱和拆箱

自動裝箱是 Java 編譯器在基本數據類型和對應得包裝類之間作的一個轉化。好比:把 int 轉化成 Integer,double 轉化成 Double 等等。反之就是自動拆箱。

原始類型:boolean、char、byte、short、int、long、float、double

封裝類型:Boolean、Character、Byte、Short、Integer、Long、Float、Double

1五、switch 語句可否做用在 byte 上,可否做用在 long 上,可否做用在 String 上?

在 switch(expr 1) 中,expr1 只能是一個整數表達式或者枚舉常量。而整數表達式能夠是 int 基本數據類型或者 Integer 包裝類型。因爲,byte、short、char 均可以隱式轉換爲 int,因此,這些類型以及這些類型的包裝類型也都是能夠的。而 long 和 String 類型都不符合 switch 的語法規定,而且不能被隱式的轉換爲 int 類型,因此,它們不能做用於 switch 語句中。不過,須要注意的是在 JDK1.7 版本以後 switch 就能夠做用在 String 上了。

1六、字節和字符的區別?

字節是存儲容量的基本單位;字符是數字、字母、漢字以及其餘語言的各類符號;1 字節 = 8 個二進制單位,一個字符由一個字節或多個字節的二進制單位組成。

1七、String 爲何要設計爲不可變類?

在 Java 中將 String 設計成不可變的是綜合考慮到各類因素的結果。主要的緣由主要有如下三點:

(1)字符串常量池的須要:字符串常量池是 Java 堆內存中一個特殊的存儲區域, 當建立一個 String 對象時,假如此字符串值已經存在於常量池中,則不會建立一個新的對象,而是引用已經存在的對象;
(2)容許 String 對象緩存 HashCode:Java 中 String 對象的哈希碼被頻繁地使用, 好比在 HashMap 等容器中。字符串不變性保證了 hash 碼的惟一性,所以能夠放心地進行緩存。這也是一種性能優化手段,意味着沒必要每次都去計算新的哈希碼;

(3)String 被許多的 Java 類(庫)用來當作參數,例如:網絡鏈接地址 URL、文件路徑 path、還有反射機制所須要的 String 參數等, 倘若 String 不是固定不變的,將會引發各類安全隱患。

1八、String、StringBuilder、StringBuffer 的區別?

String:用於字符串操做,屬於不可變類;【補充:String 不是基本數據類型,是引用類型,底層用 char 數組實現的】

StringBuilder:與 StringBuffer 相似,都是字符串緩衝區,但線程不安全;

StringBuffer:也用於字符串操做,不一樣之處是 StringBuffer 屬於可變類,對方法加了同步鎖,線程安全。

StringBuffer的補充:

說明:StringBuffer 中並非全部方法都使用了 Synchronized 修飾來實現同步:

@Override

public StringBuffer insert(int dstOffset, CharSequence s) {  

// Note, synchronization achieved via invocations of other StringBuffer methods  

// after narrowing of s to specific type  

// Ditto for toStringCache clearing 
 super.insert(dstOffset, s); 
 return this;

}

執行效率:StringBuilder > StringBuffer > String
1九、String 字符串修改實現的原理?

當用 String 類型來對字符串進行修改時,其實現方法是首先建立一個 StringBuffer,其次調用 StringBuffer 的 append() 方法,

最後調用 StringBuffer 的 toString() 方法把結果返回。

20、String str = "i" 與 String str = new String("i") 同樣嗎?

不同,由於內存的分配方式不同。String str = "i" 的方式,Java 虛擬機會將其分配到常量池中;而 String str = new String("i") 則會被分到堆內存中。

public class StringTest {    public static void main(String[] args) {
        String str1 = "abc";
        String str2 = "abc";
        String str3 = new String("abc");
        String str4 = new String("abc");
        System.out.println(str1 == str2);      // true
        System.out.println(str1 == str3);      // false
        System.out.println(str3 == str4);      // false
        System.out.println(str3.equals(str4)); // true

    }
}

在執行 String  str1 = "abc" 的時候,JVM 會首先檢查字符串常量池中是否已經存在該字符串對象,若是已經存在,那麼就不會再建立了,直接返回該字符串在字符串常量池中的內存地址;若是該字符串還不存在字符串常量池中,那麼就會在字符串常量池中建立該字符串對象,而後再返回。因此在執行 String  str2 = "abc" 的時候,由於字符串常量池中已經存在「abc」字符串對象了,就不會在字符串常量池中再次建立了,因此棧內存中 str1 和 str2 的內存地址都是指向 "abc" 在字符串常量池中的位置,因此 str1 = str2 的運行結果爲 true。

而在執行 String  str3 = new  String("abc") 的時候,JVM 會首先檢查字符串常量池中是否已經存在「abc」字符串,若是已經存在,則不會在字符串常量池中再建立了;若是不存在,則就會在字符串常量池中建立 "abc" 字符串對象,而後再到堆內存中再建立一份字符串對象,把字符串常量池中的 "abc" 字符串內容拷貝到內存中的字符串對象中,而後返回堆內存中該字符串的內存地址,即棧內存中存儲的地址是堆內存中對象的內存地址。String  str4 = new  String("abc") 是在堆內存中又建立了一個對象,因此 str 3 == str4 運行的結果是 false。str一、str二、str三、str4 在內存中的存儲情況以下圖所示:

 2一、String 類的經常使用方法都有那些?

indexOf():返回指定字符的索引。

charAt():返回指定索引處的字符。

replace():字符串替換。

trim():去除字符串兩端空白。

split():分割字符串,返回一個分割後的字符串數組。

getBytes():返回字符串的 byte 類型數組。

length():返回字符串長度。

toLowerCase():將字符串轉成小寫字母

toUpperCase():將字符串轉成大寫字符。

substring():截取字符串。equals():字符串比較。

2二、final 修飾 StringBuffer 後還能夠 append 嗎?

能夠。final 修飾的是一個引用變量,那麼這個引用始終只能指向這個對象,可是這個對象內部的屬性是能夠變化的。

官方文檔解釋:

once a final variable has been assigned, it always contains the same value. If a final variable holds a reference to an object, then the state of the object may be changed by operations on the object, but the variable will always refer to the same object.

2三、Object 的經常使用方法有哪些?
clone 方法:用於建立並返回當前對象的一份拷貝;

getClass 方法:用於返回當前運行時對象的 Class;

toString 方法:返回對象的字符串表示形式;
finalize 方法:實例被垃圾回收器回收時觸發的方法;

equals 方法:用於比較兩個對象的內存地址是否相等,通常須要重寫;

hashCode 方法:用於返回對象的哈希值;

notify 方法:喚醒一個在此對象監視器上等待的線程。若是有多個線程在等待只會喚醒一個。

notifyAll 方法:做用跟 notify() 同樣,只不過會喚醒在此對象監視器上等待的全部線程,而不是一個線程。

wait 方法:讓當前對象等待;

.......

2四、爲何 wait/notify 方法放在 Object 類中而不是 Thread 類中?
一個很明顯的緣由是 Java 提供的鎖是對象級的而不是線程級的,每一個對象都有鎖,經過線程得到。若是線程須要等待某些鎖,那麼調用對象中的 wait() 方法就有意義了。若是 wait() 方法定義在 Thread 類中,線程正在等待的是哪一個鎖就不明顯了。簡單的說,因爲 wait,notify 和 notifyAll 都是鎖級別的操做,因此把他們定義在 Object 類中由於鎖屬於對象。

2五、final、finally、finalize 的區別?

final:用於聲明屬性、方法和類,分別表示屬性不可變、方法不可覆蓋、被其修飾的類不可繼承;

finally:異常處理語句結構的一部分,表示老是執行;

finallize:Object類的一個方法,在垃圾回收時會調用被回收對象的finalize。

2六、finally 塊中的代碼何時被執行?

在 Java 語言的異常處理中,finally 塊的做用就是爲了保證不管出現什麼狀況,finally 塊裏的代碼必定會被執行。因爲程序執行 return 就意味着結束對當前函數的調用並跳出這個函數體,所以任何語句要執行都只能在 return 前執行(除非碰到 exit 函數),所以 finally 塊裏的代碼也是在 return 以前執行的。

此外,若是 try-finally 或者 catch-finally 中都有 return,那麼 finally 塊中的 return 將會覆蓋別處的 return 語句,最終返回到調用者那裏的是 finally 中 return 的值。

2七、finally 是否是必定會被執行到?

不必定。下面列舉兩種執行不到的狀況:

(1)當程序進入 try 塊以前就出現異常時,會直接結束,不會執行 finally 塊中的代碼;
(2)當程序在 try 塊中強制退出時也不會去執行 finally 塊中的代碼,好比在 try 塊中執行 exit 方法。

2八、try-catch-finally 中,若是 catch 中 return 了,finally 還會執行嗎?

會。程序在執行到 return 時會首先將返回值存儲在一個指定的位置,其次去執行 finally 塊,最後再返回。所以,對基本數據類型,在 finally 塊中改變 return 的值沒有任何影響,直接覆蓋掉;而對引用類型是有影響的,返回的是在 finally 對 前面 return 語句返回對象的修改值。

2九、try-catch-finally 中那個部分能夠省略?catch 能夠省略。try 只適合處理運行時異常,try+catch 適合處理運行時異常+普通異常。也就是說,若是你只用 try 去處理普通異常卻不加以 catch 處理,編譯是通不過的,由於編譯器硬性規定,普通異常若是選擇捕獲,則必須用 catch 顯示聲明以便進一步處理。而運行時異常在編譯時沒有如此規定,因此 catch 能夠省略,你加上 catch 編譯器也以爲無可厚非。

30、static 關鍵字的做用?
(1)靜態變量:又稱爲類變量,也就是說這個變量屬於類的,類全部的實例都共享靜態變量,能夠直接經過類名來訪問它。靜態變量在內存中只存在一份;

(2)靜態方法:靜態方法在類加載的時候就存在了,它不依賴於任何實例。因此靜態方法必須有實現,也就是說它不能是抽象方法。只能訪問所屬類的靜態字段和靜態方法,方法中不能有 this 和 super 關鍵字;

(3)靜態語句塊:靜態語句塊在類初始化時運行一次;

(4)靜態內部類:非靜態內部類依賴於外部類的實例,而靜態內部類不須要。靜態內部類不能訪問外部類的非靜態的變量和方法;

(5)初始化順序:靜態變量和靜態語句塊優先於實例變量和普通語句塊,靜態變量和靜態語句塊的初始化順序取決於它們在代碼中的順序。

  public static String staticField = "靜態變量";
  static {    System.out.println("靜態語句塊");   ---- 1  } 
  public String field = "實例變量";       ---- 2  
 {    System.out.println("普通語句塊");  ----3  }  

 // 最後纔是構造函數的初始化
  public InitialOrderTest() {    
  System.out.println("構造函數");    ----4
  }

初始化補充:

存在繼承的狀況下,初始化順序爲:

  1. 父類(靜態變量、靜態語句塊) 2. 子類(靜態變量、靜態語句塊)3. 父類(實例變量、普通語句塊)4. 父類(構造函數)5. 子類(實例變量、普通語句塊)6. 子類(構造函數)

3一、super 關鍵字的做用?
(1)訪問父類的構造函數:可使用 super() 函數訪問父類的構造函數,從而委託父類完成一些初始化的工做。

(2)訪問父類的成員:若是子類重寫了父類的某個方法,能夠經過使用 super 關鍵字來引用父類的方法實現。

(3)this 和 super 不能同時出如今一個構造函數裏面,由於 this 必然會調用其它的構造函數,其它的構造函數必然也會有 super 語句的存在,因此在同一個構造函數裏面有相同的語句,就失去了語句的意義,編譯器也不會經過。

3二、transient 關鍵字的做用?
對於不想進行序列化的變量,使用 transient 關鍵字修飾。transient 關鍵字的做用是:阻止實例中那些用此關鍵字修飾的的變量序列化。當對象被反序列化時,被 transient 修飾的變量值不會被持久化和恢復。transient 只能修飾變量,不能修飾類和方法。

3三、== 和 equals 的區別?

==:若是比較的對象是基本數據類型,則比較的是數值是否相等;若是比較的是引用數據類型,則比較的是對象的地址值是否相等。

equals 方法:用來比較兩個對象的內容是否相等。注意:equals 方法不能用於比較基本數據類型的變量。若是沒有對 equals 方法進行重寫,則比較的是引用類型的變量所指向的對象的地址(不少類從新了 equals 方法,好比 String、Integer 等把它變成了值比較,因此通常狀況下 equals 比較的是值是否相等)。

3四、兩個對象的 hashCode() 相同,則 equals() 也必定爲 true 嗎?

兩個對象的 hashCode() 相同,equals() 不必定爲 true。由於在散列表中,hashCode() 相等即兩個鍵值對的哈希值相等,然而哈希值相等,並不必定能得出鍵值對相等【散列衝突】。

3五、爲何重寫 equals() 就必定要重寫 hashCode() 方法?

這個問題應該是有個前提,就是你須要用到 HashMap、HashSet 等 Java 集合,用不到哈希表的話,其實僅僅重寫 equals() 方法也能夠。而工做中的場景是經常用到 Java 集合,因此 Java 官方建議重寫 equals() 就必定要重寫 hashCode() 方法。 

對於對象集合的判重,若是一個集合含有 10000 個對象實例,僅僅使用 equals() 方法的話,那麼對於一個對象判重就須要比較 10000 次,隨着集合規模的增大,時間開銷是很大的。可是同時使用哈希表的話,就能快速定位到對象的大概存儲位置,而且在定位到大概存儲位置後,後續比較過程當中,若是兩個對象的 hashCode 不相同,也再也不須要調用 equals() 方法,從而大大減小了 equals() 比較次數。 

因此從程序實現原理上來說的話,既須要 equals() 方法,也須要 hashCode() 方法。那麼既然重寫了 equals(),那麼也要重寫 hashCode() 方法,以保證二者之間的配合關係。 

hashCode()與equals()的相關規定:

一、若是兩個對象相等,則 hashCode 必定也是相同的;二、兩個對象相等,對兩個對象分別調用 equals 方法都返回 true;三、兩個對象有相同的 hashCode 值,它們也不必定是相等的;四、所以,equals 方法被覆蓋過,則 hashCode 方法也必須被覆蓋;五、hashCode() 的默認行爲是對堆上的對象產生獨特值。若是沒有重寫 hashCode(),則該 class 的兩個對象不管如何都不會相等(即便這兩個對象指向相同的數據)。

3六、& 和 && 的區別?

Java 中 && 和 & 都是表示與的邏輯運算符,都表示邏輯運輸符 and,當兩邊的表達式都爲 true 的時候,整個運算結果才爲 true,不然爲 false。

&&:有短路功能,當第一個表達式的值爲 false 的時候,則再也不計算第二個表達式;
&:無論第一個表達式結果是否爲 true,第二個都會執行。除此以外,& 還能夠用做位運算符:當 & 兩邊的表達式不是 Boolean 類型的時候,& 表示按位操做。

3七、Java 中的參數傳遞時傳值呢?仍是傳引用?

Java 的參數是以值傳遞的形式傳入方法中,而不是引用傳遞。當傳遞方法參數類型爲基本數據類型(數字以及布爾值)時,一個方法是不可能修改一個基本數據類型的參數。當傳遞方法參數類型爲引用數據類型時,一個方法將修改一個引用數據類型的參數所指向對象的值。即便 Java 函數在傳遞引用數據類型時,也只是拷貝了引用的值罷了,之因此能修改引用數據是由於它們同時指向了一個對象,但這仍然是按值調用而不是引用調用。

3八、Java 中的 Math.round(-1.5) 等於多少?

等於 -1,由於在數軸上取值時,中間值(0.5)向右取整,因此正 0.5 是往上取整,負 0.5 是直接捨棄。

3九、兩個二進制數的異或結果是什麼?

兩個二進制數異或結果是這兩個二進制數差的絕對值。表達式以下:a^b = |a-b|。

兩個二進制 a 與 b 異或,即 a 和 b 兩個數按位進行運算。若是對應的位相同,則爲 0(至關於對應的算術相減),若是不一樣即爲 1(至關於對應的算術相加)。因爲二進制每一個位只有兩種狀態,要麼是 0,要麼是 1,則按位異或操做可表達爲按位相減取值相對值,再按位累加。

40、Error 和 Exception 的區別?

Error 類和 Exception 類的父類都是 Throwable 類。主要區別以下:

Error 類: 通常是指與虛擬機相關的問題,如:系統崩潰、虛擬機錯誤、內存空間不足、方法調用棧溢出等。這類錯誤將會致使應用程序中斷,僅靠程序自己沒法恢復和預防;
Exception 類:分爲運行時異常和受檢查的異常。

4一、運行時異常與受檢異常有何異同?
運行時異常:如:空指針異常、指定的類找不到、數組越界、方法傳遞參數錯誤、數據類型轉換錯誤。能夠編譯經過,可是一運行就中止了,程序不會本身處理;受檢查異常:要麼用 try … catch… 捕獲,要麼用 throws 聲明拋出,交給父類處理。

4二、throw 和 throws 的區別?(1)throw:在方法體內部,表示拋出異常,由方法體內部的語句處理;throw 是具體向外拋出異常的動做,因此它拋出的是一個異常實例;(2)throws:在方法聲明後面,表示若是拋出異常,由該方法的調用者來進行異常的處理;表示出現異常的可能性,並不必定會發生這種異常。

4三、常見的異常類有哪些?
NullPointerException:當應用程序試圖訪問空對象時,則拋出該異常。SQLException:提供關於數據庫訪問錯誤或其餘錯誤信息的異常。
IndexOutOfBoundsException:指示某排序索引(例如對數組、字符串或向量的排序)超出範圍時拋出。
FileNotFoundException:當試圖打開指定路徑名錶示的文件失敗時,拋出此異常。
IOException:當發生某種 I/O 異常時,拋出此異常。此類是失敗或中斷的 I/O 操做生成的異常的通用類。
ClassCastException:當試圖將對象強制轉換爲不是實例的子類時,拋出該異常。IllegalArgumentException:拋出的異常代表向方法傳遞了一個不合法或不正確的參數。

4四、主線程能夠捕獲到子線程的異常嗎?

線程設計的理念:「線程的問題應該線程本身自己來解決,而不要委託到外部」。

正常狀況下,若是不作特殊的處理,在主線程中是不可以捕獲到子線程中的異常的。若是想要在主線程中捕獲子線程的異常,咱們能夠用以下的方式進行處理,使用 Thread 的靜態方法。

Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandle());

4五、Java 的泛型是如何工做的 ? 什麼是類型擦除 ?

 泛型是經過類型擦除來實現的,編譯器在編譯時擦除了全部類型相關的信息,因此在運行時不存在任何類型相關的信息。例如:List<String> 在運行時僅用一個 List 來表示。這樣作的目的,是確保能和 Java 5 以前的版本開發二進制類庫進行兼容。

類型擦除:泛型信息只存在於代碼編譯階段,在進入 JVM 以前,與泛型相關的信息會被擦除掉,專業術語叫作類型擦除。在泛型類被類型擦除的時候,以前泛型類中的類型參數部分若是沒有指定上限,如 <T> 則會被轉譯成普通的 Object 類型,若是指定了上限如 <T extends String> 則類型參數就被替換成類型上限。

補充:

List<String> list = new ArrayList<String>();

一、兩個 String 其實只有第一個起做用,後面一個沒什麼卵用,只不過 JDK7 纔開始支持 List<String>list = new ArrayList<> 這種寫法。

二、第一個 String 就是告訴編譯器,List 中存儲的是 String 對象,也就是起類型檢查的做用,以後編譯器會擦除泛型佔位符,以保證兼容之前的代碼。

4六、什麼是泛型中的限定通配符和非限定通配符 ?

限定通配符對類型進行了限制。有兩種限定通配符,一種是<? extends T> 它經過確保類型必須是 T 的子類來設定類型的上界,另外一種是<? super T>它經過確保類型必須是 T 的父類來設定類型的下界。泛型類型必須用限定內的類型來進行初始化,不然會致使編譯錯誤。另外一方面 <?> 表示了非限定通配符,由於 <?> 能夠用任意類型來替代。

4七、List<? extends T> 和 List <? super T> 之間有什麼區別 ?
這兩個 List 的聲明都是限定通配符的例子,List<? extends T> 能夠接受任何繼承自 T 的類型的 List,而List <? super T> 能夠接受任何 T 的父類構成的 List。例如 List<? extends Number> 能夠接受 List<Integer> 或 List<Float>。

補充:

Array 不支持泛型,要用 List 代替 Array,由於 List 能夠提供編譯器的類型安全保證,而 Array卻不能。

4八、如何實現對象的克隆?
(1)實現 Cloneable 接口並重寫 Object 類中的 clone() 方法;(2)實現 Serializable 接口,經過對象的序列化和反序列化實現克隆,能夠實現真正的深克隆。

4九、深克隆和淺克隆的區別?

(1)淺克隆:拷貝對象和原始對象的引用類型引用同一個對象。淺克隆只是複製了對象的引用地址,兩個對象指向同一個內存地址,因此修改其中任意的值,另外一個值都會隨之變化,這就是淺克隆。(2)深克隆:拷貝對象和原始對象的引用類型引用不一樣對象。深拷貝是將對象及值複製過來,兩個對象修改其中任意的值另外一個值不會改變,這就是深拷貝(例:JSON.parse() 和 JSON.stringify(),可是此方法沒法複製函數類型)。

克隆的補充:

深克隆的實現就是在引用類型所在的類實現 Cloneable 接口,並使用 public 訪問修飾符重寫 clone 方法。Java 中定義的 clone 沒有深淺之分,都是統一的調用 Object 的 clone 方法。爲何會有深克隆的概念?是因爲咱們在實現的過程當中刻意的嵌套了 clone 方法的調用。也就是說深克隆就是在須要克隆的對象類型的類中從新實現克隆方法 clone()。

50、什麼是 Java 的序列化,如何實現 Java 的序列化?

對象序列化是一個用於將對象狀態轉換爲字節流的過程,能夠將其保存到磁盤文件中或經過網絡發送到任何其餘程序。從字節流建立對象的相反的過程稱爲反序列化。而建立的字節流是與平臺無關的,在一個平臺上序列化的對象能夠在不一樣的平臺上反序列化。序列化是爲了解決在對象流進行讀寫操做時所引起的問題。

序列化的實現:將須要被序列化的類實現 Serializable 接口,該接口沒有須要實現的方法,只是用於標註該對象是可被序列化的,而後使用一個輸出流(如:FileOutputStream)來構造一個 ObjectOutputStream 對象,接着使用 ObjectOutputStream 對象的 writeObject(Object obj) 方法能夠將參數爲 obj 的對象寫出,要恢復的話則使用輸入流。

5一、什麼狀況下須要序列化?
(1)當你想把的內存中的對象狀態保存到一個文件中或者數據庫中時候;
(2)當你想用套接字在網絡上傳送對象的時候;(3)當你想經過 RMI 傳輸對象的時候。
5二、Java 中的反射是什麼意思?有哪些應用場景?

每一個類都有一個 Class 對象,包含了與類有關的信息。當編譯一個新類時,會產生一個同名的 .class 文件,該文件內容保存着 Class 對象。類加載至關於 Class 對象的加載,類在第一次使用時才動態加載到 JVM 中。也可使用 Class.forName("com.mysql.jdbc.Driver") 這種方式來控制類的加載,該方法會返回一個 Class 對象。

反射能夠提供運行時的類信息,而且這個類能夠在運行時才加載進來,甚至在編譯時期該類的 .class 不存在也能夠加載進來。Class 和 java.lang.reflect 一塊兒對反射提供了支持,java.lang.reflect 類庫主要包含了如下三個類:

(1)Field :可使用 get() 和 set() 方法讀取和修改 Field 對象關聯的字段;
(2)Method :可使用 invoke() 方法調用與 Method 對象關聯的方法;
(3)Constructor :能夠用 Constructor 建立新的對象。
應用舉例:工廠模式,使用反射機制,根據全限定類名得到某個類的 Class 實例。

5三、反射的優缺點?

優勢:

運行期類型的判斷,class.forName() 動態加載類,提升代碼的靈活度;

缺點:儘管反射很是強大,但也不能濫用。若是一個功能能夠不用反射完成,那麼最好就不用。在咱們使用反射技術時,下面幾條內容應該牢記於心。(1)性能開銷 :反射涉及了動態類型的解析,因此 JVM 沒法對這些代碼進行優化。所以,反射操做的效率要比那些非反射操做低得多。咱們應該避免在常常被執行的代碼或對性能要求很高的程序中使用反射。
(2)安全限制 :使用反射技術要求程序必須在一個沒有安全限制的環境中運行。若是一個程序必須在有安全限制的環境中運行,如 Applet,那麼這就是個問題了。
(3)內部暴露:因爲反射容許代碼執行一些在正常狀況下不被容許的操做(好比:訪問私有的屬性和方法),因此使用反射可能會致使意料以外的反作用,這可能致使代碼功能失調並破壞可移植性。反射代碼破壞了抽象性,所以當平臺發生改變的時候,代碼的行爲就有可能也隨着變化。

5四、Java 中的動態代理是什麼?有哪些應用?
動態代理:當想要給實現了某個接口的類中的方法,加一些額外的處理。好比說加日誌,加事務等。能夠給這個類建立一個代理,故名思議就是建立一個新的類,這個類不只包含原來類方法的功能,並且還在原來的基礎上添加了額外處理的新功能。這個代理類並非定義好的,是動態生成的。具備解耦意義,靈活,擴展性強。動態代理的應用:Spring 的 AOP 、加事務、加權限、加日誌。

5五、怎麼實現動態代理?

首先必須定義一個接口,還要有一個 InvocationHandler(將實現接口的類的對象傳遞給它)處理類。再有一個工具類 Proxy(習慣性將其稱爲代理類,由於調用它的 newInstance() 能夠產生代理對象,其實它只是一個產生代理對象的工具類)。利用到 InvocationHandler,拼接代理類源碼,將其編譯生成代理類的二進制碼,利用加載器加載,並將其實例化產生代理對象,最後返回。

每個動態代理類都必需要實現 InvocationHandler 這個接口,而且每一個代理類的實例都關聯到了一個 handler,當咱們經過代理對象調用一個方法的時候,這個方法的調用就會被轉發爲由 InvocationHandler 這個接口的 invoke 方法來進行調用。咱們來看看 InvocationHandler 這個接口的惟一一個方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

proxy: 指代咱們所代理的那個真實對象method: 指代的是咱們所要調用真實對象的某個方法的 Method 對象args: 指代的是調用真實對象某個方法時接受的參數Proxy 類的做用是動態建立一個代理對象的類。它提供了許多的方法,可是咱們用的最多的就是 newProxyInstance 這個方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler) throws IllegalArgumentException

loader:一個 ClassLoader 對象,定義了由哪一個 ClassLoader 對象來對生成的代理對象進行加載;interfaces:一個 Interface 對象的數組,表示的是我將要給我須要代理的對象提供一組什麼接口,若是我提供了一組接口給它,那麼這個代理對象就宣稱實現了該接口(多態),這樣我就能調用這組接口中的方法了 handler:一個 InvocationHandler 對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪個 InvocationHandler 對象上。

經過 Proxy.newProxyInstance 建立的代理對象是在 Jvm 運行時動態生成的一個對象,它並非咱們的 InvocationHandler 類型,也不是咱們定義的那組接口的類型,而是在運行是動態生成的一個對象。

5六、Java 中的 IO 流的分類?說出幾個你熟悉的實現類?
按功能來分:輸入流(input)、輸出流(output)。按類型來分:字節流 和 字符流。
字節流:InputStream/OutputStream 是字節流的抽象類,這兩個抽象類又派生了若干子類,不一樣的子類分別處理不一樣的操做類型。具體子類以下所示:

 字符流:Reader/Writer 是字符的抽象類,這兩個抽象類也派生了若干子類,不一樣的子類分別處理不一樣的操做類型。

5七、字節流和字符流有什麼區別?

字節流按 8 位傳輸,以字節爲單位輸入輸出數據,字符流按 16 位傳輸,以字符爲單位輸入輸出數據。

可是無論文件讀寫仍是網絡發送接收,信息的最小存儲單元都是字節。

5八、BIO、NIO、AIO 有什麼區別?BIO:Block IO 同步阻塞式 IO,就是咱們日常使用的傳統 IO,它的特色是模式簡單使用方便,併發處理能力低。同步阻塞I/O模式,數據的讀取寫入必須阻塞在一個線程內等待其完成。在活動鏈接數不是特別高(小於單機 1000)的狀況下,這種模型是比較不錯的,可讓每個鏈接專一於本身的 I/O 而且編程模型簡單,也不用過多考慮系統的過載、限流等問題。線程池自己就是一個自然的漏斗,能夠緩衝一些系統處理不了的鏈接或請求。可是,當面對十萬甚至百萬級鏈接的時候,傳統的 BIO 模型是無能爲力的。所以,咱們須要一種更高效的 I/O 處理模型來應對更高的併發量。NIO:New IO 同步非阻塞 IO,是傳統 IO 的升級,客戶端和服務器端經過 Channel(通道)通信,實現了多路複用。NIO  是一種同步非阻塞的  I/O 模型,在 Java1.4 中引入了 NIO 框架,對應 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。NIO 中的 N 能夠理解爲 Non-blocking,不單純是 New。它支持面向緩衝的,基於通道的 I/O 操做方法。NIO 提供了與傳統BIO模型中的 Socket 和 ServerSocket 相對應的 SocketChannel 和 ServerSocketChannel 兩種不一樣的套接字通道實現,兩種通道都支持阻塞和非阻塞兩種模式。阻塞模式使用就像傳統中的支持同樣,比較簡單,可是性能和可靠性都很差;非阻塞模式正好與之相反。對於低負載、低併發的應用程序,可使用同步阻塞 I/O 來提高開發速率和更好的維護性;對於高負載、高併發的(網絡)應用,應使用 NIO 的非阻塞模式來開發。AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實現了異步非堵塞 IO ,異步 IO 的操做基於事件和回調機制。也就是應用操做以後會直接返回,不會堵塞在那裏,當後臺處理完成,操做系統會通知相應的線程進行後續的操做。AIO 是異步 IO 的縮寫,雖然 NIO 在網絡操做中,提供了非阻塞的方法,可是 NIO 的 IO 行爲仍是同步的。對於 NIO 來講,咱們的業務線程是在 IO 操做準備好時,獲得通知,接着就由這個線程自行進行 IO 操做,IO操做自己是同步的。

相關文章
相關標籤/搜索