1——面向對象和JVM基礎
1.java中的4種訪問制權限:
(1).public:最大訪問控制權限,對所有的類都可見。
(2).protect:同一包可見,不在同一個包的所有子類也可見。
(3).default:包訪問權限,即同一個包中的類可以可見。默認不顯式指定訪問控制權限時就是default包訪問控制權限。
(4).private:最嚴格俄訪問控制權限,僅該類本身可見,對外一切類都不可以訪問(反射機制可以訪問)。
2.面向對象編程中兩種對象組合方式——is-a 和 has-a:
(1).is-a組合:一個類繼承具有相似功能的另一個類,根據需要在所繼承的類基礎上進行擴展。
優點:具有共同屬性和方法的類可以將共享信息抽象到父類中,增強代碼復用性,同時也是多態的基礎。
缺點:子類中擴展的部分對父類不可見,另外如果共性比較少的時候使用繼承會增加冗余代碼。
(2).has-a組合:has-a組合是在一個類中引用另一個類作為其成員變量。 優點:可擴展性和靈活性高。在對象組合關系中應優先考慮has-a組合關系。 缺點:具有共性的類之間看不到派生關系。
3.多態:
在面向對象編程中,子類中擁有和父類相同方法簽名的方法稱為子類方法覆蓋父類方法,當調用子類方法的某個操作時,不必明確知道子類的具體類型,只需要將子類類型看作是父類的引用調用其操作方法,在運行時,JVM會根據引用對象的具體子類類型而調用應該的方法,這就是多態。
多態的基礎是java面向對象編程的晚綁定機制。編程中有如下兩種綁定機制:
(1).早綁定:一般在非面向對象編程語言中使用,在程序編譯時即計算出具體調用方法體的內存地址。
(2).晚綁定:面向對象編程語言中經常使用,在程序編譯時無法計算出具體調用方法體的內存地址,只進行方法參數類型和返回值類型的校驗,在運行時才能確定具體要調用方法體的內存地址。
4.java單繼承的優點:
相比於C++的多繼承,java只支持類的單繼承,java中的所有類的共同基類是Object類,Object類java類樹的唯一根節點,這種單繼承有以下好處:
(1).單繼承可以確保所有的對象擁有某種共同的特性,這樣對於JVM虛擬機對所有的類進行系統級的操作將提供方便,所有的java對象可以方便地在內存堆棧中創建,傳遞參數也變的更加方便簡單。
(2).java的單繼承使得實現垃圾回收器功能更加容易,因為可以確保JVM知道所有對象的類型信息。
5.選擇容器對象兩個原則:
(1).容器所能提供不同的類型的接口和外部行為是否能夠滿足需求。
(2).不同容器針對不同的操作效率不同。
6.類型轉換:
Java中有兩種常見的類型轉換:向上類型轉換(upcast)和向下類型轉換(downcast):
(1).向上類型轉換(upcast):
向上類型轉換是將子類對象強制類型轉換為父類類型,經典用法是面向對象的多態特性。向上類型轉換時,子類對象的特性將不可見,只有子類從父類繼承的特性仍然保持可見,向上類型轉換時編譯器會自動檢查是否類型兼容,通常是安全的。
(2).向下類型轉換:
向下類型轉換是將父類類型強制轉換為子類類型,轉換過后父類中不可見的子類特性又恢復可見性,向下類型轉換時,編譯器無法自動檢測是否類型兼容,往往會產生類型轉換錯誤的運行時異常,通常不安全。
7.java中5個存放數據的地方:
(1).寄存器(Registers):位於CPU內部,是速度最快的存儲區,但是數量和容量有限。在java中不能直接操作寄存器。
(2).棧(Stack):棧位於通用隨機訪問存儲器 (General random-access memory,RAM,內存) 中,通過處理器的棧指針訪問,棧指針從棧頂向棧底分配內存,從棧底向棧頂釋放內存。棧是僅次於寄存器的速度第二快的存儲器,在java程序中,一般的8種 基本類型數據和對象的引用通常存放在棧內存中,不通過new關鍵字的字符串對象也是存放在棧的字符串池中。棧的優勢是,存取速度比堆要快,僅次於寄存器, 棧數據可以共享。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。
(3).堆(Heap):也是位於通用隨機訪問存儲器 (General random-access memory,RAM,內存) 中的共享內存池。Java的堆是一個運行時數據區,類的對象從中分配空間,凡是通過new關鍵字創建的對象都存放在堆內存中,它們不需要程序代碼來顯式的 釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因為它是在運行時動態分配內存的,Java的垃圾收集器 會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢。
(4).常量存儲器(Constant storage):java中的常量是存放在系統內嵌的只讀存儲器中(read-only memory,ROM)的。
(5).非隨機存儲器(Non-RAM storage):對於流對象和持久化對象,通常存放在程序外的存儲器,如硬盤。
8.javadoc只處理public和protected訪問控制權限的文檔注釋,private和default權限的穩定注釋將被忽略。
9.java中賦值運算:
基本類型賦值是直接復制值,賦值操作后,相互不影響。
引用類型賦值是復制引用值,相當於給對象取一個別名,賦值之后兩個引用指向同一個引用對象,相互之間有影響。
在Java中,向方法傳遞引用類型參數會改變參數的值,不讓參數受到影響的解決方法:在方法內首先先將引用克隆一份,然后操作克隆的對象。
10.移位運算:
左移運算符<<:將比特位左移指定位數,右邊部分補0,左移一位相當於乘2。
右移運算符>>:將比特位右移指定位數,如果是正數,左邊第一位(符號位)補0,其余位補0,如果是負數,左邊第一位補1,其余位補0。右移一位相當於除2。
無符號右移運算符>>>:將比特位右移指定位數,不論是正數或者負數,左邊移除位統統補0。
11.java中,比int類型小的原始類型(char、byte、short)進行數學運算或者位運算時,數據類型首先轉換成int類型,然后進行相應的運算。
12.方法重載(overloading):方法同名,參數列表不同稱為方法重載,注意方法的返回值類型不同不能作為方法重載。
13.java中的析構函數:
Java中沒有像C/C++的析構函數,用來銷毀不用的對象是否內存空間,只有以下三個方法用於通知垃圾回收器回收對象。
(1).finalize( )只是通知JVM的垃圾收集器當前的對象不再使用可以被回收了,但是垃圾回收器根據內存使用狀況來決定是否回收。
finalize()最有用的地方是在JNI調用本地方法時(C/C++方法),調用本地方法的析構函數消耗對象釋放函數。
(2). System.gc()是強制析構,顯式通知垃圾回收器釋放內存,但是垃圾回收器也不一定會立即執行,垃圾回收器根據當前內存使用狀況和對象的生命周期自行決定是否回收。
(3).RunTime.getRunTime().gc()和System.gc()類似。
注意:這三個函數都不能保證垃圾回收器立即執行,推薦不要頻繁使用。
14.垃圾回收器原理:
(1).引用計數(ReferenceCounting)垃圾回收算法:
一種簡單但是速度較慢的垃圾回收算法,每個對象擁有一個引用計數器
(Reference Counter),當每次引用附加到這個對象時,對象的引用計數器加1。當每次引用超出作用范圍或者被設置為null時,對象的引用計數器減1。垃圾回收 器遍歷整個對象列表,當發現一個對象的引用計數器為0時,將該對象移出內存釋放。
引用計數算法的缺點是,當對象環狀相互引用時,對象的引用計數器總不為0,要想回收這些對象需要額外的處理。
引用計數算法只是用來解釋垃圾回收器的工作原理,沒有JVM使用它實現垃圾回收器。
引用計數的改進算法:
任何存活的對象必須被在靜態存儲區或者棧(Stack)中的引用所引用,因此當遍歷全部靜態存儲區或棧中的引用時,即可以確定所有存活的對象。每當 遍歷一個引用時,檢查該引用所指向的對象,同時檢查該對象上的所有引用,沒有引用指向的對象和相互自引用的對象將被垃圾回收器回收。
(2).暫停復制(stop-and-copy)算法:
垃圾回收器的收集機制基於:任何一個存活的對象必須要被一個存儲在棧或者靜態存儲區的引用所引用。
暫停復制的算法是:程序在運行過程中首先暫停執行,把每個存活的對象從一個堆復制到另一個堆中,已經不再被使用的對象被回收而不再復制。 暫停復制算法有兩個問題:
a.必須要同時維護分離的兩個堆,需要程序運行所需兩倍的內存空間。JVM的解決辦法是在內存塊中分配堆空間,復制時簡單地從一個內存塊復制到另一個內存塊。
b.第二個問題是復制過程的本身處理,當程序運行穩定以后,只會產生很少的垃圾對象需要回收,如果垃圾回收器還是頻繁地復制存活對象是非常低性能的。
JVM的解決方法是使用一種新的垃圾回收算法——標記清除(mark-and-sweep)。 一般來說標記清除算法在正常的使用場景中速度比較慢,但是當程序只產生很少的垃圾對象需要回收時,該算法就非常的高效。
(3).標記清除(mark-and-sweep)算法:
和暫停復制的邏輯類似,標記清除算法從棧和靜態存儲區開始追蹤所有引用尋找存活的對象,當每次找到一個存活的對象時,對象被設置一個標記並且不被回收,當標記過程完成后,清除不用的死對象,釋放內存空間。
標記清除算法不需要復制對象,所有的標記和清除工作在一個內存堆中完成。
注意:SUN的文檔中說JVM的垃圾回收器是一個后台運行的低優先級進程,但是在早期版本的JVM中並不是這樣實現的,當內存不夠用時,垃圾回收器先暫停程序運行,然后進行垃圾回收。