JavaSE Day14 異常 泛型


Lambda表達式調用Arrays類中的方法

parallelSetAll()

package day14;

import java.util.Arrays;
import java.util.function.IntUnaryOperator;

public class TestLambda2 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        // 一元運算,用運算的結果來替換數組中的所有元素
        Arrays.parallelSetAll(arr, new IntUnaryOperator() {
            @Override          // 數組的索引,
            public int applyAsInt(int operand) {
                return operand + 1;
            }
        });

        Arrays.parallelSetAll(arr, index -> {
            return index+1;
        });
        System.out.println(Arrays.toString(arr));
    
    }
}

parallelPrefix()

package day14;

import java.util.Arrays;
import java.util.function.IntBinaryOperator;

public class TestLambda2 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        // 二元運算,用於運算的結果,來替換數組中的所有元素
        Arrays.parallelPrefix(arr, new IntBinaryOperator() {
            @Override            // left:前一個元素,right當前元素,當前元素是數組中第一個元素,前一個元素是1;
            public int applyAsInt(int left, int right) {
                return left * right;
            }
        });

        // 當前元素 和 替換後的元素進行計算,得出的結果在賦值給當前元素的位置;
        System.out.println(Arrays.toString(arr));

        Arrays.parallelPrefix(arr,((left, right) -> {
            return left* right;
        }));

        System.out.println(Arrays.toString(arr));
       
    }
}

Stream()

package day14;

import java.util.Arrays;
import java.util.function.IntConsumer;

public class TestLambda2 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
         // Stream
        Arrays.stream(arr).forEach(new IntConsumer() {
            @Override         // value 數組中的每個元素
            public void accept(int value) {
                System.out.println(value);
            }
        });

        // Lambda
        Arrays.stream(arr).forEach(value -> System.out.println(value));
        
        // 方法引用
        Arrays.stream(arr).forEach(System.out::println);
       
    }
}

Lambda練習

Lambda1

package day14;


abstract class Animal{
    public abstract void breed();
}

@FunctionalInterface
interface Fly{
    void fly();
}

@FunctionalInterface
interface Talk{
    void talk();
}


class WileGoose extends Animal{
    @Override
    public void breed() {
        System.out.println("大雁能繁殖");
    }

    Fly fly = () -> System.out.println("大雁能飛");
    /*public void fly(Fly fly){
        fly.fly();
    }*/
}

class Duck extends Animal{


    @Override
    public void breed() {
        System.out.println("鴨子能繁殖");
    }


    public void swimming(){
        System.out.println("鴨子會游泳");
    }
}

class DonaldDuck extends Duck{
    // 直接在屬性中設置Lambda,
    Talk talk = ()-> System.out.println("唐老鴨能說話");
    // 需要在主函數設置;不推薦;
    public void speak(Talk talk){
        talk.talk();
    }
}



public class TestLamabda3 {
    public static void main(String[] args) {
        DonaldDuck donaldDuck = new DonaldDuck();
        donaldDuck.talk.talk();

        WileGoose wileGoose = new WileGoose();
        wileGoose.fly.fly();

    }
}

異常處理

一般都是爲了處理非正常的情況,改變程序的執行流程。

異常的分類

異常分類圖

Error 異常

Error異常都是用代碼處理不了的

Exception異常

用代碼可以處理的異常;

Exception類的包含了運行時異常和非運行時異常;

運行時異常

RunTimeException類和它的子類;
編譯可以通過,但是在運行期出現的問題;使程序中斷了;

非運行時異常

Exception自身 和 它的部分子類(因爲包含了RunTimeException)
編譯時出現問題
是必須要處理的異常。

常見的異常

package day14;

import java.lang.reflect.Field;
import java.util.Scanner;
class A{
    private int x;

}
public class TestException1 {
    public static void main(String[] args) throws Exception  {
        //空指針  java.lang.NullPointerException
        String s = " ";
        System.out.println(s.equalsIgnoreCase("abc"));
        //     java.util.InputMismatchException
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();

        // 類型轉換java.lang.ClassCastException
       /* Object obj = new String();
        String s1 = (String)obj;
        Integer n1 = (Integer)obj;*/


        // 數字格式化 java.lang.NumberFormatException
        String s2 = "12a";
        //int n3 = Integer.valueOf(s2);


        // 不能加載類 java.lang.ClassNotFoundException
        // ClassLoader.getSystemClassLoader().loadClass("day14.TestException");

        // 訪問x:到字節碼文件中
        Class c = A.class;
        Field f = c.getDeclaredField("x");
        f.setAccessible(true);//需要授權,否則報錯
        Object o = c.newInstance();
        //java.lang.IllegalAccessException 參數異常
        f.set(o,22);
        System.out.println(f.get(o));
    }

}

