1. 程式人生 > >Java SE面向物件--06.類與物件、封裝、構造方法

Java SE面向物件--06.類與物件、封裝、構造方法

學習目標:

  • 面向物件
  • 類與物件
  • 三大特徵——封裝
  • 構造方法

一、面向物件

1.1 引入面向物件(瞭解)

在開發的過程中,我們經常會遇到在多個程式中使用到同一個功能的情況。如下需求: 需求:定義類,在類中有定義功能對陣列求和的需求

class Demo1 {
    public static void main(String[] args) {
        //定義一個數組
        int[] arr={1,2,3,4,5,6,7,8,9};
        //呼叫自定義函式,求陣列的所有資料的和
        int sum = getSum(arr);
        //輸出結果
System.out.println("sum = " + sum); } /* 為了解決程式碼的複用性所以將求和的功能封裝到函式中 */ public static int getSum( int[] arr ){ //定義變數,儲存和值 int sum = 0; //迴圈陣列,取出陣列中的所有資料 for (int i = 0;i < arr.length;i++ ){ // 將取出的每個資料累加到sum變數中 sum += arr[i]; } //將計算的和返回給呼叫者
return sum; } }

需求二:在另一個類中也需要使用到陣列求和的功能 。

分析:基於前面的學習,我們的解決方案很簡單,就是在另外一個類中完成陣列求和的程式碼實現即可。

