Java學習-第一部分-第二階段-第一節:面向對象編程(高級)

来源:https://www.cnblogs.com/wenjie2000/archive/2022/08/10/16574059.html
-Advertisement-
Play Games

面向對象編程(高級) 筆記目錄:(https://www.cnblogs.com/wenjie2000/p/16378441.html) 類變數和類方法(static) 類變數 類變數-提出問題 提出問題的主要目的就是讓大家思考解決之道,從而引出我要講的知識點.說:有一群小孩在玩堆雪人,不時有新的小 ...


面向對象編程(高級)

筆記目錄:(https://www.cnblogs.com/wenjie2000/p/16378441.html)

類變數和類方法(static)

類變數

類變數-提出問題

提出問題的主要目的就是讓大家思考解決之道,從而引出我要講的知識點.說:有一群小孩在玩堆雪人,不時有新的小孩加入,請問如何知道現在共有多少人在玩?,編寫程式解決。

傳統的方法來解決

使用我們現有的技術來解決這個問題,大家看看如何?

✔思路

  1. 在main方法中定義一個變數count

  2. 當一個小孩加入游戲後count++,最後個count 就記錄有多少小孩玩游戲

    public class ChildGame {
        public static void main(String[] args) {
            //定義一個變數count,統計有多少小孩加入了游戲
            int count = 0;
            Child child1 = new Child("白精");
            child1.join();
            count++;
    
            Child child2 = new Child("老白");
            child2.join();
            count++;
            
            Child child3 = new Child("老白");
            child3.join();
            child3.count++;
            
            System.out.println("共有" + count + "小孩加入了游戲...");
        }
    }
    class Child {//類
        private String name;
        public Child(String name) {
            this.name = name;
        }
        public void join() {
            System.out.println(name + "加入了游戲..");
        }
    }
    

✔問題分析:

  1. count是一個獨立於對象,很尷尬
  2. 以後我們訪問count很麻煩,沒有使用到OOP
  3. 3.因此,我們引出類變數/靜態變數

類變數快速入門

思考:如果,設計一個int count表示總人數,我們在創建一個小孩時,就把count加1,並且 count是所有對象共用的就ok了!,我們使用類變數來解決

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

        Child child1 = new Child("白精");
        child1.join();
        child1.count++;

        Child child2 = new Child("老白");
        child2.join();
        child2.count++;
        
        Child child3 = new Child("老白");
        child3.join();
        child3.count++;
        
        //......
        System.out.println("共有" + child1.count + "小孩加入了游戲...");
        System.out.println(child1.count);//3
        System.out.println(child2.count);//3
        System.out.println(child3.count);//3
        System.out.println(Child.count);//3
    }
}
class Child {//類
    private String name;
    //定義一個變數 count,是一個類變數(靜態變數)static靜態
    //該變數最大的特點就是會被Child類的所有的對象實例共用
    public static int count = 0;
    public Child(String name) {
        this.name = name;
    }
    public void join() {
        System.out.println(name + "加入了游戲..");
    }
}

其中child1,child2以及child3中的count指向相同空間,被共用,為同一值

jdk8以及之前,count(也就是靜態域)在方法區中。jdk8之後,jdk存放在堆中

有些書說在方法區... jdk版本有關係,記住一點: static變數是對象共用

不管static變數在哪裡,共識:

  1. static變數是同一個類所有對象共用
  2. static類變數,在類載入的時候就生成了.

什麼是類變數

類變數也叫靜態變數/靜態屬性,是該類的所有對象共用的變數,任何一個該類的對象去訪問它時,取到的都是相同的值,同樣任何一個該類的對象去修改它時,修改的也是同一個變數。這個從前面的圖也可看出來。

如何定義類變數

定義語法:

訪問修飾符 static 數據類型 變數名;[推薦]

static 訪問修飾符 數據類型 變數名;

class A{
    public static string name = "abc";
    static public int totalNum = 100;
}

如何訪問類變數

類名.類變數名

或者對象名.類變數名【靜態變數的訪問健飾符的訪問許可權和範圍和普通屬性是一樣的。】

推薦使用:類名.類變數名;

public class Test {
    public static void main(String[] args) {
        //類名.類變數名
        // 說明:類變數是隨著類的載入而創建,所以即使沒有創建對象實例也可以訪問
        System.out.println(A.name);
        A a = new A();
        System.out.println("a.name=" +a.name);
    }
}

class A {
    //類變數
    public static String name = "韓順平";
}

類變數使用註意事項和細節討論

  1. 什麼時候需要用類變數
    當我們需要讓某個類的所有對象都共用一個變數時,就可以考慮使用類變數(靜態變數):比如:定義學生類,統計所有學生共交多少錢。Student (name, fee)

  2. 類變數與實例變數(普通屬性)區別

    類變數是該類的所有對象共用的,而實例變數是每個對象獨享的。

  3. 加上static稱為類變數或靜態變數,否則稱為 實例變數/普通變數/非靜態變數

  4. 類變數可以通過類名.類變數名或者對象名.類變數名來訪問,但java設計者推薦我們使用 類名.類變數名 方式訪問。【前提是 滿足訪問修飾符的訪問許可權和範圍】

  5. 實例變數不能通過類名.類變數名方式訪問。

  6. 類變數是在類載入時就初始化了,也就是說,即使你沒有助建家,只安尖加鄆,就可以使用類變數了。

  7. 類變數的生命周期是隨類的載入開始,隨著類消亡而銷毀。

類方法

類方法基本介紹

類方法也叫靜態方法。

形式如下:

訪問修飾符 static 數據返回類型 方法名(){ }【推薦】
static 訪問修飾符 數據返回類型 方法名(){ }

類方法的調用:

使用方式:類名.類方法名 或者 對象名.類方法名【前提是滿足訪問修飾符的訪問許可權和範圍】

