隨着計算機革命的發展,「不安全」的編程方式已經逐漸成爲編程代價高昂的主因之一。html
初始化和清理,正是涉及安全的倆個問題。java
5.1 用構造器確保初始化編程
構造器名與類名相同,沒有返回值數組
5.2 方法重載安全
構造器的重載與方法重載函數
5.2.1 區分重載的方法post
參數列表的不一樣(不一樣順序也是重載,但不建議這麼作)this
5.2.2 涉及基本類型的重載url
void print(char c){System.out.println("The character is "+c); }
void print(byte b){System.out.println("The byte is "+b);}
void print(short s){System.out.println("The short is "+s);}
void print(int i){System.out.println("The int is "+i);}
void print(long l){System.out.println("The long is "+l);}
void print(float f){System.out.println("The float is "+f);}
void print(double d){System.out.println("The double is "+d); }spa
1)若是直接傳入數值,如 print(5)則5會被當作 int x = 5; print(x)去尋找最合適的print(int)
2)常量以外的類型會去尋找最合適的類型,沒有同類型,可是有更大類型時會類型提高
3)char是一個特例,若是沒有print(char)則char當作int,執行print(int)
4)須要用cast才能放入小類型的方法裏
5.2.3 用返回值區分重載方法
void f(){}
int f(){return 1;}
這樣是不能夠的,由於不少時候咱們不須要返回值,只是調用而已,那麼咱們會寫:
f();這樣是沒法區分它是哪一個方法的
5.3 默認構造器
若是類裏沒有構造器,編譯器會幫你弄一個。
有了就不會幫你造一個了。
5.4 this關鍵字
能夠用於指代調用對象方法時的「當前對象」,能夠解決參數和域重名問題。
5.4.1 構造器中調用構造器
this(參數);
必須放於第一行,且最多隻能調用一個。
5.4.2 static的含義
static方法就是沒有this的方法,它屬於類,不屬於對象。
5.5 清理:最終處理和垃圾回收
Java垃圾回收器負責回收由new建立而且再也不被使用的對象。
特殊狀況是:若是一個對象以非new方式得到了一塊內存區域,GC就不知道怎麼回收這塊特殊的內存。Java提供了finalize()方法,它的工做原理「假定」是這樣的:一旦垃圾回收器準備好釋放對象佔用的存儲空間,將首先調用finalize()方法, 而且在下一次垃圾回收動做發生時,纔會真正回收對象內存。因此finalize()方法能夠在垃圾回收時作一些重要的清理工做。
//此處疑惑感謝@Launcher的細心解答。
//---------------解答開始
也就是你有這樣一個 class,僞碼:
class A{
IntPtr m_pBuffer;
A(){
m_pBuffer = malloc(1000);
}
void finalize(){
free(_M_pBuffer);
}
}
A 對象沒有用 new ,而是使用 malloc 得到了一塊特殊的內存區 m_pBuffer。
A a = new A(); // 仍是使用 new 得到對象 A
可是對象 a 自己不用 new 得到的特殊內存區 m_pBuffer,不會被 GC 釋放,因此你須要實現 finalize,在裏面手動釋放。
//---------------解答結束
這裏有一個誤區:
finalize() 與 C++中的析構函數(銷燬對象時必需要用到這個函數)相同。
這是不正確的,C++中對象必定會被銷燬,即析構函數必定被調用,而Java裏的對象並不老是被垃圾回收。換句話說:
1)對象可能不被垃圾回收
2)垃圾回收不等於析構
5.5.1 finalize()用途何在
3)垃圾回收只與內存有關。
Java中不管對象是如何建立的,GC都會回收它的內存,對finalize()的須要限制到一種特殊狀況:用了建立對象之外的方式,爲對象分配了存儲空間。
之因此要有finalize(),是因爲在分配內存時可能採用了相似C語言的作法,這種狀況主要發生在使用Java調用本地代碼,如用C的malloc()函數系列來分配存儲空間,因此須要在finalize()裏調用本地代碼,使用free()來釋放這些內存。
因此不要過多使用finalize(),它不是進行普通清理工做的合適場所。
5.5.2 你必須實施清理
C++中全部對象都會被銷燬。包括局部對象(在棧上建立的)和用new建立的對象(delete調用析構函數)。
Java不容許建立局部對象,必須使用new建立,也沒有delete可用,釋放對象內存只能由GC來完成,可是它並不保證必定去回收。若是但願在進行釋放存儲空間以外的清理操做,得明確在finalize()裏使用,這等於析構了,可是沒析構方便。
若是JVM沒有面臨內存耗盡的狀況,它是不會浪費時間和資源去執行垃圾回收以恢復內存的。
5.5.3 終結條件
由於finalize()方法是在垃圾回收前執行的,因此複寫finalize方法能夠檢測"邏輯上"的這個對象是否應該被終結。藉此來發現程序中是否有缺陷。如:某對象表明了一個打開着的文件,而咱們規定這種對象回收時必須爲關閉狀態。爲了檢測程序是否有缺陷,咱們能夠複寫finalize方法檢測是否打開狀態,而後在某次finalize中查看是否存在問題,雖然finalize並不必定會調用,可是隻要調用了一次咱們就知道是否有問題,這纔是關鍵。
咱們能夠利用System.gc()來建議作GC操做,但JVM有可能不聽咱們的。
5.5.4 垃圾回收器如何工做
在之前所用過的程序語言中,在堆上分配對象的代價十分高昂,所以容易有JAVA中的對象在堆上分配方式也很是高昂。但實際上垃圾回收器對提升對象建立的速度有很是明顯的效果。Java中堆內存的分配方式就像傳送帶同樣,經過移動「堆指針」來完成內存分配,而其中最關鍵的是垃圾回收器在工做時,一邊回收空間,一邊使堆中對象緊密排列,從而保證了「堆指針」能夠很容易分配內存,實現了高速的無限空間的可供分配堆模型。
先看其餘系統的垃圾回收機制模型:
1.引用計數,簡單但很慢的垃圾回收技術。且難以處理循環引用問題。
2.更快的模式,從堆棧和靜態存儲區開始,用引用向下搜索全部對象,以及對象間引用,保證每一個活的對象都被搜索到且避免了循環引用問題。
1和2都是尋找「活」着對象的方式,在基於2的方式之下,Java虛擬機採用「自適應」技術。
如何處理尋找到的存活對象,取決於不一樣Java虛擬機的實現。
A.中止-複製 stop-and-copy, 暫停程序的運行,而後將全部存活的對象從當前堆複製到新的堆裏,而且緊密排列,而後就能夠簡單直接的分配新空間了。當把對象從一處搬到另外一處時,全部指向它的那些引用都必須修正。問題一:對於這種「複製式」回收器而言,效率會下降,須要在2個堆裏倒騰,還要維護比實際須要多一倍的空間。某些Java虛擬機的解決方法是,按需從堆裏分配幾塊較大的內存,複製動做發生在這些大塊內存以前。問題二:對於複製,程序進入穩定狀態以後,只會產生少許垃圾,甚至沒有垃圾,儘管如此,複製式回收器仍然會把全部內存從一處複製到另外一處,很浪費。一些Java虛擬機會進行檢查:要是沒有新垃圾產生,就會轉換到另外一種工做模式這種模式叫 標記-清掃。通常用途而言,標記清掃的效率很低,可是若是垃圾不多甚至沒有,它的速度就很快了。
B.標記清掃 mark-and-clean, 所依據的思路一樣是從堆棧和靜態存儲區出發,遍歷全部引用,進而找出全部存活對象。每當它找到一個存活對象,就會給這個對象一個標記,這個過程不會回收任何對象。只有當標記工做所有完成以後,清理動做纔會開始。清理過程當中沒有標記的對象將被釋放,不會發生任何複製動做。因此剩下的堆空間是不連續的,垃圾回收器但願獲得連續空間的話就得從新整理剩下的對象。
這裏討論內存分配以較大的「塊」爲單位的Java虛擬機。嚴格意義來講,中止-複製是要求釋放垃圾對象以前,必須把對象從一個堆複製到新堆裏的,這將致使大量的內存複製。此處有了塊以後,垃圾回收器就能夠往「廢棄」的塊裏複製對象了,每一個塊有本身的代數記錄它是否存活(廢棄)。若是塊在某處被引用,代數增長,垃圾回收器會對上次回收動做以後新分配的塊進行整理。這對處理大量短命的臨時對象頗有幫助。垃圾回收器會按期進行完整的清理動做-大型對象仍然不會被複制,內含小型對象的那些塊則被複制並整理。Java虛擬機會進行監視,若是全部對象都很穩定,垃圾回收器效率很低的話,就會切換到「標記-清掃」方式;一樣虛擬機會跟蹤「標記-清掃」的效果,要是堆空間出現不少碎片,就會切換回「中止-複製」方式。這就是自適應技術。
Java虛擬機還有不少其餘用於提高速度的附加技術。尤爲是與加載器操做有關的,被稱爲「即時Just-In-Time,JIT」編譯器技術。這種技術能夠把程序部分或所有翻譯成本地機器碼(這原本是Java虛擬機的工做),程序運行速度所以得以提高。當須要裝載某個類時,編譯器會先找到class文件,而後把字節碼裝入內存。此時有兩種方案可供選擇,一是讓即時編譯器編譯全部代碼,缺點是這種加載動做散落在整個程序生命週期內,累加起來要花費更多時間;而且會增長可執行代碼長度,致使頁面調度,從而下降程序運行速度。二是採用惰性評估,執行頻繁的方法和代碼會採用即時編譯,執行次數越多速度越快。
5.6 成員初始化
Java盡力保證:全部變量在使用前都獲得恰當的初始化。對於方法中的局部變量,以編譯錯誤形式貫徹這種保證。對類成員以默認值執行初始化。
5.6.1 指定初始化
能夠在申明變量時直接以賦值符號完成初始化。在針對類成員變量初始化時,若是直接給出值,會使得全部對象有相同的值,有時是咱們但願的,但有時顯得不夠靈活。
5.7 構造器初始化
利用構造器來初始化能夠獲得更大的靈活性。但要牢記:沒法阻止自動初始化的進行,它將在構造器被調用前發生。在構造器賦值成員變量以前,變量已經被賦予了默認值。----編譯器不會強制咱們在構造器初始化,由於初始化早已獲得了保證。
5.7.1 初始化順序
變量定義的順序決定了初始化順序。即便變量可能定義在構造器或其餘方法以後,但仍舊會在任何方法(包括構造器)被調用以前獲得初始化。
5.7.2 靜態數據的初始化
動做執行於類加載時,且只執行一次。總結對象建立的過程,假設有個Dog類
1.構造器是靜態方法,只是沒有static。因此當首次調用構造器,或首次使用Dog類的靜態域或方法時,Java解釋器必須找到類路徑,定位Dog.class文件。
2.而後載入class文件,有關靜態初始化的全部動做都會被執行。
3.當用new Dog()建立對象的時候,首先將堆上爲Dog對象分配足夠的存儲空間。
4.這塊存儲空間會被清零,這就自動的將Dog對象中全部基本類型設置爲默認值0和false,引用類型設置爲null
5.按申明順序執行全部出現於字段定義處的初始化動做。
6.執行構造器。
5.7.3 顯式的靜態初始化
即在static塊裏完成初始化
5.7.4 非靜態實例的初始化
即無static的塊裏完成初始化
總結:
5.8 數組初始化
int[] a1;
如何初始化?
1. Integer[] a1 = {1,2,3};//只能在申明處用這種方式初始化
2. Integer[] a1 = new Integer[]{1,2,3};
3. Integer[] a1;
a1 = a2;
能夠用賦值讓2個數組引用指向同一個數組對象。因此容許存在Integer[] a1;這種只有引用沒有指向的狀況
注意:全部數組都有一個固定成員,能夠經過它獲取數組裏包含了多少元素,可是不能修改它。這個成員就是length,咱們只能訪問0-(length-1)範圍,下標檢查是自動進行的,相比於C和C++用這些開銷來得到安全是值得的。
5.8.1 可變參數列表
在參數列表的末尾能夠選擇傳入0-N個參數,會自動變成數組,能夠做爲可選參數存在
5.9 枚舉類型
Java SE5開始添加了enum關鍵字用於建立枚舉類型。功能比C、C++完善得多。
public enum Spiciness{
NOT, MILD, MEDIUM, HOT, FLAMING
}
示例建立了一個名爲Spiciness的枚舉類型,它有5個常量實例。
使用時:
Spiciness howHot = Spiciness.MEDIUM;
System.out.println(howHot);
--> MEDIUM
當建立了enum時,編譯器會自動添加一些有用的特性:
1.toString()用於顯示實例名字
2.ordinal()顯示該實例常量聲明順序,從0開始
3.靜態的values() 方法,按照聲明順序產生一個全值數組
注意:
A.enum自己並非新的數據類型,它屬於編譯器行爲,本質上仍是類,有本身的方法
用類分析器分析後:
public final class study.core.反射.Spiciness extends java.lang.Enum{ //域 public static final study.core.反射.Spiciness NOT; public static final study.core.反射.Spiciness MILD; public static final study.core.反射.Spiciness MEDIUM; public static final study.core.反射.Spiciness HOT; public static final study.core.反射.Spiciness FLAMING; private static final study.core.反射.Spiciness[] ENUM$VALUES; //構造器 private Spiciness(java.lang.String, int); //方法 public static study.core.反射.Spiciness[] values( ); public static study.core.反射.Spiciness valueOf(java.lang.String);}B.enum能夠配合switch使用