class Demo2 {
    public static void main(String[] args) {
        //定義一個數組
        int[] arr={1,2,3,4,5,6,7,8,9};
        //呼叫自定義函式,求陣列的所有資料的和
        int sum = getSum(arr);
        //輸出結果
        System.out.println("sum = "
+ sum); } /* 為了解決程式碼的複用性所以將求和的功能封裝到函式中 */ public static int getSum( int[] arr ){ //定義變數,儲存和值 int sum = 0; //迴圈陣列,取出陣列中的所有資料 for (int i = 0;i < arr.length;i++ ){ // 將取出的每個資料累加到sum變數中 sum += arr[i]; } //將計算的和返回給呼叫者 return sum; } }

總結:我們觀察會發現對陣列進行求和值的程式碼在所有的類中重複。表現為程式碼的複用性不高。解決上述程式碼出現的問題,即能否將相同的程式碼功能抽離出來,單獨封裝,當需要使用該功能的時候呼叫即可。

0![這裡寫圖片描述

工具類ArrayTools中的程式碼:

/*
    建立類,用來封裝陣列求和的功能。
*/
class ArrayTools {
    public int getSum(int[] arr){
        int su m= 0;
        for (int i=0;i<arr.length;i++ ) {
            sum += arr[i];
        }
        return sum;
    }
}

當其他類中需要使用到陣列求和功能的程式碼體現:

/*
    需求:求陣列中所有資料的和
*/
class Demo2{
    public static void main(String[] args) {
        //定義一個數組
        int[] arr = {2,4,1,8};
        //然後通過new關鍵字創建出陣列求和所在類的物件
        ArrayTools  util = new ArrayTools();
        //通過物件util就可以呼叫ArrayUiltl類中的方法了
        int sum = util.getSum(arr);
        //將所求的結果列印到螢幕上
        System.out.println("sum="+sum);
    }
}

1.2 面向物件概述

概述

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

區別:

  • 面向過程:強調步驟。

  • 面向物件:強調物件或者結果,這裡的物件就是煎餅攤。

1.2 類和物件

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

    舉例:人就是一個類,屬性,就是人的特點:身高、體重、姓名、年齡和性別等。

    ​ 行為:如吃飯、睡覺、學習等。

  • 屬性:就是該事物的狀態資訊。就是特點。

  • 行為:就是該事物能夠做什麼,有什麼動態的行為。就是我們之前學習的方法。

  • 物件:是一類事物的具體體現。物件是類的一個例項,必然具備該類事物的屬性和行為。比如狗這類事物中的某一隻具體的貓: “旺財” 就是貓這類事物的一個具體物件。

類與物件的關係

  • 類是對一類事物的描述,是抽象的
  • 物件是一類事物的例項,是具體的
  • 類是物件的模板(抽象、型別),物件是類的實體

這裡寫圖片描述

這裡寫圖片描述

1.3 類的定義

類的定義格式

public class ClassName { // 類就是用來封裝事物屬性和行為
  //成員變數
  //成員方法
}
  • 定義類:就是定義類的成員,包括成員變數成員方法
  • 成員變數:和以前定義變數幾乎是一樣的。只不過位置發生了改變。在類中,方法外
  • 成員方法:和以前定義方法幾乎是一樣的。只不過把static去掉,static的作用在面向物件後面課程中再詳細講解。
//當前這個class類僅僅只是為了描述一個事物,不需要獨立執行所以可以不用寫main函式
public class Person {
    // 定義變數描述人的基本特性:成員變數
    double height;//身高
    double weight;//體重
    int age;//年齡
    char sex;//性別
    String name;//姓名

    //定義功能描述人類的行為:成員方法
    public void sleep(){ //睡覺的行為
        System.out.println("sleep");
    }

    public void eat(){ //吃飯的行為
        System.out.println("eat");
    }

    public void study(){ //學習的行為
        System.out.println("study");
    }
}

1.4 物件的使用

物件的使用格式

建立物件:

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

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

物件名.成員變數;
p.name
物件名.成員方法名(實際引數);
p.eat();

物件的使用格式舉例:

/*
    書寫一個測試類在該類中需要使用上面類中的屬性和方法
*/
public class PersonTest{
    //需要獨立執行,所以需要主方法
    public static void main(String[] args){
        //建立類Person的物件
        Person p = new Person();
        //給類中的屬性賦值
        p.age = 18; // 給物件年齡賦值
        p.name = "班導";// 給物件姓名賦值
        p.sex = '女';// 給物件性別賦值

        //使用物件呼叫Person類中的方法
        person.sleep();
    }
}

1.5 物件的記憶體圖解和練習

需求:描述生活中的車這一類事物

車的基本資料(屬性):顏色、品牌(車名)、車牌號碼

車的基本行為(功能):跑(執行)

分析和步驟:

​ 定義一個車類class,名字為Car。因為在類Car中不需要獨立執行,只是描述車的屬性,所以不用定義main方法。

定義完類Car,需要定義一個測試類CarTest來測試車的屬性和和功能。

class Car {
    //車的基本屬性
    String color;
    String name;
    String number;

    //車的行為
    public void run(){
        System.out.println("車的顏色:"+ color +",車的品牌:" + name);
    }
}
public class CarTest{
    public static void main(String[] args) {
        //建立車的物件
        Car c = new Car();
        //給車的屬性賦值
        c.color = "yellow";
        c.name = "BMW";
        c.number= "滬A88888";
        c.run();
    }
}

以上的程式碼在記憶體中執行的圖解如下圖所示:

這裡寫圖片描述

在使用Java中的new 關鍵字建立物件時, 這個物件所屬類中的所有成員變數,會隨著物件在堆中的出現而出現。並且所有的成員變數都會有預設的初始化值。

注意:只要在程式中使用new關鍵字建立物件,那麼在堆中都會有一個新的物件產生。

物件的建立過程:

​ 1、當JVM遇到new關鍵字時,首先會在堆記憶體中開闢一個空間,並分配記憶體地址。

​ 2、類中的所有成員變數會隨著物件的出現在堆中出現。並且有自己的預設初始化值。

​ 3、當所有的成員變數載入完成之後,物件建立成功。會將記憶體地址賦值給左側變數空間。

成員變數的預設值

資料型別 預設值
基本型別 整數(byte,short,int,long)
浮點數(float,double)
字元(char)
布林(boolean)
引用型別 陣列,類,介面

1.6 類與物件的練習

描述Car類的程式碼:

 class Car {    
   //車的基本屬性    
   String color;    
   String name;    
   String number;    
   //車的行為    
   public void run(){   
     System.out.println("車的顏色:"+ color +",車的品牌:" + name);    
   }
 }

測試類程式碼:

class CarTest{
    public static void main(String[] args) {
        //建立車的物件
        Car c = new Car();
        //給車的屬性賦值 給c物件空間的屬性賦值
        c.color = "yellow";
        c.name = "BMW";
        c.number= "滬A88888";
        c.run();
        //建立車的物件 
        Car c1 = new Car();
        //使用c1物件呼叫run方法 輸出資料都為null
        c1.run();
    }
}

說明:因為重新建立了一個新的車的物件,這時在堆中又會有一個新的空間。

由於建立的新的車物件沒有進行手動的賦值,這時這個物件中的所有成員變數的值都是預設的值。

出現以上的情況的記憶體圖解說明:

這裡寫圖片描述

1.6.1物件引數傳遞

這裡寫圖片描述

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

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

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

public class Demo{
    // 成員變數
    int x;
    public static void show(){
        //區域性變數
        int y = 10;
    }
}

區域性變數:定義在方法中的那些變數。區域性變數只能在定義它的方法中有效。

成員變數:定義在類的成員位置上的變數。成員變數在整個類中都有效。(全域性變數是成員變數的俗稱)。

1) 資料型別 變數名 ;

2) 資料型別 變數名 = 值;

注意:在類和該類的方法中,同時存在一個相同型別相同名稱的變數,在方法被執行時,方法中優先使用定義在方法中的變數(區域性變數)。

優先順序:先使用內部的,再使用外部的(先去內部找,有就使用。沒有則再去外部找)就近原則。

區域性變數和成員變數的區別:(重要)

1、從定義位置上來講:

​ 區域性變數定義在方法中。

​ 成員變數定義在類中。

2、從記憶體儲存位置上來講:

​ 區域性變數隨著方法的執行會在棧記憶體中出現,區域性變數儲存在棧記憶體中。

​ 成員變數會隨著物件的出現在堆中存在,成員變數儲存在堆記憶體中。

3、從初始化方式來講:

​ 區域性變數在定義時需要指定初始值(區域性變數沒有預設值),只有初始化之後才能使用。

​ 成員變數可以不用初始化,有預設值。

4、從存活時間上來講(生命週期)

​ 區域性變數是隨著方法的進棧在方法所屬的棧記憶體中存在,隨著方法的出棧就消失。

​ 成員變數是隨著物件的出現在堆中出現。隨著物件的消失而消失。

二、 封裝

2.1 封裝概述

面向物件 的三個最基本的特徵:封裝 繼承 多型

概述

對事物進行包裝。封裝會造成事物的部分細節被隱藏。

封裝好處

1) 封裝可以隱藏事物的細節(提高了安全性)。它把不需要外界直接操作的細節隱藏起來,然後通過第三方的按鈕或者介面等手段保證外界還可以間接的去使用這些被隱藏起來的元件。

