13.Java對象

轉載請保留原文鏈接: http://dashidan.com/article/java/basic/13.html

13.Java對象

Java是`OOP`(Object Oriented Programming)面向對象編程語言. java面向對象的三大特點: 封裝, 繼承和多態.

① Java中萬事萬物皆對象?


Java中萬事萬物皆對象?

這個說法的由來, 是因爲java中所有的類默認都繼承自`Object`類. 但這個說法是`不準確`的. 很多資料中這樣說, 曾經給我帶來過很大的困擾.

Java屬於`強類型語言`, `方法`作爲屬性存在於對象中, 不作爲獨立的對象存在. 所以說在Java中萬事萬物皆對象的說法是不準確的, 方法和基本類型變量都不是對象的概念.

在一些弱類型語言比如`javascript`, 方法也可以作爲`對象`的概念來使用, 作爲參數傳遞, 這一點和java是有區別的.

② 面向對象編程思想

提到面向對象, 不得不提到面向過程編程. 以C語言爲例, 每個方法作爲一個執行過程存在, 面向過程編程是一種面向計算機思維, 從計算機執行的角度思考編寫代碼的編程方式.

從面向過程編程的基礎上, 發展出來了一種新的編程思想, 面向對象編程, 來應對更復雜的應用場景.面向對象編程是一種面向的邏輯思維的編程方式, 將代碼中的通用性抽象出來, 模擬現實世界的歸類, 建立程序模型, 編寫代碼的過程. 對於程序員更加友好, 對於複雜度更高的程序更易於控制.


在生物分類學中, 分爲界門綱目科屬種, 按照生物的種羣和等級劃分.

貓:

動物界->脊索動物門->哺乳綱->哺乳綱->食肉目->貓科->貓屬->貓種

虎:

動物界->脊索動物門->哺乳綱->哺乳綱->食肉目->貓科->豹屬->虎種

同處於貓科, 是貓科子類中的一種, 具有貓科的全部屬性. 用Java面向對象的思想來編程可以這樣表達:
貓科作爲父類作爲子類, 繼承自貓科擁有父類貓科的全部屬性, 獨有的屬性寫在子類(貓類或虎類)中.

面向對象編程思想與人類的邏輯思維很接近.

③ 構造函數

構造函數是一種特殊的函數. 構造函數與類名相同, 可以有參數也可以無參數, 在對象被創建時自動執行.

1.構造函數的作用:

  • 在創建對象時完成完成對象數據成員的初始化

  • 爲對象數據成員開闢內存空間

  • 爲對象創建標識符

2.默認構造函數

當用戶沒有顯式的定義構造函數時, 編譯器會爲類生成一個默認的構造函數, 稱爲默認構造函數. 默認構造函數名與類名相同, 沒有參數.例:

public class Demo1 {
    // 默認構造函數
    public Demo1(){
    }}

默認構造函數

默認構造函數在代碼中看不到, 在編譯時自動生成.

3.構造函數的特點

無論是用戶自定義的構造函數還是默認構造函數都主要有以下特點:

  • 在對象被創建時自動執行.

  • 構造函數的函數名與類名相同.

  • 沒有返回值類型、也沒有返回值.

  • 構造函數不能被顯式調用.

4.構造函數的執行