異常的處理方式

try-catch

語法:

try{
    可能會出現異常的代碼
} catch(異常類類型 對象名){
    處理異常
}

執行結果

  1. 沒有異常;
    try執行,catch沒有執行
  2. 異常類型匹配,
    try執行,catch也執行
  3. 異常類型不匹配:
    try執行,catch不執行;但是程序會中斷;

多重catch

語法

try{
    可能會出現異常的代碼
} catch(異常類類型 對象名){
    處理異常
} catch(異常類類型 對象名){
    處理異常
} catch(異常類類型 對象名){
    處理異常
} ……

catch塊中異常類類型的順序

  1. 有普通 到特殊,
  2. 由子類到父類;
  3. catch塊中可以增加return;catch塊中可以增加return;來結束當前方法;

try-catch-finally

try{
    可能會出現異常的代碼
} catch(異常類類型 對象名){
    處理異常
} …… catch(異常類類型 對象名){
    處理異常
}finally{ //關閉流
    一定會執行
}

特殊情況

  1. catch塊中有returnfinally塊也會執行;
  2. catch塊中System.exit(0),就退出JVM了,因此就不執行finally塊了;

throws

  1. 聲明異常,把異常拋給調用者;

  2. 方法頭部聲明瞭Excepion,那麼就說明方法體中存在 非運行時異常,是必須要處理的;

  3. 方法頭部聲明瞭RunTimeException,那麼就說明方法體中存在 運行時異常,不是必須處理的;

++問題:什麼時候需要throws聲明異常++
throws與try-catch是一個級別的;
throws是將異常拋出給調用者,try-cathc是將異常自己處理了;
也就是說當調用了一個聲明瞭異常的方法時,要麼使用try捕獲異常,要麼使用throws拋出異常

throw

  1. 拋異常,
  2. 自己拋異常
  • 如果拋出的異常是 運行時異常(RunTimeException),在方法頭部不用throws聲明Exception
package homework02;

/**
 * 自定義顏色異常,繼承自RunTimeException
 */
class ColorException extends RuntimeException{
    private String emessage;
    ColorException(String emessage){
        super();
        this.emessage = emessage;
    }

    @Override
    public String toString() {
        return this.emessage;
    }
}

/**
 * 貓類
 */
class Cat{
    /**
     * 顏色
     */
    private String color;

    public String getColor() {
        return color;
    }

    /**
     * 如果顏色不是紅,黃,藍,就拋出異常
     * @param color
     */
    public void setColor(String color) {
        if("紅".equals(color) || "黃".equals(color) || "藍".equals(color)) {
            this.color = color;
        }else{
            throw new ColorException("顏色只能是紅、黃、藍");
        }
    }
}
public class TestException {
    public static void main(String[] args) {
        Cat cat = new Cat();

        cat.setColor("紅");
        cat.setColor("黑");
    }
}

  • 如果拋出的異常是 非運行時異常,那麼就需要使用throws在方法頭部聲明Exception;

    package homework02;
    
    /**
     * 自定義顏色異常,繼承自Exception
     */
    class ColorException extends Exception{
        private String emessage;
        ColorException(String emessage){
            super();
            this.emessage = emessage;
        }
    
        @Override
        public String toString() {
            return this.emessage;
        }
    }
    
    /**
     * 貓類
     */
    class Cat{
        /**
         * 顏色
         */
        private String color;
    
        public String getColor() {
            return color;
        }
    
        /**
         * 如果顏色不是紅,黃,藍,就拋出異常
         * @param color
         */
        public void setColor(String color) throws Exception {
            if("紅".equals(color) || "黃".equals(color) || "藍".equals(color)) {
                this.color = color;
            }else{
                throw new ColorException("顏色只能是紅、黃、藍");
            }
        }
    }
    public class TestException {
        public static void main(String[] args) throws Exception{
            Cat cat = new Cat();
    
            cat.setColor("紅");
            cat.setColor("黑");
        }
    }
    
    
  1. 作用:手動拋出異常;

自定義異常

自定義異常,一般都是繼承自兩個類,分別是RunTimeException(運行時異常)和 Exception(非運行時異常);

