1. 程式人生 > 實用技巧 >Java基礎之面向物件

Java基礎之面向物件

1.面向物件

面向物件的三大特性:封裝性、繼承性、多型性

封裝

1.方法就是一種封裝

2.關鍵字private也是一種封裝

封裝就是將一些細節資訊隱藏起來,對於外界不可見

package com.dcits.day05.demo03;
​
public class Demo02Method {
    public static void main(String[] args) {
        int[] array = {5,15,25,35,111};
        int max = getMax(array);
        System.out.println(max);
​
    }
    
public static int getMax(int[] array) { int max = array[0]; for (int i = 0; i < array.length; i++) { if (array[i] > max) { max = array[i]; } } return max; } }

private關鍵字的作用以及使用

一旦使用了private進行修飾,那麼本類中可以隨意訪問,但是超出了本類的範圍就不能再訪問了

可以通過間接訪問的方式,自定義一對兒Getter/Setter方法,必須叫 setXxx 或者是 getXxx

對於Getter來說,不能有引數,返回值型別和成員變數對應

對於Setter來說,不能有返回值,引數型別和成員變數對應

// Person類
package com.dcits.day05.demo03;
​
public class Person {
    String name;
    private int age;
    public void show() {
        System.out.println("我叫:" + name  +",今年" + age);
    }
    
public void setAge(int num) { if (num < 100 && num >=9) { age = num; } else { age = 0; System.out.println("資料不合理"); } } public int getAge() { return age; } } ​ // 呼叫 package com.dcits.day05.demo03; ​ public class Demo03Person { public static void main(String[] args) { Person person = new Person(); person.name = "趙麗穎"; // person.age = 18; 當成員變數被private修飾的時候,外部無法訪問,只能通過間接的方式Setter,Getter person.setAge(-20); person.show(); } ​ }

布林型別的特殊情況

//
package com.dcits.day05.demo03;
​
public class Student {
    private String name;
    private int age;
    private boolean male;
​
    public void setMale(boolean b){
        male = b;
    }
    public boolean isMale() {
        return male;
    }
​
    public void setName(String str){
        name = str;
    }
    public String getName() {
        return name;
    }
    public void setAge(int num) {
        age  = num;
    }
    public int getAge() {
        return age;
    }
}
// 呼叫
package com.dcits.day05.demo03;
​
public class Demo04Student {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.setName("alex");
        stu.setAge(10);
        stu.setMale(true);
​
        System.out.println(stu.getName());
        System.out.println(stu.getAge());
        System.out.println(stu.isMale());
    }
}
​

this關鍵字的使用

當方法的區域性變數和類的成員變數重名的時候,根據就近原則,優先使用區域性變數,如果需要訪問 本類當中的成員變數,需要使用格式:this.成員變數

通過誰呼叫的方法,誰就是this

//
package com.dcits.day05.demo04;
​
public class Person {
    String name;
    public void sayHi(String name) {
        System.out.println(this.name + "你好,我是" + name);
    }
}
// 呼叫
package com.dcits.day05.demo04;
​
public class Demo01Person {
    public static void main(String[] args) {
        Person person = new Person();
        person.name = "6666";
        person.sayHi("777");
    }
}

構造方法

構造方法是專門用來建立物件的方法,當我們使用關鍵字new來建立物件的時候,其實就是在呼叫構造方法

注意事項:

  • 構造方法的名稱必須和所在的類名稱完全一樣,就連大小寫也要完全一樣

  • 構造方法不要寫返回值型別,連void都不要寫

  • 構造方法不能return一個具體的返回值

  • 如果沒有編寫任何構造方法,那麼編譯器預設會贈送一個構造方法,沒有引數、方法體什麼都不做

  • 一旦編寫了一個構造方法,那麼編譯器就不再贈送

  • 構造 方法也是可以過載的

//
package com.dcits.day05.demo04;
​
public class Student {
    private String name;
    private int age;
    public Student(String name,int age) {
        this.name = name;
        this.age = age;
        System.out.println("有引數的構造方法!!");
    }
    public Student() {
        System.out.println("無引數的構造方法執行啦!!");
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setAge(int age) {
        this.age = age;
​
    }
    public int getAge() {
        return age;
    }
}
// 呼叫
package com.dcits.day05.demo04;
​
public class Demo02Student {
    public static void main(String[] args) {
        Student stu = new Student();
        Student stu1 = new Student("aaa",20);
        stu1.setAge(23);
        System.out.println(stu1.getAge());
        System.out.println(stu1.getName());
​
    }
​
}
​

區域性變數和成員變數

  1. 定義的位置不一樣

