標籤:
宏觀來看,任何語言都不能迴避的兩個問題:初始化和清理
一、初始化
1. java中初始化是和建立對象綁定在一起的
首先要明確一點,java中更普遍的類型是參考型別,雖然每本java的書籍裡面都會先介紹八種基礎資料型別 (Elementary Data Type),但是這八種實際上是特例。
一個值如果是屬於某個類本身,那麼它叫做成員變數;如果是在某個方法中定義的,那麼它叫做局部變數。關於成員變數,java在建立對象的時候,會預設地給其成員變數自動初始化(這是出於安全性的考慮,避免程式員建立對象的時候忘記初始化)
自動初始化規則是:(後面介紹數組的時候,數組也會自動初始化,同樣遵循下面的規則)
short,byte,int ,long :0
float,double :0.0
char: 空格
boolean :false
參考型別:null
事實上,自動初始化甚至發生在建構函式賦值之前,比如:
1 public class Test { 2 public static void main(String[] args) { 3 A a = new A(1); 4 } 5 } 6 7 class A { 8 int i; 9 public A() {}10 public A(int i) {11 this.i = i;12 }13 }
上面這段簡單的代碼, 在new A的時候,首先執行自動初始化i賦值為0,然後才執行的this.i=i,把i的值變為1.
2. 函數重載
上面的小例子中,A()和 A(int i)方法構成重載,這裡就順便提一點:區分重載的方式
參數的個數或者類型(順序不同也可以作為重載的區分,但是不推薦)
-----注意兩點:不能根據傳回值來區分重載; 不能根據許可權區分符public ,protected來區分;
這個地方這麼來理解:因為你調用方法,肯定是要在方法執行之前就能夠確定你要執行的是哪個方法, java也是這樣的, “必須在執行前”這一點,就決定了不能夠根據傳回值來確定重載,因為能夠得到傳回值了,方法都執行完了;
3. 關於建構函式,主要提兩點:
-----自訂一個類,最好是顯式定義其無參構造方法;
-----this可以用於調用構造方法
普通方法裡面不能通過this來調用構造方法,只有構造方法裡面才可以;
一個構造方法裡面只能運用一次this的方式來調用構造方法;
this調用構造方法,必須寫在調用this的構造方法的第一句;
4.涉及到static的初始化:牢記一點,優先靜態
1 public class Test{ 2 static Bowl bowl = new Bowl(1); 3 Test() { 4 bowl.f1(1); 5 bowl2.f1(2);//注意哦,bowl2的建立語句寫在Test()之後 6 } 7 static Bowl bowl2 = new Bowl(2); 8 9 public static void main(String[]args) {10 Test t = new Test();11 }12 }13 14 class Bowl {15 Bowl(int i) {16 System.out.println("bowl:"+i);17 }18 void f1(int i) {19 System.out.println("f1:"+i);20 }21 }
對象中的待用資料,會比構造方法還要先執行,因此上面調用Test()方法的時候bowl2已經執行完了,所以雖然bowl2的new語句寫在後面,但是編譯和運行都不會出問題。如果是非靜態,這裡編譯就會報錯。
關於static再看一個例子:
1 public class Test{ 2 static int i=1; 3 void nor() { 4 System.out.println("普通方法執行:"+i); 5 } 6 static void f() { 7 i++; 8 System.out.println("靜態方法執行"+i); 9 }10 Test() {11 i++;12 System.out.println("構造方法執行:"+i);13 }14 public static void main(String[]args) {15 System.out.println(Test.i);16 Test.f();17 new Test().nor();18 }19 }
結果如下:
第一行結果為1,因為只執行 Test.i,在編譯期間就能知道值,java不需要載入Test.class ,所以此時i還是1;
第二行調用f(),雖然它是靜態,但是還是需要載入Test.class ,所以i 變成 2;
第三行執行普通方法,要先建對象,更需要載入Test.class,i 變成 3;
並且,靜態變數只會初始化一次
5.數組初始化
1)這裡注意一下數組初始化的三中寫法(面試常考)
int[] a = {1,2,3};
int[] a = new int[]{1,2,3};
int[] a = new int[3]; a[0]= 1,a[1]= 2,a[2]=3; //注意不要加 () ,就是 new int[3]
2)這裡順便提一下可變參數列表(貌似比較少看到有人用它),見下例:
1 public class Test{ 2 public static void main(String[]args) { 3 Object[] objs = {"String類型測試",1,1.1}; 4 new Test().printArray(objs); 5 System.out.println("********"); 6 new Test().printArray2(objs); 7 System.out.println("********"); 8 new Test().printArray2("我是String",2,2.2); 9 System.out.println("********");10 new Test().printArray2();11 }12 13 public void printArray(Object[] objs) {14 for(Object obj : objs) {15 System.out.println(obj.toString());16 }17 }18 19 20 public void printArray2(Object... objs ){21 for(Object obj:objs) {22 System.out.println(obj.toString());23 }24 }25 }
結果如下:
可以看到 ... 這個符號的含義,就是可變參數列表
上面printArray2中,如果傳入普通參數,它會自動把獲得的參數組裝成為數組,然後自動放到objs中, 所以就可以直接用for-each迴圈列印了
而如果傳入的本身就是數組形式,那麼就不會再進行自動轉換。
並且最後一行顯示,表明,可變參數列表,即使什麼都不傳入也是可以的。
使用可變參數類型的時候,就需要注意一下重載的時候的一個小問題:
之前介紹重載的時候講到區分重載的方式是參數類型不同,這個例子可以認為是一個例外(也可以認為不是例外,因為可變參數列表底層最終也是轉換成為數組來處理的)
二、清理
1)finalize()
finalize()方法可以用來做一些清理前的工作,但是它不是類似於C++中的解構函式, 並不是寫了就馬上會被執行,這點一定要注意。
關於這一點的設計,也是java中一個比較智能的設計,因為去執行這些清理操作本身,就會要浪費你的記憶體,所以java設計的是沒有必要寫了finalize()馬上就調用它(就像你被蚊子咬了一下,並不需要去住院一樣)
關於finalize()方法不一定什麼時候執行,必須要注意的是比如你寫了資料庫連接的語句,在執行完後要記得關閉流,這個關閉的操作要寫在fanally中,而不是寫在finalize()中。
2)finalize()的另一點
java中是可以調用C,C++的,但是java的清理機制並不能夠自動銷毀C,C++產生的記憶體消耗(比如java不能操作寄存器,但是C,C++卻可以),那麼常見的就是在finalize()裡面寫對應的C,C++的解構函式,來清理它們產生的垃圾。
三、java的建立與銷毀