類與對象 - Java學習(二)

弄清楚類與對象的本質與基本特徵,是進一步學習面向對象編程語言的基本要求。面向對象程序設計與面向過程程序設計在思惟上存在着很大差異,改變一種思惟方式並非一件容易的事情。html

1、面向對象程序設計

程序由對象組成,對象包含對用戶公開的特定功能部分,和隱藏在其內部的實現部分。從設計層面講,咱們只關心對象可否知足要求,而無需過多關注其功能的具體實現。面對規模較小的問題時,面向過程的開發方式是比較理想的,但面對解決規模較大的問題時,面向對象的程序設計每每更加合適。java

對象是對客觀事物的抽象,類是對對象的抽象,是構建對象的模板。由類構造(construct)對象的過程稱爲建立類的實例(instance)或類的實例化。編程

封裝是將數據和行爲組合在一個包中,並對使用者隱藏數據的實現方式。對象中的數據稱爲實例域(instance field)或屬性、成員變量,操縱數據的過程稱爲方法(method)。對象通常有一組特定的實例域值,這些值的集合就是對象當前的狀態。封裝的關鍵在於不讓類中的方法直接的訪問其餘類的實例域,程序僅經過對象的方法與對象數據進行交互。封裝可以讓咱們經過簡單的使用一個類的接口便可完成至關複雜的任務,而無需瞭解具體的細節實現。oracle

對象的三個主要特徵

  1. 對象的行爲(behavior):能夠對對象施加哪些操做,經過方法(method)實現。
  2. 對象的狀態(state):存儲對象的特徵信息,經過實例域(instance field)實現。
  3. 對象的標識(identity):辨別具備不一樣行爲與狀態的不一樣對象。

設計類

傳統的面向過程的程序設計,必須從頂部的 main 入口函數開始編寫程序。面向對象程序設計沒有所謂的頂部,咱們要從設計類開始,而後再往每一個類中添加方法。那麼咱們該具體定義什麼樣的類?定義多少個?每一個類又該具有哪些方法呢?這裏有一個簡單的規則能夠參考 —— 「找名詞與動詞」原則。編程語言

咱們須要在分析問題的過程當中尋找名詞和動詞,這些名詞頗有可能成爲類,而方法對應着動詞。固然,所謂原則,只是一種經驗,在建立類的時候,哪些名詞和動詞是重要的,徹底取決於我的的開發經驗(抽象能力)。ide

類之間的關係

最多見的關係有:依賴(use-a)、聚合(has-a)、繼承(is-a)。可使用UML(unified modeling language)繪製類圖,用來可視化的描述類之間的關係。函數

2、預約義類與自定義類

在 Java 中沒有類就沒法作任何事情,Java 標準類庫中提供了不少類,這裏稱其爲預約義類,如 Math 類。要注意的是:並不是全部類都具備面向對象的特徵(如 Math 類),它只封裝了功能,不須要也沒必要要隱藏數據,因爲沒有數據,所以也沒必要擔憂生成以及初始化實例域的相關操做。工具

要使用對象,就必須先構造對象,並指定其初始狀態。咱們可使用構造器(constructor)構造新實例,本質上,構造器是一種特殊的方法,用以構造並初始化對象。構造器的名字與類名相同。如需構造一個類的對象,須要在構造器前面加上 new 操做符,如new Date()。一般,但願對象能夠屢次使用,所以,須要將對象存放在一個變量中,不過要注意,一個對象變量並無實際包含一個對象,而僅僅是引用一個對象。單元測試

訪問器與修改器 咱們把只訪問對象而不修改對象狀態的方法稱爲 訪問器方法(accessor method)。若是方法會對對象自己進行修改,咱們稱這樣的方法稱爲 更改器方法(mutator method)。學習

用戶自定義類

要想建立一個完成的程序,應該將若干類組合在一塊兒,其中只有一個類有 main 方法。其它類( workhorse class)沒有 main 方法,卻有本身的實例域和實例方法,這些類每每須要咱們本身設計和定義。

一個源文件中,最多隻能有一個公有類(訪問級別爲public),但能夠有任意數目的非公有類。儘管一個源文件能夠包含多個類,但仍是建議將每個類存在一個單獨的源文件中。 不提倡用public標記實例域(即對象的屬性),public 數據域容許程序中的任何方法對其進行讀取和修改。當實例域設置爲 private 後,若是須要對其進行讀取和修改,能夠經過定義公有的域訪問器或修改器來實現。這裏要注意:不要編寫返回引用可變對象的訪問器方法,如:

