1. 程式人生 > 其它 >P06_類與物件-封裝-構造方法

P06_類與物件-封裝-構造方法

第1章 面向物件思想

1.1 面向物件思想概述

概述

​ Java語言是一種面向物件的程式設計語言,而面向物件思想是一種程式設計思想,我們在面向物件思想的指引下,使用Java語言去設計、開發計算機程式。 這裡的物件泛指現實中一切事物,每種事物都具備自己的屬性和行為。面向物件思想就是在計算機程式設計過程中,參照現實中事物,將事物的屬性特徵、行為特徵抽象出來,描述成計算機事件的設計思想。 它區別於面向過程思想,強調的是通過呼叫物件的行為來實現功能,而不是自己一步一步的去操作實現。

特點

​ 面向物件思想是一種更符合我們思考習慣的思想,它可以將複雜的事情簡單化,並將我們從執行者變成了指揮者。面向物件的語言中,包含了三大基本特徵,即封裝、繼承和多型。

1.2 類和物件

什麼是類

  • 類:是一組相關屬性和行為的集合。可以看成是一類事物的模板,使用事物的屬性特徵和行為特徵來描述該類事物。

現實中,描述一類事物:

  • 屬性:就是該事物的狀態資訊。
  • 行為:就是該事物能夠做什麼。

什麼是物件

  • 物件:是一類事物的具體體現。物件是類的一個例項(物件並不是找個女朋友),必然具備該類事物的屬性和行為.

類與物件的關係

  • 類是對一類事物的描述,是抽象的。
  • 物件是一類事物的例項,是具體的。
  • 類是物件的模板,物件是類的實體。

1.3 類的定義

事物與類的對比

現實世界的一類事物:

  • 屬性:事物的狀態資訊。

  • 行為:事物能夠做什麼。

Java中用class描述事物也是如此:

  • 成員變數:對應事物的屬性
  • 成員方法:對應事物的行為

類的定義格式

public class ClassName {
    //成員變數
    //成員方法
}
  • 定義類:就是定義類的成員,包括成員變數和成員方法。
  • 成員變數:和以前定義變數幾乎是一樣的。只不過位置發生了改變。在類中,方法外。
  • 成員方法:和以前定義方法幾乎是一樣的。只不過把static去掉,static的作用在面向物件後面課程中再詳細講解。

類的定義格式舉例:

public class Student {
    //成員變數
    String name;//姓名
    int age;//年齡
        
    //成員方法
    //學習的方法
    publicvoid study() {
    	System.out.println("好好學習,天天向上");
    }
    //吃飯的方法
    publicvoid eat() {
        System.out.println("學習餓了要吃飯");
    }
}

1.4 物件的使用

物件的使用格式

建立物件:

類名 物件名 = new 類名();

使用物件訪問類中的成員:

物件名.成員變數;
物件名.成員方法();

1.5 類與物件的練習

定義手機類:

public class Phone {
    // 成員變數
    String brand; //品牌
    int price; //價格
    String color; //顏色
    // 成員方法
    //打電話
    public void call(String name) {
    	System.out.println("給"+name+"打電話");
    }
    //發簡訊
    public void sendMessage() {
         System.out.println("群發簡訊");
    }
}

定義測試類:

public class Test02Phone {
    public static void main(String[] args) {
        //建立物件
        Phone p = new Phone();
        //輸出成員變數值
        System.out.println("品牌:"+p.brand);//null
        System.out.println("價格:"+p.price);//0
        System.out.println("顏色:"+p.color);//null
        System.out.println("‐‐‐‐‐‐‐‐‐‐‐‐");
        //給成員變數賦值
        p.brand = "錘子";
        p.price = 2999;
        p.color = "棕色";
        //再次輸出成員變數值
        System.out.println("品牌:"+p.brand);//錘子
        System.out.println("價格:"+p.price);//2999
        System.out.println("顏色:"+p.color);//棕色
        System.out.println("‐‐‐‐‐‐‐‐‐‐‐‐");
        //呼叫成員方法
        p.call("紫霞");
        p.sendMessage();
    }
}

1.6 物件記憶體圖

一個物件,呼叫一個方法記憶體圖

  • 通過上圖,我們可以理解,在棧記憶體中執行的方法,遵循"先進後出,後進先出"的原則。變數p指向堆記憶體中的空間,尋找方法資訊,去執行該方法。
  • 但是,這裡依然有問題存在。建立多個物件時,如果每個物件內部都儲存一份方法資訊,這就非常浪費記憶體了,因為所有物件的方法資訊都是一樣的。那麼如何解決這個問題呢?請看如下圖解。

兩個物件,呼叫同一方法記憶體圖

  • 物件呼叫方法時,根據物件中方法標記(地址值),去類中尋找方法資訊。這樣哪怕是多個物件,方法資訊只儲存一份,節約記憶體空間。

一個引用,作為引數傳遞到方法中記憶體圖

  • 引用型別作為引數,傳遞的是地址值。

1.7 成員變數和區域性變數區別

變數根據定義位置的不同,我們給變數起了不同的名字。如下圖所示:

  • 在類中的位置不同
    成員變數:類中,方法外 區域性變數:方法中或者方法宣告上(形式引數)
  • 作用範圍不一樣
    成員變數: 類中 區域性變數: 方法中
  • 初始化值的不同
    成員變數:有預設值 區域性變數:沒有預設值。必須先定義,賦值,最後使用
  • 在記憶體中的位置不同
    成員變數:堆記憶體 區域性變數:棧記憶體
  • 生命週期不同
    成員變數:隨著物件的建立而存在,隨著物件的消失而消失 區域性變數:隨著方法的呼叫而存在,隨著方法的呼叫完畢而消失

