Java類和對象 詳解(二)

上一篇Java類和對象 詳解(一)講解了類和對象的基本概念,定義和使用,以及對象引用傳遞的初步分析,下面接着來講其他內容。

一、面向對象的封裝性

封裝(encapsulation)又叫隱藏實現(Hiding the implementation)。就是隻公開代碼單元的對外接口,而隱藏其具體實現。比如手機,手機的鍵盤,屏幕,聽筒等,就是其對外接口。你只需要知道如何按鍵就可以使用手機,而不需要了解手機內部的電路是如何工作的。封裝機制就像手機一樣只將對外接口暴露,而不需要用戶去了解其內部實現。

在研究封裝性之前,我們先來看一段代碼:

package com.wz.classandobj;

class Book{

    String title;
    double price;

    public void getInfo(){
        System.out.println("圖書的名稱:"+title+" 圖書的價格:"+price);
    }
}

public class TestDemo {
        public static void main(String args[]) {
            Book book = new Book();
            book.title = "Java開發";
            book.price = -89.9;
            book.getInfo();
        }
}

運行結果:

圖書的名稱:Java開發 圖書的價格:-89.9

以上代碼沒有任何語法錯誤,卻存在一個業務邏輯的錯誤,因爲圖書的價格不能爲負數。造成這種情況的的原因在於:對象可以在一個類的外部直接訪問屬性。
如何解決?我們需要將Book類中的屬性設置爲對外不可見(只能是本類訪問),可以使用private關鍵字來定義屬性。

修改之前的代碼如下:

package com.wz.classandobj;

class Book{

    private String title;
    private double price;

    public void getInfo(){
        System.out.println("圖書的名稱:"+title+" 圖書的價格:"+price);
    }
}

public class TestDemo {
        public static void main(String args[]) {
            Book book = new Book();
            book.title = "Java開發";
            book.price = -89.9;
            book.getInfo();
        }
}

運行:

Exception in thread "main" java.lang.Error: Unresolved compilation problems: 
    The field Book.title is not visible
    The field Book.price is not visible

    at com.wz.classandobj.TestDemo.main(TestDemo.java:16)

我們發現,在訪問屬性的時候,外部的對象無法再直接調用類中的屬性了,此時就相當於Book類的屬性對外部不可見。

但是,要想讓程序可以正常運行,那麼必須讓外部可以操作Book類的屬性。在Java開發中,針對屬性有這樣的定義,在類中定義的屬性都要求使用private聲明,如果屬性需要被外部所使用,那麼按照要求,定義屬性相應的setter和getter方法,以Book類中的String title 爲例:
(1)setter方法是設置屬性內容:

public void setTitle(String t)

主要:有參數。

(2)getter方法是取得屬性內容:

public String getTitle()

注意:無參數。

範例:爲Book類的封裝屬性設置setter和getter。

package com.wz.classandobj;

class Book{

    private String title;
    private double price;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public double getPrice() {
        return price;
    }


    public void setPrice(double price) {
        this.price = price;
    }

    public void getInfo(){
        System.out.println("圖書的名稱:"+title+" 圖書的價格:"+price);
    }
}

public class TestDemo {
        public static void main(String args[]) {
            Book book = new Book();
            book.setTitle("Java開發"); 
            book.setPrice(-89.9);
            book.getInfo();
        }
}

運行結果:

圖書的名稱:Java開發 圖書的價格:-89.9

發現,圖書的價格是負數,需要加入檢查業務邏輯錯誤的代碼,可以在setter中增加驗證,如果值爲正,賦值,否則爲默認值0.0:

    public void setPrice(double price) {
        if(price > 0.0){
            this.price = price;
        }
    }

對於數據驗證,在Java標準開發中應該由輔助代碼完成。在實際開發中,setter往往只是簡單的設置屬性內容,getter只是簡單的取得屬性內容。

開發建議:以後在定義類的時候,所有的屬性都要編寫private封裝,封裝之後的屬性如果需要被外部操作,則編寫setter、getter。

二、類中的構造方法

先來看對象的產生格式:

①類名稱 ②對象名稱 = ③new ④類名稱();

①類名稱:規定了對象的類型。即:對象可以使用哪些屬性和方法都是由類定義的;
②對象名稱:如果需要使用對象,需要有一個名稱,這是一個唯一的標記;
③new:分配新的堆內存空間;
④類名稱():調用了名稱和類名稱相同的方法,這就是構造方法。

實際上,構造方法一直在被我們調用,但我們並沒有去定義它,爲什麼能夠使用呢?這是因爲在整個Java開發中,爲了保證程序可以正常執行,即便用戶沒有定義任何構造方法,也會在程序編譯後自動爲類增加一個沒有參數,方法名稱與類名稱相同,沒有返回值的構造方法。

構造方法的定義:方法名稱和類名稱相同,沒有返回值聲明。

    //無參,無返回值的構造方法
    public Book() {

    }

如果在Book類中沒有定義以上的構造方法,那麼也會自動生成一個無參,無返回值的構造方法。

我們再看:

package com.wz.classandobj;

class Book{

    //無參,無返回值的構造方法
    public Book() {
        System.out.println("無參構造方法");
    }

}

