Core Java 3/15 - 對象與類

對象與類

三個重要概念:java

  • 封裝app

  • 繼承dom

  • 多態ui

類之間的常見關係有:this

  • 依賴(uses-a):一個類的方法操縱另外一個類的對象lua

  • 聚合(has-a):一個類的對象包含另外一個類的對象spa

  • 繼承(is-a):一個類是另外一個類的拓展(子集)設計

表達類關係的統一建模語言符號:3d

在設計類時,應儘量將相互依賴的類減至最少,用軟件工程的術語來講,就是讓類的之間的耦合度最小指針

區分對象引用和對象實體

一個對象變量(引用)並無實際包含一個對象,而僅僅存放了這個對象實體的內存地址,換句話說,任何對象變量的值都是對存儲在堆(heap)中的一個對象的引用。能夠將 Java 的對象變量看做 C++ 的對象指針(而不是 C++ 的引用),這個值能夠是 null ,代表目前沒有指向任何對象。

對引用的值進行拷貝,結果以下:

1、自定義類

一個源文件只能有一個公有類,但能夠有任意數目的非公有類,源文件名必須與公有類的名字相同,不然沒法編譯。

關於多個源文件的編譯順序,「能夠認爲 Java 編譯器內置了 make 功能。」

構造器

  • 老是伴隨着 new 操做被調用

  • 不能對一個已經存在的對象調用構造器

  • 若是沒有編寫構造器,默認的無參數構造器會將實例域設置爲默認值

  • 一旦編寫了構造器,就再也不提供默認的無參數構造器

  • 能夠把構造器的訪問權限設爲 private,這樣就沒法在外部用 new 來建立實例,Math 類就是這麼作的

方法

隱式參數是出如今實例方法名前的類對象,也就是方法調用的目標或接受者,能夠在方法內部用 this 表示,與之對應,列在方法聲明中的參數就屬於顯式參數。

不要編寫返回引用可變對象的訪問器方法,由於對這個引用進行操做會改變原有的實例。若是須要返回一個可變數據域的拷貝,應該使用 clone 方法。

一個類的方法能夠訪問這個類的私有特性,即全部對象的私有數據,而不侷限於調用它的單一對象的私有數據。

重載

方法名以及參數類型(不包括返回類型)構成了方法的簽名(signature)。若是多個方法有相同的名字、不一樣的參數類型或順序,便產生了重載(overload),編譯器經過用各個方法給出的參數類型,與特定方法調用所使用的值類型進行匹配,挑選出相應的方法。 

final

用 final 修飾的實例域必須在構建對象時進行初始化,在後面的操做中不能再對它進行修改,大都應用於基本(primitive)類型域,或不可變(immutable)類的域(該類中的每一個方法都不會改變其對象)。

對可變的類使用 final 修飾符可能會難以理清關係,例如:

public class Employee {
    private final StringBuilder evaluations;
    // ... 
    Employee() {
        evaluations = new StringBuilder(); 
      // evaluations 不可引用其餘 StringBuilder 對象,但能夠對當前對象的狀態進行更改: evaluations.append(str);  
        // ...
    }
}

static

在絕大多數面嚮對象語言中,靜態域被稱爲類域,由這個類的全部對象共享

靜態方法是一種沒有隱式參數的方法,用類名進行調用(不提倡用其對象的引用),用於如下狀況:

  • 一個方法不須要訪問對象狀態,所需參數皆顯式提供

  • 一個方法只須要訪問類的靜態域

  • 工廠方法(factory method)

  • main 方法

2、按值調用

  • 按值調用(call by value):方法接收的是調用者提供的值

  • 按引用調用(call by reference):方法接收的是調用者提供的變量地址

Java 老是按值調用,方法獲得的是參數值的拷貝,不能修改傳遞參數變量的內容。爲了說明這個問題,咱們先作一個小實驗:

public static void swap(Employee x, Employee y) {
    Employee temp = x;
    x = y;
    y = temp;
}
Employee a = new Employee("Alice", ...);
Employee b = new Employee("Bob", ...);
swap(a, b);
// x -> Bob, y -> Alice, a -> Alice, b -> Bob 

下面兩幅圖能很好地分清「不能夠修改變量」和「能夠修改引用指向的對象」之間的不一樣:

 總之,一個方法

  • 不能修改一個基礎數據類型的參數

  • 能夠改變一個對象參數的狀態

  • 不能讓對象參數引用一個新的對象

3、構造對象

初始化

必須明確地初始化方法中的局部變量,但若是沒有初始化類中的域,將會被自動初始化爲默認值(0 / false / null)。

顯式的域初始化方法:

  • 定義實例域時直接賦值

  • 用 this 關鍵字調用另外一個構造器(節省代碼)

  • 初始化塊

class Person {
    private int age;
    private boolean sex;
    private String name;
    private double asset;

    // initialization block
    {
        asset = 0;
    }

    // ...
}

在一個類的聲明中,能夠包含多個代碼塊,這些塊會在構造對象時執行,運行順序先於構造器

構建一個對象實例時,具體的處理步驟是:

  1. 全部數據域初始化爲默認值

  2. 按照類聲明中的次序,依次執行全部域初始化語句和初始化塊

  3. 若是構造器第一行調用了第二個構造器,執行第二個構造器主體

  4. 執行這個構造器主體

靜態初始化

在初始化塊以前使用 static 關鍵字能夠對靜態域進行初始化,適用於沒法準確知道賦值內容的時候:

// static initialization block
static {
    Random generator = new Random();
    nextId = generator.nextInt(10000);
}

類第一次加載時就會進行靜態域的初始化,全部的靜態域初始化語句、靜態初始化塊都將按照定義的順序進行。

析構

Java 有自動的垃圾回收器(GC),不須要人工回收內存,所以 Java 不支持析構器。不過,若是對象使用了內存以外的其餘資源(好比文件),那麼當資源再也不須要時,應該將其回收和再用。能夠在類中添加 finalize 方法,它會在 GC 清除對象以前調用以回收資源。

因爲難以預料 GC 何時會清除對象,因此不要依賴 finalize 方法來回收短缺的資源。想在某個資源使用後馬上關閉,能夠在對象的方法中調用一個相應的 close 方法。

4、package

名字相同的類放置於不一樣的包中,就不會產生衝突。藉助包能夠方便組織本身的代碼,並與別人提供的代碼庫分開管理。標準的 Java 包具備一個層次結構,它們都處於 java 和 javax 包層次中。不過從編譯器的角度來看,嵌套的包之間沒有任何關係,每個包的類集合都是獨立的。

import

一個類可使用所屬包中的全部類,以及導入的其餘包中的公有類。 import 語句能夠導入一個特定的類或者整個包(用 *),不存在一條語句導入多個包的狀況。Java 中的 package  和 import 語句相似於 C++ 中的 namespace 和 using 指令。

前面提到過靜態導入,結合 static 關鍵字並在類名後再接上星號,就可使用指定類的靜態方法和靜態域,沒必要再加類名前綴。例如:

import static java.lang.System.*;
// ...
    out.println("good good study");
    exit(0);
// ...

做用域

若是一個類、方法或變量沒有指定爲 private 或 public,則這個部分能夠被同一個包中的全部方法訪問(默認訪問權限)。對類來講,這種設計有必定的方便性,但對變量來講並不合適,有時會忘記加上修飾符,從而破壞封裝性。所以,最好記得在聲明變量時對訪問權限做出顯式標記

5、類設計技巧

  • 保證數據私有

  • 對數據初始化

  • 儘可能用類替代多個基本類型的使用

  • get 方法和 set 方法並不是必需

  • 將職責過多的類進行分解

  • 名字要體現職責

  • 優先使用不可變的類

  • 迪米特法則(LoD,一個對象應當對其餘對象有儘量少的瞭解,「talk only to your immediate friends」)
相關文章
相關標籤/搜索