class TestClass{
    private Date theDate;
    public getDate(){
        return theDate; // Bad
    }
}

上面的訪問器返回的是對實例屬性 theDate 的引用,這致使在後續能夠隨意修改當前實例的 theDate 屬性,好比執行x.getDate().setTime(y),破壞了封裝性!若是要返回一個可變對象的引用,應該首先對他進行克隆,以下:

class TestClass{
    private Date theDate;
    public getDate(){
        return (Date) theDate.clone(); // Ok
    }
}

構造器

構造器與類同名,當實例化某個類時,構造器會被執行,以便將實例域初始化爲所需的狀態。構造器老是伴隨着 new 操做符的調用被執行,不能對一個已經存在的對象調用構造器來重置實例域。

  1. 構造器與類同名
  2. 每一個類能夠有多個構造器
  3. 構造器能夠有 0 個或多個參數
  4. 構造器沒有返回值
  5. 構造器老是伴隨着 new 操做一塊兒調用

基於類的訪問權限

方法能夠訪問所屬類的全部對象的私有數據。[*]

在實現一個類時,應將全部的數據域都設置爲私有的。多數時候咱們把方法設計爲公有的,但有時咱們但願將一個方法劃分紅若干個獨立的輔助方法,一般這些輔助方法不該該設計成爲公有接口的一部分,最好將其標記爲 private 。只要方法是私有的,類的設計者就能夠確信:他不會被外部的其餘類操做調用,能夠將其刪去,若是是公有的,就不能將其刪除,由於其餘的代碼可能依賴它。

final 實例域

在構建對象時必須對聲明的 final 實例域進行初始化,就是說必須確保在構造器執行以後,這個域的值被設置,而且在後面的操做中,不可以再對其進行修改。final 修飾符大都用於基本類型,或不可變類的域。

靜態域和靜態方法

靜態域和靜態方法,是屬於類且不屬於對象的變量和函數。

經過 static 修飾符,能夠標註一個域爲靜態的,靜態域屬於類,而不屬於任何獨立的對象,可是每一個對象都會有一份這個靜態域的拷貝。靜態方法是一種不能對對象施加操做的方法,它能夠訪問自身類的靜態域,類的對象也能夠調用類的靜態方法,但更建議直接使用類名調用靜態方法。

使用靜態方法的場景 : 一個方法不須要訪問對象狀態,其所需參數都是經過顯式參數提供;一個方法只須要訪問類的靜態域。

靜態方法還有另一種常見用途,做爲工廠方法用以構造對象。之所已使用工廠方法,兩個緣由:一是沒法命名構造器,由於構造器必須與類名相同;二是當時用構造器時沒法改變構造的對象類型。

程序入口 main 方法就是一個典型的靜態方法,其不對任何對象進行操做。在啓動程序時尚未任何一個對象,靜態的 main 方法將執行並建立程序所須要的對象。每一個類均可以有一個 main 方法,做爲一個小技巧,咱們能夠經過這個方法對類進行單元測試。

3、方法參數

Java 中的方法參數老是按值調用,也就是說,方法獲得的是全部參數的值的一個拷貝,特別是,方法不能修改傳遞給它的任何參數變量的內容。然而,方法參數有兩種類型:基本數據類型和對象引用。

4、對象構造

若是在構造器中沒有顯式的爲域賦值,那麼域會被自動的賦予默認值:數值爲 0、布爾之爲 false、對象引用爲 null。在類沒有提供任何構造器的時候,系統會提供一個默認的構造器。

有些類有多個構造器,這種特徵叫作重載(overloading)。若是多個方法有相同的名字、不一樣的參數,便產生了重載。 Java 中容許重載任何方法,而不只是構造器方法。要完整的描述一個方法,須要指出方法名以及其參數類型,這個描述被稱做方法的簽名。

經過重載類的構造器方法,能夠採用多種形式設置類的實例的初始狀態。當存在多個構造器的時候,也能夠在構造器內部經過 this 調用另外一個構造器,要注意的是這個調用必須在當前構造器的第一行:

