一個前端的java後端之旅(二) 面向對象繞不過?那就幹掉它

java跟咱們用過的(c++/JavaScript)有很大的不一樣,儘管都支持面向對象,但你仍能夠在c++,JavaScript中不使用類和對象,而java就不一樣了,就連 main入口方法都是類的方法,它是一個純粹的面向對象的語言,因此面向對象在java學習中是沒法繞過的,那既然繞不過,就先幹掉他,在適應了它的規則後再深刻了解它。

java中的類Class

說到類,其實如今作前端的同窗也會很熟悉,若是你使用過TypeScript,那應該對類的概念就更清楚不過了,這裏不對類和對象的概念再作無心義的描述,讓咱們直接開始coding,複習一下類和對象的基本使用方法。html

因爲沒有很好的想法,那就創造一個接近實際的需求,咱們來用收貨地址來熟悉類和對象基本使用前端

創造需求---收貨地址類

根據上一篇文章中建立的java項目,咱們在src下建立一個包learning.oop,而後在裏面建立一個Class文件DiliveryAddress,注意類名最好大寫開頭,駝峯結構命名,idea會自動幫咱們建立與Class文件名相同的類,文件名與類名保持一致。java

package learning.oop;

public class DeliveryAddress {
}

複製代碼

好了,如今該想一想裏面應該設計什麼字段了。我就直接提供了:c++

  • id: 收貨地址自己須要一個id,來表明具體哪個收貨地址
  • userId: 用戶Id
  • receiverName: 收件人名字
  • receiverPhone: 收件人電話
  • receiverProvince: 省
  • receiverCity: 市
  • receiverDistrict: 區
  • receiverAddress: 詳細地址
  • receiverZip: 郵政編碼
  • isDefault: 是不是默認地址
  • createTime: 建立時間
  • updateTime: 修改時間

你是否露出了驚訝的表情,長大了嘴巴,心想,不就是個例子嘛,要不要這麼認真,舉個貓狗動物的例子它不香嗎?要搞這麼多字段的東西。(請合攏你張大的嘴巴),咱們不是兒戲,咱們要經過接近真實的東西快速上手java。git

初始化屬性

在類中,咱們能夠包含僅表示數據的屬性以及操做數據的方法,對於表示數據的屬性,儘可能使用private關鍵字,不要把屬性直接暴露出去,若是你有修改和獲取數據的需求,則應該使用修改器setter獲取器getter的方式來進行。github

接下來先根據上述需求,來建立類中的屬性,設計過程當中想一想它應該是哪一種數據類型spring

import java.util.Date;

public class DeliveryAddress {
    private Integer id;
    private Integer userId;
    private String receiverName;
    private String receiverPhone;
    private String receiverProvince;
    private String receiverCity;
    private String receiverDistrict;
    private String receiverAddress;
    private String receiverZip;
    private Boolean isDefault;
    private Date createTime;
    private Date updateTime;
}

複製代碼

以上,咱們使用了四種數據類型,分別是Integer,String,Boolean,Date類型,Date類型引入了一個java.util.Date的包,說明它是那個包中所定義的一種類,並非基礎的數據類型。其餘類型也很容易認,認真觀察這些類型都是大寫開頭,因此他們本質也是一種類,這種形式叫作包裝類。說到包裝了類,有些包裝類還對應有基礎類型(好比int Interger均可以定義整數)。windows

好比int類型爲整型(定義整數用的),他是定義整數變量的基本類型,對應的Interger是它的包裝類,定義的是一種對象,純粹定義的數字能夠理解爲就是一種數字,可是數字要想使用一些例如toString()的方法,就要藉助對象,因此就會進行Integer類型的包裝,若是用int定義的變量,在使用方法的時候java會自動幫咱們進行包裝,在java中這種轉變叫作自動裝箱,說到這裏,若是用int變量接收一個Interger定義的變量,java就會自動拆除變量的方法,讓他變成一個純數字,在java中這種轉變就是自動裝箱(手動拆箱裝箱本身理解咯)。後端

若是想詳細瞭解java中的變量,能夠自行查閱資料,但更建議的是邊學邊記。這裏就再也不作拓展(好吧,是由於我會的也很少)。api