2) 封裝可以提高程式中程式碼的複用性。(比如方法和類)

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

訪問封裝屬性方式

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

2.2 封裝的步驟

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

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

需求:描述人這類事物。

/*
    描述人的類
*/
class Person{
    String name;//姓名
    int age;//年齡
    public void say(){
        System.out.println("姓名:"+name+",年齡:"+age);
    }
}
/*
    測試人的類
*/
public class  PersonTest{
    public static void main(String[] args) {
        //建立person物件
        Person person = new Person();
        //通過person物件給屬性賦值
        person.name = "黑旋風";
        person.age = -18;
        //使用person物件呼叫Person類中的功能
        person.say();
    }
}

上述的程式在執行的過程中,給年齡賦了人類非正常的年齡值,但是程式依然可以正常執行。

問題:怎麼樣控制Person類中的age保證外界在訪問的時候,資料是合法的?

要保證在另外一個類中可以控制其他類中的變數時,那麼就需要對這個類中的成員變數使用相對應的成員許可權修飾符完成。可以使用private 來修飾。從而讓外界無法直接訪問。

private的含義

  1. private是java中的關鍵字。一個許可權修飾符,代表最小許可權。

  2. public和private關鍵字都屬於java中的許可權修飾符,可以修飾成員(成員變數、成員方法)。

    private:私有的 訪問範圍只能在本類中使用 (在java中屬於最小的訪問許可權)

    public:公共的、公開的 訪問範圍沒有底限 (在java中屬於最大的訪問許可權)

  3. 被private修飾後的成員變數和成員方法,只在本類中才能訪問。

private的使用格式

private 資料型別 變數名 ;
private 返回值型別 方法名(){}

