1. 程式人生 > 其它 >02-static、介面、多型、內部類

02-static、介面、多型、內部類

day02【static、介面、多型、內部類】

今日內容

  • static----------必須掌握
    • 修飾成員變數
    • 修飾成員方法
    • 修飾程式碼塊
  • 介面----------必須掌握
    • 定義介面
    • 實現介面
    • 介面中成員訪問特點
  • 多型----------必須掌握------>相對難點
    • 實現多型
    • 多型時成員訪問特點
    • 多型的使用場景
    • 多型的好處和弊端
    • 引用型別轉換
  • 內部類
    • 成員內部類
    • 匿名內部類----------必須掌握
  • 引用型別使用小結

第一章 static關鍵字

1.static關鍵字

1.1 概述

  • 概念: static是靜態修飾符,表示靜態的意思,可以修飾成員變數和成員方法以及程式碼塊。

1.2 修飾成員變數

格式
  • 格式: static 資料型別 變數名;
特點
  • 被static修飾的成員變數叫做靜態成員變數\類變數
  • 被static修飾的成員變數會被該類的所有物件共享
  • 被static修飾的成員變數被該類的某個物件修改了,那麼該類的所有物件使用的都是修改後的值
訪問方式
  • 方式一: 物件名.靜態成員變數名 ---->不推薦
  • 方式二: 類名.靜態成員變數名 ----->推薦
案例:
public class Chinese {
    String name;// 姓名
    static String country;// 國籍

    public Chinese(String name, String country) {
        this.name = name;
        this.country = country;
    }

    public Chinese() {
    }
}

public class Test {
    public static void main(String[] args) {
        /*
            ##### 格式
            - 格式:  `static 資料型別 變數名;`

            ##### 特點
            - 被static修飾的成員變數叫做靜態成員變數\類變數
            - 被static修飾的成員變數會被該類的所有物件共享
            - 被static修飾的成員變數被該類的某個物件修改了,那麼該類的所有物件使用的都是修改後的值

            ##### 訪問方式
            - 方式一:  `物件名.靜態成員變數名`  ---->不推薦
            - 方式二:  `類名.靜態成員變數名`     ----->推薦
         */
        // 給靜態成員變數country賦值
        Chinese.country = "中國";

        // 建立Chinese物件
        Chinese c1 = new Chinese();
        // null,中國
        System.out.println(c1.name+","+c1.country);
        System.out.println("----------------");

        // 建立Chinese物件
        Chinese c2 = new Chinese();
        // null,中國
        System.out.println(c2.name+","+c2.country);
        System.out.println("----------------");

        // 使用c2修改靜態成員變數country的值
        c2.country = "中華人民共和國";
        // 中華人民共和國,中華人民共和國
        System.out.println(c1.country+","+c2.country);

    }
}

1.3 修飾成員方法

格式
  • 格式: 修飾符 static 返回值型別 方法名(形參列表){ 方法體 };
特點
  • 被static修飾的成員方法叫做靜態方法,可以使用類名直接呼叫
訪問方式
  • 方式一: 物件名.靜態方法名(實參); ------->推薦
  • 方式二: 類名.靜態方法名(實參); ------->推薦
案例:
public class Chinese {

    // 非靜態成員方法
    public void method(){
        System.out.println("非靜態成員方法 method...");
    }

    // 靜態成員方法
    public static void show(){
        System.out.println("靜態成員方法 show...");
    }
}

public class Test {
    public static void main(String[] args) {
        // 方式一
        Chinese c = new Chinese();
        c.show();
        
        // 方式二
        Chinese.show();
    }
}

1.4 靜態方法呼叫的注意事項

  • 靜態方法中不能出現this關鍵字

  • 靜態方法中只能直接訪問靜態成員變數和靜態成員方法

  • 靜態方法中不能直接訪問非靜態成員變數和非靜態成員方法

  • 非靜態方法中可以直接訪問一切成員變數和成員方法

    public class Chinese {
        String name;
        static String country;
    
        public void show1(){
            System.out.println("非靜態成員方法");
        }
    
        public void show2(){
            //- 非靜態方法中可以直接訪問一切成員變數和成員方法
            System.out.println(name);// 非靜態成員變數
            System.out.println(country);// 靜態成員變數
            show1();// 非靜態成員方法
            method2();// 靜態成員方法
        }
    
        public static void method1(){
            //- 靜態方法中不能出現this關鍵字
            //System.out.println(this.name);// 編譯報錯
    
            //- 靜態方法中不能直接訪問非靜態成員變數和非靜態成員方法
            //System.out.println(name);// 編譯報錯
            //show1();// 編譯報錯
    
            //- 靜態方法中只能直接訪問靜態成員變數和靜態成員方法
            System.out.println(country);
            method2();
        }
    
        public static void method2(){
            System.out.println("靜態成員方法");
        }
    
    }
    
    
    public class Test {
        public static void main(String[] args) {
             //Chinese.method1();
    
            new Chinese().show2();
        }
    
    }
    
    

1.5 修飾程式碼塊

格式: 靜態程式碼塊
static {
    
}
位置
  • 類中方法外
