day09_認識物件導向

java_pedestrian發表於2020-12-22

程式導向和麵向物件的區別

“程式導向”(Procedure Oriented):是一種以過程為中心的程式設計思想,簡稱 OP。“程式導向”也可稱之為“面向記錄”程式設計思想,就是分析出解決問題所需要的步驟,然後用函式把這些步驟一步一步實現,使用的時候一個一個依次呼叫就可以了。所以程式導向的程式設計方式關注點不在“事物”上,而是做這件事分幾步,先做什麼,後做什麼。例如:早晨起來:起床、穿衣、洗漱、上班,只要按照這個步驟來,就能實現“一天”的功能,整個這個過程中關注的是一步一步怎麼做,並沒有關注“人”這個事物。再例如:開門、調整座椅、繫好安全帶、踩離合、

啟動、掛檔、給油,只要按照這個步驟來,車就走了,顯然關注點還是在步驟上,只要實現每一步就行,整個過程並沒有關注“汽車”這個事物。

“物件導向”(Object Oriented):是一種以物件為中心的程式設計思想,簡稱 OO。隨著計算機技術的不斷提高,計算機被用於解決越來越複雜的問題。一切事物皆物件,通過物件導向的方式,將現實世界的事物抽象成物件。通過物件導向的方法,更利於用人理解的方式對複雜系統進行分析、設計與程式設計。同時,物件導向能有效提高程式設計的效率,通過封裝技術,可以像搭積木的一樣快速開發出一個全新的系統。物件導向將物件作為程式的基本單元,將程式和資料封裝其中,以提高軟體的重用性、靈活性和擴充套件性。

使用物件導向程式設計思想開發系統,在現代開發中會將物件導向貫穿整個過程,一般包括:OOA/OOD/OOP:

  • OOA:物件導向分析(Object-Oriented Analysis)
  • OOD:物件導向設計(Object-Oriented Design)
  • OOP:物件導向程式設計(Object-Oriented Programming)

程式導向和麵向物件有什麼關係呢?

  • 程式導向其實是最為實際的一種思考方式,就算是物件導向的方法也是含有程式導向的思想。可以說程式導向是一種基礎的方法。它考慮的是實際地實現。一般的程式導向是從上往下步步求精。物件導向主要是把事物給物件化,物件包括屬性與行為。當程式規模不是很大時,程式導向的方法還會體現出一種優勢。因為程式的流程很清楚,按著模組與函式的方法可以很好的組織。但對於複雜而龐大的系統來說,程式導向顯得就很無力了。物件導向的好處之一就是顯著的改善了軟體系統的可維護性。對於程式語言來說,基於 C 語言的程式設計是程式導向的,C++只能說一半程式導向一半物件導向,java 語言就是一門完全物件導向的程式語言。因為 java 底層是 C++語言實現的。當然,除了 java 語言之外,還有很多都是完全物件導向的程式語言,例如:C#、Python 等。

物件導向分析方法分析問題的思路和步驟:

  1. 根據問題需要,選擇問題所針對的現實世界中的實體。
  2. 從實體中尋找解決問題相關的屬性和功能,這些屬性和功能就形成了概念世界中的類。
  3. 把抽象的實體用計算機語言進行描述,形成計算機世界中類的定義。即藉助某種程式語言,把類構造成計算機能夠識別和處理的資料結構。
  4. 將類例項化成計算機世界中的物件。物件是計算機世界中解決問題的最終工具。

物件導向三大特徵

  • 封裝(Encapsulation)
  • 繼承(Inheritance)
  • 多型(Polymorphism)

物件導向的思想概述

軟體存在的意義就是為了解決現實世界當中的問題,它必然模擬現實世界,也就是說現實世界中有什麼,軟體中就對應有什麼。類(Class)和物件(Object)是物件導向的核心概念。

那麼類和物件分別是什麼,它們的區別和聯絡是什麼呢?

  • 類是對一類事物的描述,是抽象的、概念上的定義。
  • 物件是實際存在的該類事物的每個個體,因而也稱為例項(instance)。

例如:“沈騰”、“趙本山”、“宋丹丹”都是實際存在的物件,他們都屬於“笑星”類,類描述事物的共同特徵,那麼“笑星”類都有哪些共同特徵呢?笑星類都有姓名、性別、年齡等狀態資訊(屬性),他們還有一個共同的行為就是“演出”(方法)。但當具體到某個物件上之後,我們發現姓名是不同的,性別是不同的,年齡也是不同的,演出的效果也是不同的。所以我們在訪問姓名、性別、年齡的時候,必須先有笑星物件,通過真實存在的笑星物件去訪問他的屬性,包括“演出”的時候,只有“笑星”類是不行的,必須先有笑星物件,讓笑星物件去執行“演出”這個動作。

  • 通過類可以建立物件,物件又被稱為例項(instance),這個過程也可以稱為例項化
  • 物件導向程式設計的重點是類的設計 
  • 類的設計,其實就是類的成員的設計

通過以上的描述,我們得知:類 = 屬性 + 方法,而屬性描述的是狀態,方法描述的是行為動作。行為動作以方法的形式存在,那屬性以什麼形式存在呢?例如:姓名、性別、年齡,

