1. 程式人生 > 實用技巧 >[Java Spring] Implementing Spring Security

[Java Spring] Implementing Spring Security

引入

說到異常先寫一個demo

public class Introduce {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("請輸入被除數:");
        int divisor = scanner.nextInt();
        System.out.print("請輸入除數:");
        int dividend = scanner.nextInt();
        System.out.println(div(divisor, dividend));
        System.out.println("繼續執行----------------");
    }

    public static int div(int divisor, int dividend) {
        return divisor / dividend;
    }
}

正常情況下沒問題,比如

萬一手欠把除數輸成0,那麼執行結果就變成這樣


或者:

像上面那樣在程式執行期間出現了不正常的情況而導致程式無法正常執行就稱作為異常,出現異常之後的程式碼也就不能執行!

那如何解決呢?第一種方案是可以用if-else來解決

public class Solution {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("請輸入被除數:");
        if (scanner.hasNextInt()) {
            int divisor = scanner.nextInt();
            System.out.print("請輸入除數:");
            if (scanner.hasNextInt()) {
                int dividend = scanner.nextInt();
                if (dividend != 0) {
                    System.out.println(div(divisor, dividend));
                } else {
                    System.out.println("除數不能為0");
                }
            } else {
                System.out.println("錄入的不是int資料!");
            }
        } else {
            System.out.println("錄入的不是int資料!");
        }
        System.out.println("繼續執行----------------");
    }

    public static int div(int divisor, int dividend) {
        return divisor / dividend;
    }
}

try-catch語句

看完之後是不是感覺這個if巢狀程式碼有些臃腫,業務程式碼也與處理異常的程式碼混在一起;可讀性還很差;最重要的是程式設計師無法想到所有可能發生異常情況。基於if-else處理機制的缺點,java提供了一套處理異常機制

public class Solution2 {
    public static void main(String[] args) {
        try {
            Scanner scanner = new Scanner(System.in);
            System.out.print("請輸入被除數:");
            int divisor = scanner.nextInt();
            System.out.print("請輸入除數:");
            int dividend = scanner.nextInt();
            System.out.println(div(divisor, dividend));
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("繼續執行----------------");
    }

    public static int div(int divisor, int dividend) {
        return divisor / dividend;
    }
}

這段程式碼的try-catch塊包圍的程式碼就是用來處理異常的也叫做捕獲異常,try塊用來處理可能出現異常的程式碼,catch塊用來處理try塊中出現的異常

語法:try{
		可疑程式碼,將異常生成對應的異常物件,傳遞給catch塊   
	 }catch(異常){
    	異常處理
	 }

發生情況:try塊中出現異常,則異常之後的程式碼不會執行,直接執行catch塊中的語句

​ try塊中沒出現異常,catch塊中的語句不會執行

​ catch中的異常型別和你給出的異常型別匹配的話,會執行catch塊中的語句

​ catch中的異常型別和你給出的異常型別不匹配的話,則相當於沒進行異常處理,catch塊中和之後的程式碼不會執行

//catch中處理異常
public class Catch {
    public static void main(String[] args) {
        try {
            int[] num = {42, 25, 32, 20, 14, 18};
            System.out.println(num[num.length]);
        } catch (Exception e) {
            //第一種:空處理,什麼都不寫

            //第二種:輸出自定義異常資訊
            System.out.println("程式碼出現異常!");

            //第三種:列印異常資訊
            System.out.println(e.toString());//列印異常的全限定名(包名+類名)
            System.out.println(e.getMessage());// 顯示異常描述資訊,這段程式碼提示你陣列長度是6
            e.printStackTrace();//顯示異常的詳細資訊
            
            //第四種:丟擲異常
            throw e;
        }
    }
}

另外try-catch語句還可以進行巢狀,catch語句也可以有多個形成多層catch

public class MultipleCatch {
    public static void main(String[] args) {
        try {
            Scanner scanner = new Scanner(System.in);
            System.out.print("請輸入被除數:");
            int divisor = scanner.nextInt();
            System.out.print("請輸入除數:");
            int dividend = scanner.nextInt();
            new MultipleCatch().div(divisor, dividend);
            //多層catch分別匹配InputMismatchException和ArithmeticException異常,匹配到哪一個就執行哪一個,如果沒匹配到相當於沒處理異常
        } catch (InputMismatchException e) {
            e.printStackTrace();
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("繼續執行----------------");
        }
    }