類方法應用案例

請大家看一個靜態方式小案例。(統計學費總和)

public class Test {
    public static void main(String[] args) {
        //創建2個學生對象,叫學費
        Stu tom = new Stu("tom");
        tom.payFee(100);
        Stu mary = new Stu("mary");
        mary.payFee(200);
        
        //輸出當前收到的總學費
        Stu.showFee();//300
    }
}

class Stu {
    private String name;//普通成員
    // 定義一個靜態變數,來累積學生的學費
    private static double fee = 0;

    public Stu(String name) {
        this.name = name;
    }

    //說明
    //1.當方法使用了static修飾後,該方法就是靜態方法
    //2.靜態方法就可以訪問靜態屬性/變數
    public static void payFee(double fee) {
        Stu.fee += fee;//累積到
        
    }
    public static void showFee() {
        System.out.println("總學費有:" + Stu.fee);
    }
}

類方法經典的使用場景

當方法中不涉及到任何和對象相關的成員,則可以將方法設計成靜態方法,提高開發效率。(不需要額外創建對象)

比如:工具類中的方法utils
Math類、Arrays類、Collections集合類看下源碼:

小結
在程式員實際開發,往往會將一些通用的方法,設計成靜態方法,這樣我們不需要創建對象就可以使用了,比如列印一維數組,冒泡排序,完成某個計算任務等..

舉例

public class Test {
    public static void main(String[] args) {
        System.out.println(MyTools.calSum(10,30));
    }
}

class MyTools {
    //開發自己的工具類時,可以將方法做成靜態的,方便調用class MyTools {
//求出兩個數的和
    public static double calSum(double n1, double n2) {
        return n1 + n2;
    }
}

類方法使用註意事項和細節討論

  1. 類方法和普通方法都是隨著類的載入而載入,將結構信息存儲在方法區:類方法中無this的參數
    普通方法中隱含著this的參數
  2. 類方法可以通過類名調用,也可以通過對象名調用。
  3. 普通方法和對象有關,需要通過對象名調用,比如對象名.方法名(參數),不能通過類名調
    用。
  4. 類方法中不允許使用和對象有關的關鍵字,比如this和super。普通方法(成員方法)可以。[舉例]
  5. 類方法(靜態方法)中只能訪問靜態變數或靜態方法。【如何理解】
  6. 普通成員方法,既可以訪問普通變數(方法),也可以訪問靜態變數(方法)。

小結:靜態方法,只能訪問靜態的成員,非靜態的方法,可以訪問靜態成員和非靜態成員(必須遵守訪問許可權)

理解main方法語法

●深入理解main方法

解釋main方法的形式:public static void main(String[] args){}

  1. main方法時虛擬機調用

  2. java虛擬機需要調用類的main()方法,所以該方法的訪問許可權必須是public

  3. java虛擬機在執行main()方法時不必創建對象,所以該方法必須是static

  4. 該方法接收String類型的數組參數,該數組中保存執行java命令時傳遞給所運行的類的參數,案例演示,接收參數.

  5. java 執行的程式 參數1 參數2 參數3

特別提示:

  1. 在main()方法中,我們可以直接調用main方法所在類的靜態方法或靜態屬性。

  2. 但是,不能直接訪問該類中的非靜態成員,必須創建該類的一個實例對象後,才能通過這個對象去訪問類中的非靜態成員

代碼塊

●基本介紹

代碼化塊又稱為初始化塊,屬於類中的成員[即 是類的一部分],類似於方法,將邏輯語句封裝在方法體中,通過{}包圍起來。
但和方法不同,沒有方法名,沒有返回,沒有參數,只有方法體,而且不用通過對象或類顯式調用,而是載入類時,或創建對象時隱式調用。

●基本語法

[修飾符]{
​ 代碼
};

註意:

  1. 修飾符可選,要寫的話,也只能寫static
  2. 代碼塊分為兩類,使用static修飾的叫靜態代碼塊,沒有static修飾的,叫普通代碼塊。
  3. 邏輯語句可以為任何邏輯語句(輸入、輸出、方法調用、迴圈、判斷等)
  4. ;號可以寫上,也可以省略。

實例

package com.hspedu.p386;

public class codeBlock01 {
    public static void main(String[] args) {
        Movie movie = new Movie("你好,李煥英");
    }
}

class Movie {
    private String name;
    private double price;
    private String director;

    //3個構造器-》重載
    //老韓解讀
    //(1)下麵的三個構造器都有相同的語句
    //(2)這樣代碼看起來比較冗餘
    //(3)這時我們可以把相同的語句,放入到一個代碼塊中,即可
    //(4)這樣當我們不管調用哪個構造器,創建對象,都會先調用代碼塊的內容
    //(5)代碼塊調用的順序優先於構造器。。
    {
        System.out.println("電影屏幕打開...");
        System.out.println("廣告開始...");
        System.out.println("電影正是開始...");
    }

    public Movie(String name) {
        System.out.println("Hovie(Strihg name)被調用。.。");
        this.name = name;
    }

    public Movie(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public Movie(String name, double price, String director) {
        this.name = name;
        this.price = price;
        this.director = director;
    }
}

代碼塊使用註意事項和細節討論

  1. static代碼塊也叫靜態代碼塊,作用就是對類進行初始化,而且它隨著類的載入而執行,並且只會執行一次。如果是普通代碼塊,每創建一個對象,就執行。

  2. 類什麼時候被載入

    ①創建對象實例時(new)
    ②創建子類對象實例,父類也會被載入
    ③使用類的靜態成員時(靜態屬性,靜態方法)

    案例演示: A類 extends B類 的靜態塊