通常是通過new關鍵字創建對象時執行.

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 構造函數的執行
 */public class Demo1 {

    public static void main(String args[]) {
        System.out.println("Demo1運行開始");
        TestStructure testStructure = new TestStructure(1, "大屎蛋教程網", "dashidan.com");
        int id = testStructure.getId();
        String url = testStructure.getUrl();
        String name = testStructure.getName();
        System.out.println("id " + id + " url " + url + " name " + name);
        System.out.println("運行完畢");
    }}/**
 * 測試構造方法類
 */class TestStructure {
    private int id;
    private String name;
    private String url;

    /**
     * 構造函數
     */
    public TestStructure(int id, String name, String url) {
        /** 構造函數初始化成員變量*/
        System.out.println("執行TestStructure構造函數");
        this.id = id;
        this.name = name;
        this.url = url;
        System.out.println("id " + id + " name " + name + " url " + url);
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getUrl() {
        return url;
    }}

輸出:

Demo1運行開始
執行TestStructure構造函數
id 1 name 大屎蛋教程網 url dashidan.com
id 1 url dashidan.com name 大屎蛋教程網
運行完畢

析構函數

c++除了`構造函數`,還有`析構函數`的概念. 析構函數也是一種特殊的成員函數. 它執行與構造函數相反的操作,通常用於撤消對象時的一些清理任務,如釋放分配給對象的內存空間等. Java中只有構造函數,而沒有析構函數.

④ 參數的值傳遞與引用傳遞

Java中方法中給變量賦值有2種方式: 值傳遞和引用傳遞.傳遞基本類型,String參數時是值傳遞. 傳遞引用類型參數時, 是引用傳遞.

1.值傳遞

傳遞的是值的拷貝. 也就是說傳遞參數時, 將值拷貝了一份, 與原有變量不再有關係. 基本類型字符串傳遞採用的是值傳遞.

示例代碼:

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 值傳遞
 */public class Demo2 {

    public static void main(String[] args) {
        int a = 1;
        String name = "大屎蛋";
        changeValue(a, name);
        /** 原值不變*/
        System.out.println("原值 a : " + a);
        /** 原字符串不變*/
        System.out.println("原值 name : " + name);
    }

    public static void changeValue(int num, String str) {
        num++;
        /** 輸出改變的值, 改變了參數的值, 原值不變*/
        System.out.println("changeValue num : " + num);
        /** 添加字符串, 改變了作爲參數傳進來的字符串的值, 原字符串不變*/
        str += "dashidan.com";
        System.out.println("changeValue str : " + str);
    }}

輸出:

changeValue num : 2
changeValue str : 大屎蛋dashidan.com
原值 a : 1
原值 name : 大屎蛋

2.引用傳遞

引用傳遞是指給索引對象賦值時, 是將對象內存的地址作爲參數傳遞. 新的對象與原有指向同一份數據. 當修改對象的變量時, 原有指向改數據的對象都發生改變.

代碼示例:

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 引用傳遞
 */public class Demo3 {
    public static void main(String[] args) {
        TestObject testObject1 = new TestObject();
        testObject1.setName("大屎蛋教程網");
        /** 對象賦值,這裏是引用傳遞,testObject1 將對象地址賦值給 testObject2, 他們指向同一個對象*/
        TestObject testObject2 = testObject1;
        System.out.println("修改前 testObject1 name " + testObject1.getName());
        System.out.println("修改前 testObject2 name " + testObject2.getName());
        /** 改變 testObject2 的name,testObject1的name也發生變化,因爲他們指向同一個數據*/
        testObject2.setName("dashidan.com");
        System.out.println("修改後 testObject1 name " + testObject1.getName());
        System.out.println("修改後 testObject2 name " + testObject2.getName());
    }}/**
 * 引用傳遞測試對象
 */class TestObject {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }}

輸出:

修改前 testObject1 name 大屎蛋教程網
修改前 testObject2 name 大屎蛋教程網
修改後 testObject1 name dashidan.com
修改後 testObject2 name dashidan.com

⑤ 對象的封裝

1.隱藏實現過程

封裝是把過程和數據隱藏起來,對數據的訪問只能通過已定義的接口. 面向對象計算始於這個基本概念. 通過private關鍵字可以將變量或者方法設置爲只有本類可以訪問, 其他類無法訪問該數據, 這樣便實現了數據的隱藏.


你有一隻寵物, 寵物的名字你不希望別人隨便改, 可以將寵物的名字設置爲私有private.

package com.dashidan.lesson5;/**
 * 大屎蛋教程網-dashidan.com
 *
 * Java教程進階篇: 5.Java對象(4):封裝
 */public class Pet {
    /** 私有屬性名字*/
    private String name;}

2.公開數據訪問接口

你這隻寵物的名字編程私有之後, 你希望給他改個名字, 可以將改名的方法設置爲公開方法. 這樣就能通過統一的開放接口來設置寵物的名字, 並且可以開放一個獲取名字的接口, 來獲得這個私有的屬性值.

示例代碼:

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 引用傳遞
 */public class Demo3 {
    public static void main(String[] args) {
        TestObject testObject1 = new TestObject();
        testObject1.setName("大屎蛋教程網");
        /** 對象賦值,這裏是引用傳遞,testObject1 將對象地址賦值給 testObject2, 他們指向同一個對象*/
        TestObject testObject2 = testObject1;
        System.out.println("修改前 testObject1 name " + testObject1.getName());
        System.out.println("修改前 testObject2 name " + testObject2.getName());
        /** 改變 testObject2 的name,testObject1的name也發生變化,因爲他們指向同一個數據*/
        testObject2.setName("dashidan.com");
        System.out.println("修改後 testObject1 name " + testObject1.getName());
        System.out.println("修改後 testObject2 name " + testObject2.getName());
    }}/**
 * 引用傳遞測試對象
 */class TestObject {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }}