執行
  • 隨著類的載入而執行,並且只執行一次
  • 類的載入: 第一次使用某個類的時候,類載入器就會把該類載入到記憶體中,只會載入一次
使用場景
  • 如果某些程式碼只需要執行一次,就可以放在靜態程式碼塊中
  • eg: 載入驅動, 讀配置檔案......
案例
public class Chinese {
    // 靜態程式碼塊
    static {
        System.out.println("Chinese 靜態程式碼塊...");
    }

    public Chinese() {
        System.out.println("Chinese 構造方法...");
    }
}

public class Test {
    public static void main(String[] args) {
        Chinese c1 = new Chinese();
        Chinese c2 = new Chinese();
    }
}

1.6 以後開發中static的應用

概述

以後的專案中,通常會需要一些“全域性變數”或者“全域性的工具方法”,這些全域性變數和方法,可以單獨定義在一個類中,並宣告為static(靜態)的,可以很方便的通過類名訪問

例如
public class Utils {
    public static final String USERNAME = "root";
    public static final String PASSWORD = "123456";
    public static final int WIDTH = 600;
    public static final int HEIGHT = 600;

    // 工具方法
    public static int getArrMax(int[] arr){
        int max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > max){
                max = arr[i];
            }
        }
        return max;
    }
}

第二章 介面

3.1 概述

  • 概述: 介面也是一種引用資料型別,在介面中專門用來定義方法和常量
  • 介面中的成員:
    • 常量(jdk7及其以前)
    • 抽象方法(jdk7及其以前)
    • 預設方法(有方法體的方法)(jdk8及其以上)
    • 靜態方法(jdk8及其以上)
    • 私有方法(jdk9及其以上)
  • 定義一個介面需要使用interface關鍵字---->介面編譯之後會產生class檔案
  • 介面不能建立物件,只能被類實現(類似繼承),實現介面的類叫做實現類\子類
  • 實現介面的類如果是普通類,就必須重寫介面中所有的抽象方法,如果是抽象類,就可以不重寫,也可以重寫

3.2 定義格式

格式

修飾符 interface 介面名{    - 常量(jdk7及其以前)    - 抽象方法(jdk7及其以前)    - 預設方法(有方法體的方法)(jdk8及其以上)    - 靜態方法(jdk8及其以上)    - 私有方法(jdk9及其以上)}

案例

public interface A {    // - 常量(jdk7及其以前) 使用public static final關鍵字修飾,這3個關鍵字可以省略不寫    public static final int NUM = 10;    // - 抽象方法(jdk7及其以前) 使用public abstract關鍵字修飾,這2個關鍵字可以省略不寫    public abstract void method1();        // - 預設方法(有方法體的方法)(jdk8及其以上)     // 使用public default關鍵字修飾,其中public可以省略不寫,default不可以省略    public default void method2(){        System.out.println("A介面 預設方法...");    }        // - 靜態方法(jdk8及其以上) 使用public static關鍵字修飾,其中public可以省略,static不可以省略    public static void method3(){        System.out.println("A介面 靜態方法...");    }        // - 私有方法(jdk9及其以上) 使用private關鍵字修飾    /*    private void mehtod4(){            }        private static void mehtod5(){            }    */}

3.3 實現介面

實現概述
  • 概述:類和介面的關係就是實現關係,類似繼承,使用implements關鍵字來實現介面
實現格式:
  • 單實現

    修飾符 class 類名 implements 介面名{}
    
  • 多實現

    修飾符 class 類名 implements 介面名1,介面名2,介面名3,...{}
    
  • 先繼承後實現

    修飾符 class 類名 extends 父類名 implements 介面名1,介面名2,介面名3,...{}
    
案例演示:
interface A{}
interface B{}
class Fu{}

// 單實現
class Imp1 implements A{}
// 多實現
class Imp2 implements A,B{}
// 先繼承後實現
class Imp3 extends Fu implements A,B{}

public class Test {
    public static void main(String[] args) {
        
    }
}

3.4 介面中成員的訪問特點

  • 訪問特點:

    介面中成員的訪問特點:
    	常量: 主要是供介面名直接訪問
        抽象方法: 就是供實現類重寫的
        預設方法: 就是供實現類重寫或者直接呼叫
        靜態方法: 只供介面名直接訪問
        私有方法: 只能在介面內部訪問
    
  • 案例:

    class Imp implements A{
    
        @Override
        public void method1() {
            System.out.println("實現類 重寫method1抽象方法");
        }
    
        @Override
        public void method2() {
            System.out.println("實現類 重寫method2預設方法");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            /*
                介面中成員的訪問特點:
                    常量: 主要是供介面名直接訪問
                    抽象方法: 就是供實現類重寫的
                    預設方法: 就是供實現類重寫或者直接呼叫
                    靜態方法: 只供介面名直接訪問
                    私有方法: 只能在介面內部訪問
             */
            // 訪問介面中的常量
            System.out.println(A.NUM);// 推薦
            System.out.println(Imp.NUM);// 不推薦
    
            // 訪問介面中的抽象方法
            Imp imp = new Imp();
            imp.method1();
    
            // 訪問介面中的預設方法
            imp.method2();
    
            // 訪問介面中的靜態方法
            A.method3();
    
        }
    }
    
    
    public interface A {
        // - 常量(jdk7及其以前) 使用public static final關鍵字修飾,這3個關鍵字可以省略不寫
        public static final int NUM = 10;
    
