1. 程式人生 > 實用技巧 >JAVA筆記整理-內部類與異常

JAVA筆記整理-內部類與異常

一、內部類(inner class)

1、定義

​ 在一個類中,定義另一個類的程式碼結構,通常定義在類內部的類稱為 “內部類” ,外面的類稱為“外部類” , 在邏輯關係上 內部類與外部類是從屬關係,例如 一個People類 存在收貨地址類(收貨人,收貨聯絡方式)

2、分類

2.1、 普通內部類(inner class),一個類A中定義另一個類B,其中類B就是類A的內部類,也是類A的一部分

//定義一個people類(外部類)
public class People {
    private String pname="張三";
    public void sayHello(){
        System.out.println("Let us say Hello");
        // 知識點1 :外部類的方法中,可以使用內部類的屬性、方法
        Address address = new Address();
        address.addressName ="湖北武漢";
        address.contentName="張某某";
        address.showAddressInfo();
    }
    /**
     * 定義普通內部類   收貨地址類
     */
    class Address{
        private String addressName;// 收貨地址
        private String contentName;// 聯絡人

        public void showAddressInfo(){
            System.out.println("聯絡人:"+contentName + "--收貨地址:"+addressName);
            // 內部類的方法 可以直接訪問外部類的屬性  (由於通常情況屬性的訪問必須通過物件才可以使用,而內部類中可以直接訪問)
            System.out.println("訪問外部類的屬性:"+pname);
        }


    }



}

​ 注意兩點

  • ​ 外部類的方法中,可以直接訪問內部類的所有成員(包括私有)
  • ​ 內部類的方法中,也可以直接方法外部類的所有成員,當外部和內部的成員名相同時,就近原則訪問成員,或者引入外部類的物件訪問

2.2、 靜態內部類(static inner class): 在普通內部類基礎上,增加“static”關鍵字,與靜態方法相似,滿足靜態的要求

public class People{
/**
     * 2、定義靜態內部類
     *   身份證資訊
     */
    static  class Card{
        private static String cardNo="3455434535345334";
        private String cardName="張三";

        // 定義靜態方法
        public static void showCard(){
            System.out.println("身份證號:"+ cardNo);
        }
        // 定義非靜態方法
        public void showCard2(){
            System.out.println("姓名:"+cardName + "身份證號:"+ cardNo);
        }
    }
	// 外部類的方法 訪問內部類的方法
    public void method2(){
        Card card = new Card();
        // 對於靜態方法可以直接  類名.方法名
        // 對於非靜態方法,需要建立Card類的物件訪問
        card.showCard2();
    }
}

使用:
        // 2 建立靜態內部類的物件呼叫內部類方法
        People.Card.showCard();
        // 建立靜態內部類的物件
        People.Card  card = new People.Card();
        card.showCard2();

2.3、方法內部類: 在一個方法中定義的類,其中這個類只屬於該方法,也只能在該方法中使用

 /**
     * 3、方法內部類 (將一個類定義在方法裡面)
     */
    public void method3(){
         int score = 98;

         // 在這裡定義一個類
        class MyClass{
            String subject="Java";
            public void getSubjectScore(){
                //方法內部類中 也可以使用方法的屬性
                System.out.println(pname+"的"+subject+":"+score);
            }
        }

        //呼叫方法裡面的類
        MyClass  mycls = new MyClass();
        mycls.getSubjectScore();
    }
    People  people = new People();
    // 3 呼叫方法
        people.method3();

注意:內部類中的class檔案 命名 外部類$內部類名.class

2.4 匿名內部類: 定義一個沒有類名,只有對方法的具體實現。通常它依賴於實現關係(介面)或繼承關係(父類)

a、基於實現關係

public interface MyInterface {
    // 學習
    public void  study();
    //  工作
    public void work();
}

  // 建立一個匿名類(讓介面的引用 指向匿名類的物件)
        MyInterface  person = new MyInterface() {
            @Override
            public void study() {
                System.out.println("這個人也好好學習");
            }

            @Override
            public void work() {
                System.out.println("這個人也好好工作");
            }
        };

        person.study();
        person.work();

b、基於繼承關係

public class MyClass {

   public void service(){
        System.out.println("提供服務的方法。");
    }
}
 // 父類 new 一個 匿名類,這個匿名類是它的子類
        MyClass cls = new MyClass(){
            @Override //匿名類重寫父類的方法 service
            public void service() {
                System.out.println("這是子類的方法");
            }
        };
        cls.service();

二、異常

1、異常的概述

​ 異常定義: 在程式中,發生“不正常”的事件,導致程式無法正常執行,並使JVM中斷,稱為異常

​ 生活中的異常: 早上起床上課,平時騎車20分鐘可以到達教室,由於天氣原因或者鬧鐘響了自動關閉,不能按時到達教室上課,遲到了,此時就屬於異常現象 。

​ 捕獲異常: 當程式在執行時,發生了異常 ,為了讓程式正常執行,需要對異常捕獲(catch),稱之為捕獲異常