解決問題方案

我們希望對age這個變數進行控制,於是在Person類中給age 添加了許可權修飾符 private ,然後類內提供getXxx和setXx來處理。

/*
    描述人的類
*/
class Person{
    String name;//姓名
    private int age;//年齡
    //對外提供一個給age賦值的函式
    public void setAge( int a ){
        //在給age賦值之前,我們可以通過放方法對傳遞過來的引數進行判斷
        if(a > 0 && a <= 130){
            age = a;
        }else{
            System.out.println("沒有這個年齡");
             //throw new RuntimeException("沒有這個年齡");
        }
    }
    public void say(){
        System.out.println("姓名:"+name+",年齡:"+age);
    }
}
/*
    測試人的類
*/
class  PersonTest{
    public static void main(String[] args) {
        //建立person物件
        Person person = new Person();
        //通過person物件給屬性賦值
        person.name = "黑旋風";
        person.setAge(-18);
        //person.age=-18;
        //使用person物件呼叫Person類中的功能
        person.say();
    }
}

要求:在定義類的時候,要求類中的所有成員變數全部私有,並對外提供相應公開的訪問方法**

一個類中成員變數全部私有,對外提供getXxxx方法或者setXxxx方法 , Xxxx表示的成員變數的名字,而成員變數的名字中的第一個字母要大寫。**

演示程式碼:

/*
    描述人的類
*/
class Person {
    private String name;//姓名
    private int age;//年齡
    //對外提供一個給age賦值的函式
    public void setAge(int a){
        //在給age賦值之前,我們可以通過函式傳遞過來的引數進行判斷
        if(a > 0 && a <= 130){
            age = a;
        }else{
            System.out.println("沒有這個年齡");
            //throw new RuntimeException("沒有這個年齡");
        }
    }
    public int getAge(){
        return age;
    }
    public void setName(String nm){
        name = nm;
    }
    public String getName(){
        return name;
    }
    public void say(){
        System.out.println("姓名:" + name + ",年齡:" + age);
    }
}
/*
    測試人的類
*/
class  PersonTest{
    public static void main(String[] args) {
        //建立person物件
        Person person = new Person();
        //同過person物件給屬性賦值
        //person.name = "黑旋風";
        person.setAge(18);
        int age = person.getAge();
        //person.age=-18;
        //使用person物件呼叫Person類中的功能
        person.say();
        System.out.println(age);
    }
}

總結:

在以後寫程式碼的時候,要求類中的所有成員變數全部使用private關鍵字修飾私有,然後提供公開的getXxxx() 和 setXxxx()方法。

2.4 封裝之——this關鍵字

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

/*
    描述人的類
*/
class Person{
    private String name;//姓名
    private int age;//年齡
    //對外提供一個給age賦值的函式
    public void setAge(int age){
        //在給age賦值之前,我們可以通過函式傳遞過來的引數進行判斷
        if(age > 0 && age <= 130){
            age = age;
        }else{
            System.out.println("沒有這個年齡");
            //throw new RuntimeException("沒有這個年齡");
        }
    }
    public int getAge(){
        return age;
    }
    public void setName(String name){
        name = name;
    }
    public String getName(){
        return name;
    }
    public void say(){
        System.out.println("姓名:" + name + ",年齡:" + age);
    }
}

經過修改和測試,我們發現新的問題,成員變數賦值失敗了。也就是說,在修改了setXxx() 的形參變數名後,方法並沒有給成員變數賦值!這是由於形參變數名與成員變數名重名,基於就近原則,無法訪問到成員變數,從而賦值失敗。想要解決這個問題,我們可以使用使用this關鍵字來完成。

this的含義

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

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

this使用格式

this.成員變數名;

當成員變數和區域性變數重名時,可以使用 this 進行區分,程式碼如下:

/*
    描述人的類
*/
class Person{
    private String name;//姓名
    private int age;//年齡
    //對外提供一個給age賦值的函式
    public void setAge(int age){
        //System.out.println(this);
        //在給age賦值之前,我們可以通過函式傳遞過來的引數進行判斷
        if(age > 0 && age <= 130){
            this.age = age;
        }else{
            System.out.println("沒有這個年齡");
            //throw new RuntimeException("沒有這個年齡");
        }
    }
    public int getAge(){
        return this.age;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return this.name;
    }
    public void say(){
        System.out.println("姓名:"+this.name+",年齡:"+this.age);
    }
} 