輸出:

哈拆遷

⑥ 對象的繼承

Java繼承的本質是子類繼承父類, 擁有父類的屬性和方法, 是代碼複用基礎. 子類可以有自己的方法和屬性.

1.繼承方式

通過關鍵字extends.我們以貓和狗繼承是寵物, 寵物都有的行爲. 貓有吃魚的行爲, 狗有吃骨頭的行爲爲例實現這個繼承關係.

父類Pet:

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 對象的繼承
 * Pet類 作爲 父類
 */public class Pet {

    public void walk() {
        System.out.println("Pet walk");
    }

    public void eat() {
        System.out.println("Pet eat");
    }}

子類Cat:

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 對象的繼承
 * Cat 繼承自 Pet類
 */public class Cat extends Pet {
    public void eatFish() {
        System.out.println("Cat eatFish.");
    }

    @Override
    public void eat() {
        System.out.println("Cat eat");
    }}

子類Dog:

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 對象的繼承
 * Dog 繼承自 Pet類
 */public class Dog extends Pet {
    public void eatBone() {
        System.out.println("Dog eatBone.");
    }

    @Override
    public void eat() {
        System.out.println("Dog eat");
    }}

執行類

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 對象的繼承
 */public class Demo5 {

    public static void main(String[] args) {
        Cat cat = new Cat();
        Dog dog = new Dog();
        /** 調用父類的方法*/
        cat.walk();
        dog.walk();
        /** 調用子類的方法*/
        cat.eatFish();
        dog.eatBone();
    }}

輸出:

Pet walk
Pet walk
Cat eatFish.
Dog eatBone.

java單根繼承

java單根繼承是指單個類只能繼承一個父類, 在c++中是多根繼承, 一個類可以同時繼承多個父類.

3.對象靜態綁定與動態綁定

綁定指的是一個方法的調用與方法所在的類(方法主體)關聯起來. java綁定分爲靜態綁定和動態綁定.

  • 靜態綁定

靜態綁定也稱作前期綁定. java中的方法只有finalstaticprivate構造方法靜態綁定. 靜態綁定發生在編譯時, 靜態綁定用信息來完成.

  • 動態綁定

動態綁定(dynamic binding)也稱作後期綁定. java程序運行時根據具體對象的類型進行綁定. 動態綁定發生在運行時. 動態綁定則需要用對象信息來完成.

⑦ 對象的多態

1.多態的前提

當子類和父類存在同一個方法, 子類覆寫了父類的方法, 程序在運行時調用父類的方法還是子類覆寫的方法呢? Java的多態解決了整個問題. 運行時根據對象類型來判斷執行對應對象的方法.

多態的前提條件

1. 有繼承關係.

2. 子類重寫父類的方法.

3. 父類引用指向子類.


貓和狗都繼承自寵物類, 寵物有吃的行爲. 貓吃魚狗吃骨頭, 他們吃的行爲不一樣. 我們可以採用多態的方式來實現.

代碼示例:

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程進階篇: 7.Java對象(6):靜態綁定動態綁定與多態
 */public class Demo6 {
    public static void main(String[] args) {
        Cat cat = new Cat();
        Dog dog = new Dog();
        /**
         * cat 和 dog 覆寫了 父類的方法 eat 調用eat方法 時動態判斷對象類型
         * cat 對象 執行了 Cat中的 eat方法
         * dog 對象 執行了 Dog中的 eat方法
         */
        cat.eat();
        dog.eat();
    }}

輸出:

Cat eat
Dog eat

⑧ 引用當前對象與父對象

java中使用用this來引用當前對象, super引用當前對象的父對象.

1.this

使用this獲取當前對象的索引.

省略this

如果省略this. 系統會默認自動添加加上`this.`來指向當前對象.

以下代碼中的setName方法, 該方法中的傳入的參數name與成員變量name重名, 這時需要用this.name來表明引用成員變量.

代碼示例:

package com.dashidan.lesson8;/**
 * 大屎蛋教程網-dashidan.com
 *
 * Java教程進階篇: 8.Java對象(7):引用當前對象與父對象:this, super
 * Pet類
 */public class Pet {
    /**
     * 私有成員變量name
     */
    private String name;

    public String getName() {
        /** 默認可以省略this. 系統會自動加上this.*/
        return name;
    }

    public void setName(String name) {
        /** this.name 通過this來表明引用當前對象成員變量name*/
        this.name = name;
    }}

2.super

super引用當前對象的父對象. 需要訪問父類方法或者變量的時候, 可以使用super.來訪問.示例代碼:

cat類:

在調用Cat類的eat()方法時, 方法體中通過super.eat()調用了父類的eat()方法. 故父類的eat()方法被執行, Pet eat.輸出到控制檯.

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 對象的繼承
 * Cat 繼承自 Pet類
 */public class Cat extends Pet {
    public void eatFish() {
        System.out.println("Cat eatFish.");
    }

    @Override
    public void eat() {
        System.out.println("Cat eat");
    }

    /**
     *調用父類方法
     */
    public void petEat() {
        super.eat();
    }}

運行程序:

package com.dashidan.lesson8;/**
 * 大屎蛋教程網-dashidan.com
 *
 * Java教程進階篇: 8.Java對象(7):引用當前對象與父對象:this, super
 */public class Demo1 {

    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.eat();
    }}

輸出:

Pet eat.

3.this與super在構造函數中的使用

  • superthis均需放在構造方法內第一行. 否則編譯不通過.

每個子類構造方法的第一條語句, 都是隱式調用super(), 如果父類沒有這種形式的構造函數, 那麼在編譯的時候就會報錯.

  • thissuper不能同時出現在一個構造函數裏面.

因爲this必然會調用其它的構造函數, 其它的構造函數必然也會有super語句的存在. 所以在同一個構造函數裏面有相同的語句, 就失去了語句的意義, 編譯器也不通過.

`static`環境不能使用`this`, `super`

this和super都指的是對象, 是動態綁定, 所以都不能在`static`環境中使用(靜態綁定)包括: static變量, static方法, static語句塊.

⑨ 抽象方法與抽象類

1.抽象方法

抽象方法是一種特殊的方法, 只有聲明, 而沒有具體的實現. 抽象方法必須用abstract關鍵字進行修飾. 抽象方法的聲明格式爲:

public abstract void eat();

2.抽象類

如果一個類含有抽象方法,則稱這個類爲抽象類,抽象類必須在類前用abstract關鍵字修飾.只要包含抽象方法, 必須命名爲抽象類. 抽象類也可以包含普通方法.

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 抽象方法與抽象類
 * AbstractPet類 作爲 父類 抽象類
 */public abstract class AbstractPet {
    public abstract void sleep();}

Fish類作爲子類:

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 抽象方法與抽象類
 * Fish 類 繼承自 AbstractPet類
 */public class Fish extends AbstractPet {
    /**
     *實現抽象方法 sleep
     */
    @Override
    public void sleep() {
        System.out.println("Never sleep.");
    }}

運行類

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 抽象方法與抽象類
 */public class Demo9 {
    public static void main(String[] args) {
        Fish fish = new Fish();
        fish.sleep();
    }}

輸出:

Never sleep.

抽象類與普通類的區別:

  1. 抽象方法必須爲publicprotected(private不能被子類繼承,子類無法實現該方法), 缺省情況下默認爲public.

  2. 如果一個類繼承於一個抽象類, 則子類必須實現父類的抽象方法. 如果子類沒有實現父類的抽象方法, 則必須將子類也定義爲爲abstract類.

3.抽象類創建對象


抽象類不能用來創建對象嗎?

答案是:抽象類可以被用`new`的方式創建. 通過`匿名內部類`方式.

附帶真相的代碼:

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 抽象類創建對象
 */public class Demo10 {
    public static void main(String[] args) {
        AbstractPet abstractPet = new AbstractPet() {
            @Override
            public void sleep() {
                System.out.println("AbstractPet sleep");
            }
        };
        abstractPet.sleep();
    }}

輸出:

AbstractPet sleep.

雖然可以用new的方式創建抽象類, 但不建議這麼用, 這樣做失去了抽象的意義.

示例代碼中出現了`@Override`標籤.

Java中@Override標籤作用

⑩ 接口

接口interface在軟件工程中, 泛指供其他人調用的方法或者函數. 從這裏可以體會到Java語言設計者的初衷-對行爲的抽象.