​ Java是面向物件的語言, 異常本身就是一個類(Exception),當發生異常時會建立異常物件,捕獲的就是該物件。

  System.out.println("請輸入一個數字");
        Scanner sc = new Scanner(System.in);
        // 對可能發生的異常 進行處理
        int num = sc.nextInt();
        if(num%2==0){
            System.out.println("這個數是偶數");
        }

​ 異常程式碼可能發生異常, 當用戶輸入非數字時, 導致程式丟擲一個異常物件 :

Exception in thread "main" java.util.InputMismatchException
	at java.util.Scanner.throwFor(Scanner.java:864)

2、異常關鍵字 以及層次關係

​ a、try: 試一試 ,將可能發生的程式碼使用try包裹 ,try不能單獨出現

​ b、catch : 捕獲異常, 當發生指定的異常物件時,執行catch程式碼

  System.out.println("請輸入一個數字");
        Scanner sc = new Scanner(System.in);
        // 對可能發生的異常 進行處理
        try {
            int num = sc.nextInt();  // 發生異常後,try裡面的程式碼不再執行
            if (num % 2 == 0) {
                System.out.println("這個數是偶數");
            }
            System.out.println("結束");
        }catch(Exception ee){// 對應的異常類 來捕獲對應的異常物件  ,不能確定異常類,可以使用父類Exception
            System.out.println("你的輸入不正確");
        }

        System.out.println("程式繼續執行直到結束。。。。");

​ 一個try + 多個catch

 	//  丟擲的異常 不能被catch捕獲,會發生什麼?
        try {
            int[] num = {1, 2, 3};
            System.out.println(num[1]); // 沒有捕獲該異常物件,JVM依然終止執行
            System.out.println(10/0);
        }catch(NullPointerException ee){
            System.out.println("這是空指標異常");
        }catch(ArrayIndexOutOfBoundsException  ee){
            System.out.println("陣列下標越界異常");
        }catch(Exception ee){
            // 輸出異常 堆疊訊息  方便程式設計師排錯(儘可能避免使用者看見)
            ee.printStackTrace();
            System.out.println("系統繁忙!"+ee.getMessage());
        }
        System.out.println("程式結束");

​ c: finally : 異常之後的最終處理 (無法是否發生異常,程式都執行 )

​ try... finally 結構

	 try{
            System.out.println("請輸入兩個數 ,計算兩個數相除");
            Scanner sc = new Scanner(System.in);
            int  num1 =  sc.nextInt();
            int num2 = sc.nextInt();
            double  s = num1/num2; // 可能出錯
            System.out.println(" try裡面結束,結果:"+s);
        }finally{
            System.out.println("無論是否發生異常,都會執行這個語句塊,一般用於資源回收");
        }

try... catch...finally 結構

 try {
            System.out.println("請輸入兩個數 ,計算兩個數相除");
            Scanner sc = new Scanner(System.in);
            int num1 = sc.nextInt();
            int num2 = sc.nextInt();
            double s = num1 / num2; // 可能出錯
            System.out.println(" try裡面結束,結果:" + s);
        }catch(ArithmeticException ee){
            ee.printStackTrace();
            System.out.println("除數不能為0 !!");
        }catch(Exception ee){
            ee.printStackTrace();
            System.out.println("系統繁忙!!!");
        }finally {
            System.out.println("用於資源回收。");
        }

3、捕獲異常

​ try...catch...finally

4、丟擲異常

  /**
     * 根據下標訪問陣列元素
     * @param array
     * @param index
     * @return
     */
    public static int getEleByIndex(int [] array , int index){
         // 丟擲異常: 可以在異常發生時 或發生之前 建立一個異常物件並丟擲
        //  手動丟擲一個異常  throw new 異常類([異常訊息]);
        if(index <0 || index > array.length-1){
            //丟擲異常
            throw new ArrayIndexOutOfBoundsException("你的下標越界了");
        }
        int n =  array[index];
        return n;
    }

public static void main(String[] args) {
          //陣列
        int  [] array = {2,1,4,5};
        int index=4;
        // 定義方法訪問下標的元素  此時會產生異常 並丟擲給方法的呼叫者
        try {
            int num = getEleByIndex(array, index);
            System.out.println("訪問的元素:" + num);
        }catch(ArrayIndexOutOfBoundsException ee){
            System.out.println(ee.getMessage());
        }
        System.out.println("結束。。。");

    }

5、異常分類

由於有些異常是不能直接丟擲的 ,需要先宣告才可以丟擲,異常可以分為兩大類:

1、 編譯期異常(check 異常或者檢查異常):在編譯期間檢查異常,如果沒有處理異常,則編譯出錯。

        //建立一個檔案類的物件
        File  file = new File("d:/aaa.txt");
         // 在寫程式碼(編譯之前)時 一定要處理的異常(try..catch 或者 throws),就是編譯時異常 
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }

​ 這裡的IOException 就是 編譯期異常,需要手動處理的

2、執行期異常(runtime 異常或者執行異常):在執行期間檢查異常, 編譯期可以不處理異常。

        // 在執行期間丟擲異常  不需要事先處理的  NullPointException是執行異常
        String str=null;
        System.out.println(str.length());

Exception中常用的異常類

  • ​ RuntimeException
    • ArrayIndexOutOfBoundsException :陣列下標越界異常
    • NullPointerException:空指標異常
    • ArithmeticException: 算術異常
    • NumberFormatException :數字格式化異常
    • ClassNotFoundException: 類沒找到異常
    • ClassCaseException: 類轉換異常
  • 檢查異常(check Exception)

​ IOException :IO操作

​ FileNotFoundException: 檔案未找到異常

​ SQLException:

​ EOFException:讀寫檔案尾異常

​ DateFormatException:日期格式化異常

​ SocketException:SocketException

注意: 對於丟擲檢查異常,需要使用throws宣告,對於丟擲執行時異常,必須要使用throws宣告

宣告丟擲異常語法:

 宣告丟擲異常語法:  
         public ...  方法名([引數]) throws 異常類1,異常類2{
             
              // 通過throw丟擲   或 處理 檢查異常 
      }
  /**
     *   宣告丟擲異常語法:
     *       public ...  方法名([引數]) throws 異常類1,異常類2{
     *
     *       }
     */
    //建立檔案
    public  static void createFile() throws FileNotFoundException ,IOException {
        File file = new File("d:/hello.txt");
        if(file.exists()){
             // 不能建立  ,需要提示使用者  該檔案存在
            throw new FileNotFoundException("這個檔案已存在,不能建立");
        }else{
            //建立
            file.createNewFile();
        }
    }

面試題: 關於 finally 和 return的執行順序問題?

回答: 當方法有返回值時,先執行fianlly,再return, 但是 finally的程式碼不會改變return結果

    /**
     *  方法有返回值 有 finally
     * @param n
     * @return
     */
    public static  int  getNum(int n){
          try{
                if(n%2==0){
                    n++;
                }else{
                    n--;
                }
      
             return n;    
          }catch(Exception ee){
              System.out.println("catch--"+n);
            return 0;
          }finally {
              // return 如果放在 try或catch中,不會受finally的改變
              //  如果放在最下面,會受finally的改變
              n++; // 5
              System.out.println("fially----n:" + n); // 5
          }
    }

結果 返回

fially----n:5
4

6、自定義異常

1、為什麼需要使用自定義異常

​ 在Java中每一個異常類都表示特定的異常型別, 例如 NullPointerException表示空指標 ,ArithmeticException表示算術異常, 但是sun公司提供的API中不可能將實際專案中的業務問題全部定義為已知的異常類 ,這是需要程式設計師根據業務需求來定製異常類,例如 使用者註冊,可以定義使用者註冊異常(RegisterException),分數不能為負數也可以定製異常(ScoreExcecption)。

2、什麼是自定義異常

​ 在開發中根據自己的業務情況來定義異常類 , 靈活性較高,且方便易用。

3、如何實現自定義異常

​ a、定義編譯期異常類,建立一個類繼承 java.lang.Exception ;

​ b、定義執行期異常類,建立一個類繼承java.lang.RuntimeException;

4、案例分析:自定義異常應用

​ 要求: 模擬使用者註冊操作, 使用者輸入使用者名稱 ,驗證使用者名稱是否存在,如果存在,則丟擲一個異常訊息 “親,該使用者已存在,不能註冊” ,通過自定義異常提示訊息

public class RegisterException  extends  Exception {
    public RegisterException(){

    }

    public RegisterException(String message){
        // 將message 賦值給父類的構造
        super(message); //  將message賦值給父類的 屬性,可通過getMessage()方法

    }
}

public class TestRegister {
     // 模擬已存在的使用者
    String []  users = {"張三","李四","王五"};

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("請輸入你要註冊的使用者:");
        String uname = sc.next();
        TestRegister obj = new TestRegister();

        try {
            // 呼叫方法
            obj.checkUserName(uname);
            System.out.println("註冊成功");
        } catch (RegisterException e) {
            System.out.println("註冊失敗");
            System.out.println(e.getMessage());
        }
    }

    /**
     * 檢查使用者是否存在
     * @return   true  表示通過
     *    異常表示不通過
     */
    public boolean  checkUserName(String username) throws RegisterException{
         // 使用foreach遍歷
        /**
         *   for(資料型別 變數名  : 陣列名/集合名 ){
         *        迴圈中的 變數名代表的就是陣列的元素
         *   }
         */
        for(String  u : users){
            // 判斷u是否與 username相等 ,相等說明使用者存在,需要丟擲異常
            if(u.equals(username)){
                throw new RegisterException("親,"+username+" 已存在,不能註冊");
            }
        }
        return true;
    }
}