        // - 抽象方法(jdk7及其以前) 使用public abstract關鍵字修飾,這2個關鍵字可以省略不寫
        public abstract void method1();
    
        // - 預設方法(有方法體的方法)(jdk8及其以上)
        // 使用public default關鍵字修飾,其中public可以省略不寫,default不可以省略
        public default void method2(){
            System.out.println("A介面 預設方法...");
        }
    
        // - 靜態方法(jdk8及其以上) 使用public static關鍵字修飾,其中public可以省略,static不可以省略
        public static void method3(){
            System.out.println("A介面 靜態方法...");
        }
    
        // - 私有方法(jdk9及其以上) 使用private關鍵字修飾
        /*
        private void mehtod4(){
    
        }
    
        private static void mehtod5(){
    
        }
        */
    }
    
    

3.5 多實現時的幾種衝突情況

公有靜態常量的衝突
  • 如果實現類實現多個介面,這多個介面中有相同的常量,那麼實現類是不會繼承的
interface A{
    public static final int A = 10;
    public static final int B = 10;
}

interface B{
    public static final int A = 20;
}

class Imp implements A,B{

}

public class Test {
    public static void main(String[] args) {
        //System.out.println(Imp.A);// 編譯報錯
        System.out.println(Imp.B);
    }
}

公有抽象方法的衝突
interface A{
    public abstract void method();
}

interface B{
    public abstract void method();
}

class Imp implements A,B{
    
    // 如果實現的多個父介面中有相同的抽象方法,實現類只需要重寫一次即可
    @Override
    public void method() {
        
    }
}

public class Test {
    public static void main(String[] args) {
        
    }
}

公有預設方法的衝突
interface A{
    public default void method(){
        System.out.println("A介面 預設方法method...");
    }
}

interface B{
    public default void method(){
        System.out.println("B介面 預設方法method...");
    }
}

class Imp implements A,B{

    // 如果實現的多個父介面中有相同的預設方法,實現類必須重寫該預設方法
    @Override
    public void method() {
        System.out.println("Imp實現類 重寫的預設方法method...");
    }
}

public class Test {
    public static void main(String[] args) {
        new Imp().method();
    }
}

公有靜態方法的衝突
interface A{
    public static void method(){
        System.out.println("A介面 靜態方法method...");
    }
}

interface B{
    public static void method(){
        System.out.println("B介面 靜態方法method...");
    }
}

class Imp implements A, B {
    // 不報錯,因為介面中的靜態方法,實現類無法繼承
}

public class Test {
    public static void main(String[] args) {
        
    }
}

私有方法的衝突
  • 不存在衝突,因為私有方法不能被實現類繼承

3.6 介面和介面的關係

介面與介面之間的關係
  • 介面和介面之間是繼承關係

  • 單繼承

    修飾符 interface 子介面名 extends 父介面名{}
    
  • 多繼承

    修飾符 interface 子介面名 extends 父介面名1,父介面名2,...{}
    
  • 多層繼承

    修飾符 interface 父介面名 extends 爺爺介面名{}
    修飾符 interface 子介面名 extends 父介面名{}
    
介面繼承介面的衝突情況
公有靜態常量的衝突
interface A{
    public static final int A = 10;
    public static final int B = 10;
}

interface B{
    public static final int A = 10;
}

interface C extends A,B{
    
}

public class Test {
    public static void main(String[] args) {
        // 子介面可以繼承父介面中的常量,如果多個父介面中有相同的常量,那麼子介面就不會繼承這些相同的常量
        //System.out.println(C.A);// 編譯報錯
        System.out.println(C.B);
    }
}

公有抽象方法衝突
interface A{
    public abstract void method();
}

interface B{
    public abstract void method();
}

interface C extends A,B{

}

class Imp implements C{

    // 如果多個父介面中有相同的抽象方法,子介面只會繼承一個,所以實現類只需要重寫一次即可
    @Override
    public void method() {
        
    }
}

public class Test {
}

公有預設方法的衝突
interface A{
    public default void method(){
        System.out.println("A介面 預設方法method...");
    }
}

interface B{
    public default void method(){
        System.out.println("B介面 預設方法method...");
    }
}

interface C extends A,B{

