一些JavaSE學習過程當中的思路整理(二)(主觀性強,持續更新中...)

一些JavaSE學習過程當中的思路整理(二)(主觀性強,持續更新中...)

未經做者容許,不可轉載,若有錯誤,歡迎指正o( ̄▽ ̄)ojava

將一個子類的引用對象賦值給超類的對象(多態)

  • 賦予了子類對象的超類對象只能調用超類中定義的public成員變量和方法
  • 若是子類重寫了超類中的方法,就會調用子類中的方法(這裏真的有點搞心態)
  • 關於多態:具體使用有兩種常見方式(核心原理都是將子類的對象賦值給超類的對象引用)
    • 定義方法時形參類型爲父親,傳入的類型爲子類類型參數
    • 定義方法時返回類型爲父親,實際調用時返回的類型子類對象

建議本身寫個小的測試demo體會一下,下面是個人測試代碼c++

public class Test1 {
    public static void main(String[] args) {
        VIP vip = new VIP();
        SuperVIP superVIP = new SuperVIP();
        Owner owner = new Owner();
        //這裏owner對象的custom成員變量所屬的類是兩個VIP類的超類
        owner.setCustom(superVIP);
        owner.settlement();
        //custom本質上是一個SuperVIP的對象的引用
        Custom custom = owner.getCustom();
        //這裏調用的是子類重寫後的方法
        custom.buyBook();
        //這個num是父類的public成員變量,可是沒法獲取custom的私有成員變量
        //private修飾的成員變量只能在同一個類中直接訪問,一般用該類的public方法訪問
        System.out.println(custom.num);
        //System.out.println(custom.getName());也沒法獲取子類的私有成員屬性
        SuperVIP superVIP1 = (SuperVIP) custom;
        //之因此能強制類型轉化是由於custom本質上就是SuperVIP對象引用
        //因此說繼承是多態的前提
        System.out.println(superVIP1.getName());
    }
}

class Custom {
    private int money = 50;
    public int num = 100;
    public void buyBook() {
        System.out.println("店主買書不花錢");
    }
}

class VIP extends Custom {
    public void buyBook() {
        System.out.println("普通會員買書打八折");
    }
}

class SuperVIP extends Custom {
    private String name = "我是SVIP";
    public void buyBook() {
        System.out.println("超級會員買書打六折");
    }
    public String getName() {
        return name;
    }
}

class Owner {
    private Custom custom;

    public Custom getCustom() {
        return custom;
    }

    public void setCustom(Custom custom) {
        this.custom = custom;
    }

    public void settlement() {
        custom.buyBook();
    }
}

抽象方法和抽象類

  • 抽象類中能夠有普通方法,普通類中不能有抽象方法
  • 當子類繼承抽象父類,若子類不是抽象類,則要重寫父類的全部抽象方法

簡單歸納如下包裝器類的做用

對應8個基本數據類型有8個包裝器類,這些類能夠用於新建對應8個基本數據類型的對象,而且有着自動裝箱,拆箱的功能(如:不少時候一些方法的參數時Obj類型的對象,可是咱們直接傳入基本數據類型的參數能夠完成調用),在這些類中還定義了許多靜態方法用於基本數據類型和包裝器類之間的轉換,這裏感受力扣上刷點題應該就會熟悉這些API了程序員

面向接口編程時的一些細節

有一種說法:接口其實就是抽象類,極度抽象的抽象類,接口中不能存在非抽象方法,接口中的全部方法必須所有是抽象方法。在接口使用時有如下一些要求:編程

  • 接口中成員變量只能定義public和默認訪問權限修飾符
  • 接口中的成員變量默認爲static final類型,能夠經過接口直接訪問,同時不能修改值,必須在聲明時完成初始化
  • Java支持單繼承,可是能夠實現多個接口,這裏就體現了接口的解耦合以及制定規範的做用

final關鍵字的功能

  • 修飾一個方法:該方法能夠被繼承但沒法被重寫ide

  • 修飾一個類:該類爲最終類,沒法被繼承函數

  • 修飾一個基本數據類型:該值爲常量沒法被修改學習

以byte類型爲例解釋計算機中以補碼存儲的細節

在八位二進制下,原碼左側第一位爲符號位,除符號位外其他位取反爲反碼,反碼+1爲補碼,計算機中以補碼形式存儲數據,其中要注意0的存儲與-128的存儲測試

  • 0:因爲以補碼形式存儲,0不分正負,這裏將0000 0000做爲0的補碼
  • -128:-128沒有原碼與反碼,而本來「-0」的補碼1000 0000用於表示-128的補碼,這樣就充分利用了每個補碼的表示
  • 補碼的意義:舉個栗子,1按照0000 0001的補碼存儲,-1按照1111 1111的補碼存儲,-1+1 = 0 <=> 0000 0001 + 1111 1111,補碼相加後溢出正好爲0,符合-1+1 == 0的結果,這就正好自圓其說了

下面是一個byte型的1左移6位,7位,8位後的結果測試:this