當自定義異常繼承自 Excetpion時,手動拋出異常的方法的頭部必須 聲明異常(throws Exception),

  • 並且如果在調用時使用try-catch捕獲異常,那麼最後必須有一個catch (Exception e)的catch語句;
  • 如果不捕獲異常,那麼需要在主方法的頭部,聲明異常(throws Exception);

當自定義異常繼承自 RunTimeExcetpion時,手動拋出異常的方法的頭部不需要 聲明異常(throws),

  • 在使用try-catch捕獲異常時,也不需要catch (Exception e)語句。
  • 如果不捕獲異常,那麼需要在主方法的頭部,也不需要聲明異常;

自定義 運行時異常

package homework01;


import java.util.Scanner;

/**
 * 自定義分數異常類,繼承自RunTimeException,
 */
class ScoreException extends RuntimeException{
    /**
     * 分數錯誤時的信息
     */
    private String smessing;
    
    public ScoreException(String smessing) {
        super();
        this.smessing = smessing;
    }

    @Override
    public String getMessage() {
        return this.smessing;
    }
}

/**
 * 學生類
 */
class Student {
    private int Score;

    public int getScore() {
        return Score;
    }

    /**
     * 當分數不在 0~100之間時,拋出分數異常,
     * @param score 分數
     */
    public void setScore(int score) {
        if (score >=0  && score <=100) {
            Score = score;
        }else{
            throw new ScoreException("分數必須在0-100之間");
        }
    }
}

public class TestStudent{
    public static void main(String[] args) {
        Student s1 = new Student();
        Scanner input = new Scanner(System.in);

        /**
         * 當分數不在1~100範圍時,需要重新輸入;
         */
        while(true) {
            try {
                System.out.println("請輸入成績:");
                s1.setScore(input.nextInt());
                break;
            } catch (ScoreException e) {
                System.out.println(e.getMessage());
                // 由於Scanner裏面有緩存,無法將錯誤的值賦值給變量。因此需要清空緩存,重新創建一個Scanner類;
                input = new Scanner(System.in);
            } 
        }
        System.out.println("設置成功。。。");
    }
}

自定義 非運行時異常

package homework01;


import java.util.Scanner;

/**
 * 自定義分數異常類,繼承自Exception,
 */
class ScoreException extends Exception{
    /**
     * 分數錯誤時的信息
     */
    private String smessing;

    public ScoreException(String smessing) {
        super();
        this.smessing = smessing;
    }

    @Override
    public String getMessage() {
        return this.smessing;
    }
}

/**
 * 學生類
 */
class Student {
    private int Score;

    public int getScore() {
        return Score;
    }

    /**
     * 當分數不在 0~100之間時,拋出分數異常,
     * @param score 分數
     */
    public void setScore(int score) throws Exception{
        if (score >=0  && score <=100) {
            Score = score;
        }else{
            throw new ScoreException("分數必須在0-100之間");
        }
    }
}

public class TestStudent{
    public static void main(String[] args) {
        Student s1 = new Student();
        Scanner input = new Scanner(System.in);

        /**
         * 當分數不在1~100範圍時,需要重新輸入;
         */
        while(true) {
            try {
                System.out.println("請輸入成績:");
                s1.setScore(input.nextInt());
                break;
            } catch (ScoreException e) {
                System.out.println(e.getMessage());

            } catch (Exception e){
                System.out.println("未知錯誤");
            }
        }
        System.out.println("設置成功。。。");
    }
}

總結

throw和throws的區別

  • throw
    在代碼塊中執行,主要是手工進行異常類的拋出
  • throws
    在方法定義上使用,表示此方法可能產生的異常明確告訴給調用處,有調用處進行處理;

關於循環中Scanner重複輸入的問題

Scanner 類的input方法在try語句中時,如果需要在catch語句中重新賦值,那麼就需要在catch中重新創建一個Scanner的對象,否則Scanner中有緩存機制,可能會出現跳過賦值,直接拋出異常的情況;

異常處理原則

  1. 只用於處理非正常的情況
  2. 避免過大的try塊
  3. 推薦使用多重catch
  4. 不要忽略catch塊中的異常
  5. 改正代碼
  6. 文檔聲明 - 聲明瞭異常的方法要詳細的說明異常;

泛型