  3. 普通的代碼塊,在創建對象實例時,會被隱式的調用。被創建一次,就會調用一次。
    如果只是使用類的靜態成員時,普通代碼塊並不會執行。

小結:

  1. static代碼塊是類載入時,執行,只會執行一次

  2. 普通代碼塊是在創建對象時調用的,創建一次,調用一次

  3. 類載入的3種情況,需要記住.

    public class Test {
        public static void main(String[] args) {
    //        new A();
    //        new A();
    //
    //        System.out.println("------------------");
    //        new B();
    
    //        System.out.println("------------------");
    //        System.out.println(A.a);
            System.out.println(B.b);
        }
    }
    
    class A {
        public static int a=10;
        static {//只在載入類時運行
            System.out.println("111111");
        }
        {//普通代碼塊,在new對象時,被調用,而且是每創建一個對象,就調用一次
         //可以這樣簡單的,理解普通代碼塊是構造器的補充
            System.out.println("普通");
        }
    }
    class B extends A{
        static {
            System.out.println("2222222");
        }
    }
    
  1. 創建一個對象時,在一個類調用順序是:(重點,難點)∶
    ①調用靜態代碼塊和靜態屬性初始化(註意:靜態代碼塊和靜態屬性初始化調用的優先順序一樣,如果有多個靜態代碼塊和多個靜態變數初始化,則按他們定義的順序調用)
    ②調用普通代碼塊和普通屬性的初始化(註意:普通代碼塊和普通屬性初始化調用的優先順序一樣,如果有多個普通代碼塊和多個普通屬性初始化,則按定義順序調用)
    ③調用構造方法。
  1. 構造器的最前面其實隱含了super()和調用普通代碼塊,新寫一個類,靜態相關的代碼塊,屬性初始化,在類載入時,就執行完畢,因此是優先於構造器和普通代碼塊執行的

    class A2 {
        public void A() { //構造器
            //這裡有隱藏的執行要求
            //(1) super():這個知識點,在前面講解繼承的時候,老師說
            // (2)調用普通代碼塊的
            System.out.println("ok");
        }
    }
    
  2. 我們看一下創建一個子類時(繼承關係),他們的靜態代碼塊,靜態屬性初始化,普通代碼塊,普通屬性初始化,構造方法的調用順序如下:

    ①父類的靜態代碼塊和靜態屬性(優先順序一樣,按定義順序執行)

    ②子類的靜態代碼塊和靜態屬性(優先順序一樣,按定義順序執行)

    ③父類的普通代碼塊和普通屬性初始化(優先順序一樣,按定義順序執行)

    ④父類的構造方法

    ⑤子類的普通代碼塊和普通屬性初始化(優先順序一樣,按定義順序執行)

    ⑥子類的構造方法//面試題

public class Test {
    public static void main(String[] args) {
        //老師說明
        //(1)進行類的載入
        //1.1 先載入父類A02 1.2 再載入Bo2//(2)創建對象
        new B02();//對象
    }
}

class A02 {//父類
    private static int n1 = getVal01();
    static {
        System.out.println("A02的一個靜態代碼塊..");//2
    }
    {
        System.out.println("A02的第一個普通代碼塊..");//5
    }
    public int n3 = getVal02();
    public static int getVal01() {
        System.out.println("getVal01");//1
        return 10;
    }
    public int getVal02() {
        System.out.println("getVal02");//6
        return 10;
    }
    public A02() {
        System.out.println("A02的構造器");//7
    }
}

class B02 extends A02 { //
    private static int n3 = getVal03();
    static {
        System.out.println("BO2的一個靜態代碼塊..");//4
    }
    public int n5 = getVal04();
    {
        System.out.println("B02的第一個普通代碼塊..");//9
    }
    public static int getVal03() {
        System.out.println("getVal03");//3
        return 10;
    }
    public int getVal04() {
        System.out.println("getVal04");//8
        return 10;
    }
    public B02() {
        System.out.println("BO2的構造器");//10
    }
}
  1. 靜態代碼塊只能直接調用靜態成員(靜態屬性和靜態方法),普通代碼塊可以調用任意成員。

單例設計模式

什麼是單例模式

  1. 所謂類的單例設計模式,就是採取一定的方法保證在整個的軟體系統中,對某個類只能存在一個對象實例,並且該類只提供一個取得其對象實例的方法
  2. 單例模式有兩種方式: 1)餓漢式 2)懶漢式

單例模式應用實例

演示餓漢式和懶漢式單例模式的實現。步驟如下:

  1. 構造器私有化=》防止直接new
  2. 類的內部創建對象
  3. 向外暴露一個靜態的公共方法。getInstance
  4. 代碼實現

餓漢式

public class Test {
    public static void main(String[] args) {
        GirlFriend instance = GirlFriend.getInstance();
        System.out.println(instance);

        GirlFriend instance2 = GirlFriend.getInstance();
        System.out.println(instance2);

        System.out.println(instance == instance2);//T
    }
}

class GirlFriend {
    private String name;
    //為了能夠在靜態方法中,返回gf對象,需要將其修飾為static
    private static GirlFriend gf = new GirlFriend("小紅紅");
    //如何保障我們只能創建一個 GirlFriend對象
    //步驟
    //步驟[單例模式-餓漢式]
    //1。將構造器私有化
    // 2.在類的內部直接創建
    //3.提供一個公共的static方法,返回 gf對象
    private GirlFriend(String name) {
        this.name = name;
    }

    public static GirlFriend getInstance() {
        return gf;
    }
}

懶漢式

package com.hspedu.test;

public class Test {
    public static void main(String[] args) {
        //new Cat("大黃");
        Cat instance = Cat.getInstance();
        System.out.println(instance);

    }
}

class Cat {
    private String name;
    private static Cat cat;//步驟

