1. 程式人生 > >Java程式設計思想 第五章:初始化與清理

Java程式設計思想 第五章:初始化與清理

1. 用構造器初始化

Java中通過提供構造器,確保每個類的物件都可以得到初始化,構造器的形式為:

className(){
   //---
}

可以看見程式在初始化物件的時候自動執行了構造方法。

如果類中只有唯一的一個帶引數的構造器,那麼預設的無參構造器將不可用。

2. 方法過載

假如我們有多種多樣的需求,想要初始化不同形態的物件,這時候我們可能需要多個構造器才能滿足需求。而構造器的名字又是固定的,所以這個時候變化的就是引數型別或者是引數個數。我們把這種方法名不變,只改變引數型別或者引數個數的操作叫做方法過載。方法過載不僅支援構造器方法過載,也支援普通方法的過載。

public class ConstructorTest {
    private int age;
    private String name;
    ConstructorTest(){

    }
    ConstructorTest(int i){
        age=i;
    }
    ConstructorTest(String j){
        name=j;
    }
    ConstructorTest(int i,String j){
        age=i;
        name=j;
    }
    public static
void main(String[] args) { ConstructorTest ct1 = new ConstructorTest(); ConstructorTest ct2 = new ConstructorTest(22); ConstructorTest ct3 = new ConstructorTest("張三"); ConstructorTest ct4 = new ConstructorTest(23,"李四"); System.out.println("ct1 age=" + ct1.age +
"---name=" + ct1.name); System.out.println("ct2 age=" + ct2.age + "---name=" + ct2.name); System.out.println("ct3 age=" + ct3.age + "---name=" + ct3.name); System.out.println("ct4 age=" + ct4.age + "---name=" + ct4.name); } }

執行結果:

ct1 age=0---name=null
ct2 age=22---name=null
ct3 age=0---name=張三
ct4 age=23---name=李四

過載方法中如果傳入了低型別的引數,那麼如果找不到合適的方法,會被隱式的提升成高型別的資料。如果傳入了高型別的引數,如果不進行型別轉換,那麼編譯器會報錯。Java中區分過載方法的依據是引數型別和引數個數,引數順序區分會造成程式易讀性較差,在一些情況下,也可以使用返回值區分,當然前提是如果你關心方法的返回值。

3. 預設構造器

如前文所述,預設的構造器,就是沒有形參的構造器,作為一個類的預設構造器。如果程式設計師沒有顯示的在程式碼中建立一個構造器,那麼Java會自動幫你建立一個無參構造器來完成初始化。當然如果程式設計師顯示的建立了建構函式,那麼Java就不會給你建立預設構造器了。

4. this關鍵字

this關鍵字也是Java中尤為重要的一個關鍵字,主要有三個作用:

  1. 表示當前物件的引用,並返回當前物件.
  2. 表示類的成員變數,在有的建構函式中,形參與成員變數使用同一個字串,這時候可以用this來區分,帶有this的表示成員變數,用法如下:
  3. 可以在構造器中呼叫另一個構造器,使用this帶引數代替構造器的方法名,用法如下:

5. 清理:終結處理和垃圾回收

Java中的記憶體清理使用的是Java自帶的垃圾回收機制,但是不管怎麼來說,這種方式都不是絕對安全的。Java的垃圾回收機制清理的是通過new建立的物件,而在某些特殊情況下,可能有些物件不是通過new建立的,這些物件如果不使用的時候,垃圾回收器是不能準確的清理他們的從而造成了這塊特殊的記憶體區域一直得不到釋放。Java中提供了finalize()方法來處理這一部分特殊的記憶體區域。它的處理流程是這樣的,在Java垃圾回收器啟用之前,會先呼叫這個方法進行一些必要的回收操作。但是針對這一塊特殊的區域,或者是new建立的需要被回收的物件,一般情況下只有當Java虛擬機器記憶體快要消耗殆盡的時候,垃圾回收器才會啟動,畢竟啟動垃圾回收器也是需要消耗資源的,所以不可能說實時存在。所說的特殊的建立物件方式,一般是指“本地方法”使用時,也就是在Java中呼叫非Java程式碼的時候發生的。所以一般情況下是不需要使用finalize()方法的。

finalize()通常還有另一個用法,由於它是在Java垃圾回收器啟動之前執行的,所以可以用它來判斷終結狀態,即判斷一個物件是否滿足回收條件。

6. 成員初始化

Java儘量保證每個變數在使用之前都進行了初始化操作,變數分為區域性變數和成員變數,區域性變數如果沒有顯示的初始化,在使用它的時候會報錯,而成員變數不會,如果沒有顯示的初始化一個成員變數,那麼它會被預設的分配一個指定的值。

public class InitialValues {
    boolean t;
    char c;
    byte b;
    short s;
    int i;
    long l;
    float f;
    double d;
    InitialValues reference;
    void printInitialValues() {
        System.out.println("Data type      Initial value");
        System.out.println("boolean        " + t);
        System.out.println("char           [" + c + "]");
        System.out.println("byte           " + b);
        System.out.println("short          " + s);
        System.out.println("int            " + i);
        System.out.println("long           " + l);
        System.out.println("float          " + f);
        System.out.println("double         " + d);
        System.out.println("reference      " + reference);
    }
    public static void main(String[] args) {
        InitialValues iv = new InitialValues();
        iv.printInitialValues();
    }
}