第2章 封裝

2.1 概述和原則

概述

​ 面向物件程式語言是對客觀世界的模擬,客觀世界裡成員變數都是隱藏在物件內部的,外界無法直接操作和修改。封裝可以被認為是一個保護屏障,防止該類的程式碼和資料被其他類隨意訪問。要訪問該類的資料,必須通過指定的方式。適當的封裝可以讓程式碼更容易理解與維護,也加強了程式碼的安全性。

原則

將屬性隱藏起來,若需要訪問某個屬性,提供公共方法對其訪問。

2.2 封裝的操作步驟

  1. 使用 private 關鍵字來修飾成員變數。
  2. 對需要訪問的成員變數,提供對應的一對 getXxx 方法 setXxx 方法。

2.3 封裝的操作——private關鍵字

private的含義

  1. private是一個許可權修飾符,代表最小許可權。
  2. 可以修飾成員變數和成員方法。
  3. 被private修飾後的成員變數和成員方法,只在本類中才能問。

private 使用格式

private 資料型別 變數名 ;

使用 private 修飾成員變數,程式碼如下:

public class Student {
    private String name;
    private int age;
}

提供 getXxx 方法 / setXxx 方法,可以訪問成員變數,程式碼如下:

public class Student {
    private String name;
    private int age;
    public void setName(String n) {
        name = n;
    }
    public String getName() {
        return name;
    }
    public void setAge(int a) {
        age = a;
    }
    public int getAge() {
        return age;
    }
}

2.4 封裝的優化1——this關鍵字

我們發現 setXxx 方法中的形參名字並不符合見名知意的規定,那麼如果修改與成員變數名一致,是否就見名知意了呢?程式碼如下:

public class Student {
    private String name;
    private int age;
    public void setName(String name) {
        name = name;
    }
    public void setAge(int age) {
        age = age;
    }
}

​ 經過修改和測試,我們發現新的問題,成員變數賦值失敗了。也就是說,在修改了setXxx() 的形參變數名後,方法並沒有給成員變數賦值!這是由於形參變數名與成員變數名重名,導致成員變數名被隱藏,方法中的變數名,無法訪問到成員變數,從而賦值失敗。所以,我們只能使用this關鍵字,來解決這個重名問題。

this的含義

this代表所在類的當前物件的引用(地址值),即物件自己的引用。

記住 :方法被哪個物件呼叫,方法中的this就代表那個物件。即誰在呼叫,this就代表誰。

this使用格式

this.成員變數名;

使用 this 修飾方法中的變數,解決成員變數被隱藏的問題,程式碼如下:

public class Student {
    private String name;
    private int age;
    public void setName(String name) {
        //name = name;
    this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setAge(int age) {
        //age = age;
        this.age = age;
    }
    public int getAge() {
        return age;
    }
}
  • :方法中只有一個變數名時,預設也是使用 this 修飾,可以省略不寫。

2.5 封裝的優化2——構造方法

​ 當一個物件被建立時候,構造方法用來初始化該物件,給物件的成員變數賦初始值。

  • 無論你與否自定義構造方法,所有的類都有構造方法,因為Java自動提供了一個無引數構造方法,一旦自己定義了構造方法,Java自動提供的預設無引數構造方法就會失效。

構造方法的定義格式

修飾符 構造方法名(引數列表){
    // 方法體
}

構造方法的寫法上,方法名與它所在的類名相同。它沒有返回值,所以不需要返回值型別,甚至不需要void。使用構造方法後,程式碼如下:

public class Student {
    private String name;
    private int age;
    // 無引數構造方法
    public Student() {}
    // 有引數構造方法
    public Student(String name,int age) {
        this.name = name;
        this.age = age;
    }
}

注意事項

  1. 如果你不提供構造方法,系統會給出無引數構造方法。
  2. 如果你提供了構造方法,系統將不再提供無引數構造方法。
  3. 構造方法是可以過載的,既可以定義引數,也可以不定義引數。

2.6 標準程式碼——JavaBean

JavaBean 是 Java語言編寫類的一種標準規範。符合JavaBean 的類,要求類必須是具體的和公共的,並且具有無引數的構造方法,提供用來操作成員變數的set 和get 方法。

public class ClassName{
    //成員變數
    //構造方法
    //無參構造方法【必須】
    //有參構造方法【建議】
    //成員方法
    //getXxx()
    //setXxx()
}

編寫符合JavaBean 規範的類,以學生類為例,標準程式碼如下:

public class Student {
    //成員變數
    private String name;
    private int age;
    //構造方法
    public Student() {}
    public Student(String name,int age) {
        this.name = name;
        this.age = age;
    }
    //成員方法
    publicvoid setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    publicvoid setAge(int age) {
        this.age = age;
    }
    publicint getAge() {
        return age;
    }
}

測試類,程式碼如下:

public class TestStudent {
    public static void main(String[] args) {
        //無參構造使用
        Student s= new Student();
        s.setName("柳巖");
        s.setAge(18);
        System.out.println(s.getName()+"‐‐‐"+s.getAge());
        //帶參構造使用
        Student s2= new Student("趙麗穎",18);
    	System.out.println(s2.getName()+"‐‐‐"+s2.getAge());
    }
}