class Test{
    Test(int number) {
        this(number, (String)number);   // 位於當前構造器的第一行
    }

    Test(int number, String str) {
        _number = number;
        _string = str;
    }
}

初始化塊

在一個類的聲明中,能夠包含多個代碼塊。只要構造類的對象,這些塊就會被執行。例如:

class Test{
    private int number;
    private String name;

    /**
     * 初始化塊
     */
    {
        number = 5;
    }

    Test(){
        name = 'Kelsen'
    }

    public void pring(){
        System.out.println(name + "-" + number);
    }
}

執行順序爲,首先運行初始化塊,而後再運行構造器的主體部分。這種機制不是必須的,也不常見。一般會直接將初始化代碼放在構造器中。

Java 中不支持析構器,它有自動的垃圾回收器,不須要人工進行內存回收。但,若是某個資源須要在使用完畢後馬上被關閉,那麼就須要人工來管理。對象用完時能夠應用一個 close 方法來完成相應的清理操做。

5、包

藉助於包,能夠方便的組織咱們的類代碼,並將本身的代碼與別人提供的代碼庫區分管理。標準的 Java 類庫分佈在多個包中,包括 java.lang、java.util 和 java.net 等。標準的 Java 包具備一個層次結構。如同硬盤文件目錄嵌套同樣,也可使用嵌套層次組織包。全部的標準 Java 包都處於 javajavax 包層次中。從編譯器角度看,嵌套的包之間沒有任何關係,每個都擁有獨立的類集合。

一個類可使用所屬包中的全部類,以及其餘包中的公有類(pbulic class)。 import 語句是一種引用包含在包中的類的簡明描述。packageimport 語句相似 C++ 中的 namespaceusing 指令。

import 語句還能夠用來導入類的靜態方法和靜態域。

若是要將一個類放入包中,就必須將包的名字放在源文件的開頭,包中定義類的代碼以前。如:

package com.kelsem.learnjava;

public class Test{
    // ...
}

若是沒有在源文件中放置 package 語句,這個源文件中的類就被放置在一個默認包中。

包做用域

標記爲 private 的部分只能被定義他們的類訪問,標記爲 public 的部分能夠被任何類訪問;若是沒有指定訪問級別,這個部分(類/方法/變量)能夠被同一個包中的全部方法訪問。

類路徑

類存儲在文件系統的目錄中,路徑與包名匹配。另外,類文件也能夠存儲在 JAR 文件中。爲了使類可以被多個程序共享,一般把類放到一個目錄中,將 JAR 文件放到一個目錄中,而後設置類路徑。類路徑是全部包含類文件的路徑的集合,設置類路徑時,首選使用 -calsspath 選項設置,不建議經過設置 CLASSPATH 這個環境變量完成該操做。

6、文檔註釋

JDK 包含一個很是有用的工具,叫作 javadoc 。它經過分析咱們的代碼文件註釋,自動生成 HTML 文檔。每次修源碼後,經過運行 javadoc 就能夠輕鬆更新代碼文檔。Javadoc 功能包括:Javadoc搜索,支持生成HTML5輸出,支持模塊系統中的文檔註釋,以及簡化的Doclet API。詳細使用說明可參考 https://docs.oracle.com/en/java/javase/11/javadoc/javadoc.html

7、類的設計

必定要保證數據私有 務必確保封裝性不被破壞。

必定要對數據初始化 Java 不會對局部變量進行初始化,但會對對象的實例域進行初始化。最好不要依賴於系統默認值,而是顯式的對實例域進行初始化。

不要在類中使用過多的基本類型 經過定義一個新的類,來代替多個相關的基本類型的使用。

不是全部的域都須要獨立的域訪問器和域更改器

將職責過多的類進行分解 若是明顯的能夠將一個複雜的類分解爲兩個更簡單的類,就應該將其分解。

類名和方法名要可以體現他們的職責 對於方法名,建議:訪問器以小寫 get 開頭,修改器以小寫 set 開頭;對於類名,建議類名是採用一個名詞(Order)、前面有形容詞修飾的名詞(RushOrder)或動名詞(ing後綴)修飾名詞(BillingAddress)。

優先使用不可變的類 要儘量讓類是不可變的,固然,也並非全部類都應當是不可變的。

相關文章
相關標籤/搜索