    • 區域性變數:在方法的內部

    • 成員變數:在方法的外部,直接寫在類當中

  2. 作用範圍不一樣

    • 區域性變數:只有方法 才可以使用,出了方法就不能再使用了

    • 成員變數:整個類都可以通用

  3. 預設值不一樣

    • 區域性變數:沒有預設值,如果想要用,必須手動賦值

    • 成員變數:如果沒有賦值,會有預設值,規則和陣列一樣

  4. 記憶體的位置不一樣

    • 區域性變數:位於棧記憶體

    • 成員變數:位於堆記憶體

  5. 生命週期不一樣

    • 區域性變數:隨著方法進棧而誕生,隨著方法出棧而消失

    • 成員變數:隨著物件建立而誕生,隨著物件被垃圾回收而消失

標準類的組成部分

一個標準的類通常擁有下面的四個部分:

  1. 所有的成員變數都要使用private關鍵字來修飾

  2. 為每一個成員變數編寫一對Getter、Setter方法

  3. 編寫一個無引數的構造方法

  4. 編寫一個全引數的構造方法

繼承

繼承是多型的前提,如果沒有整合,就沒有多型

繼承解決的主要問題就是:共性抽取

定義類時的兩個注意事項:

  • 成員變數時直接定義在類當中的,在方法外面

  • 成員方法不要寫static關鍵字

繼承的格式:

// 父類
package com.dcits.day08.demo01;
​
public class Employee {
    public void method() {
        System.out.println("父類執行!");
    }
}
// Teacher子類
package com.dcits.day08.demo01
​
public class Teacher extends Employee{
​
}
// Assistant子類
package com.dcits.day08.demo01;
​
public class Assistant extends Employee {
}
// 呼叫
package com.dcits.day08.demo01;
​
public class Demo01Extends {
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        Assistant assistant = new Assistant();
        teacher.method();
        assistant.method();
    }
}

在父子類的繼承關係當中,如果成員變數重名,則建立子類時,訪問有兩種方式:

  • 直接通過子類物件訪問成員變數

    • 等號左邊是誰,就優先使用誰,沒有則向上找

  • 間接通過成員方法訪問成員變數

    • 該方法屬於誰,就優先用誰,沒有則向上找

// 父類
package com.dcits.day08.demo02;
​
public class Fu {
    int numFu = 10;
    int num = 100;
    public void methodFu() {
        System.out.println(num);
    }
}
​
// 子類
package com.dcits.day08.demo02;
​
public class Zi extends Fu {
    int numZi = 20;
    int num = 200;
    public void methodZi() {
        System.out.println(num);
    }
}
​
// 呼叫
package com.dcits.day08.demo02;
​
public class Demo01ExtendsField {
    public static void main(String[] args) {
        Fu fu = new Fu();
        System.out.println(fu.numFu);
​
        Zi zi =  new Zi();
        System.out.println(zi.numFu);
        System.out.println(zi.numZi);
​
        // 當父類與子類的成員變數重名的時候
        System.out.println(zi.num);
//        System.out.println(zi.abc);
        //
        zi.methodZi();
        zi.methodFu();
    }
​
​
}

區分子類方法中的三種重名變數

  • 直接使用的方法中的變數

  • this.變數名:呼叫本類中的成員變數

  • super.變數名:呼叫父類中的成員變數

// 父類
package com.dcits.day08.demo03;
​
public class Fu {
    int num = 10;
}
// 子類
package com.dcits.day08.demo03;
​
public class Zi extends Fu {
    int num = 20;
    public void method() {
        int num = 30;
        System.out.println(num); // 30 區域性變數
        System.out.println(this.num); // 20 本類的成員變數
        System.out.println(super.num); // 10 父類的成員變數
    }
}
// 呼叫
package com.dcits.day08.demo03;
​
public class Demo01ExtendsField {
    public static void main(String[] args) {
        Zi zi = new Zi();
        zi.method();
    }
}
​

繼承中成員方法的訪問特點

在父子類的繼承關係當中,建立子類物件,訪問成員方法的規則:建立的物件是誰,就優先使用誰,如果沒有則向上找

注意:無論是成員方法還是成員變數,如果沒有都是向上找父類u,絕不會向下找子類

// 父類
package com.dcits.day08.demo04;
​
public class Fu {
​
    public void methodFu() {
        System.out.println("父類中的方法執行啦!");
    }
​
    public void method() {
        System.out.println("父類重名執行啦!");
    }
}
​
// 子類
package com.dcits.day08.demo04;
​
public class Zi extends Fu {
    public void methodZi() {
        System.out.println("子類中的方法執行啦!");
    }
    public void method() {
        System.out.println("子類重名執行啦!");
    }
}
​
// 呼叫
package com.dcits.day08.demo04;
​
public class Zi extends Fu {
    public void methodZi() {
        System.out.println("子類中的方法執行啦!");
    }
    public void method() {
        System.out.println("子類重名執行啦!");
    }
}