執行結果:

Data type      Initial value
boolean        false
char           [ ]
byte           0
short          0
int            0
long           0
float          0.0
double         0.0
reference      null

7. 構造器初始化

如前邊對構造器的闡述,可以使用構造器來初始化類的成員變數,當物件被例項化之後,物件的成員變數會被初始化。靜態成員變數只有在第一次使用它是才會被初始化,後邊再次用到時不會被初始化。初始化順序為建立物件時,先初始化這個類的靜態變數,然後在堆上為這個物件分配記憶體,最後執行建構函式。

總結一下物件的建立過程,假設有一個名字為Dog的類:

  1. 當首次建立型別為Dog的物件時(構造器可以看做是靜態方法),或者Dog類的靜態方法/靜態域被首次訪問時,Java直譯器必須查詢類路徑,以定位到Dog.class檔案。
  2. 然後載入Dog.class,有關靜態初始化的所有動作都會被執行。包括靜態方法和靜態程式碼塊,具體執行順序為靜態方法和靜態程式碼塊的定義順序。因此靜態初始化在Class物件首次被載入的時候進行一次。
  3. 當用new Dog()建立物件的時候,首先將在堆上為Dog物件分配足夠的儲存空間。
  4. 這快儲存空間會被請零,這就自動地將Dog物件中所有基本資料型別都設定稱為了預設值,並且引用型別被設定為null。
  5. 執行所有出現欄位定義的初始化工作。
  6. 執行構造器。

8. 陣列初始化

資料是一系列相同資料型別封裝起來的序列,它的初始化可以發生在任何時候,int[] a1表示一個int型別的陣列,這個陣列內部所有的值都是int型別,a1只是這個陣列的一個引用,可以顯示的通過如int[] a1={1,2,3};的形式進行初始化。如果不能確定陣列的內容或者是長度,則可以通過new的形式來建立一個數組。int[] a = new int[20];這種建立也只是建立了一個引用陣列,直到陣列中的每一個欄位都有確切的值了,初始化才真正的完成。如a[1]=3;

使用陣列我們可以構建一個變參的函式,就是當方法的引數型別和個數都不確定的時候,我們可以使用一個數組作為形參。因為Object類是所有型別的父類,所以這個形引數組的型別就是Obejct類,對於基本資料型別,因為都有對應的包裝類,所以也可以轉換成Obejct類進行使用。

public class DifArgTest {
    public static void printArray(Object...args){
        for(Object obj:args){
            System.out.print(obj + "");
        }
    }
    public static void main(String[] args) {
        DifArgTest.printArray("我","今年",24,"歲");
    }
}

9. 列舉型別

建立一個列舉型別,型別內部的例項值是常量,因此按照通用的命名規範進行大寫。同時需要使用列舉時,可以初始化一個引用然後進行賦值。

當我們建立列舉的時候編譯器會自動為我們新增一些有用的特性,我覺得這是與其它語言相比更加完備的地方,比如它會建立一個toString()方法,這也就是為什麼我們上邊可以使用syso打印出來。編譯器還會建立ordinal()方法,用來表示特定enum常量的宣告順序,以及一個static values()方法,該方法是一個靜態的方法可以通過enum名字進行訪問,方法的作用是按照enmu的宣告順序,產生一個由enum常量值組成的陣列。

public class EnumOrder {
    public static void main(String[] args) {
        for(Spiciness s : Spiciness.values())
            System.out.println(s + ", ordinal " + s.ordinal());
    }
}

執行結果:

NOT, ordinal 0
MILD, ordinal 1
MEDIUM, ordinal 2
HOT, ordinal 3
FLAMING, ordinal 4

euum看起來像是一種新的資料型別,但是實際上enum是一個類,並且具有自己的方法。除了上邊的特性之外,enum還有個更加實用的特性,由於它是一個常量集,因此可以和switch語句完美匹配。

public class EnumTest{
    public EnumSet es;
    EnumTest(EnumSet es){
        this.es = es;
    }
    public enum EnumSet{
        FIRST,SECOND,THIRD
    }
    public void doSwitchPrint(){
        switch(es){
            case FIRST:
                System.out.println("This is First");
                break;
            case SECOND:
                System.out.println("This is Second");
                break;
            case THIRD:
                System.out.println("This is Third");
                break;
            default:
                System.out.println("This is Error");
        }
    }
    public static void main(String[] args) {
        EnumTest et1 = new EnumTest(EnumSet.FIRST);
        EnumTest et2 = new EnumTest(EnumSet.SECOND);
        EnumTest et3 = new EnumTest(EnumSet.THIRD);
        et1.doSwitchPrint();
        et2.doSwitchPrint();
        et3.doSwitchPrint();
    }
}

執行結果:

This is First
This is Second
This is Third