添加無參構造函數

既然是前端者的後端之旅,那麼你們對JavaScript的構造函數必定不會陌生,它是經過constructor進行定義的,java不一樣於此,是經過與類名相同的方法來進行構造函數的定義的,若是咱們不定義構造函數,系統會默認給咱們添加一個沒有參數且函數體爲空的構造函數,固然咱們也能夠自定義帶參數的構造函數。

package learning.oop;

import java.util.Date;

public class DeliveryAddress {
    private Integer id;
    private Integer userId;
    private String receiverName;
    private String receiverPhone;
    private String receiverProvince;
    private String receiverCity;
    private String receiverDistrict;
    private String receiverAddress;
    private String receiverZip;
    private Boolean isDefault;
    private Date createTime;
    private Date updateTime;

    // 無參構造函數
    public DeliveryAddress(){
        this.createTime = new Date();
    }

    public Date getCreateTime() {
        return createTime;
    }
}

複製代碼

這裏添加了一個無慘構造函數,裏面對createTime作了初始化操做,由於這個屬性應該是不能夠改變的,並且是在建立對象時候就應該擁有值的。下面還定義了一個public方法,類型爲Date,返回createTime,注意構造函數和getCreateTime中的寫法,一個帶this,一個不帶,兩種形式均可以。

tips 注意構造函數也要加public修飾符

如今咱們去src下的Main方法裏面實例化幾個對象試試看。

import learning.oop.DeliveryAddress;

public class Main {

    public static void main(String[] args) {
        DeliveryAddress address1 = new DeliveryAddress();
        System.out.println(address1);
        System.out.println(address1.toString());
        System.out.println(address1.getClass());
        System.out.println(address1.getCreateTime());
        System.out.println(address1.getCreateTime().getTime());
    }
}

複製代碼

我打印了其中的toString()getClass()方法的運行結果,toString()與JavaScript中的方法相似,在咱們呢直接打印這個變量的時候,就會輸出它的toString()結果,所以咱們能夠看到直接打印與打印toString()結果相同,而後好奇打印了getClass(),獲得了這個對象對應的類所在具體地址(包名)learning.oop.DeliveryAddress

經過getCreateTime()方法,咱們也成功得到了對象建立的時間,,這個createTime實際上是個Date類型的對象,咱們能夠經過它上面的方法獲取本身想要的時間形式,例如想要時間戳,能夠經過getTime()方法獲取。

你們能夠經過文檔看看Date類定義了哪些方法。其實徹底不用所有看,本身須要用哪些就看哪些。

添加帶參構造函數/方法

what?構造函數能夠添加多個?使得沒錯,在C++/Java中都是能夠的,這種特色叫作重載,TypeScript中也支持重載,但因爲它自己仍是由JavaScript寫的,因此它的重載並非真正的重載。

public DeliveryAddress( Integer userId, String receiverName, String receiverPhone, String receiverProvince, String receiverCity, String receiverDistrict, String receiverAddress, String receiverZip ) {
        this.userId = userId;
        this.receiverName = receiverName;
        this.receiverPhone = receiverPhone;
        this.receiverProvince = receiverProvince;
        this.receiverCity = receiverCity;
        this.receiverDistrict = receiverDistrict;
        this.receiverAddress = receiverAddress;
        this.receiverZip = receiverZip;
        this.isDefault = false;
    }
    
    public DeliveryAddress( Integer userId, String receiverName, String receiverPhone, String receiverProvince, String receiverCity, String receiverDistrict, String receiverAddress, String receiverZip, Boolean isDefault ) {
        this.userId = userId;
        this.receiverName = receiverName;
        this.receiverPhone = receiverPhone;
        this.receiverProvince = receiverProvince;
        this.receiverCity = receiverCity;
        this.receiverDistrict = receiverDistrict;
        this.receiverAddress = receiverAddress;
        this.receiverZip = receiverZip;
        this.isDefault = isDefault;
    }
複製代碼