繼承方法中的覆蓋重寫

重寫:方法的名稱一樣,引數列表也一樣,覆蓋、覆寫

過載:方法的名稱一樣,引數列表不一樣

方法的覆蓋重寫特點:建立的是子類物件,則優先用子類方法

方法覆蓋重寫的注意事項:

  1. 必須保證父子類之間的方法名稱相同、引數列表也相同

  2. @Override:寫在方法前面,用來檢測是不是有效的正確覆蓋重寫,這個註釋就算不寫,只要滿足要求,也是正確的覆蓋重寫

  3. 子類方法的返回值必須小於等於父類方法的返回值範圍。Object類是所有類的父類

  4. 子類方法的許可權必須大於等於父類方法的許可權修飾符。public > protected > (default) > private 注意:(default)不是關鍵字default,而是什麼都不寫,留空

繼承中方法的覆蓋重寫應用場景

// 父類
package com.dcits.day08.demo06;
// 本來的老款手機
public class Phone {
    public void   call() {
        System.out.println("打電話");
    }
​
    public void send() {
        System.out.println("發簡訊");
    }
​
    public void show() {
        System.out.println("顯示號碼");
    }
}
​
// 子類
package com.dcits.day08.demo06;
// 上市的新款手機
public class NewPhone extends Phone {
    @Override
    public void show() {
//        System.out.println("顯示號碼");
        super.show();
        System.out.println("顯示姓名");
        System.out.println("顯示頭像");
    }
}
​
// 呼叫
package com.dcits.day08.demo06;
​
public class Demo01Phone {
    public static void main(String[] args) {
        Phone phone = new Phone();
        phone.call();
        phone.send();
        phone.show();
        System.out.println("===========");
​
        NewPhone newPhone = new NewPhone();
​
        newPhone.call();
        newPhone.send();
        newPhone.show();
    }
}

繼承中構造方法的訪問特點

  1. 子類構造方法當中有一個預設隱含的 "super()" 呼叫,所以一定是先呼叫的父類構造,後執行的子類構造

  2. 子類構造可以通過super關鍵字來呼叫父類過載構造

  3. super的父類構造呼叫,只能是第一個語句 ,不能一個子類構造呼叫多次super構造

  4. 子類必須呼叫父類構造方法,不寫贈送super() 寫了則用寫的指定該的super呼叫,super只能有一個,還必須是第一個

super關鍵字的用法(訪問父類的內容):

  1. 在子類的成員方法中,訪問父類的成員變數

  2. 在子類的成員方法中,訪問父類的成員方法

  3. 在子類的構造方法中,訪問父類的構造方法

// 父類
package com.dcits.day08.demo08;
​
public class Fu {
    int num = 10;
    public void method(){
        System.out.println("父類方法");
    }
}
​
// 子類
package com.dcits.day08.demo08;
​
public class Zi extends Fu {
    int num = 20;
    public Zi(){
        super();
    }
    public void methodZi() {
        System.out.println(super.num); // 父類的num
    }
    public void method(){
        super.method();
        System.out.println("子類方法");
    }
}
​

this關鍵字的三種使用方法(訪問本類的內容)

  1. 在本類的成員方法中,訪問本類的成員變數

  2. 在本類的成員方法中訪問本類的另一個成員方法

  3. 在本類的構造方法中,訪問本類的另一個構造方法

注意:

  • 在第三種用法中要注意:this(..)呼叫必須是構造方法的一個語句,唯一一個

  • super和this兩種構造呼叫,不能同時使用

// 父類
package com.dcits.day08.demo09;
​
public class Fu {
    int num = 30;
}
// 子類
package com.dcits.day08.demo09;
​
public class Zi extends Fu {
    int num = 20;
    public Zi(){
        this(123); // 本類的無參構造,呼叫本類的有參構造
//        this(1,2)
    }
    public Zi(int n){
​
    }
    public Zi(int n,int m){
​
    }
    public void showNum(){
        int num = 10;
        System.out.println(num);
        System.out.println(this.num); // 本類中的成員變數
        System.out.println(super.num); // 父類中的 成員變數
    }
    public void methodA() {
        System.out.println("AAA");
    }
    public void methodB() {
        methodA();
        this.methodA();
        System.out.println("BBB");
    }
}