變數用來儲存資料。不錯,物件的屬性以變數形式存在,並且這裡所說的變數是我們之前提過的“成員變數當中的例項變數”。為什麼是例項變數呢,例項變數就是物件級別的變數,這樣的變數要求必須先存在物件,通過物件才能訪問。例如:“中國人”這個類,有一個屬性是“身份證號”,每一箇中國人的“身份證號”都是不一樣的,所

以身份證號必須使用一個真實存在的“中國人物件”來訪問。不能使用“中國人”這個類去訪問身份證號。一個類可以例項化 N 多個物件,假設通過“中國人”這個類建立了 100 個“中國人物件”,那麼“身份證號”必然會有 100 個例項變數空間去儲存。

總結一下常見的類的成員

  • 現實中的屬 性:對應類中的成員變數
  • 現實中的行 為對應類中的成員方法

類的語法格式

建立Java自定義類步驟如下:

  1. 定義類(考慮修飾符、類名)
  2. 編寫類的屬性(考慮修飾符、屬性型別、屬性名、初始化值)
  3. 編寫類的方法(考慮修飾符、返回值型別、方法名、形參等)

程式碼示例

package demo;

/*
 * 要求:
 * (1)建立Person類的物件,設定該物件的name、age和sex屬性,呼叫study方法,
 * 輸出字串“studying”,呼叫showAge()方法顯示age值,
 * 呼叫addAge()方法給物件的age屬性值增加2歲。
 *
 */
public class Person {
    //成員變數
    String name;
    int age;
    /**
     * sex:1 表明是男性
     * sex:0 表明是女性
     */
    int sex;

    //成員方法
    public void study(){
        System.out.println("studying");
    }

    public void showAge(){
        System.out.println("age:" + age);
    }

    public int addAge(int i){
        age += i;
        return age;
    }
}

類的訪問機制:

  • 在一個類中的訪問機制類中的方法可以直接訪問類中的成員變數。(例外:static方法訪問非static,編譯不通過。)
  • 在不同類中的訪問機制:先建立要訪問類的物件,再用物件訪問類中定義的成員。

物件的建立和使用

                                              

類定義之後,就可以使用類這個“模板”來創造“物件”了,一個類是可以建立多個物件的哦!怎麼建立呢,語法是什麼?其實語法格式很簡單:new 類名(),這樣就可以完成物件的建立了。俗話說,你想要什麼 java 都可以給你,想要啥你就 new啥。建立物件完整語法:

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

使用“物件名.物件成員”的方式訪問物件成員(包括屬性和方法)

使用上面自定義的類,建立物件

package demo;

public class PersonTest {
    public static void main(String[] args) {
        // 建立物件p1
        Person p1 = new Person();
        //給變數賦值
        p1.name = "Tom";
        p1.age = 18;
        p1.sex = 1;

        //呼叫成員方法
        p1.study();//studying
        p1.showAge();//age:18
        //呼叫成員方法,把方法的返回值賦值給變數newAge
        int newAge = p1.addAge(2);
        System.out.println(p1.name + "的新年齡為:" + newAge);//Tom的新年齡為:20

        System.out.println(p1.age);//20

        //建立物件p2
        Person p2 = new Person();
        //呼叫常用方法
        p2.showAge();//age:0
        p2.addAge(10);
        p2.showAge();//age:10

        p1.showAge();//age:20
    }
}

以上程式碼最初接觸的時候,大家肯定會感覺非常陌生,這也是正常的,Person p1 = new Person();實際上和 int i = 10 是類似的,對於 int i = 10 來說,int 是一種基本資料型別,i 是變數名,10 是 int 型別的字面量。那對於 Person p1 = new Person() 來說,其中 Person 是一種引用資料型別,p1 是變數名,new Person()執行之後是一個 Person 型別的物件。大家要注意了,java 語言當中凡是使用 class 關鍵字定義的類都屬於引用資料型別,類名本身就是這種引用資料型別的型別名。

注意:

  • 如果建立了一個類的多個物件,對於類中定義的屬性(成員變數),每個物件都擁有各自的一套副本,且互不干擾。
  • 在 java 語言當中,當例項變數沒有手動賦值,在建立物件的時候,也就是說在 new 的時候,系統會對例項變數預設賦值,它們的預設值請參考下表:

  

物件建立和使用的深層次解密

為了更好的理解上面的程式,先來看看java 虛擬機器是如何管理它的記憶體的,請看下圖:

 

程式計數器:

  • 概念:可以看做當前執行緒所執行的位元組碼的行號指示器。
  • 特點:執行緒私有的記憶體