小貼士:方法中只有一個變數名時,預設也是使用 this 修飾,可以省略不寫。

注意:在方法中,如果區域性變數和成員變數名字和型別都一致,那麼方法會先使用區域性變數。

總結:

this的應用:

1、this是用來記錄當前呼叫方法的物件的地址。

2、this可以區分成員變數和區域性變數。格式:this.成員變數名 ;

三、構造方法

當我們在建立一個物件的時候,如果需要物件建立好之後,就擁有一些屬於自己本身的特定資料,而不是後面通過程式去修改。那麼也就是說在建立物件的過程中,就應該把這些資料賦值完成。

需求:描述學生這類事物

/*
    建立一個學生的類
*/
class Student{
    //學生的姓名
     String name;
    //學生的年齡
     int age;
    //學生的住址
    String address;
    // 成員方法
    public void speak(){
        System.out.println("姓名:"+name+"年齡:"+age+"住址:"+address);
    }
}
public class  StudentTest{
    public static void main(String[] args) {
        //建立學生的物件
        Student s = new Student();
        //使用學生的物件student呼叫Student類中的方法
        s.speak();
    }
}

在StudentTest類中,建立完的Student物件所有的屬性都是預設的值, 而在現實生活中,每個學生入校之前 ,都會有自己的年齡、姓名、地址。那麼我們更希望在物件建立完之後就讓每個學生具備屬於自己的這些資料,而不是通過物件建立完之後,再通過其他的程式碼進行修改。

要解決這個問題,就必須使用Java中提供的構造方法完成。

3.1 構造方法介紹

什麼是構造方法?

構造方法:構造方法是用來初始化該物件,給物件的成員變數賦初始值,從而完成物件的建立。

在我們的程式中如果使用new 關鍵字,這時就是在建立物件,而我們在建立完物件的過程中,會呼叫當前這個類的構造方法來完成物件的建立。那麼既然在new物件的時候,會呼叫對應的構造方法,這時我們就可以在new物件的過程中,給構造方法傳遞引數,完成物件資訊的初始化動作。

一般方法的定義格式:

修飾符  返回值型別  方法名(引數列表....){
    方法體;
}

構造方法的定義格式:

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

構造方法定義的細節:

​ 1、構造方法是用來建立物件的,它不需要書寫返回值型別。

​ 2、構造方法的名字要求必須和當前的類名一致。因此構造方法的名稱書寫和類名一致。

​ 3、引數列表,可以有參構造和無參構造

學生類使用構造方法完成物件的初始化建立:

// 建立學生類
class Student{
    //學生的姓名
     String name;
    //學生的年齡
     int age;
    //學生的住址
    String address;
    //建立構造方法
    public Student(String name,int age,String address){//接收建立物件時傳遞的引數
        this.name = name;
        this.age = age;
        this.address = address;
    }
    // 成員方法
    public void speak(){
        System.out.println("姓名:"+name+"年齡:"+age+"住址:"+address);
    }
}
public class  StudentTest{
    public static void main(String[] args) {
        //建立學生的物件
        Student s = new Student("黑旋風",20,"上海");//建立物件時傳遞具體的資料
        //呼叫方法檢視輸出資料
        s.speak();
    }
}

構造方法的作用:

​ 在建立物件的過程中,根據new物件時傳遞的資料,對當前創建出來的物件中的成員變數進行指定資料的初始化。沒有構造方法就無法完成物件的建立。

3.2 預設構造方法

預設構造方法: 沒有學習構造方法之前,物件就可以正常建立。也就是說,在物件所屬的類中,本身存在構造方法,否則是不能建立物件的。只是這個構造方法是隱式存在的,我們看不見當。java編譯器編譯完某個java原始碼之後,生成的class檔案中,會自動生成一個沒有引數的構造方法即:預設構造方法。

因為類定義完成之後,類中就已經存在一個預設的構造方法了,這也是我們可以在沒有接觸構造方法之前依然能正常建立物件的原因。