    //1.仍然構造器私有化
    //2.定義一個static屬性對象
    //3.提供一個public的static方法,可以返回一個Cat對象
    private Cat(String name) {
        this.name = name;
    }

    public static Cat getInstance() {
        if (cat == null) {//如果沒有創建cat封象
            cat = new Cat("小可愛");
        }
        return cat;
    }
}

餓漢式VS懶漢式

  1. 二者最主要的區別在於創建對象的時機不同:餓漢式是在類載入就創建了對象實例,而懶漢式是在使用時才創建。

  2. 餓漢式不存線上程安全問題,懶漢式存線上程安全問題。(後面學習線程後,會完善)

  3. 餓漢式存在浪費資源的可能。因為如果程式員一個對象實例都沒有使用,那麼餓漢式創建的對象就浪費了,懶漢式是使用時才創建,就不存在這個問題。

    在我們javaSE標準類中,java.lang.Runtime就是經典的單例模式。

final關鍵字

●基本介紹

final中文意思:最後的,最終的.
final 可以修飾類、屬性、方法和局部變數.
在某些情況下,程式員可能有以下需求,就會使用到final:

  1. 當不希望類被繼承時,可以用final修飾.

    final class A{ }
    class B extends A {}//會報錯
    
  2. 當不希望父類的某個方法被子類覆蓋/重寫(override)時,可以用final關鍵字修飾。【案例演示:訪問修飾符 final 返回類型方法名】

    class c {
    	//如果我們要求hi不能被子類重寫
    	//可以使用final修飾hi方法
    	public final void hi() {}
    }
    class D extends C {
        @0verride
    	public void hi {//報錯
    		System.out.println("重寫了C類的hi方法..");
    	}
    }
    
  3. 當不希望類的的某個屬性的值被修改,可以用final修飾.【案例演示: public final double TAX_RATE=0.08】

  4. 當不希望某個局部變數被修改,可以使用final修飾【案例演示: final double TAX_RATE=0.08)

    class F {
    	public void cry() {
    		//這時,NUM也稱為局部常量
        	final double NUM = 0.01;
            NUM= 0.9;//報錯
    		System.out.println("NUM=" + NUM);
    	}
    }
    

final使用註意事項和細節討論

  1. final修飾的屬性又叫常量,一般用XX_XX_XX 來命名

  2. final修飾的屬性在定義時,必須賦初值,並且以後不能再修改,賦值可以在如下位置之一【選擇一個位置賦初值即可】:

    ①定義時:如public final double TAX_RATE=0.08;
    ②在構造器中
    ③在代碼塊中。

  3. 如果final修飾的屬性是靜態的,則初始化的位置只能是
    ①定義時
    ②在靜態代碼塊賦值,不能在構造器中賦值。

  4. final類不能繼承,但是可以實例化對象。

  5. 如果類不是final類,但是含有final方法,則該方法雖然不能重寫,但是可以被繼承。

  6. 一般來說,如果一個類已經是final類了,就沒有必要再將方法修飾成final方法。(多此一舉)

  7. final不能修飾構造方法(即構造器)

  8. final和static往往搭配使用,效率更高,底層編譯器做了優化處理。

  9. 包裝類(Integer,Double,Float,Boolean等都是final),String也是final類。

抽象類

當父類的某些方法,需要聲明,但是又不確定如何實現時,可以將其聲明為抽象方法,那麼這個類就是抽象類

abstract class Animal {
    private String name;

    public Animal(String name) {
        this.name = name;
    }
    //思考:這裡eat這裡你實現了,其實沒有什麼意義//即:父類方法不確定性的問題
    //===>考慮將該方法設計為抽象(abstract)方法//===>所謂抽象方法就是沒有實現的方法
    //===>所謂沒有實現就是指,沒有方法體
    //===>當一個類中存在抽象方法時,需要將該類聲明為abstract類
    
//    public void eat() {
//        System.out.println("這是一個動物,但是不知道吃什麼..");
//    }
    public abstract void eat() ;
}

抽象類的介紹

  1. 用abstract關鍵字來修飾一個類時,這個類就叫抽象類

    訪問修飾符 abstract 類名{
    }

  2. 用abstract關鍵字來修飾一個方法時,這個方法就是抽象方法
    訪問修飾符 abstract 返回類型 方法名(參數列表);//沒有方法體

  3. 抽象類的價值更多作用是在於設計,是設計者設計好後,讓子類繼承並實現抽象類()

  4. 抽象類,是考官比較愛問的知識點,在框架和設計模式使用較多

抽象類使用的註意事項和細節討論

  1. 抽象類不能被實例化

    public class AbstractDetail01 {
        public static void main(String[] args) {
    		//抽象類,不能被實例化
            new A();//報錯
        }
    }
    abstract class A { 
    }
    
  2. 抽象類不一定要包含abstract方法。也就是說,抽象類可以沒有abstract方法

  3. 一旦類包含了abstract方法,則這個類必須聲明為abstract

    class B {//報錯
    	public abstract void hi();
    }
    
  4. abstract只能修飾類和方法,不能修飾屬性和其它的。

  5. 抽象類可以有任意成員【抽象類還是類】,比如:非抽象方法、構造器、靜態屬性等等

  6. 抽象方法不能有主體,即不能實現.如圖所示
    abstract void aaa(){......}//報錯,不能存在“{}”

  7. 如果一個類繼承了抽象類,則它必須實現抽象類的所有抽象方法,除非它自己也聲明為abstract類。[舉例 A類,B類,C類]

    //如果一個類繼承了抽象類,則它必須實現抽象類的所有抽象方法,除非它自己也聲明為abstract類
    abstract class E {
        public abstract void hi();
    }
    
    abstract class F extends E {
    }
    
    class G extends E {
        @Override
        public void hi() { //這裡相等於G子類實現了父類E的抽象方法,所謂實現方法,就是有方法體
        }
    }
    
  8. 抽象方法不能使用private、final和static來修飾,因為這些關鍵字都是和重寫相違背的。