    public void div(int num1, int num2) {
        System.out.println(num1 / num2);
    }
}

下面這個InputStream和OutputStream不知道是什麼,這沒關係,重點是演示try-catch語句

public class NestedTryCatch {
    public static void main(String[] args) {
        //InputStream和OutputStream都是一個流
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = new FileInputStream(new File("F:\\study"));
            outputStream = new FileOutputStream(new File("F:\\study"));
            //多層catch,子類異常必須寫在父類異常上面
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            try {
                //流使用完畢需要關閉,這裡就是一個巢狀try-catch語句
                inputStream.close();
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

上面的多層匹配分別匹配FileNotFoundException、IOException和Exception異常,匹配到哪一個就執行哪一個

值得注意的是這幾個異常有繼承關係的,FileNotFoundException繼承自IOException,它也是接著繼承Exception的

異常體系

再看一下繼承體系

編譯時異常:就指程式編譯時產生的異常,必須處理,否則程式碼不能通過執行

執行時異常:執行之後才可能出現的異常,可以不做處理,一般是程式的邏輯錯誤,儘量避免!

越往上就是父類能處理的異常範圍越大,所以就可以想象出為什麼子類異常必須寫在父類,如果同級別的異常就不用關心順序

異常程式碼之後可能還有程式碼語句,但try-catch語句塊可能執行完之後後續程式碼不會執行

  1. throw丟擲異常

  2. catch語句塊沒有捕獲成功

  3. try語句塊中有return語句

public class Try {
    public static void main(String[] args) {
        try {
            String[] str = {"a", "b", "c", "d"};
            //System.out.println(str[str.length]);
            System.out.println(str.length);
            return;
        } catch (Exception e) {
            //throw e;
        }
        System.out.println("後續程式碼-------------");
    }
}

finally語句

但就想異常程式碼處理之後,無論異常資訊是否捕獲成功,後續的程式碼都會執行,可以加finally語句

public class Finally {
    public static void main(String[] args) {
        int num = 10;
        try {
            System.out.println(num); //執行結果:10
            return;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("後續程式碼-------------");
            num += 100;
            System.out.println(num); //執行結果:110,因為先執行finally再執行try中的return
        }
    }
}
//finally塊一般操作關閉資料庫資源,關閉IO流資源,關閉socket資源。

在進行異常捕獲的時候,return語句位置的不同其實可能會造成結果不同

//1、try中有return,finally中沒有return
public class Test01 {
    public static void main(String[] args){
        System.out.println(test());
    }

    private static int test(){
        int num = 10;
        try{
            System.out.println("try");
            return num += 80;
        }catch(Exception e){
            System.out.println("error");
        }finally{
            if (num > 20){
                System.out.println("num>20 : " + num);
            }
            System.out.println("finally");
        }
        return num;
    }
}

/*
執行結果:
	try
	num>20 : 90
	finally
	90
	
	這裡把return num += 80拆分成兩個語句了,num+=80和return,看一下class反編譯的程式碼
*/
public class Test1 {
    public TryTest() {
    }

    public static void main(String[] args) {
        System.out.println(test());
    }

    private static int test() {
        int num = 10;

        try {
            System.out.println("try");
            num += 80;
            int var1 = num;
            return var1;
        } catch (Exception var5) {
            System.out.println("error");
        } finally {
            if (num > 20) {
                System.out.println("num>20 : " + num);
            }

            System.out.println("finally");
        }
        return num;
    }
}

//2、try和finally中均有return
public class Test2 {
    public static void main(String[] args) {
        System.out.println(test());
    }

    private static int test() {
        int num = 10;
        try {
            System.out.println("try");
            return num += 80;
        } catch (Exception e) {
            System.out.println("error");
        } finally {
            if (num > 20) {
                System.out.println("num>20 : " + num);
            }
            System.out.println("finally");
            num = 100;
            return num;
        }
    }
}

/*
執行結果:
	try
	num>20 : 90
	finally
	100
	
	這裡同樣把return num += 80拆分成兩個語句了,num+=80和return,而且try中的return被省略了,直接執行finally中的return語句,得到返回值。看一下class反編譯的程式碼
*/
public class Test2 {
    public Test2() {
    }

    public static void main(String[] args) {
        System.out.println(test());
    }

    private static int test() {
        int num = 10;

        try {
            System.out.println("try");
            num += 80;
        } catch (Exception var5) {
            System.out.println("error");
        } finally {
            if (num > 20) {
                System.out.println("num>20 : " + num);
            }

            System.out.println("finally");
            int num = 100;
            return num;
        }
    }
}

//3、finally中改變返回值num,但是不返回
public class Test3 {
    public static void main(String[] args){
        System.out.println(test());
    }

    private static int test(){
        int num = 10;
        try{
            System.out.println("try");
            return num;
        }catch(Exception e){
            System.out.println("error");
        }finally{
            if (num > 20){
                System.out.println("num>20 : " + num);
            }
            System.out.println("finally");
            num = 100;
        }
        return num;
    }
}

/*
執行結果:
	try
	finally
	10
	
	finally沒有return時,這裡把num的值用第三方變數儲存起來了,finally執行結束return的是那個第三方變數,還是看一下class反編譯的程式碼
*/
public class Test3 {
    public Test3() {
    }

    public static void main(String[] args) {
        System.out.println(test());
    }

    private static int test() {
        byte num = 10;

        try {
            System.out.println("try");
            byte var1 = num;
            return var1;
        } catch (Exception var5) {
            System.out.println("error");
        } finally {
            if (num > 20) {
                System.out.println("num>20 : " + num);
            }

            System.out.println("finally");
            num = 100;
        }

        return num;
    }
}
//將num的值包裝在Num類中
public class Test4 {
    public static void main(String[] args){
        System.out.println(test().num);
    }

    private static Num test(){
        Num number = new Num();
        try{
            System.out.println("try");
            return number;
        }catch(Exception e){
            System.out.println("error");
        }finally{
            if (number.num > 20){
                System.out.println("number.num>20 : " + number.num);
            }
            System.out.println("finally");
            number.num = 100;
        }
        return number;
    }
}

class Num{
    public int num = 10;
}

/*
執行結果:
	try
	finally
	100

	這裡finally也沒有return,這裡第三方變數儲存的是建立物件的那個地址值,finally執行結束建立的物件改變值,還是看一下class反編譯的程式碼
*/
public class Test4 {
    public Test4() {
    }

    public static void main(String[] args) {
        System.out.println(test().num);
    }

    private static Num test() {
        Num number = new Num();

        try {
            System.out.println("try");
            Num var1 = number;
            return var1;
        } catch (Exception var5) {
            System.out.println("error");
        } finally {
            if (number.num > 20) {
                System.out.println("number.num>20 : " + number.num);
            }

            System.out.println("finally");
            number.num = 100;
        }

        return number;
    }
}

class Num {
    public int num = 10;

    Num() {
    }
}

throws

java除了提供了try-catch塊這種捕獲異常的解決方案,還提供了一種宣告丟擲異常的解決方案throws,即自己不處理這些異常,而是丟給呼叫方處理,如果整個程式的執行過程中都沒有異常的處理的話,最終異常會拋給jvm,不太友好,一般都要對異常進行處理

public class ThrowsDemo {
    public static void main(String[] args) throws Exception {
        new ThrowsDemo().test();
    }
    public void test() throws FileNotFoundException {
        FileInputStream inputStream=new FileInputStream("F:\\study\\test.txt");
    }
}

⾃定義異常

開發人員還可以⾃定義異常,⼀般通過繼承Exception的⼦類的⽅式實現,本質上是覆蓋原有異常API的資訊

public class CustomException extends Exception{

    static final long serialVersionUID = -70348971907L;

    public CustomException(String message) {
        super(message);
    }
}
//模擬一下餘額不足的情況
public class Test {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int money = scanner.nextInt();
        new Test().getMoney(money, 2000);
    }

    public int getMoney(int money, int price) {
        if (money < price) {
            try {
                throw new CustomException("餘額不⾜!");
            } catch (CustomException e) {
                e.printStackTrace();
            }
        }
        System.out.println("繼續執行程式~~~");
        return money;
    }
}

自定義異常的步驟:

  1. 繼承於現的異常結構:RuntimeException 、Exception

  2. 提供全域性常量:serialVersionUID

  3. 編寫構造方法,可以傳入自己想列印的異常資訊

  4. 呼叫的時候通過throw向外丟擲異常

如果繼承的是執行時異常,那麼在使用的時候無需額外處理;如果繼承的是檢查異常,那麼使用的時候需要try-catch捕獲或者throws向上拋

throw和throws的區別:

  1. 位置不同:throw:方法內部,throws: 方法的宣告處
  2. 內容不同:throw+異常物件(檢查異常,執行時異常);throws+異常的型別(可以多個型別,用,拼接)
  3. 作用不同:throw:異常出現的源頭,製造異常。throws:在方法的宣告處,告訴方法的呼叫者,這個方法中可能會出現我宣告的這些異常。然後呼叫者對這個異常進行處理,要麼自己處理要麼再繼續向外丟擲異常

jdk7新寫法

  1. 在JDK1.7以後,異常新處理方式:可以並列用|符號連線

  1. try-with-resources語句

Java裡,對於檔案操作IO流、資料庫連線等開銷非常昂貴的資源,用完之後必須及時通過close方法將其關閉,否則資源會一直處於開啟狀態,可能會導致記憶體洩露等問題。

關閉資源的常用方式就是在finally塊裡是釋放,即呼叫close方法。比如,我們經常會寫這樣的程式碼:

public static void test() {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        File f = new File("F:\\demo.txt");
        FileWriter fw = null;
        BufferedWriter bw = null;
        try {
            fw=new FileWriter(f);
            bw = new BufferedWriter(fw);
            String s = br.readLine();
            while (!s.equals("exit")) {
                bw.write(s);
                bw.newLine();//檔案中換行
                s = br.readLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //4.關閉流:
        try {
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

從Java 7開始,jdk提供了一種更好的方式關閉資源,使用try-with-resources語句,改寫一下上面的程式碼,效果如下:

 public static void test2(){
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        try(FileWriter fw=new FileWriter(new File("F:\\demo.txt"));
            BufferedWriter bw=new BufferedWriter(fw)){
            String s = br.readLine();
            while (!s.equals("exit")) {
                bw.write(s);
                bw.newLine();
                s = br.readLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

是不是感覺簡潔了好多,其實這就是一個語法糖,它將在編譯時編譯成關閉資源的程式碼。我們將上述例子中的程式碼編譯成class檔案,再反編譯回java檔案,就能看到如下程式碼:

 public static void test2() {
        InputStreamReader isr = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(isr);
        File f = new File("F:\\demo.txt");

        try {
            FileWriter fw = new FileWriter(f);
            Throwable var4 = null;

            try {
                BufferedWriter bw = new BufferedWriter(fw);
                Throwable var6 = null;

                try {
                    for(String s = br.readLine(); !s.equals("exit"); s = br.readLine()) {
                        bw.write(s);
                        bw.newLine();
                    }
                } catch (Throwable var31) {
                    var6 = var31;
                    throw var31;
                } finally {
                    if (bw != null) {
                        if (var6 != null) {
                            try {
                                bw.close();
                            } catch (Throwable var30) {
                                var6.addSuppressed(var30);
                            }
                        } else {
                            bw.close();
                        }
                    }

                }
            } catch (Throwable var33) {
                var4 = var33;
                throw var33;
            } finally {
                if (fw != null) {
                    if (var4 != null) {
                        try {
                            fw.close();
                        } catch (Throwable var29) {
                            var4.addSuppressed(var29);
                        }
                    } else {
                        fw.close();
                    }
                }
            }
        } catch (IOException var35) {
            var35.printStackTrace();
        }
    }

除了異常程式碼,我們看到其實關閉流的處理是底層幫我們處理的