    // 如果多個父介面中有相同的預設方法,那麼子介面必須重寫一次該預設方法
    @Override
    default void method() {
        System.out.println("C介面 重寫父介面中method預設方法...");
    }
}

class Imp implements C{

}

public class Test {
    public static void main(String[] args) {
        new Imp().method();
    }
}
公有靜態方法和私有方法
  • 不存在衝突,因為靜態方法和私有方法不會被子介面繼承

3.7 繼承的同時又實現存在的問題

父類和介面的公有靜態常量的衝突
interface A{
    public static final int NUM = 10;
}

class Fu{
    public static final int NUM = 10;
}

class Zi extends Fu implements A{
    
}

public class Test {
    public static void main(String[] args) {
        // 子類不會繼承的父類和父介面中相同的常量
        //System.out.println(Zi.NUM);// 編譯報錯    
    }
}

父類和介面的抽象方法衝突
interface A {
    public abstract void method();
}

abstract class Fu {
    public abstract void method();
}

class Zi extends Fu implements A {

    @Override
    public void method() {
        // 如果父介面和父類中有相同的抽象方法,子類必須重寫一次
    }
}

public class Test {
}

父類和介面的公有預設方法的衝突
interface A {
    public default void method(){
        System.out.println("A介面 method預設方法");
    }
}

abstract class Fu {
    public void method(){
        System.out.println("Fu類 method預設方法");
    }
}

class Zi extends Fu implements A {
    
}

public class Test {
    public static void main(String[] args) {
        // 如果父類和父介面中有相同的預設方法,子類會優先訪問父類中的預設方法
        new Zi().method();
    }
}

父類和介面的公有靜態方法衝突
interface A {
    public static void method(){
        System.out.println("A介面 method靜態方法");
    }
}

abstract class Fu {
    public static void method(){
        System.out.println("Fu類 method靜態方法");
    }
}

class Zi extends Fu implements A {

}


public class Test {
    public static void main(String[] args) {
        // 父介面中的靜態方法不會被實現類繼承,而父類中的靜態方法是會被子類繼承
        Zi.method();
    }
}

父類和介面的私有方法
  • 不存在衝突

3.8 抽象類和介面的練習

介面和抽象類的區別

  • 介面和抽象類中的成員不同(........)
  • 使用場景不同:
    • 抽象類一般是作為同一類事物的父類
    • 介面可以作為不同一類事物的父介面
  • 繼承抽象父類只能繼承一個,而實現介面可以實現多個介面

需求:

通過例項進行分析和程式碼演示抽象類和介面的用法。

1、舉例:

​ 犬: 作為抽象父類

​ 行為:吼叫;吃飯;

​ 緝毒犬: 繼承犬類

​ 行為:吼叫;吃飯;緝毒;

抽象類: 如果同一群事物共同的功能就定義在抽象類中

介面: 如果不同型別的事物共同擁有的功能就定義在介面中,讓這些事物的類實現該介面

實現:

  • Dog

    public abstract class Dog {
    
        public abstract void houJiao();
    
        public abstract void eat();
    
    
    }
    
    
  • JiDu

    public interface JiDu {
        public abstract void jiDu();
    }
    
    
  • JiDuDog

    public class JiDuDog extends Dog implements JiDu{
    
        public void houJiao(){
            System.out.println("旺旺旺...");
        }
    
        public void eat(){
            System.out.println("狗在吃骨頭...");
        }
    
        public void jiDu(){
            System.out.println("狗在緝毒...");
        }
    
    }
    
    
  • Test

    public class Test {
        public static void main(String[] args) {
            JiDuDog jiDuDog = new JiDuDog();
            jiDuDog.eat();
            jiDuDog.jiDu();
            jiDuDog.houJiao();
        }
    }
    
    

第三章 多型

3.1 概述

  • 多型是面向物件三大特徵之一。
  • 多型指對於同一行為,對於不同的物件,具有不同的表現形式。在程式中,表示對同一方法,不同的物件有不同實現。
  • 實現多型的前提條件
    • 繼承 或者 實現
    • 父類引用指向子類物件 或者 父介面引用指向實現類物件
    • 方法的重寫(沒有重寫方法多型是沒有意義的)

3.2 實現多型

  • 繼承時的多型:

    class Animal{
        public void eat(){
            System.out.println("吃東西...");
        }
    }
    
    class Dog extends Animal{
        @Override
        public void eat() {
            System.out.println("狗吃骨頭...");
        }
    }
    
    class Cat extends Animal{
        @Override
        public void eat() {
            System.out.println("貓吃魚...");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // 父類引用指向子類的物件
            Animal anl = new Dog();
            anl.eat();
    
            anl = new Cat();
            anl.eat();
        }
    }
    
    
  • 實現時的多型:

    interface A{
        public abstract void method();
    }
    
    class Imp1 implements A{
        @Override
        public void method() {
            System.out.println("Imp1 重寫method方法...");
        }
    }
    
    class Imp2 implements A{
        @Override
        public void method() {
            System.out.println("Imp2 重寫method方法...");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // 父介面的引用指向實現類的物件
            A a = new Imp1();
            a.method();
    
            a = new Imp2();
            a.method();
        }
    }
    
    

3.3 多型時訪問成員的特點

  • 特點:

    • 成員變數: 編譯看左邊(父類),執行看左邊(父類)
    • 成員方法:
      • 非靜態成員方法: 編譯看左邊(父類),執行看右邊(子類)
      • 靜態成員方法: 編譯看左邊(父類),執行看左邊(父類)
  • 程式碼:

    class Fu{
        int num = 20;
    