public class TestDemo {
        public static void main(String args[]) {
            Book book = null;//聲明對象
        }
}

運行,什麼也沒有打印。
在主方法中加入實例化對象的代碼:

public class TestDemo {
        public static void main(String args[]) {
            Book book = null;//聲明對象
            book = new Book();//實例化對象
        }
}

運行:

無參構造方法

以上說明,構造方法是在對象使用關鍵字new實例化的時候被調用

構造方法與普通方法最大的區別是:
構造方法在實例化對象(new)的時候只調用一次,而普通方法是在實例化對象之後可以隨意調用多次。

在實際開發中,構造方法的作用是在類對象實例化的時候設置屬性的初始化內容,範例如下:

package com.wz.classandobj;

class Book{

    private String title;
    private double price;

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public double getPrice() {
        return price;
    }


    public void setPrice(double price) {
        this.price = price;
    }

    public void getInfo(){
        System.out.println("圖書的名稱:"+title+" 圖書的價格:"+price);
    }
}

public class TestDemo {
        public static void main(String args[]) {
            Book book = new Book("Java開發",89.9);//設置屬性的初始化內容
            book.getInfo();

        }
}

運行結果:

圖書的名稱:Java開發 圖書的價格:89.9

如果一個類中已經明確定義了一個構造方法,則無參構造方法將不會自動生成。而且,一個類之中至少存在一個構造方法。另外,既然構造方法也屬於方法,那麼構造方法也可以重載,但是由於構造方法的特殊性,所以在構造方法重載時注意其參數的類型及參數的個數即可。

範例如下:

package com.wz.classandobj;

class Book{

    private String title;
    private double price;


    public Book() {
        System.out.println("無參的構造方法");
    }


    public Book(String title) {
        this.title = title;
        System.out.println("有一個參數的構造方法");
    }


    public Book(String title, double price) {
        this.title = title;
        this.price = price;
        System.out.println("有倆個參數的構造方法");
    }

    public void getInfo(){
        System.out.println("圖書的名稱:"+title+" 圖書的價格:"+price);
    }
}

public class TestDemo {
        public static void main(String args[]) {
            Book book1 = new Book();
            book1.getInfo();

            Book book2 = new Book("Java開發");
            book2.getInfo();

            Book book3 = new Book("Java開發",89.9);
            book3.getInfo();

        }
}

運行結果:

無參的構造方法
圖書的名稱:null 圖書的價格:0.0
有一個參數的構造方法
圖書的名稱:Java開發 圖書的價格:0.0
有倆個參數的構造方法
圖書的名稱:Java開發 圖書的價格:89.9

在進行構造方法重載時有一個編寫建議:所有重載的構造方法按照參數的個數由多到少,或者是由少到多排列。

我們再來看,如果在類中爲屬性直接設置默認值,結果會怎樣?

package com.wz.classandobj;

class Book{

    private String title = "Android開發";
    private double price = 199.9;


    public Book() {
        System.out.println("無參的構造方法");
    }


    public Book(String title) {
        this.title = title;
        System.out.println("有一個參數的構造方法");
    }


    public Book(String title, double price) {
        this.title = title;
        this.price = price;
        System.out.println("有倆個參數的構造方法");
    }

    public void getInfo(){
        System.out.println("圖書的名稱:"+title+" 圖書的價格:"+price);
    }
}

public class TestDemo {
        public static void main(String args[]) {
            Book book1 = new Book();
            book1.getInfo();

            Book book2 = new Book("Java開發");
            book2.getInfo();

            Book book3 = new Book("Java開發",89.9);
            book3.getInfo();

        }
}

運行結果:

無參的構造方法
圖書的名稱:Android開發 圖書的價格:199.9
有一個參數的構造方法
圖書的名稱:Java開發 圖書的價格:199.9
有倆個參數的構造方法
圖書的名稱:Java開發 圖書的價格:89.9

從上面可以發現:如果在類中爲屬性直接設置默認值,而且這個默認值是在構造方法執行完後纔會設置的,且屬性內容爲對應數據類型的默認值時才設置類中定義的這個默認值。但是,要注意的是在構造方法沒有執行之前,屬性內容都是其對應數據類型的默認值。

三、類中的匿名對象

沒名字的對象稱爲匿名對象,對象的名字按照之前的內存關係來講,在棧內存之中,而對象的具體內容在堆內存之中保存,這樣,沒有棧內存指向堆內存空間,就是一個匿名對象。

範例:

package com.wz.classandobj;

class Book{

    private String title;
    private double price;

    public Book(String title, double price) {
        this.title = title;
        this.price = price;
        System.out.println("有倆個參數的構造方法");
    }

    public void getInfo(){
        System.out.println("圖書的名稱:"+title+" 圖書的價格:"+price);
    }
}

public class TestDemo {
        public static void main(String args[]) {
            //匿名對象
            new Book("Java開發",89.9).getInfo();

        }
}

運行結果:

有倆個參數的構造方法
圖書的名稱:Java開發 圖書的價格:89.9

匿名對象由於沒有對應的棧內存指向,所以只能使用一次,一次之後就將成爲垃圾,並且等待被GC回收釋放。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章