class Teacher{
    //屬性
    String name;
    int age;
    public void say(){
        System.out.println("姓名:"  name+",年齡:" + age);
    }
}
public class TeacherTest{
    public static void main(String[] args) {
        Teacher t = new Teacher();
        System.out.println("Hello World!");
    }
}

上述的Teacher類編譯完之後,class檔案中就會生成一個 public Teacher(){} 構造方法。在new物件的時候,沒有傳遞任何資料,就會呼叫這個建構函式。

注意:定義類的時候,如果類中定義了有引數的構造方法,系統將不再提供預設的構造方法。所以一般在定義類的時候,如果手動定義了有參的構造方法,根據需求的不同,有時也需要定義無參的構造方法。這樣就會導致一個類中有多個構造方法以過載的方式存在。

程式碼演示:

// 描述人這類事物
class Person{
    // 成員變數
    String  name; // 姓名
    int age; //年齡
    char sex; // 性別

    // 無參構造方法
    public Person(){}

    // 給姓名 年齡 初始化的構造方法
    public Person( String name, int age,char sex ){
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
}
// 測試構造方法
public class PersonTest {
    public static void main(String[] args) {
        // 使用無參構造 建立物件
        Person p1 = new Person();

        // 使用有參構造 建立物件
        Person p2 = new Person("張三丰",120,'男');
    }
}

提問:

​ 在一個類中,方法可以以過載的方式出現,並且過載的方法之間也可以相互呼叫。那麼構造方法之間可以相互呼叫嗎? 如果在一個類中出現了一個普通的方法和構造方法名一致,會出現什麼情況?

程式碼演示:

class ConsDemo{
    // 成員變數
    int a;
    int b;
    int c;
    // 給a b兩個成員變數賦值的構造方法
    public ConsDemo(int a, int b) {
        this.a = a;
        this.b = b;
    }
    /*
        給 abc3個成員變數賦值的方法,因為給ab變數賦值的構造方法已經存在,
        那麼在給abc3個變數賦值的構造方法中能夠直接呼叫給ab賦值的構造呢?
        這樣就可以不用在給3個變數的構造方法中多寫一次賦值的程式碼,提高程式碼
        的複用性。
    */
    public ConsDemo(int a, int b, int c) {
        /*
            這裡在使用方法名直接呼叫的時候發現呼叫的不是給ab賦值的構造方法
            而是方法名和類名一致的普通方法。
        */
        // ConsDemo(a,b);

        /*
            想要完成構造方法之間的相互呼叫,可以使用this關鍵字完成,語法格式:
                this(引數列表);
                this語句呼叫構造方法,需要放在構造方法的第一行位置
                具體呼叫的是哪一個構造方法,取決於傳遞的引數,會呼叫和傳遞引數
                一致的構造方法
        */
        this(a,b);
        this.c = c;
        System.out.println("3個引數的構造方法執行....");
    }
    // 空參的構造方法
    public ConsDemo() {}
    // 方法名和類名一致的普通方法,僅為演示,開發中禁止這樣給普通方法命名。
    public void ConsDemo( int x , int y ){
        System.out.println( "Demo= " + (x + y) );
    }
}
public class StaticCodeDemo {
    public static void main(String[] args) {
       Demo d = new Demo(1,2,3);
       System.out.println(d.a + "..." + d.b + "..." + d.c);
    }
}

this作用總結

this表示的一個變數,指向的是一個引用地址,它有3個作用。

​ 1、this可以區分成員變數和區域性變數。

​ 2、this可以記錄當前方法是被哪個物件呼叫的,哪個物件呼叫的方法this就指向哪個物件

​ 3、this可以完成構造方法之間的呼叫:this(引數列表); 呼叫和傳遞引數一致的構造方法

​ this在呼叫構造方法的時候需要注意:

​ a) this語句必須放在第一行

​ b) 兩個構造方法之間不能相互呼叫,否則會導致記憶體的溢位。編譯時直接報錯。

3.3 構造方法和一般方法異同

1、它們的執行時間不同:

​ 構造方法是在建立物件的過程中執行。當物件建立完成了,構造方法就已經執行結束。

​ 一般方法是建立完物件之後,通過物件直接呼叫。如果在同一個類中,可以直接呼叫。