回顧方法重寫

  • 子類中,實例方法,方法名相同,參數列表相同,返回值類型形同
    1. 訪問修飾符權限比父類要大
    2. 拋出的異常範圍要比父類小
    3. 返回值類型可以是父類返回值類型的子類;
    4. 父類參數擦除後與子類一致;

概念

參數化類型

類型參數

  1. 定義時:形式類型參數
  2. 應用時:實際類型參數(必須是引用類型)

參數化類型

類型<實際類型參數>
合在一起就叫參數化類型;

好處

  1. 在編譯器進行類型檢查;
  2. 類型不確定。

定義泛型類

語法

Class  類名<類型參數1,類型參數2>{

}

參數類型

用一個大寫字母表示;

  1. T -> Type
  2. K -> Key
  3. V -> Value
  4. E -> Element

多參數類型

可以定義多個類型參數,用逗號分隔;

原生類型

一個類型的後邊沒有指定具體的類型參數,這樣的泛型類型,稱爲原生類型

字節碼文件對泛型的處理

字節碼文件中 擦除泛型類型信息

通配符

  1. ? 無界通配符 匹配任意類型
  2. ? extends 上限類 匹配上限類和上限的子類
  3. ? Super 下限類 匹配下限類和下限類的父類

提問

String類 是 Object類 的子類,Object 對象 = String 對象,自動轉型,那麼Point和Point有什麼關係?
Point<String>不是Point<Object>的子類型;
通過查看字節碼文件發現 Point的class文件就一個,因此不存在繼承關係;

類型參數與通配符的區別

  1. 類型參數可以表示一種類型,泛型類型;通配符不能表示爲一種類型。
  2. 類型參數只能指定上限;通配符能指定上限,和下限;
  3. 類型參數 可以指定多個上限;(能匹配各個上限的任一子類,); 通配符不能指定多個上限;

泛型構造器

類名<對象泛型> 對象名 = new <構造器泛型>類名<對象泛型>();

package day14;

class Point2<T> {
    private T x;
    private T y;

    // 泛型構造器的定義
    public <E> Point2(E e) {
        System.out.println(e);
    }

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }

    public T getY() {
        return y;
    }

    public void setY(T y) {
        this.y = y;
    }
}

public class TestPoint3 {
    public static void main(String[] args) {
        // 自動類型推斷: 用參數的類型 自動推斷出構造器的類型
        Point2<String> p = new Point2<>(11);

        //構造器的類型如果是顯示指定,那麼對象的類型也要用顯示指定的 格式 new <構造器泛型>類名<對象泛型>();
        Point2<String> p1 = new <Integer>Point2<String>(11);


    }
}

泛型方法

package day14;

class Demo{
    // 泛型方法
    public <T> void f(T t){
        System.out.println(t);
    }
    public <T> T ff(T t){
        // 類型推斷
        f(22);// 調用方法
        // 顯示指定,必須使用對象調用
        this.<Double>f(22.2);
        return t;
    }
}
public class TestPoint4 {
    public static void main(String[] args) {
        Demo demo = new Demo();
        // 類型推斷
        demo.f("hello");
        demo.f(123);

        //顯示指定方法類型
        demo.<Double>f(22.2);

    }
}

擦除的原則

對於參數化類型,

擦除之後爲 原生類型

Point3<String> p; -> point3 p

對於類型參數

無界類型參數,擦除後爲Object;

Class Point<T>{

  T x;

} -> class Point<Object>{

  Object x;

}

有一個上限的類型參數,用上限類型替換;

<T extends A> -> A

有多個上限的類型參數,用第一個上限來替換;

<T extends A && B> --> A

泛型重載和重寫

package day14;
// 泛型類
class Point3<T>{
    private T x;
    private T y;

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }

    public T getY() {
        return y;
    }

    public void setY(T y) {
        this.y = y;
    }
}
class Demo2{}
interface IX{}
interface IY{}
class Demo1{
    // public void f(Object t){}
    public <T> void f(T t){}
    // 是否能重載:擦除後就一樣了;
//    public void f(Point3<String> p){ }
    public void f(Point3<Integer> p){}

    //                            Demo2
    public <T extends Demo2> void f(T t){}
    //                               IX
    public <T extends IX & IY> void f(T t){}
    //                               IY
//    public <T extends IY & IX> void f(T t){}
}
class Parent1{
    //          f(Point3 p)
    public void f(Point3<String> p){}

}
class Child2 extends Parent1{
    @Override
    public void f(Point3 p) {
    }
}
public class TestPoint5 {
    public static void main(String[] args) {

    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章