java 虛擬機器棧(重點):

  • 概念:描述的是 java 方法執行的記憶體模型。(每個方法在執行的時候會建立一個棧幀,用於儲存區域性變數表,區域性變數表存放了編譯期可知長度的各種基本資料型別(boolean、byte、char 、 short 、 int 、 float 、 long 、double)、物件引用(reference型別,它不等同於物件本身,是物件在堆記憶體的首地址運算元棧,動態連結,方法出口等資訊。每個方法從呼叫直至完成的過程,就對應一個棧幀從入棧到出棧的過程。)
  • 特 點 : 線 程 私 有, 生 命 週期 和 線 程 相同 。 這 個 區域 會 出 現 兩種 異 常 :StackOverflowError 異常: 若線 程請求 的深 度大於 虛擬 機所允 許的 深度 。OutOfMemoryError異常:若虛擬機器可以動態擴充套件,如果擴充套件是無法申請到足夠的記憶體。

本地方法棧:

  • 概念:它與虛擬機器棧所發揮的作用是相似的,區別是 java 虛擬機器棧為執行 java 方法服務,而本地方法棧是為本地方法服務。
  • 特點:執行緒私有,也會丟擲兩類異常:StackOverflowError 和 OutOfMemoryError。

java 堆(重點):

  • 概念:是被所有執行緒共享的一塊區域,在虛擬機器啟動時建立。
  • 特點:執行緒共享,存放的是物件例項(所有的物件例項和陣列),GC 管理的主要區域。可以處於物理上不連續的記憶體空間。這一點在Java虛擬機器規範中的描述是:所有的物件例項以及陣列都要在堆上分配。

⑤ 方法區(重點):

  • 概念:儲存已被虛擬機器載入的類資訊、常量、靜態變數,即時編譯器編譯後的程式碼等資料。
  • 特點:執行緒共享的區域,丟擲異常 OutOfMemory 異常:當方法區無法滿足記憶體分配需求的時候。

以上所描述內容,有看得懂的,也有看不懂的,例如:執行緒、本地方法等,這個需要大家在學習後面內容之後,返回來再看一看,那個時候你就全部明白了。針對於目前來說,大家必須要知道 java 虛擬機器有三塊主要的記憶體空間,分別是“虛擬機器棧(後面簡稱棧)”、“方法區”、“堆區”,方法區儲存類的資訊,棧中儲存方法執行時的棧幀以及區域性變數,堆區中主要儲存 new 出來的物件,以及物件內部的例項變數。其中垃圾回收器主要針對的是堆記憶體,方法區中最先有資料,因為程式執行之前會先進行類載入。棧記憶體活動最頻繁,因為方法不斷的執行並結束,不斷的進行壓棧彈棧操作。將目前階段需要掌握的記憶體空間使用一張簡單的圖表示出來,這個圖是大家需要掌握的:

大概瞭解了 java 虛擬機器記憶體分配之後,來看看以下程式碼在執行過程中,記憶體是如何變化的:

package demo;

public class StudentTest {
    public static void main(String[] args) {
        int i = 10;
        Student s1 = new Student();
    }
}

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

第一步:進行類載入 

第二步: main 方法呼叫,給 main 方法分配棧幀(壓棧)

第三步:執行 int i = 10,區域性變數

第四步:執行 new Student(),在堆中建立物件,同時初始化例項變數

第五步:將堆區中學生物件的記憶體地址賦值給區域性變數 s1 

上圖所描述記憶體圖有些地方為了幫助大家更好的理解,有些位置畫的不是很精確,隨著後面內容的學習我們再進一步修改,目前上圖已經夠大家用了。上圖中 i 變數和 s1 變數都是區域性變數,都在棧記憶體當中,只不過 i 變數是基本資料型別 int,而 s1 變數是引用資料型別 Student。上圖中堆區當中的稱為“物件”,該“物件”內部 no、name、age、sex 都是例項變數/屬性,這些變數在 new物件的時候初始化,如果沒有手動賦值,系統會賦預設值。上圖堆區中“物件”建立完成之後,該物件在堆區當中的記憶體地址是:0x1111,程式中的“=”將 0x1111 這個堆記憶體地址賦值給 s1 變數,也就是說 s1 變數儲存了堆記憶體物件的記憶體地址,我們對於這種變數有一種特殊的稱呼,叫做“引用”。也就是說對於 Student s1 = new Student()程式碼來說,s1 不是物件,是一個引用,物件實際上是在堆區當中,s1 變數持有這個物件的記憶體地址。java 中沒有指標的概念(指標是 C 語言當中的機制),所以 java 程式設計師沒有權利直接操作堆記憶體,只能通過“引用”去訪問堆記憶體中的物件,例如:s1.no、s1.name、s1.sex、s1.age。訪問一個物件的記憶體,其實就是訪問該物件的例項變數,而訪問例項變數通常包括兩種形式,要麼就是讀取資料,要麼就是修改資料,例如:System.out.println(s1.no)這就是讀取資料,s1.no= 100 這就是修改資料。

例項變數執行賦值運算之後的記憶體圖 

建立多個物件的記憶體結構圖 

匿名物件

我們也可以不定義物件的控制程式碼,而直接呼叫這個物件的方法。這樣的物件叫做匿名物件。如:

new Person().shout();

使用情況

  • 如果對一個物件只需要進行一次方法呼叫,那麼就可以使用匿名物件。
  • 我們經常將匿名物件作為實參傳遞給一個方法呼叫。

相關文章