Java基礎系列(七):物件與類(下)

weixin_34185364發表於2018-11-11

前言

上節我們學習了物件和物件變數的概念,以及它們之間的區別。與此同時,我們還知道了如何自定義一個屬於我們自己的類,以及多個原始檔的時候的編譯機制。這節課我們來剖析一下我們上節課建造的這個類。

解讀類

Employee類如下:

public class Employee {

    //fields
    private String name;
    private double salary;
    private LocalDate hireday;

    //constructor
    public Employee(String n, double s, int year, int month, int day) {
        name = n;
        salary = s;
        hireday = LocalDate.of(year, month, day);
    }

    //methods
    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public LocalDate getHireday() {
        return hireday;
    }

    public void raiseSalary(double byPercent) {
        double raise = salary * byPercent / 100;
    }
}

對這個的方法進行剖析,我們會發現這個類有一個構造器和四個方法:

  //構造器
  public Employee(String n, double s, int year, int month, int day) {}
  
  //方法
  public String getName(){}
  public double getSalary(){}
  public localDate getHireDay(){}
  public void raiseSalary(double byPercent){}

這些類的所有方法被標記為public,關鍵字public意味著任何類的任何方法都可以通過構造一個物件的方式來呼叫這些方法。
當然,在Employee類的例項中有三個例項域用來存放將要操作的資料:

private String name;
private double salary;
private LocalDate hireDay;

關鍵字private確保只有Employee類自身的方法能夠訪問這些例項域,而其他類的方法不能訪問這些例項域,這確保了類與類之間的隔離性,它們之間的資料不會相互影響。在這需要注意一點,類通常包括型別屬於另外某個類型別的例項域,比如name域是String類物件,hireDay是LocalDate類物件。

構造器

public Employee(String n, double s, int year, int month, int day) {
        name = n;
        salary = s;
        hireday = LocalDate.of(year, month, day);
}

可以看到,構造器必須與類名保持一致,在構造Employee類的物件時,構造器會執行,以便將例項域初始化為所希望的狀態。這裡需要注意一點,構造器與其他的方法有一個重要的不同,構造器總是伴隨著new操作符的執行被呼叫,而不能對一個已經存在的物件呼叫構造器來達到重新設定例項域的目的。比如:

faker.Employee("Faker", 1000000000, 1997, 08, 25)   //會報編譯錯誤

後面我會單開一節詳細講解構造器,我們現在只需記住:

  • 構造器與類同名
  • 每個類可以有一個以上的構造器
  • 構造器可以有0個,1個或多個引數
  • 構造器沒有返回值
  • 構造器總是隨著new操作一起呼叫

方法以及它的引數

方法用於操作物件以及存取它們的例項域,比如:

public void raiseSalary(double byPercent) {
    double raise = salary * byPercent / 100;
    salary += raise;
}

faker.raiseSalary(5)

方法呼叫的結果是把faker.salary域的值增加5%,實際上,它執行了以下的過程:

double raise = faker.salary * byPercent / 100;
faker.salary += raise;

由此可以看出,這個方法並不像我們想像的那樣只有一個byPercent引數,它實際上還包括了一個隱式引數,是出現在方法名前的Employee物件,第二個引數是位於方法名後面括號中的數值。這是一個顯式引數。
在每一個方法中,關鍵字this表示隱式引數,比如剛剛那個方法可以寫成如下形式:

public void raiseSalary(double byPercent) {
    double raise = this.salary * byPercent / 100;
    this.salary += raise;
}

域訪問器和域修改器

  //域訪問器
  public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public LocalDate getHireday() {
        return hireday;
    }
    
    //域修改器
    public void setName(String name) {
        this.name = name;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
    
    public void setHireday(int year, int month, int day) {
        this.hireday = LocalDate.of(year, month, day);
    }

我們為什麼會需要這些方法呢?直接將例項域設定為public不是更好?這裡就需要知道一點,類的例項域如果是public ,當我們在許多地方構造了這個類的物件並對例項域進行修改,出現錯誤之後,你將無法找到是哪個方法對他進行了修改,比如,上例中的salary,它只能通過raiseSalary()進行修改,一旦出錯,我們只需要去除錯這個方法即可。

如果想要獲得或者設定例項域的值,應該提供以下三個內容:

  • 私有的資料域
  • 公有的域訪問器(get方法)
  • 公有的域修改器(set方法)

這樣看起來要比提供一個公有資料域要複雜,但是卻有其他的好處,利大於弊:

  • 可以改變內部實現,除了該類的其他方法以外,不會影響其他程式碼(安全性)
  • 更改器可以進行錯誤檢查,而直接對域賦值不能進行這樣的操作
  • 這樣做,更符合物件導向的程式設計思想,每個物件的個體都是隱私且獨立的。

final例項域

可以將例項域定義為final,構造物件的時候必須初始化這樣的域,也就是說,必須確保在每一個構造器執行之後,這個域的值被設定,並且在後面的操作中,不能再對它進行修改。

final修飾符大都應用於基本型別域,或不可變類的域(比如String),對於可變的類,雖然可以使用final修飾符,但是我們需要注意一點,final指定的該域的地址值不會變,而這個物件本身可變,則不受影響,只是表示這個物件變數不會指向其他的物件。

//定義一個可變的例項域
private final StringBuilder sb;

//初始化
sb = new StringBuilder();

//更改
sb.append("Hello Java!")

公眾號

10641481-961b717327e333ab.jpg

相關文章