        public void method1(){
            System.out.println("Fu 非靜態成員方法method1...");
        }
    
        public static void method2(){
            System.out.println("Fu 靜態成員方法method2...");
        }
    }
    
    class Zi extends Fu{
        int num = 10;
    
        @Override
        public void method1(){
            System.out.println("Zi 非靜態成員方法method1...");
        }
    
        public static void method2(){
            System.out.println("Zi 靜態成員方法method2...");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // 父類的引用指向子類的物件
            Fu f = new Zi();
    
            // 多型時訪問成員變數:編譯看左邊(父類),執行看左邊(父類)
            System.out.println(f.num);// 20
    
            // 多型時訪問非靜態成員方法: 編譯看左邊(父類),執行看右邊(子類)
            f.method1();
    
            // 多型時訪問靜態成員方法: 編譯看左邊(父類),執行看左邊(父類)
            f.method2();
    
        }
    }
    
    

3.4 多型的幾種表現形式

  • 普通父類多型

    class Animal{
        public void eat(){
            System.out.println("吃東西...");
        }
    }
    
    class Dog extends Animal{
        @Override
        public void eat() {
            System.out.println("狗吃骨頭...");
        }
    }
    
    
    public class Test {
        public static void main(String[] args) {
            // 父類的引用指向子類的物件
            Animal anl = new Dog();
            anl.eat();
        }
    }
    
    
  • 抽象父類多型

    abstract class Animal{
        public abstract void eat();
    }
    
    class Dog extends Animal{
        @Override
        public void eat() {
            System.out.println("狗吃骨頭...");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // 父類的引用指向子類的物件
            Animal anl = new Dog();
            anl.eat();
        }
    }
    
    
  • 父介面多型

    interface Animal{
        public abstract void eat();
    }
    
    class Dog implements Animal{
        @Override
        public void eat() {
            System.out.println("狗吃骨頭...");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // 父介面的引用指向實現類的物件
            Animal anl = new Dog();
            anl.eat();
        }
    }
    
    

3.5 多型的應用場景

  • 變數多型 -----> 意義不大

    class Animal{
        public void eat(){
            System.out.println("吃東西...");
        }
    }
    
    class Dog extends Animal{
        @Override
        public void eat() {
            System.out.println("狗吃骨頭...");
        }
    }
    
    class Cat extends Animal{
        @Override
        public void eat() {
            System.out.println("貓吃魚...");
        }
    }
    
    
    public class Test {
        public static void main(String[] args) {
            // 變數多型: 變數的資料型別為父類型別,接收該父類型別的子類物件
            Animal anl = new Dog();
            anl.eat();
    
            anl = new Cat();
            anl.eat();
        }
    }
    
    
  • 形參多型----> 常用

    • 結論: 引數的型別是父類型別,那麼就可以接收該父類型別的物件以及該父類型別的所有子類物件

      class Animal{
          public void eat(){
              System.out.println("吃東西...");
          }
      }
      
      class Dog extends Animal{
          @Override
          public void eat() {
              System.out.println("狗吃骨頭...");
          }
      }
      
      class Cat extends Animal{
          @Override
          public void eat() {
              System.out.println("貓吃魚...");
          }
      }
      
      public class Test {
      
          public static void main(String[] args) {
              // 建立Animal物件,呼叫method方法
              Animal anl = new Animal();
              method(anl);
      
              // 建立Dog物件,呼叫method方法
              Dog dog = new Dog();
              method(dog);
      
              // 建立Cat物件,呼叫method方法
              Cat cat = new Cat();
              method(cat);
          }
      
          // 定義一個方法,既可以接收Animal類的物件以及Animal類的所有子類物件
          // 傳入Dog類物件,實參給形參賦值,其實就是:  Animal anl = new Dog();
          // 傳入Cat類物件,實參給形參賦值,其實就是:  Animal anl = new Cat();
          // 結論: 如果方法的形參型別為父類型別,那麼可以接收該父類型別物件或者其所有子類物件
          public static void method(Animal anl){
              anl.eat();
          }
      
          /*public static void method(Dog dog){
      
          }
      
          public static void method(Cat cat){
      
          }
      
          // ...*/
      
      }
      
      
  • 返回值多型---> 常用

    • 返回值型別為父類型別,那麼就可以返回該父類型別的物件,或者該父類型別的子類物件
    class Animal{
        public void eat(){
            System.out.println("吃東西...");
        }
    }
    
    class Dog extends Animal{
        @Override
        public void eat() {
            System.out.println("狗吃骨頭...");
        }
    }
    
    class Cat extends Animal{
        @Override
        public void eat() {
            System.out.println("貓吃魚...");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // 當返回值返回的時候,其實就是執行:  Animal anl = new Animal();
            Animal anl = getAnimal("Animal");
            anl.eat();
    
            System.out.println("-------------");
    
            // 當返回值返回的時候,其實就是執行:  Animal anl1 = new Dog();
            Animal anl1 = getAnimal("Dog");
            anl1.eat();
    
            System.out.println("-------------");
    
            // 當返回值返回的時候,其實就是執行:  Animal anl2 = new Cat();
            Animal anl2 = getAnimal("Cat");
            anl2.eat();
    
        }
    