抽象類最佳實踐-模板設計模式

最佳實踐

需求

  1. 有多個類,完成不同的任務job
  2. 要求統計得到各自完成任務的時間
  3. 請編程實現

感情的自然流露

  1. 先用最容易想到的方法
  2. 分析問題,提出使用模板設計模式
public class Test {
	public static void main(String[] args) {
		new A().job();
		new B().job();
	}
}
//如果一個類繼承了抽象類,則它必須實現抽象類的所有抽象方法,除非它自己也聲明為abstract類
class A {
	public void job() {
        //得到開始的時間
        long start = System.currentTimeMillis();
        long num = 0;
        for (long i = 1; i <= 80000000; i++) {
        	num += i;
        }
        //得的結束的時間
        long end = System.currentTimeMillis();
        System.out.println("執行時間" + (end - start));
    }
}
class B {
    public void job() {
        //得到開始的時間
        long start = System.currentTimeMillis();
        long num = 0;
        for (long i = -10; i <= 800000; i++) {
            num += i;
        }
        //得的結束的時間
        long end = System.currentTimeMillis();
        System.out.println("執行時間" + (end - start));
    }
}

最佳實踐(使用動態綁定機制)

設計一個抽象類(Template),能完成如下功能:

  1. 編寫方法calculateTime( ,可以計算某段代碼的耗時時間
  2. 編寫抽象方法job()
  3. 編寫子類AA、BB,繼承抽象類Template,並實現job方法。
  4. 編寫一個測試類Test,看看是否好用。
public class Test {
    public static void main(String[] args) {
        AA aa = new AA();
        aa.calculateTime();
        BB bb = new BB();
        bb.calculateTime();
    }
}
abstract class Template {//抽象類-模板設計模式
    public abstract void job();//抽象方法
    public void calculateTime() {//實現方法,調用job方法
        //得到開始的時間
        long start = System.currentTimeMillis();
        job();
        //得的結束的時間
        long end = System.currentTimeMillis();
        System.out.println("執行時間" + (end - start));
    }
}
//如果一個類繼承了抽象類,則它必須實現抽象類的所有抽象方法,除非它自己也聲明為abstract類
class AA extends Template{
    public void job() {
        long num = 0;
        for (long i = 1; i <= 80000000; i++) {
            num += i;
        }
    }
}
class BB extends Template{
    public void job() {
        //得到開始的時間
        long num = 0;
        for (long i = -10; i <= 800000; i++) {
            num += i;
        }
    }
}

介面

為什麼有介面

先看一張圖:

image

usb插槽就是現實中的介面。

你可以把手機,相機,u盤都插在usb插槽上,而不用擔心那個插槽是專門插哪個的,原因是做usb插槽的廠家和做各種設備的廠家都遵守了統一的規定包括尺寸,排線等等。

介面快速入門

這樣的設計需求在java編程/php/.net/go中也是會大量存在的,我曾經說過,一個程式就是一個世界,在現實世界存在的情況,在程式中也會出現。我們用程式來模擬一下。

介面

public interface UsbInterface {
    //規定介面的相關方法
    public void start();
    public void stop();
}

相機

public class Camera implements UsbInterface{//實現介面,就是把介面方法實現
    //IDEA中alt+insert,選擇implement methods(或ctrl+i)快速創建相應的方法
    @Override
    public void start() {
        System.out.println("相機開始工作");
    }
    @Override
    public void stop() {
        System.out.println("相機停止工作");
    }
}

手機

//Phone 類 實現 UsbInterface
//解讀1.即 Phone類需要實現UsbInterface介面 規定/聲明的方法
public class Phone implements UsbInterface{
    @Override
    public void start() {
        System.out.println("手機開始工作。..");
    }
    @Override
    public void stop() {
        System.out.println("手機停止工作..... ");
    }
}

電腦

public class Computer {
    //編寫一個方法,電腦工作
    public void work(UsbInterface usbInterface) {
        usbInterface.start();
        usbInterface.stop();
    }
}

調用

public class Test {
    public static void main(String[] args) {
        //創建手機,相機對象
        Camera camera = new Camera();
        Phone phone = new Phone();
        //創建電腦
        Computer computer = new Computer();
        computer.work(phone);//把手機接入到電腦
        System.out.println("-----------------");
        computer.work(camera);
    }
}

●基本介紹

介面就是給出一些沒有實現的方法,封裝到一起,到某個類要使用的時候,在根據具體情況把這些方法寫出來。語法:

interface 介面名{
//屬性
//方法(1.抽象方法 2.預設實現方法 3.靜態方法)
}
class 類名 implements 介面{
自己屬性;
自己方法;
必須實現的介面的抽象方法
}

小結:

  1. 在Jdk7.0前介面里的所有方法都沒有方法體,即都是抽象方法。(已被淘汰,基本不用)
  2. Jdk8.0後介面可以有靜態方法,預設方法,也就是說介面中可以有方法的具體實現
interface AInterface {
    //寫屬性
    public int n1 = 10;//寫方法

    //在介面中,抽象方法,可以省略abstract關鍵字
    public void hi();

    //在jdk8後,可以有預設實現方法,需要使用default關鍵字修飾
    default public void ok() {
        System.out.println("ok ...");
    }

    //在jdk8後,可以有靜態方法
    public static void cry() {
        System.out.println("cry........");
    }
}

深入討

對初學者講,理解介面的概念不算太難,難的是不知道什麼時候使用介面,下麵我例舉幾個應用場景:

  1. 說現在要製造戰鬥機,武裝直升機.專家只需把飛機需要的功能/規格定下來即可,然後讓別的人具體實現就可。
  2. 說現在有一個項目經理,管理三個程式員,功能開發一個軟體,為了控制和管理軟體,項目經理可以定義一些介面,然後由程式員具體實現。

註意事項和細節

  1. 介面不能被實例化

  2. 介面中所有的方法是public方法,介面中抽象方法,可以不用寫public和abstract,如下:

    void aa();實際上是public abstract void aa();

  3. 一個普通類實現介面,就必須將該介面的所有方法都實現。

    class Phone implements UsbInterface {//報錯
    }
    
  4. 抽象類實現介面,可以不用實現介面的方法。

    abstract class Phone implements UsbInterface {//不報錯
    }
    
  5. 一個類同時可以實現多個介面

    class Pig implements IB,IC {
    }
    
  6. 介面中的屬性,只能是final的,而且是public static final修飾符。比如:
    int a=1;實際上是public static final int a=1;(必須初始化)

  7. 介面中屬性的訪問形式:介面名.屬性名

  8. 一個介面不能繼承其它的類,但是可以繼承多個別的介面
    interface A extends B,C{}

  9. 介面的修飾符只能是public和預設,這點和類的修飾符是一樣的。

實現介面Vs繼承類

▶介面和繼承解決的問題不同

繼承的價值主要在於:解決代碼的復用性和可維護性。
介面的價值主要在於:設計,設計好各種規範(方法),讓其它類去實現這些方法。即更加的靈活..

▶介面比繼承更加靈活

介面比繼承更加靈活,繼承是滿足is -a的關係(小明是學生),而介面只需滿足like - a的關係(飛機像鳥一樣飛)

▶介面在一定程度上實現代碼解耦[即:介面規範性+動態綁定]

介面的多態特性(和類的繼承中的多態類似)

  1. 多態參數(前面案例體現)

    在前面的Usb介面案例,Usb usb,既可以接收手機對象,又可以接收相機對象,就體現了介面多態(介面引用可以指向實現了介面的類的對象)

  2. 多態數組

    演示一個案例:給Usb數組中,存放 Phone 和相機對象,Phone類還有一個特有的方法call),請遍歷Usb數組,如果是Phone對象,除了調用Usb介面定義的方法外,還需要調用Phone特有方法call.

    Usb usbs[] = new Usb[2];
    usbs[0]= new Phone();
    usbs[1] = new Camera();
    for (int i = 0; i< usbs.length; i++){
        usbs[i].start();
        usbs[i].stop();
        if (usbs[i] instanceof Phone){
            ((Phone)usbs[i]).call();
        }
    }
    
  3. 介面存在多態傳遞現象。

    class InterfacePolyPass {
        public static void main(String[] args) {
            //介面類型的變數可以指向,實現了該介面的類的對象實例
            IG ig = new Teacher();
            //如果IG 繼承了IH 介面,而Teacher類實現了 IG介面
            // 那麼,實際上就相當於Teacher類也實現了IH介面,
            //這就是所謂的介面多態多態傳遞現象.
            IH ih = new Teacher();
        }
    }
    interface IH {
        void hi();
    }
    interface IG extends IH {
    }
    class Teacher implements IG {
        @Override
        public void hi() {
        }
    }
    