1.接口的聲明

interface關鍵字聲明接口.接口中可以含有變量和方法. 但是要注意, 接口中的變量會被隱式地指定爲public static final變量. 接口中的方法必須都是抽象方法.

public interface Pet{...}

2.接口的實現

一個類實現特定的接口需要使用implements關鍵字. 需要實現該接口中定義的方法, 如果沒有全部實現, 需要將該類聲明爲abstract.

public class Bird implements IPet {...}

示例代碼:

IPet接口:

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 對象的繼承
 * 接口 IPet
 */public interface IPet {
    void eat();}

Bird類:

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 對象的繼承
 * 接口
 */public class Bird implements IPet {
    @Override
    public void eat() {
        System.out.println("Bird eat.");
    }}

運行類:

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 對象的繼承
 * 接口
 */public class Demo11 {
    public static void main(String[] args) {
        Bird bird = new Bird();
        bird.eat();
    }}

輸出:

Bird eat.

4.抽象類和接口的區別

  • 抽象類可以提供成員方法的實現細節, 而接口中只能存在public abstract方法.

  • 抽象類中的成員變量可以是各種類型, 而接口中的成員變量只能是public static final類型.

  • 接口中不能含有靜態代碼塊以及靜態方法, 而抽象類可以有靜態代碼塊和靜態方法.

  • 一個類只能繼承一個抽象類, 而一個類卻可以實現多個接口.

  • 抽象類作爲很多子類的父類, 它是一種模板式設計, 而接口是一種行爲規範.

  • 抽象類和接口所反映的設計理念是不同的, 抽象類所代表的是is-a的關係, 而接口所代表的是like-a的關係.

對象實例類型判斷

Java中的instanceof運算符是用來在運行時判斷對象是否是指定類的實例.子類實例instanceof父類, 返回ture. 因爲繼承關係, 子類的實例也是父類的實例.instanceof是二目運算符, 返回一個布爾值, 表示該對象是否是指定類的實例.用法:

boolean result = object instanceof class

示例代碼:

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 對象實例類型判斷
 */public class Demo12 {
    public static void main(String[] args) {
        Pet pet = new Pet();
        boolean isInstance = pet instanceof Pet;
        System.out.println("pet instanceof Pet: " + isInstance);
        Cat cat = new Cat();
        /** 子類也屬於父類的一個實例*/
        isInstance = cat instanceof Pet;
        System.out.println("cat instanceof Pet: " + isInstance);
    }}

輸出:

pet instanceof Pet: true
cat instanceof Pet: true

對象向上轉型和向下轉型

java中子類對象轉爲父類對象稱爲向上轉型, 反之稱爲向下轉型.

1.向上轉型

向上轉型時, 子類獨有的方法會遺失, 只保留父類擁有的方法. 如果子類覆寫了父類的方法則調用子類的這個方法. 向上轉型不用強制轉型.

2.向下轉型

父類引用的對象轉換爲子類類型稱爲向下轉型. 向下轉型需要強制轉型。

示例類型與轉型類型需要一致, 不一致會報錯.

示例代碼:

package com.dashidan.lesson12;/**
 * 大屎蛋教程網-dashidan.com
 * <p>
 * Java教程基礎篇: 12.Java對象
 * 對象向上轉型和向下轉型
 */public class Demo13 {
    public static void main(String[] args) {
        Cat cat = new Cat();
        /**
         * testUpcasting 需要傳入父類Pet實例,這裏傳入的是子類cat實例的引用
         * 自動向上轉型
         * 由於子類Cat覆寫了父類的cat()方法,執行子類的eat()方法
         */
        testUpCasting(cat);


        /** 父類對象的引用,指向子類實例*/
        Pet pet = new Cat();
        /**
         * testUpcasting 需要傳入父類Pet實例,這裏傳入的是父類Pet的引用
         * 強制向下轉型
         * 由於子類Cat覆寫了父類的cat()方法,執行子類的eat()方法
         */
        testDownCasting((Cat) pet);
    }

    /**
     * 測試向上轉型
     */
    public static void testUpCasting(Pet pet) {
        pet.eat();
    }

    /**
     * 測試向下轉型
     */
    public static void testDownCasting(Cat pet) {
        pet.eat();
    }}

輸出:

Cat eat.
Cat eat.

相關文章

 Java從入門到精通目錄


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章