this、super的關鍵字圖解

Java語言繼承的三個特點:

  • 一個類的 直接父類只能有唯一一個

  • Java語言可以多繼承

  • 一個子類的直接父類是唯一的,但是一個父類可以擁有很多個子類

多型

多型的定義以及基本使用

extends繼承或者implements實現,是多型的前提。

小明這個物件既有學生形態,也有人類形態。一個物件擁有多種形態,這就是:物件的多型性

程式碼當中體現多型性,其實就是一句話:父類引用指向子類物件

格式:

  • 父類名稱 物件名 = new 子類名稱()

  • 介面名稱 物件名 = new 實現類名稱()

// 父類
package com.dcits.day09.demo04;
​
public class Fu {
    public void method(){
        System.out.println("父類方法");
    }
    public void methodFu(){
        System.out.println("父類特有方法");
    }
}
// 子類
package com.dcits.day09.demo04;
​
public class Zi extends Fu {
    @Override
    public void method() {
        System.out.println("子類方法");
    }
}
// 呼叫
package com.dcits.day09.demo04;
​
​
​
public class Demo01Multi {
    public static void main(String[] args) {
        // 使用多型的寫法
        // 左側父類的引用指向右側子類的物件
        Fu obj = new Zi();
        // new 的是誰就呼叫誰 的方法
        obj.method(); // 子類方法
        obj.methodFu(); // 父類特有方法
    }
}
​

多型中成員變數的使用特點

訪問成員變數的兩種方式:

  1. 直接通過物件名稱訪問成員變數,看等號左邊是誰,優先用誰,沒有則向上找

  2. 間接通過成員方法訪問成員變數,看該方法屬於誰,優先用誰,沒有則像上找

// 父類
package com.dcits.day09.demo05;
​
public class Fu {
    int num = 10;
​
    public void showNum(){
        System.out.println(num);
    }
}
// 子類
package com.dcits.day09.demo05;
​
public class Zi extends Fu {
    int num = 20;
​
    int age = 16;
​
    @Override
    public void showNum() {
        System.out.println(num);
    }
}
// 呼叫
package com.dcits.day09.demo05;
​
public class Demo01MultiField {
    public static void main(String[] args) {
       Fu obj = new Zi();
        System.out.println(obj.num); // 父類中的10
        System.out.println("=====================");
        obj.showNum(); // 子類沒有覆蓋重寫,就是父類中的num,一旦子類重寫後就是子類中的num
    }
}
​

多型中成員方法的使用特點

在多型的程式碼當中,成員方法的優先訪問規則是:看new的是誰,就優先用誰,沒有則向上找

注意:編譯看左邊,執行看右邊

對比一下:

  • 成員變數:編譯看左邊,執行還看左邊

  • 成員方法:編譯看左邊,執行看右邊

// 父類
package com.dcits.day09.demo05;
​
public class Fu {
    int num = 10;
​
    public void showNum(){
        System.out.println(num);
    }
​
    public void method(){
        System.out.println("父類方法");
    }
    public void methodFu(){
        System.out.println("父類特有方法");
    }
}
// 子類
package com.dcits.day09.demo05;
​
public class Zi extends Fu {
    int num = 20;
​
    int age = 16;
​
    @Override
    public void showNum() {
        System.out.println(num);
    }
​
    @Override
    public void method() {
        System.out.println("子類方法");
    }
    public void methodZi(){
        System.out.println("子類特有方法");
    }
}
​
// 呼叫
package com.dcits.day09.demo05;
​
public class Demo01MultiField {
    public static void main(String[] args) {
       Fu obj = new Zi();
       obj.method(); // 父子都有,優先使用子類
       obj.methodFu(); // 子類沒有,父類有,向上找到父類
        // 編譯看左,左邊是Fu,沒有methodZi方法,所以編譯報錯
//        obj.methodZi(); // 錯誤寫法
//        System.out.println(obj.num); // 父類中的10
//        System.out.println("=====================");
//        obj.showNum(); // 子類沒有覆蓋重寫,就是父類中的num,一旦子類重寫後就是子類中的num
    }
}
​

使用多型的好處

物件的向上轉型

物件的向上轉型,其實就是多型寫法

格式: 父類名稱 物件名 = new 子類名稱()

含義:右側建立一個子類物件,把它當作父類來看待使用

注意事項:

  • 向上轉型一定是安全的,從小範圍轉到了大範圍

  • 但是有一個弊端:物件一旦向上轉型為父類,那麼就無法呼叫子類原本的特有內容

類似於:double num = 100 正確 int----double 自動型別轉換