類定義的進一步完善

image

內部類

基本介紹

一個類的內部又完整的嵌套了另一個類結構。被嵌套的類稱為內部類(inner class),嵌套其他類的類稱為外部類(outer class)。是我們類的第五大成員【思考:類的五大成員是哪些?屬性 方法 構造器 代碼塊 內部類】,內部類最大的特點就是可以直接訪問私有屬性,並且可以體現類與類之間的包含關係。(難點)

●基本語法

class Outer{//外部類
	class Inner{//內部類
	}
}
class Other{//外部其他類
}

內部類的分類

定義在外部類局部位置上(比如方法內):

  1. 局部內部類(有類名)
  2. 匿名內部類(沒有類名,重點!!!!!!)

定義在外部類的成員位置上:

  1. 成員內部類(沒用static修飾)
  2. 靜態內部類(使用static修飾)

局部內部類的使用

說明:局部內部類是定義在外部類的局部位置,比如方法中,並且有類名.

  1. 可以直接訪問外部類的所有成員,包含私有的

  2. 不能添加訪問修飾符,因為它的地位就是一個局部變數。局部變數是不能使用修飾符的。但是可以使用final修飾,因為局部變數也可以使用final

  3. 作用域:僅僅在定義它的方法或代碼塊中。

  4. 局部內部類---訪問---->外部類的成員[訪問方式:直接訪問]

  5. 外部類---訪問---->局部內部類的成員
    訪問方式:創建對象,再訪問(註意:必須在作用域內)

  6. 外部其他類---不能訪問----->局部內部類(因為局部內部類地位是一個局部變數)

  7. 如果外部類和局部內部類的成員重名時,預設遵循就近原則,如果想訪問外部類的成員,則可以使用(外部類名.this.成員)去訪問

    System.out.println("外部類的n2=”+外部類名.this.n2);

記住:(1)局部內部類定義在方法中/代碼塊 (2)作用域在方法體或者代碼塊中 (3)本質仍然是一個類

public class Test {
    public static void main(String[] args) {
        Outer02 outer02 = new Outer02();
        outer02.m1();
    }
}

class Outer02 {//外部類
    private int n1 = 100;

    private void m2() {
        System.out.println("Outer02 m2()");
    }//私有方法

    public void m1() {//方法
        //1.局部內部類是定義在外部類的局部位置,通常在方法
        // 3.不能添加訪問修飾符,但是可以使用final修飾
        // 4.作用域︰僅僅在定義它的方法或代碼塊中
        String name = "xXx";
        final class Inner02 {//局部內部類(本質仍然是一個類)
            private int n1=800;
            //2.可以直接訪問外部類的所有成員,包含私有的
            public void f1() {
                //5.局部內部類可以直接訪問外部類的成員,比如下麵外部類n1 和 m2()
                //7.如果外部類和局部內部類的成員重名時,預設遵循就近原則,如果想訪問外部類的成員,
                //   使用 外部類名.this.成員)去訪問
                System.out.println("n1=" + n1 +"外部類的n1="+Outer02.this.n1);
                m2();
            }
        }
        //6.外部類在方法中,可以創建Inner02對象,然後調用方法即可
        Inner02 inner02 = new Inner02();
        inner02.f1();

//        class Inner022 extends Inner02{
//        }
    }

    { //代碼塊
        class Inner03 {
        }
    }
}

匿名內部類的使用(重要!!!)

//(1)本質是類 (2)內部類 (3)該類沒有名字 (4)同時還是一個對象

必須繼承—個抽象類或者實現一個介面

說明:匿名內部類是定義在外部類的局部位置,比如方法中,並且沒有類名

  1. 匿名內部類的基本語法

    new 類或介面(參數列表){
    	類體
    };
    
  2. 匿名內部類的語法比較奇特,請大家註意,因為匿名內部類既是一個類的定義同時它本身也是一個對象,因此從語法上看,它既有定義類的特征,也有創建對象的特征,對前面代碼分析可以看出這個特點,因此可以調用匿名內部類方法。

  3. 可以直接訪問外部類的所有成員,包含私有的

  4. 不能添加訪問修飾符,因為它的地位就是一個局部變數。

  5. 作用域:僅僅在定義它的方法或代碼塊中。

  6. 匿名內部類---訪問---->外部類成員[訪問方式:直接訪問]

  7. 外部其他類---不能訪問----->匿名內部類(因為匿名內部類地位是一個局部變數)

  8. 如果外部類和匿名內部類的成員重名時,匿名內部類訪問的話,預設遵循就近原則如果想訪問外部類的成員,則可以使用(外部類名.this.成員)去訪問

例子(基於介面)

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

class Outer04 {//外部類
    private int n1 = 10;//屬性

    public void method() {//方法
        //基於介面的匿名內部類//老韓解讀
        //1.需求:想使用IA介面,並創建對象
        //2.傳統方式,是寫一個類,實現該介面,並創建對象
        //3.老韓需求是 Tiger/Dog 類只是使用一次,後面再不使用
        //4.可以使用匿名內部類來簡化開發
        //5.tiger的編譯類型? IA
        //6.tiger的運行類型?就是匿名內部類(對象名.getClass()可獲取名字)  Outer04$1
        /*我們看底層
        class XXXX implements IA {
            @override
            public void cry(O {
                System.out'.println("老虎叫喚...");
            }
        }
        */
        //7.jdk底層在創建匿名內部類Outer04$1,立即馬上就創建了Outer04$1實例,並且把地址
        // 返回給tiger
        //8.匿名內部類使用一次,就不能再使用(但此處的tiger對象可重覆使用)
        IA tiger = new IA(){
            @Override
            public void cry(){
                System.out.println("老虎叫喚");
            }
        };
        System.out.println("tiger的運行類型=" + tiger.getClass());
        tiger.cry();
        tiger.cry();
    }
}
interface IA {//介面
    public void cry();
}

例子(基於方法)

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

class Outer04 {//外部類
    private int n1 = 10;//屬性

    public void method() {//方法
        //演示基於類的匿名內部類
        // 分析
        //1.father編譯類型 Father
        //2.father運行類型Outer04$2
        //3.底層會創建匿名內部類
        /*
            class Outer04$1 extends Father{
                override
                public void test() {
                    System.out.println("匿名內部類重寫了test方法");
                }
            }
        */
        //4.同時也直接返回了匿名內部類Outer04$2的對象
        Father tiger = new Father("jack") {

            public void test() {
                System.out.println("匿名內部類重寫了test方法");
            }
        };
        System.out.println("tiger的運行類型=" + tiger.getClass());//Outer04$1
        tiger.test();
        //基於抽象類的匿名內部類
        Animal animal = new Animal() {
            @Override
            void eat() {
                System.out.println("小狗吃骨頭...");
            }
        };
        animal.eat();
    }
}

class Father {//類

    public Father(String name) {//構造器
    }

    public void test() {//方法
    }
}

abstract class Animal {//抽象類
    abstract void eat();
}

成員內部類的使用

說明:成員內部類是定義在外部類的成員位置,並且沒有static修飾。

  1. 可以直接訪問外部類的所有成員,.包含私有的

  2. 可以添加任意訪問修飾符(public、protected、預設、private),因為它的地位就是一個成員。

  3. 作用域

    和外部類的其他成員一樣,為整個類體比如前面案例,在外部類的成員方法中創建成員內部類對象,再調用方法.

  4. 成員內部類---訪問---->外部類(比如:屬性)[訪問方式:直接訪問](說明)

  5. 外部類---訪問------>內部類(說明)訪問方式:創建對象,再訪問

  6. 外部其他類---訪問---->成員內部類

  7. 如果外部類和內部類的成員重名時,內部類訪問的話,預設遵循就近原則,如果想訪問外部類的成員,則可以使用(外部類名.this.成員)去訪問

public class Test {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.t1();
        
        //6.外部其他類,使用成員內部類的兩種方式
        // outer08.new Inner08();相當於把 new Inner08()當做是outer08成員
        //這就是一個語法,不要特別的糾結.
		Outer08.Innter08 innter08 = outer08.new Innter08();
        
        //第二方式在外部類中,編寫一個方法,可以返回 Inner08對象
		Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
        
        
        
    }
}