此次我添加了兩個帶參的構造方法,他們的惟一不一樣就在於isDefault屬性的設置,由於java不支持設置默認參數,因此只能用重載的形式來進行設置。同時仔細觀察這裏的全部屬性設置都會使用this.xxx = xxx,而沒有直接使用xxx = xxx,後者實際上是錯誤的,這樣java分辨不出來你到底要給誰設置值,因此若是屬性名跟參數名相同,那就必須加this

咱們能夠去Main方法中添加兩個實例試試

import learning.oop.DeliveryAddress;

public class Main {

    public static void main(String[] args) {
        DeliveryAddress address1 = new DeliveryAddress();
        DeliveryAddress defaultAddress = new DeliveryAddress(
                1,
                "張三",
                "13122888888",
                "上海",
                "上海",
                "浦東新區",
                "XX小區XX樓XX戶",
                "20000",
                true
                );
        DeliveryAddress address2 = new DeliveryAddress(
                1,
                "張三",
                "13122888888",
                "上海",
                "上海",
                "浦東新區",
                "XX小區XX樓XX戶",
                "20000",
        );

        System.out.println(address1.getCreateTime());
        System.out.println(defaultAddress.getCreateTime());
        System.out.println(address2.getCreateTime());
    }
}

複製代碼

enter description here

咱們打印了三個地址的建立時間,但一看,彷佛不對啊,爲何後面兩個是null,難道不該該是正常時間?

這實際上是正常現象,三個構造方法是獨立的,後兩個調用的時候並無去設置時間,因此不會有。

好吧,這真的是故意的,但又一點不是故意的我忘記重寫toString()方法了,那麼下面咱們來解決這些問題,同時兩個帶參的構造函數彷佛很大一部分代碼是重複的,咱們也順手將代碼重複的問題進行解決。

改良

...
    private static int counter = 0;
    
    public DeliveryAddress(){
        this.id = DeliveryAddress.counter;
        DeliveryAddress.counter++;
        this.createTime = new Date();
        this.updateTime = this.createTime;
    }

    public DeliveryAddress( Integer userId, String receiverName, String receiverPhone, String receiverProvince, String receiverCity, String receiverDistrict, String receiverAddress, String receiverZip ) {
        this();
        this.userId = userId;
        this.receiverName = receiverName;
        this.receiverPhone = receiverPhone;
        this.receiverProvince = receiverProvince;
        this.receiverCity = receiverCity;
        this.receiverDistrict = receiverDistrict;
        this.receiverAddress = receiverAddress;
        this.receiverZip = receiverZip;
        this.isDefault = false;
    }

    public DeliveryAddress( Integer userId, String receiverName, String receiverPhone, String receiverProvince, String receiverCity, String receiverDistrict, String receiverAddress, String receiverZip, Boolean isDefault ) {
        this(
            userId,
            receiverName,
            receiverPhone,
            receiverProvince,
            receiverCity,
            receiverDistrict,
            receiverAddress,
            receiverZip
        );
        this.isDefault = isDefault;
    }

    @Override
    public String toString() {
        String res = "id: " + this.id + "\n" +
                "userId: " + this.userId + "\n" +
                "receiverName: " + this.receiverName + "\n" +
                "receiverPhone: " + this.receiverPhone + "\n" +
                "receiverCity: " + this.receiverCity + "\n" +
                "receiverDistrict: " + this.receiverDistrict + "\n" +
                "receiverAddress: " + this.receiverAddress + "\n" +
                "receiverZip: " + this.receiverZip + "\n" +
                "isDefault: " + this.isDefault + "\n";
        return res;
    }
複製代碼

這裏咱們仍是三個構造方法,第一個無慘,後兩個有參,第三個比第二個多個isDeafult,這些都沒問題,前面提出的問題咱們又是怎麼解決的呢?咱們經過給第二個構造函數添加this(),這表明若是要執行第二個構造方法,就會先執行第一個,給第三個構造方法添加了個this(xxxxx)裏面傳進了不少參數,這表明要使用第三個構造方法,就必須調用第二個,同時,也作到了代碼的簡化,第三個構造函數僅僅是比第二個多了個isDefault的參數而已,其餘代碼同樣,那就直接調用第二個就行了。

