初始化和清理是涉及安全的兩個問題,java中采用了構造器,并額外提供了“垃圾回收器”,對于不再使用的內存資源,垃圾回收器能自動將其釋放。
一、用構造器確保初始化
java中,通過提供構造器,類的設計者可以確保每個對象都會得到初始化。創建對象時,如果其類具有構造器,java就會在用戶有能力操作對象之前自動調用相應的構造器,從而保證了初始化的進行。考慮到在初始化期間要自動調用構造器,所以構造器與類的名稱必須相同。public class IfElse {
public static void main(String[] args){
new Test();//在創建對象時,會為對象分配存儲空間,并調用相應的構造器。
}
}
class Test{
Test(){
System.out.println("this is test");
}
}
運行結果:this is test
不接受任何參數的構造器叫做默認構造器(無參構造器),構造器也能帶有形式參數,以便指定如何創建對象。public class IfElse {
public static void main(String[] args){
new Test(5);
}
}
class Test{
Test(int i){
System.out.println("this is test"+i);
}
}
運行結果:this is test5
從概念上講,“初始化”與“創建”是彼此獨立的,然而在java中,“初始化”與“創建”捆綁在一起,兩者不能分離。
二、方法重載
1.區分重載方法
重載的方法具有相同的名字,但是每個重載的方法都必須有一個獨一無二的參數列表,甚至參數順序的不同也足以區分兩個方法,不過一般情況下別這么做,這會使代碼難以維護。
2.涉及基本類型的重載
基本類型能從一個“較小”的類型自動提升為一個“較大”的類型,此過程一旦涉及到重載,可能會造成一些混淆。如果傳入的數據類型(實際參數類型),小于方法中聲明的形式參數類型,實際數據類型就會被提升。char型略有不同,如果無法找到剛好接受char參數的方法,就會把char直接提升至int。如果傳入的實際參數類型大于方法中聲明的參數類型,就要通過類型轉化做窄化轉換,否則編譯器會報錯。
三、默認構造器
如果寫的類中沒有構造器,則編譯器會自動幫你創建一個默認的構造器。但是如果已經定義了一個構造器(無論是否有參數),編譯器就不會幫你自動創建默認構造器。
四、this關鍵字
1.在構造器中調用構造器
可能為一個類寫了多個構造器,有時可能會想在一個構造器中調用另一個構造器,以免重復代碼,可以用this關鍵字做到。通常寫this關鍵字的時候,都是指“這個對象”或者“當前對象”,而且它本身表示對當前對象的引用。
public class Flower {
int petalCount = 0;
String s = "initial value";
Flower(int petals){
petalCount = petals;
System.out.println("int arg only,petalCount = "+petalCount);
}
Flower(String ss){
System.out.println("String arg only,s = "+s);
s = ss;
}
Flower(String s,int petals) {
this(petals);
//this(s);此處編譯器會報錯
this.s = s;
System.out.println("String & int arg");
}
Flower() {
this("hi",47);
System.out.println("no args");
}
public static void main(String args[]){
Flower x = new Flower();
}
}
運行結果:int arg only,petalCount = 47
String & int arg
no args
在有兩個參數的構造方法中,試圖調用兩次構造方法,但是編譯器報錯。這表明,盡管可以用this調用一個構造器,但卻不能調用兩個,此外,必須將構造器置于最初始處,否則編譯器會報錯。
五、清理:終結處理和垃圾回收
java有垃圾回收器負責回收無用對象占據的內存資源,但也有特殊情況:假定你的對象(并非使用new關鍵字)獲得了一塊“特殊”的內存區域,由于垃圾回收器只知道釋放那些經由new分配的內存,所以它不知道該如何釋放該對象的這塊“特殊”內存。為了應對這種情況,java允許在類中定義一個名為finalize()的方法。它的工作原理“假定”是這樣的:一旦垃圾回收器準備好釋放對象占用的存儲空間,將首先調用其finalize()方法,并且在下一次垃圾回收動作發生時,才會真正回收對象占用的內存。要記住以下三點:
(1)對象可能不被垃圾回收
(2)垃圾回收不等于“析構”
(3)垃圾回收只與內存有關
1.垃圾回收器如何工作
在java虛擬機中,堆的實現像是一個傳送帶,每分配一個新對象,它就往前移動一格。這意味著對象存儲空間分配的速度非常快,java的“堆指針"只是簡單地移動到尚未分配的區域。但實際情況未必像傳送帶那樣,因此,高效率的垃圾回收依賴于垃圾回收器的介入。當它工作時,將一面回收空間,一面使堆中的對象緊湊排列,這樣,”堆指針“就可以很容易地移動到更靠近傳送帶的開始處。通過垃圾回收器對對象的重新排列,實現了一種高速的,有無限空間可以分配的堆模型。
垃圾回收機制了解:
(1)引用記數:簡單,但速度很慢。每個對象都含有一個引用計數器,當有引用連接至對象時,引用記數加1,當引用離開作用域或被置于null時,引用記數減1。這個方法有個缺陷,如果對象之間循環引用,可能會出現”對象應該被回收,但引用記數卻不為0“的情況。對于垃圾回收器而言,定位這樣的交互自引用的對象組所需工作量極大。引用記數常用來說明垃圾收集的工作方式,但似乎從未被應用于任何一種虛擬機。
(2)自適應:java虛擬機采用自適應的垃圾回收技術,停止-復制:先暫停程序,將所有存活的對象復制到另一個堆中,沒有被復制的即為垃圾;標記-清掃:遍歷所有引用,進而找出所有存活的對象,當沒有引用時,變給對象標記,這個過程中不會有任何清理操作。在java虛擬機運行過程中,根據堆空間的對象情況,在以上兩種狀態切換,以達到垃圾回收。
六、成員初始化
java盡力保證,所有的變量在使用前都能得到恰當的初始化,對于方法的局部變量,java以編譯時錯誤的形式來貫徹這種保證。void f(){
int i;
i++;
}
以上代碼編譯期會報錯變量沒有初始化。但是對于類的數據成員,情況有所不同,類的每個基本數據成員保證都會有一個初始值。即使代碼沒有給出,編譯器也會自動賦初值。
七、構造器初始化
1.初始化順序
在類的內部,變量定義的先后順序決定了初始化的順序。即使變量定義散布于方法定義之間,它們仍然會在任何方法(包括構造器)被調用之前得到初始化。class Window{
Window(int marker) {
System.out.println("window("+marker+")");
}
}
class House{
Window w1 = new Window(1);
House(){
System.out.println("House()");
w3 = new Window(33);
}
Window w2 = new Window(2);
void f(){
System.out.println("f()");
}
Window w3 = new Window(3);
}
public class OrderOfInitialization {
public static void main(String[] args){
House h = new House();
h.f();
}
}
運行結果:window(1)
window(2)
window(3)
House()
window(33)
f()
在House類中,故意把幾個window對象散布在各處,以證明它們全都會在調用構造器或其他方法之前得到初始化。
2.靜態數據的初始化
無論創建多少個對象,靜態數據都只占一份存儲區域。static關鍵字不能應用于局部變量,因此它只能作用于域。 初始化的順序是先靜態對象(如果之前并未被初始化),而后是非靜態對象。
對象的創建過程,假設有個名為Dog的類:
(1)當首次創建類型為Dog的對象時,java解釋器查找類路徑,定位Dog.class文件
(2)載入Dog.class,有關靜態初始化的所有動作都會被執行。因此,靜態初始化只在Class對象首次加載的時候進行一次。
(3)當用new Dog()創建對象時,首先將在堆上為Dog對象分配足夠的存儲空間。
(4)這塊存儲空間會被清零,這就自動地將Dog對象中的所有基本類型數據都設置成了默認值,而引用則被置為null。
(5)執行所有出現于字段定義處的初始化動作。
(6)執行構造器。
八、數組初始化
數組只是相同類型的,用一個標識符名稱封裝到一起的一個對象序列或者基本類型數據序列。
可變參數:String[] args的寫法可以寫為String...args。有了可變參數,就不用顯示地編寫數組語法了,當指定參數時,編譯器會去填充數組,但是如果傳遞的本來就是數組,編譯器就不會再執行任何轉換。因此,如果你有一組事物,可以把它們當作列表傳遞,而如果你已經有了一個數組,該方法可以把它們當作可變參數列表來接受。將0個參數傳遞給可變參數列表依舊是可行的。
九、枚舉類型
java SE5中添加了一個看似很小的特性,即enum關鍵字,它使得我們在需要群組并使用枚舉類型集時,可以很方便地處理。enum Simple {
NOT,MILD,MEDIUM,HOT
}
public class SimpleEnumUse{
public static void main(String[] args){
Simple howHot = Simple.MEDIUM;
System.out.println(howHot);
}
}
運行結果:MEDIUM
enum有一個特別實用的特性,即它可以在switch語句內使用。