        // 定義一個方法,既可以返回Animal物件,又可以返回Animal類的所有子類物件
        public static Animal getAnimal(String type){
            if ("Dog".equals(type)){
                // 返回的是Dog物件
                return new Dog();
            }else if ("Cat".equals(type)){
                // 返回的是Cat物件
                return new Cat();
            }else if ("Animal".equals(type)){
                // 返回的是Animal物件
                return new Animal();
            }else{
                return null;
            }
        }
    }
    
    

3.6 多型的好處和弊端

  • 好處:

    • 可以將方法的引數定義為父類引用,使程式編寫的更簡單,提高程式的靈活性,擴充套件性
    • 案例參考多型的應用場景
  • 弊端:

    • 無法直接訪問子類獨有的成員變數和成員方法(因為多型時成員訪問特點編譯都是看左邊(父類))

    • 案例:

      class Animal{
          public void eat(){
              System.out.println("吃東西...");
          }
      }
      
      class Dog extends Animal{
          @Override
          public void eat() {
              System.out.println("狗吃骨頭...");
          }
      
          public void lookHome(){
              System.out.println("狗在看家...");
          }
      }
      
      class Cat extends Animal{
          @Override
          public void eat() {
              System.out.println("貓吃魚...");
          }
      
          public void catchMouse(){
              System.out.println("貓抓老鼠...");
          }
      }
      
      
      public class Test {
          public static void main(String[] args) {
              // 無法直接訪問子類獨有的成員變數和成員方法(因為多型時成員訪問特點編譯都是看左邊(父類))
              // 父類的引用指向子類的物件
              Animal anl = new Dog();
              anl.eat();
              //anl.lookHome();// 編譯報錯,因為編譯看左邊(父類),而父類沒有這個方法,所以編譯報錯
      
              anl = new Cat();
              anl.eat();
              //anl.catchMouse();//  編譯報錯,因為編譯看左邊(父類),而父類沒有這個方法,所以編譯報錯
          }
      }
      
      

3.7 引用型別轉換

  • 為什麼要轉換

    • 向上轉型為了實現多型
    • 向下轉型為了解決多型的弊端
  • 如何轉型

    • 向上轉型: 子類型別向父類型別轉換的過程叫做向上轉型,這個過程是自動的
      • 格式: 父類型別 變數名 = 子類物件;
      • eg: Fu f = new Zi();
    • 向下轉型: 父類型別的變數向子類型別轉換的過程叫做向下轉型,這個過程是強制的
      • 格式: 子類型別 變數名 = (子類型別)父類型別的變數;
      • eg: Zi zi = (Zi)f;
      • 注意:
        • 向下轉型要求右邊父類型別的變數指向的物件一定是屬於左邊子類型別,否則會報型別轉換異常ClassCastException
  • 避免轉型異常

    • 轉型判斷格式

      if(變數名 instanceof 資料型別){}
      如果前面變數名指向的物件是屬於後面資料型別,那麼返回true,否則返回false
      
  • 案例:

    class Animal{
        public void eat(){
            System.out.println("吃東西...");
        }
    }
    
    class Dog extends Animal{
        @Override
        public void eat() {
            System.out.println("狗吃骨頭...");
        }
    
        public void lookHome(){
            System.out.println("狗在看家...");
        }
    }
    
    class Cat extends Animal{
        @Override
        public void eat() {
            System.out.println("貓吃魚...");
        }
    
        public void catchMouse(){
            System.out.println("貓抓老鼠...");
        }
    }
    
    
    public class Test {
        public static void main(String[] args) {
            // 父類的引用指向子類的物件
            Animal anl = new Dog();
            anl.eat();
            //anl.lookHome();// 編譯報錯,因為編譯看左邊(父類),而父類沒有這個方法,所以編譯報錯
            // 轉型之前應該判斷anl指向的是否是Cat型別的物件
            if (anl instanceof Dog) {
                // 向下轉型
                Dog dog = (Dog) anl;
                dog.lookHome();
            }
            System.out.println("-------------");
    
            anl = new Cat();
            anl.eat();
            //anl.catchMouse();//  編譯報錯,因為編譯看左邊(父類),而父類沒有這個方法,所以編譯報錯
            // 轉型之前應該判斷anl指向的是否是Cat型別的物件
            if (anl instanceof Cat) {
                // 向下轉型
                Cat cat = (Cat) anl;
                cat.catchMouse();
            }
            System.out.println("-------------");
    
        }
    }
    
    

第四章 內部類

4.1 內部類的概述

  • 概述: 將一個類定義在另一個類的裡面,裡面的那個類就叫做內部類,外面的那個類就叫做外部類
  • 特點: 內部類是一個獨立的類,在編譯後,有自己獨立的class檔案,前面冠以:外部類名+$+內部類類名.class
  • 分類:
    • 成員內部類
    • 區域性內部類(自己瞭解)
    • 匿名內部類

4.2 成員內部類