// 父類
package com.dcits.day09.demo06;
​
public abstract class Animal {
    public abstract void eat();
}
// 子類
package com.dcits.day09.demo06;
​
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("貓吃魚。。。");
    }
}
​
// 呼叫
package com.dcits.day09.demo06;
​
public class Demo01Main {
    public static void main(String[] args) {
        Animal animal = new Cat();
        animal.eat();
    }
​
}

物件的向下轉型

物件的向下轉型,其實是一個還原動作

格式:子類名稱 物件名 = (子類名稱) 父類物件

含義:將父類物件,還原成為原本的子類物件

注意事項:

  • 必須保證物件本來建立的時候,就是貓,才能向下轉型成為貓

  • 如果物件建立的時候本來不是貓,現在非要向下轉型成為貓,就會報錯

類似於:int num = (int) 10.0 正確 int num = (int) 10.5 錯誤,發生精度損失

// 父類
package com.dcits.day09.demo06;
​
public abstract class Animal {
    public abstract void eat();
}
// 貓子類
package com.dcits.day09.demo06;
​
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("貓吃魚。。。");
    }
​
    public void catchMouse() {
        System.out.println("貓抓老鼠!!");
    }
}
​
// 狗子類
package com.dcits.day09.demo06;
​
public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃shit");
    }
​
    public void watchMouse() {
        System.out.println("狗看家!!");
    }
}
​
// 呼叫
package com.dcits.day09.demo06;
​
public class Demo01Main {
    public static void main(String[] args) {
​
        Animal animal = new Cat(); // 物件的向上轉型
        animal.eat();
​
        // 向下轉型
        Cat cat = (Cat) animal;
        cat.catchMouse(); // 貓抓老鼠!!
// 下面是錯誤的向下轉型
        // 本來new的時候是一隻貓,現在非要轉成狗
        // java.lang.ClassCastException
        Dog dog = (Dog) animal; // 錯誤寫法
​
    }
​
}
​

用instanceof 關鍵字進行型別判斷

package com.dcits.day09.demo06;
​
public class Demo01Instanceof {
    public static void main(String[] args) {
        Animal animal = new Cat(); // 本來是一隻貓
        animal.eat();
​
        // 如果需要呼叫子類特有的方法,需要向下轉型
        if (animal instanceof Dog){
            Dog dog = (Dog) animal;
            dog.watchMouse();
        }
        if (animal instanceof Cat){
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }
        giveMeAPet(new Dog()); // 在你呼叫這個方法的時候,方法本身不知道傳遞過來的是哪個類,所以需要進行判斷
​
    }
​
    public static void giveMeAPet(Animal animal){
        if (animal instanceof Dog){
            Dog dog = (Dog) animal;
            dog.watchMouse();
        }
        if (animal instanceof Cat){
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }
​
    }
}

介面多型的綜合案例

// USB介面類:兩個抽象方法:開啟USB、關閉USB
package com.dcits.day09.demo07;
​
public interface USB {
​
    public abstract void open();
​
    public abstract void close();
}
​
// 電腦類:開機、關機、連線USB介面並對USB裝置進行對應操作
package com.dcits.day09.demo07;
​
public class Computer {
    public void powerOn(){
        System.out.println("膝上型電腦開機");
    }
​
    public void powerOff(){
        System.out.println("膝上型電腦關機");
    }
​
    // 使用USB裝置的方法,使用介面作為方法的引數
    public void useDevice(USB usb) {
        usb.open();
        // 判斷當前類是屬於哪個類之後,在獲取類中的特有方法
        if (usb instanceof Mouse){
            Mouse mouse = (Mouse) usb;
            mouse.click();
        } else if (usb instanceof KeyBoard){
            KeyBoard keyboard = (KeyBoard) usb;
            keyboard.type();
        }
        usb.close();
    }
}
​
// 滑鼠類:重寫介面類中的開啟、關閉功能,並實現自己的獨有功能
package com.dcits.day09.demo07;
​
public class Mouse implements USB {
    @Override
    public void open() {
        System.out.println("開啟滑鼠");
    }
​
    @Override
    public void close() {
        System.out.println("關閉滑鼠");
    }
​
    public void click(){
        System.out.println("點選滑鼠!");
    }
}
​
// 鍵盤類:重寫介面類中的開啟、關閉功能,並實現自己的獨有功能
package com.dcits.day09.demo07;
​
public class KeyBoard implements USB {
    @Override
    public void open() {
        System.out.println("開啟鍵盤");
    }
​
    @Override
    public void close() {
        System.out.println("關閉鍵盤");
    }
​
    public void type(){
        System.out.println("敲鍵盤!");
    }
}