public class Test1 {
    public static void main(String[] args) {
        byte test = 1; test <<= 6;
        System.out.println(test);
        test = 1; test <<= 7;
        System.out.println(test);
        test = 1; test <<= 8;
        System.out.println(test);
		//如下兩個是int左移7位和31位,這裏是用於區分的,由於默認是int
        System.out.println(1<<7);
        System.out.println(1<<31);
    }
}

異常類的分類&捕獲與throw & throws的區分

異常類是一個樹形的族譜結構,如下兩個異常爲Throwable類的兩個子類.net

  • Error:系統錯誤,程序沒法處理
  • Exception:程序運行時出現的錯誤,程序員能夠處理()
try {
	//可能會拋出異常的代碼
} catch {
	//對異常進行處理	
} finally {
	//必定會執行的代碼
}

throws與throw的用法原文出處

throws關鍵字的做用:

  • throws做用於方法,修飾符 返回值類型 方法名(參數列表) throws Exception
  • throw是寫在邏輯處理過程當中的代碼,人爲拋出異常
  • 若是方法 throw 的是 RuntimeException 異常或者其子類,外部調用時能夠不處理,JVM 會處理。
  • 當人爲拋出Exception對象(或者其子類異常對象時),咱們必須處理這個異常對象(在本方法內對拋出的異常進行捕獲操做),可是也能夠選擇本身不捕獲,試圖甩鍋?這時能夠用throws修飾這個方法,這樣throws就會將異常對象拋出給方法的調用者處理(本身不處理給別人處理),此時方法的調用者就能夠選擇try-catch進行捕獲,或者依舊將本身用throws修飾,繼續甩鍋?最終若是main方法不處理就會交給JVM處理(中斷程序)

接下來時三種throws的使用狀況

1.下面的代碼是異常拋出方法自己不處理,而且調用者依舊選擇不處理,將其交給JVM中斷程序處理

public class Test1 {
    
    public static void main(String[] args) throws Exception {
        int[] array = {1, 2, 3};
        //此時main方法中並未對這個拋出異常對象的方法進行捕獲操做,選擇交給JVM中斷處理程序
        test(array, -1);
    }

    public static void test(int[] array, int index) throws Exception {
        if (index < 0 || index >= 3)
            //這句拋出就必須處理這個異常對象,此時在方法後用throws至關於
            //將這個拋出的異常交給方法的調用者main方法處理
            throw new Exception("下標越界");
        System.out.println(array[index]);
    }
}

2.下面代碼是test方法不處理,調用者main方法選擇本身try-catch捕獲拋出的異常,本身處理

public class Test1 {
    public static void main(String[] args) {
        int[] array = {1, 2, 3};
        //此時方法調用者main選擇捕獲這個拋出異常的方法
        try {
            test(array, -1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void test(int[] array, int index) throws Exception {
        if (index < 0 || index >= 3)
            //這句拋出就必須處理這個異常對象,此時在方法後用throws至關於
            //將這個拋出的異常交給方法的調用者main方法處理
            throw new Exception("下標越界");
        System.out.println(array[index]);
    }
}

3.下面的代碼時test方法選擇本身直接捕獲本身拋出的異常,並進行處理

public class Test1 {

    public static void main(String[] args) {
        int[] array = {1, 2, 3};
        test(array, -1);
    }

    public static void test(int[] array, int index) {
        if (index < 0 || index >= 3)
            //此時test本方法直接本身處理本身拋出的異常對象
            try {
                throw new Exception("下標越界");
            } catch (Exception e) {
                e.printStackTrace();
            }
        System.out.println(array[index]);
    }
}

Java中開啓線程的兩種方式

  • 經過繼承Thread類,Thread類自己繼承了Runnable接口,因此內部已經有了run方法,須要重寫run方法爲咱們須要的業務邏輯代碼,以後實例化Thread類的實例後調用start方法,而不是run方法(start方法包含了開啓線程,等待資源分配,而後調用run方法的過程,直接調用run方法就是單線程的普通調用方法的過程)
  • 經過實現Runnable接口,實現該接口的run方法爲咱們須要的業務邏輯,因爲單單Runnable的實現類是沒有開啓線程的能力的,須要有Thread類的實例來開啓線程。綜上:利用Thread的構造函數,以Runnable接口的實現類爲參數,聲明初始化一個Thread類(要明確線程類是用於搶佔CPU資源的,而搶佔到資源後就會執行run方法中的任務,這裏的Runnable接口的實現類就是run方法的承載者)

下面是一個例子實現了兩種不一樣的開啓線程的方式

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

        //經過實現接口的方式能夠解耦合
        MyRunnable myRunnable = new MyRunnable();
        //調用Thread類的構造函數傳入線程須要執行的任務(業務邏輯)
        Thread thread = new Thread(myRunnable);
        //開啓線程等待資源分配,而後執行run方法業務
        thread.start();

        //經過繼承的方式
        MyThread myThread = new MyThread();
        myThread.start();

        for (int i = 0; i < 1000; i++)
            System.out.println("++++++++++主線程++++++++");
    }
}

class MyRunnable implements Runnable {
    //Runnable是一個接口,內部只有一個抽象的run方法(表明開啓線程後須要執行的業務邏輯)
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("--------MyRunnable--------");
        }
    }
}