  • 概述: 定義在類中方法外的類就叫做成員內部類,是外部類的一個成員

  • 格式:

    public class 外部類名{
        public class 內部類名{
        
    	}
    }
    
  • 成員內部類的成員訪問特點:

    • 特點: 想要在其他類中訪問成員內部類的成員變數和成員方法,需要建立成員內部類物件

    • 格式: 外部類名.內部類名 物件名 = new 外部類名().new 內部類名();

    • 案例:

      public class Body {// 外部類
      
          public class Heart{// 成員內部類
              // 成員變數
              String name;
      
              // 構造方法
              public Heart(String name) {
                  this.name = name;
              }
      
              public Heart() {
              }
      
              // 成員方法
              public void methodN1(){
                  System.out.println("成員內部類的成員方法methodN1...");
              }
              
          }
      
      }
      
      
      public class Test {
          public static void main(String[] args) {
              // 外部類名.內部類名   物件名 = new 外部類名().new 內部類名();
              // 建立Body外部類中的Heart內部類物件
              Body.Heart bh = new Body().new Heart();
      
              // 訪問成員內部類的成員變數
              System.out.println(bh.name);
              // 訪問成員內部類的成員方法
              bh.methodN1();
          }
      }
      
      
  • 注意事項:

    • 在成員內部類中,可以直接訪問外部類的一切成員,包括外部類的私有成員

    • 在外部類中,訪問內部類的成員,需要建立內部類物件來訪問

      public class Body {// 外部類
      
          // 成員變數
          String name = "外";
          private int age = 10;
      
          // 成員方法
          public void methodW1(){
              System.out.println("外部類的成員方法methodW1....");
          }
      
          public class Heart{// 成員內部類
              // 成員變數
              String name = "內";
      
              // 構造方法
              public Heart(String name) {
                  this.name = name;
              }
      
              public Heart() {
              }
      
              // 成員方法
              public void methodN1(){
                  System.out.println("成員內部類的成員方法methodN1...");
              }
      
              // 在成員內部類中,可以直接訪問外部類的一切成員,包括外部類的私有成員
              public void methodN2(){
                  System.out.println("成員內部類的成員方法methodN2...");
                  String name = "局";
                  System.out.println("區域性的name:"+name);
                  System.out.println("內部類的name:"+this.name);
                  System.out.println("外部類的name:"+Body.this.name);
                  System.out.println("外部類的age:"+age);
                  methodW1();
              }
      
          }
      
      
          // - 在外部類中,訪問內部類的成員,需要建立內部類物件來訪問
          public void methodW2(){
              System.out.println("外部類的成員方法methodW2....");
              // 建立內部類物件
              //Body.Heart bh = new Body().new Heart();
              Heart h = new Heart();
              System.out.println(h.name);
              h.methodN1();
          }
      }
      
      
      public class Test {
          public static void main(String[] args) {
              // 外部類名.內部類名   物件名 = new 外部類名().new 內部類名();
              // 建立Body外部類中的Heart內部類物件
              Body.Heart bh = new Body().new Heart();
      
              // 訪問成員內部類的成員變數
              System.out.println(bh.name);
              // 訪問成員內部類的成員方法
              bh.methodN1();
              bh.methodN2();
      
              System.out.println("-----");
      
              new Body().methodW2();
          }
      }
      
      

4.3 匿名內部類(重點)

  • 概述

    • 本質其實就是一個繼承了某個類的子類物件
    • 本質其實就是一個實現了某個介面的實現類物件
  • 格式

    new 類名(){
        重寫父類中所有抽象方法
    };
    
    new 介面名(){
        重寫介面中所有抽象方法
    };
    
  • 使用場景

    • 如果想得到某個類的子類物件,就可以建立該類的匿名內部類
    • 如果想得到某個介面的實現類物件,就可以建立該介面的匿名內部類
  • 作用:

    • 匿名內部類沒有特殊的功能,就是來簡化程式碼的(不用建立子類,而是直接得到子類物件)
  • 案例1:

    abstract class Animal{
        public abstract void eat();
    }
    
    class Dog extends Animal{
        @Override
        public void eat() {
            System.out.println("狗吃骨頭...");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // 需求:呼叫Animal類中的eat方法
            // 建立Animal類的Dog子類物件
            Animal anl1 = new Dog();
            anl1.eat();
    
            // 使用Animal類的匿名內部類 ==> Animal類的匿名子類物件
            Animal anl2 = new Animal() {
                @Override
                public void eat() {
                    System.out.println("匿名內部類的eat方法...");
                }
            };
            anl2.eat();
        }
    }
    
    
  • 案例2:

    interface A{
        void method();
    }
    
    class Imp implements A{
        @Override
        public void method() {
            System.out.println("實現類重寫method方法...");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // 需求: 呼叫A介面中的method方法
            // 建立A介面的Imp實現類的物件
            A a1 = new Imp();
            a1.method();
    
            // 建立A介面的匿名內部類 ===> A介面的匿名實現類物件
            A a2 = new A() {
                @Override
                public void method() {
                    System.out.println("匿名內部類重寫的method方法...");
                }
            };
            a2.method();
        }
    }
    
    

第五章 引用型別使用小結

5.1 引用型別作為方法引數和返回值