2、它們的呼叫次數不同:

​ 構造方法只有在new物件的時候會被呼叫,物件建立完成,不能手動的人為去呼叫構造方法。

​ 一般方法可以通過物件隨意的呼叫,沒有次數限制。

3、定義方式不同:

​ 一般方法: 修飾符 返回值型別 方法名( 引數列表……){ 方法體 }

​ 構造方法: 修飾符 類名( 引數列表…… ){ 方法體 }

3.4 注意事項

  1. 如果不定義造方法,編譯器會自動生成無引數構造方法。

  2. 如果定義了構造方法,編譯器將不再生成無引數構造方法。

  3. 構造方法是可以過載的,既可以定義引數,也可以不定義引數。

3.5 構造方法的執行和記憶體圖解

在建立物件的過程中構造方法的執行時機

​ 1、通過new 關鍵字建立物件,首先會在堆中分配物件的記憶體空間

​ 2、給物件所屬類中的所有成員變數進行預設的初始化賦值。

​ 3、呼叫和new物件時傳遞引數一致的構造方法。

​ 4、開始執行構造方法中的所有程式碼。

​ (構造方法中有隱式的3步:super()、成員變數顯示賦值、執行構造程式碼塊。後面課程會講到)

​ 5、構造方法中的所有程式碼執行結束,構造方法出棧,此時才表示當前這個物件建立完成。

​ 6、把物件的記憶體地址賦值給對應的引用變數。

這裡寫圖片描述

3.6 構造程式碼塊

程式碼塊是指被{}封裝起來的程式碼。

構造程式碼塊:定義在類的成員位置,直接使用{ },在{}中寫程式碼內容。

位置:類中,方法外,和方法並列,和先後位置無關。

執行時機:建立物件的時候執行一次。在構造方法隱式3步的最後一步。當構造程式碼塊執行結束之後,開始執行構造方法本身的程式碼內容。

格式:

public class ClassName{
  // 構造程式碼塊
  {
    // 執行語句 
  }
}

作用:所有構造方法中都需要執行的程式碼,書寫在構造程式碼塊中。

程式碼演示:

/*
    演示構造程式碼塊的執行
    需求: 在每一個構造方法中都需要執行一句: HelloWorld!
 */
class ConsCode{
    // 成員變數
    int a;
    int b;

    // 空參構造
    public ConsCode() {
        //System.out.println("HelloWorld!");
        System.out.println("我在構造程式碼塊執行結束之後執行...");
    }

    // 給變數a賦值的構造
    public ConsCode(int a) {
        this.a = a;
        System.out.println("我在構造程式碼塊執行結束之後執行...");
        //System.out.println("HelloWorld!");
    }

    // 給變數ab同時賦值的構造
    public ConsCode(int a , int b) {
        this.a = a;
        this.b = b;
        //System.out.println("HelloWorld!");
        System.out.println("我在構造程式碼塊執行結束之後執行...");
    }

    // 構造程式碼塊
    {
        System.out.println("HelloWorld!");
    }
}
public class ConstructorCodeDemo {
    public static void main(String[] args) {
        // 分別使用3個構造方法建立物件,並執行程式,顯示執行3遍HelloWorld!
        ConsCode c1 = new ConsCode();
        ConsCode c2 = new ConsCode(10);
        ConsCode c3 = new ConsCode(10,20);
    }
}

四、 標準程式碼——JavaBean

什麼是JavaBean

JavaBean是 Java語言編寫類的一種標準規範。JavaBean在任何官方網站都沒有一個正式的概念來定義它,只是我們根據它的特點來定義它為JavaBean,其實他就是一個普通的Java類,用來封裝資料的,它具備如下特點:

1、要求這個類必須擁有一個公共的空引數的構造方法,外界可以訪問到;

2、它如果有屬性(成員變數),全部必須私有;

3、對外提供公共的get或者set方法,並且方法的命名要遵守一定的規則。

舉例:

屬性 name —– 方法 getName setName (屬性首字母大寫,在前面加上 get和set )。

屬性 age —– 方法 getAge setAge 。

把這樣的一個類稱為一個JavaBean類。這個類在程式中主要用於封裝資料。

new一個物件,通過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());
    }
}