最後實現了個publish String toString()方法,這個方法奇怪的地方是在它頭上多了個@Override的東西,這個東西叫作註解,能夠暫時理解爲要重寫方法,就必須用這個註解進行標識(實際上是由於我也不會註解,後面再研究好了)。

tips 這裏注意一下又添加了一個屬性,private static int counter = 0,這個屬性與其餘不一樣的在於多了個static關鍵詞的描述,而且直接初始化爲0,添加了static的屬性與JavaScript中的做用同樣,它就變成了類的屬性,而不是某個實例的屬性,這裏經過它模擬了收貨地址id的自增。固然,也可使用static建立靜態方法,它也是屬於類的方法,既能夠經過類名調用,也能夠經過實例進行調用。

而後咱們能夠回去Main方法中完善一下三個實例的輸出

import learning.oop.DeliveryAddress;

public class Main {

    public static void main(String[] args) {
        DeliveryAddress address1 = new DeliveryAddress();
        DeliveryAddress defaultAddress = new DeliveryAddress(
                1,
                "張三",
                "13122888888",
                "上海",
                "上海",
                "浦東新區",
                "XX小區XX樓XX戶",
                "20000",
                true
                );
        DeliveryAddress address2 = new DeliveryAddress(
                1,
                "張三",
                "13122888888",
                "上海",
                "上海",
                "浦東新區",
                "XX小區XX樓XX戶",
                "20000"
        );

        System.out.println(address1.getCreateTime());
        System.out.println(defaultAddress.getCreateTime());
        System.out.println(address2.getCreateTime());
        System.out.println(address1);
        System.out.println(defaultAddress);
        System.out.println(address2);
    }
}

複製代碼

此次就成功看到了三個建立時間的輸出,而且看到直接打印對象,打印出了咱們toString()方法返回的值。也證實以前說的直接打印對象其實輸出的就是toString()

屬性默認值

在上面運行結果中咱們看到第一個地址的不少屬性值都是null,這明顯是不行的,若是這些屬性沒有被賦值,咱們須要給他們添加默認值,添加默認值有兩種方式:

  1. 能夠在設置屬性的時候直接初始化話,就像上面所說的private static int counter = 0同樣,只不過普通屬性不加static
  2. 也能夠像createTime同樣放在無慘構造函數裏進行初始化

這裏我將其完善一下,使用第一種方式進行初始化。

private Integer id;
    private Integer userId = -1;
    private String receiverName = "";
    private String receiverPhone = "";
    private String receiverProvince = "";
    private String receiverCity = "";
    private String receiverDistrict = "";
    private String receiverAddress = "";
    private String receiverZip = "";
    private Boolean isDefault = false;
    private Date createTime;
    private Date updateTime;
    private static int counter = 0;
複製代碼

回到Main函數從新運行代碼結果爲

這樣就舒服多了,未填的值直接爲設置的初始值。

工欲善其事必先利其器:idea的利用

以上全部的屬性,構造方法,普通方法都是咱們本身寫的,其實徹底沒有必要讓本身這麼累,idea工具能夠幫助咱們作這些事,右鍵點擊idea寫代碼的區域,點擊generate,或者按相應的快捷鍵,generate選項右邊有快捷鍵標識,Mac的是⌘+N,windows自行查看(個人win懶得開機😂)

經過這些選項,能夠自行生成getter setter 構造函數 toString()等方法,這裏你們自行體驗,我也去體驗一把。

所謂的getter就是獲取私有屬性的方法,setter就是設置私有屬性的方法,由於他們的書寫只是體力活,因此你們能夠用idea工具生成,若是有須要更改的再去更改。

小結

本篇文章簡單介紹了java中類的使用,包括構造方法,構造方法的重載,調用其餘構造方法,toString()方法,屬性值設置等,而且裏面穿插了包裝類,拆箱裝箱等小知識點。

固然,本文知識java面向對象的開始,由於java總體都是面向對象的,後續將繼續分享接口、繼承反射、bean、泛型、註解、等內容,儘可能讓快速掌握可使用spring boot的程度,而後再補充java的其餘知識。有一點要保證的是,這些內容加起來確定不會像字典同樣厚,但關鍵內容仍然會保證不缺失。

相關文章
相關標籤/搜索