  • 引用型別作為方法的引數傳遞的是地址值

  • 引用型別作為方法的返回值返回的是地址值

  • 案例1

    class Person {
        String name;
    
        public Person(String name) {
            this.name = name;
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // - 引用型別作為方法的引數傳遞的是地址值
            //- 引用型別作為方法的返回值返回的是地址值
            // 需求: 呼叫method1方法
            // 建立Person物件
            Person p1 = new Person("itheima");
    
            // 呼叫method1方法,傳入Person物件
            method1(p1);
            System.out.println("p1的name:" + p1.name);// itcast
    
        }
    
        public static void method1(Person p) {
            p.name = "itcast";
        }
    }
    
    
  • 案例2

    class Person {    String name;    public Person(String name) {        this.name = name;    }}public class Test {    public static void main(String[] args) {        //- 引用型別作為方法的返回值返回的是地址值        // 需求: 呼叫method2方法        // 建立Person物件        Person p2 = new Person("java");        // 呼叫method2方法,傳入Person物件        Person person = method2(p2);        System.out.println("person的name:"+person.name); // itcast        System.out.println("p2的name:"+p2.name);// itcast    }    public static Person method2(Person p){        p.name = "itcast";        return p;    }}
    

5.2 引用型別作為成員變數

abstract class Pet{    public abstract void happy();}interface FaShu{    public abstract void fly();}class Dog extends Pet{    @Override    public void happy() {        System.out.println("寵物給我快樂...");    }}class Imp implements FaShu{    @Override    public void fly() {        System.out.println("飛行...");    }}class Person{    // 姓名    String name;    // 寵物    Pet pet;    // 法術    FaShu faShu;    public Person() {    }    public Person(String name, Pet pet, FaShu faShu) {        this.name = name;        this.pet = pet;        this.faShu = faShu;    }}public class Test {    public static void main(String[] args) {        // 引用型別: 陣列,類,介面,...        // 建立Person類物件        Person p = new Person();        // 給p物件的屬性賦值        p.name = "張三";        p.pet = new Dog();        p.faShu = new Imp();        // 訪問p物件寵物的happy方法        p.pet.happy();        // 訪問p物件法術的fly方法        p.faShu.fly();        // 鏈式程式設計    }}

總結

必須練習:	1.static修飾成員變數的格式和使用    2.static修飾成員方法的格式和使用    3.static修飾程式碼塊的格式和使用     4.定義介面,實現介面,介面中成員的訪問特點        5.實現多型    6.多型場景下成員訪問特點    7.多型的應用場景---->形參多型,返回值多型  重點重點    8.解決多型的弊端--->向下轉型,轉型判斷    9.匿名內部類----> 本質,格式        - 能夠使用static修飾成員變數    格式: static 資料型別 變數名    使用: 類名.變數名;    特點: 被該類的所有物件共享        - 能夠使用static修飾成員方法    格式: 方法的返回值型別前面加static    使用: 類名.方法名(實參);- 能夠使用static修飾靜態程式碼塊       格式: static{}	位置: 類中方法外	執行: 隨著類的載入而執行,並且只執行一次,優先於構造方法執行        - 能夠寫出介面的定義格式    格式: public interface 介面名{}- 能夠寫出介面的實現格式   格式: 單實現,多實現,先繼承後實現       public class 實現類名 implements 介面名{}       public class 實現類名 implements 介面名1,介面名,...{}       public class 實現類名 extends 父類 implements 介面名1,介面名,...{}- 能夠說出介面中的成員特點    介面中的常量: 主要供介面名直接訪問,當然也可以被實現類繼承    介面中的抽象方法: 就是供實現類重寫的    介面中的預設方法: 可以供實現類物件直接繼承呼叫,也可以供實現類重寫    介面中的靜態方法: 只能供介面名直接訪問,不能被實現類繼承    介面中的私有方法: 只能在介面內部直接訪問,不能被實現類繼承        - 能夠說出多型的前提    繼承\實現    父類的引用指向子類的物件\介面的引用指向實現類的物件    方法的重寫        - 能夠寫出多型的格式   父類型別 變數名 = new 子類型別(實參);   介面型別 變數名 = new 實現類型別(實參);- 能夠理解多型向上轉型和向下轉型      向上轉型: 父類型別 變數名 = new 子類型別(實參);   存在的意義就是為了實現多型    向下轉型: 子類型別 變數名 = (子類型別)父類型別的變數; 存在的意義就是為了解決多型的弊端             注意: 父類型別的變數一定要指向左邊子類型別的物件,否則會報型別轉換異常ClassCastException    避免轉型異常: 變數名 instanceof 資料型別        - 能夠說出內部類概念          一個類中包含另一個類,被包含的類就是內部類        - 能夠理解匿名內部類的編寫格式     本質: 表示一個類的子類物件,或者一個介面的實現類物件     格式:			new 類名(){重寫抽象方法};            new 介面名(){重寫抽象方法};