class Outer08 {//外部類
    private int n1 = 10;
    public String name = "張三";
    private void hi() {
    	System.out.println("hi()方法...");
    }

    //1.註意:成員內部類,是定義在外部內的成員位置上
    //2.可以添加任意訪問修飾符(public、protected 、預設、private),因為它的地位就是一個成員
    private class Inner08 {//成員內部類
        private double sal = 99.8;
        public void say() {
            //4.可以直接訪問外部類的所有成員,包含私有的
            System.out.println("n1 = " + n1 + " name = " + name);
            hi();//4
        }
    }
    
    //方法,返回一個Inner08實例
    public Inner08 getInner08Instance(){
		return new Inner08();
	}

    //寫方法
    public void t1() {
        //4.使用成員內部類
        //創建成員內部類的對象,然後使用相關的方法
        Inner08 inner08 = new Inner08();
        inner08.say();
        System.out.println(innerO8.sal);
    }
}

靜態內部類的使用

說明:靜態內部類是定義在外部類的成員位置,並且有static修飾

  1. 可以直接訪問外部類的所有靜態成員,包含私有的,但不能直接訪問非靜態成員
  2. 可以添加任意訪問修飾符(public.protected、預設、private),因為它的地位就是一個成員。
  3. 作用域:同其他的成員。為整個類體
  4. 靜態內部類---訪問---->外部類(比如:靜態屬性)[訪問方式:直接訪問所有靜
    態成員]
  5. 外部類---訪問------>靜態內部類訪問方式:創建對象,再訪問
  6. 外部其他類---訪問--->靜態內部類
  7. 如果外部類和靜態內部類的成員重名時,靜態內部類訪問的時,預設遵循就近原則,如果想訪問外部類的成員,則可以使用(外部類名.成員)去訪問