class MyThread extends Thread {
    //經過繼承Thread類的方式,因爲Thread類自己就繼承了Runnable接口
    // 所以所建立的Thread類的實例內部有run方法
    //這裏是重寫父類的run方法,替換爲本身想要執行的任務
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++)
            System.out.println("=========MyThread============");
    }
}

線程的五種狀態以及狀態之間的轉換

  • 建立狀態:實例化一個新的線程對象,還未啓動
  • 就緒狀態:建立好的線程對象調用start方法完成啓動,進入線程池等待搶佔CPU資源
  • 運行狀態:線程對象獲取了CPU資源,在必定的時間內執行任務
  • 阻塞狀態:正在運行的線程暫停執行任務,釋放佔用CPU的資源,並在解除阻塞態以後也不能直接回到運行狀態,而是從新回到就緒狀態,等待獲取CPU資源
  • 終止狀態:線程運行完畢或者由於異常致使程序終止運行
建立——》就緒、就緒《——》運行、運行——》阻塞、阻塞——》就緒、運行——》終止

線程的調度:休眠&合併&禮讓&中斷

  • 線程休眠:讓當前線程暫停執行,從運行狀態進入阻塞狀態(至關於被掛起必定的時間,且還不容許回到就緒狀態),將CPU資源釋放,經過sleep()實現,sleep()是Thread類的靜態本地方法,具體實現用c/c++,對於任何一個線程的實例對象來講(包括主線程),均可以使用Thread.sleep()的靜態方法實現對當前線程的掛起,但若是該對象已經繼承了Thread類,則能夠直接打點調用sleep()方法

注意點:在外部調用時,sleep休眠要放在啓動以前;不然就是在內部調用sleep則能夠隨時休眠,下面是一個小的例子

public class Test1 {
    public static void main(String[] args) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //下面開啓子線程的代碼也是包含在main主線程中的,因此主線程掛起後
        //下面的代碼也沒法繼續執行
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++)
            System.out.println("-----MyRunnable------");
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++)
            System.out.println("++++++++MyThread+++++++++");
    }
}
  • 線程合併:線程甲和線程乙,線程甲執行到某個時間點的時候調用線程乙.join方法,則表示從當前時間點開始,CPU資源被線程乙獨佔,線程甲進入阻塞狀態,直到線程乙執行完畢,線程甲進入就緒狀態(至關於乙的執行結束時解除甲的阻塞狀態的條件),等待獲取CPU資源進入運行狀態,而且也能夠經過給join方法輸入參數表示線程乙將獨佔CPU的時間(不輸入參數則是始終獨佔直到乙線程執行結束)

這裏我作了一個測試,若是由三個線程:主線程、甲線程、乙線程,在主線程中某個位置調用甲線程.join(),會使得除主線程外的另外兩個線程繼續爭奪CPU的資源,直到甲結束,主線程纔會回到就緒態去競爭CPU的使用權,若是此時乙線程尚未結束(我估計在測試代碼中將乙線程的循環寫的很長),則會繼續與主線程進行CPU資源的競爭

public class Test1 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();

        MyThread myThread = new MyThread();
        myThread.start();

        for (int i = 0; i < 100; i++) {
            System.out.println(i + "========main========");
            if (i == 50) {
                try {
                    Thread.sleep(2000);
                    myThread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

class MyRunnable implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 1000000; i++)
            System.out.println(i + "-----MyRunnable------");
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++)
            System.out.println(i + "++++++++MyThread+++++++++");
    }
}
  • 線程禮讓:yield是一個靜態方法,經過調用該方法,是在某個特定的時間點讓線程暫停搶佔CPU資源的行爲,只是暫時的禮讓

  • 線程中斷:不少狀況致使線程中止運行,如線程執行完畢自動中止,線程執行過程當中遇到錯誤拋出異常並中止,或者線程執行過程當中根據需求手動中止,有以下三個實例方法

    • public void stop() 強行中斷線程,不推薦使用
    • public void interrupt() 中斷當前線程
    • public boolean isInterrupted() true表示已經中斷,而false表示未中斷(固然若是線程處於沒有運行的就緒態那就稱不上中斷)
  • 一些獲取當前線程信息的實例方法:

    • thread.setName("線程一") 爲當前線程設置一個名稱
    • thread.getState() 獲取當前線程的狀態,是一個枚舉類型

匿名內部類實現接口或者繼承父類

new SuperType(construction parameters) {
	inner class methods and data
}

其中,SuperType能夠是接口,若是是這樣,內部類就要實現這個接口,Super Type也能夠是一個類,若是是這樣,內部類就要擴展這個類。

相關文章
相關標籤/搜索