public class Test {
    public static void main(String[] args) {
        Outer10 outer10 = new Outer10();
        outer10.m1();

        //外部其他類使用靜態內部類
        // 方式1
        // 因為靜態內部類,是可以通過類名直接訪問(前提是滿足訪問許可權)
        Outer10.Inner10 inner10 = new Outer10.Inner10();
        inner10.say();
        //方式2
        //編寫一個方法,可以返回靜態內部類的對象實例。
        Outer10.Inner10 inner101 = outer10.getInner10();
        System.out.println("============");
        inner101.say();

        Outer10.Inner10 inner10_ = outer10.getInner10_();
        System.out.println("============");
        inner10_.say();
    }
}

class Outer10 {//外部類
    private int n1 = 10;
    private static String name = "張三";
    private static void cry(){}
    // Inner10就是靜態內部類
    //1.放在外部類的成員位置
    //2.使用static修飾
    //3,可以直接訪問外部類的所有靜態成員,包含私有的,但不能直接訪問非靜態成員
    //4,可以添加任意訪問修飾符(public、 protected 、預設、private),因為它的地位就是一個成員//5。作用域:同其他的成員,為整個類體
    //5.作用域:同其他的成員,為整個類體
    static class Inner10 {
        private static String name ="李四";
        public void say() {
            System.out.println(name);
            System.out.println(Outer10.name);//7
            cry();
        }
    }
    public void m1() {
        Inner10 inner10 = new Inner10();
        inner10.say();
    }
    public Inner10 getInner10(){
        return new Inner10();
    }
    public static Inner10 getInner10_(){
        return new Inner10();
    }
}

這裡:小結
(1)內部類有四種局部內部類,匿名內部類成員內部類,靜態內部類
(2)重點還是掌握匿名內部類使用

new 類/介面(參數列表){
	//...
};

(3)成員內部類,靜態內部類是放在外部類的成員位置,本質就是一個成員.
(4)其他細節看之前筆記...


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 冪等性在我們的工作中無處不在,無論是支付場景還是下訂單等核心場景都會涉及,也是分散式系統最常遇到的問題,除此之外,也是大廠面試的重災區。 知道了冪等性的重要性,下麵我就詳細介紹冪等性以及具體的解決方案,希望對大家有所幫助@mikechen 什麼是冪等性 冪等是一個數學與電腦學概念,在數學中某一元運 ...
  • 異常 筆記目錄:(https://www.cnblogs.com/wenjie2000/p/16378441.html) 運行下麵的代碼,看看有什麼問題->引出異常和異常處理機制 public static void main(String[] args) { int num1 =10; int n ...
  • 枚舉和註釋 筆記目錄:(https://www.cnblogs.com/wenjie2000/p/16378441.html) 自定義類實現枚舉 先看一個需求 要求創建季節(Season)對象,請設計並完成。 創建Season對象有如下特點 1.季節的值是有限的幾個值(spring, summer, ...
  • 一、分庫分表解決的現狀問題 解決資料庫本身瓶頸 連接數: 連接數過多時,就會出現‘too many connections’的錯誤,訪問量太大或者資料庫設置的最大連接數太小的原因 Mysql預設的最大連接數為100.可以修改,而mysql服務允許的最大連接數為16384 資料庫分表可以解決單表海量數 ...
  • #反射機制 AVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。 Java反射機制在框架設計中極為廣泛,需要深入理解。 ##反射基礎 RTTI(R ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • MySQL之JDBC 一、JDBC是什麼 Java DatabaseConnectivity (java語言連接資料庫) 二、JDBC的本質 JDBC是SUN公司制定的一套介面(interface)。 介面都有調用者和實現者。 面向介面調用、面向介面寫實現類,這都屬於面向介面編程。 三、為什麼要面向 ...
  • 1.認識shiro 除Spring Security安全框架外,應用非常廣泛的就是Apache的強大又靈活的開源安全框架 Shiro,在國內使用量遠遠超過Spring Security。它能夠用於身份驗證、授權、加密和會話管理, 有易於理解的API,可以快速、輕鬆地構建任何應用程式。而且大部分人覺得 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...