1. 程式人生 > 其它 >遺傳演算法解決TSP最短路徑問題

遺傳演算法解決TSP最短路徑問題

技術標籤:日常記錄java

前言

宣告:該文章中所有測試都是在JDK1.8的環境下。

該文章是我在學習Java中的異常處理這方面知識時,做的一些總結和記錄。

如果有不正確的地方請大家多多包涵並作出指點,謝謝!

一、異常機制介紹

1.1 基本概念

什麼是異常?顧名思義就是非正常的現象。就是Java程式在執行的時候出現不正常的情況。舉個例子:我們有一個等式"10/0=?"需要計算,我們都知道,10除以0結果是無意義的,所以我們用Java程式計算該等式就會出現非正常現象。

1.2 異常類體系

在瞭解了異常的概念之後,那麼接下來需要知道Java中異常是如何表示的。

首先需要認識java.lang.Throwable類,這個類是Java中所有錯誤(Error)和異常(Exception)的超類。

  • 錯誤(Error):一般是Java虛擬機器相關的,無法解決的嚴重錯誤。例如系統崩潰,虛擬機器錯誤。
  • 異常(Exception):主要是一些程式設計錯誤,可以通過編寫程式碼解決。比如上面講的計算0作為除數的等式。

所以,我們重點需要學習的是能夠通過編寫程式碼解決問題的"異常(Exception)"。

1.3 異常的種類

java.lang.Exception這是所有異常的超類。然後我們檢視java.lang.Exception類,發現該異常類下面有好多子類。那這麼多異常類,該如何區分和記憶呢?

我們根據程式異常在編譯時期能否被檢測出來,把異常分為兩種:

  • RuntimeException:執行時異常,也叫非檢測性異常。
  • 其他異常:非執行時異常,也叫檢測性異常。

下面給非檢測性異常和檢測性異常舉例:

public class Test {

    public static void main(String[] args) {

        //1.非檢測性異常
        System.out.println(10 / 0); //編譯成功,程式可以直接執行,但是在執行程式碼時會發生算術異常(java.lang.ArithmeticException)

        //2.檢測性異常
        FileInputStream file = new FileInputStream("d:/a.txt"
); //編譯時直接錯誤,程式不能直接執行 } }

RuntimeException的主要子類:

  1. ArithmeticException:算術異常
  2. ArrayIndexOutOfBoundsException:陣列下標越界異常
  3. NullPointerException:空指標異常
  4. IllegalArgumentException:不合法引數異常
  5. ClassCastException:型別轉換異常
  6. NumberFormatException:數字格式異常

1.4 異常類結構體系圖

根據上面列出的異常種類,可以構成以下的異常類結構體系圖:

二、異常處理

開始前需要先明白為什麼需要異常處理?

程式中出現異常,並且沒對其進行處理,會導致程式的中斷執行,一旦產生異常,異常之後的語句並不會被執行,而是直接結束程式。

2.1 異常的避免

可以用if條件判斷語句進行執行時異常的避免,我們就根據上面最常見的幾個子類舉例子。

首選我們碰到如下三種異常:

public class ExceptionAvoid {

    public static void main(String[] args) {

        //出現算術異常
        int a = 10;
        int b = 0;
        System.out.println(a / b);

        //出現數組下標越界異常
        int[] arr = new int[3];
        int c = 3;
        System.out.println(arr[c]);

        //出現空指標異常
        String str = null;
        System.out.println(str.length());

    }
}

使用if條件判斷語句進行異常避免:

public class ExceptionAvoid {

    public static void main(String[] args) {
        
        int a = 10;
        int b = 0;
        //避免算術異常
        if (0 != b) {
            System.out.println(a / b);
        }
        
        int[] arr = new int[3];
        int c = 3;
        //避免陣列下標越界異常
        if (c >= 0 && c < 3) {
            System.out.println(arr[c]);
        }
        
        String str = null;
        //避免空指標異常
        if (null != str) {
            System.out.println(str.length());
        }

    }
}

缺陷:雖然if條件判斷語句可以很好的進行異常避免,但是過多會導致程式碼加長,可讀性差。

2.2 異常的捕獲

概念:對程式中可能出現的異常進行捕獲處理,如果程式發生異常,則會被捕獲,進行一些自定義操作。

語法格式:

try {
    可能發生異常的程式碼
} catch (異常型別 引用變數名) {
    出現異常後進行處理的程式碼
} finally {
    無論是否發生異常都要執行的程式碼
}

注:catch程式碼塊可以有多個,finally程式碼塊可以沒有

不想使用if條件判斷語句進行異常避免,或者該異常是非執行時異常,可以進行異常的捕獲處理,如下所示。

public class ExceptionCatch {

    public static void main(String[] args) {

        //執行時異常
        int a = 10;
        int b = 0;
        try {
            System.out.println(a / b);
        } catch (ArithmeticException e) {
            System.out.println("出現算術異常");
        } 

        //非執行時異常
        try {
            FileInputStream fis = new FileInputStream("d:/a.txt");
        } catch (FileNotFoundException e) {
            System.out.println("出現檔案未找到異常");
        } finally {
            System.out.println("該程式碼一定被執行");
        }
    }
}

流程分析:

​ 1、如果在try程式碼塊中沒有異常,則程式不會進入catch程式碼塊,而是把try程式碼塊中的程式走完,例子如下:

public class ExceptionCatch {

    public static void main(String[] args) {

        int a = 10;
        int b = 2;
        try {
            System.out.println("1");
            System.out.println(a / b);
            System.out.println("2");
        } catch (ArithmeticException e) {
            System.out.println("3");
        }
        
    }
}

則輸出結果:
1
2
5

​ 2、如果在try程式碼塊中的某一行程式碼出現異常,則程式不會在try程式碼塊中繼續執行,而是直接進入catch程式碼塊,並把catch程式碼塊的程式走完,例子如下:

public class ExceptionCatch {

    public static void main(String[] args) {

        int a = 10;
        int b = 0;
        try {
            System.out.println("1");
            System.out.println(a / b);
            System.out.println("2");
        } catch (ArithmeticException e) {
            System.out.println("3");
        }
        
    }
}

則輸出結果:
1
3

​ 3、無論異常是否出現並被捕獲,finally程式碼塊中程式碼一定會執行,例子如下:

public class ExceptionCatch {

    public static void main(String[] args) {

        int a = 10;
        int b = ?;
        try {
            System.out.println("1");
            System.out.println(a / b);
            System.out.println("2");
        } catch (ArithmeticException e) {
            System.out.println("3");
        } finally {
            System.out.println("4");
        }

    }
}

如果b = 0;則輸出結果:
1
3
4
如果b = 2;則輸出結果:
1
2
4

​ 4、如果有異常出現並被捕獲,則程式不會被終止,例子如下:

public class ExceptionCatch {

    public static void main(String[] args) {

        int a = 10;
        int b = 0;
        try {
            System.out.println("1");
            System.out.println(a / b);
            System.out.println("2");
        } catch (ArithmeticException e) {
            System.out.println("3");
        } finally {
            System.out.println("4");
        }
        
        System.out.println("5");
    }
}

則輸出結果:
1
3
4
5

提出問題:程式碼中存在多個異常的可能,那該如何解決?

解決方法:使用多個catch程式碼塊捕獲可能出現的異常,最後一個catch程式碼塊中使用Exception兜底。

public class ExceptionCatch {

    public static void main(String[] args) {

        try {
           div(4,0);
        } catch (ArithmeticException e) {
            System.out.println("出現算術異常");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("出現指正越界異常");
        } catch (Exception e) {
            System.out.println("出現異常");
        }
    }

    public static int div(int a, int b) {
        int []arr = new int [a];
        System.out.println(arr[4]);//製造的第一處異常
        return a/b;//製造的第二處異常
    }
}

注:在存在多個catch程式碼塊的程式中,父類異常不能放在子類異常前面,例如:Exception不能放在ArithmeticException之前。

2.3 異常的丟擲

概念:

​ 在某些特殊的情況下有些異常不能處理或者不便處理,就可以將該異常轉移給該方法的呼叫者。當方法執行出現異常,則底層生成一個物件類丟擲,此時異常程式碼後續的程式碼就不會再執行。

語法格式:

​ 訪問許可權 返回值型別 方法名稱(引數列表) throws 異常型別1,異常型別2…{方法體;}

public void test(int a) throws Exception {}

示例程式碼:

在異常捕獲的章節中我們還有最後一個思考的問題

給出以下程式碼,會發現我在show方法中丟擲了異常,所以我在主方法呼叫show方法時需要處理該異常,但是我們需要捕獲異常,而不是丟擲異常,

public class ExceptionThrow {

    public static void main(String[] args) {
        try {
            show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void show() throws IOException {
        FileInputStream fileInputStream = new FileInputStream("d:/a.txt");
        fileInputStream.close();
    }
}

根據示例程式碼,說明三個問題:

  1. 有時候為了集中處理異常,我們可以在存在異常的方法中進行異常丟擲,到最後進行集中異常處理。而異常捕獲只能在該方法中直接處理。

  2. 主方法中進行異常捕獲而不是異常丟擲是因為主方法再往上拋異常就把異常拋給了JVM,不利於異常處理。

  3. show方法中的異常底層來源:

    ①首先進入FileInputStream類中,可以看到該構造方法中建立了File的例項。

在這裡插入圖片描述

②進入File類中,可以看到底層丟擲的異常。

在這裡插入圖片描述

方法重寫後丟擲異常的注意事項:

  • 子類不允許丟擲的異常

    • 比父類更大的異常
    • 和父類平級但是不一樣的異常
  • 子類允許丟擲的異常

    • 能丟擲一樣的異常
    • 能丟擲更小的異常
  • 子類可以不丟擲異常

2.4 自定義異常

概念:

​ 當程式需要表達年齡不合理時,java官方又沒提供這個針對性的異常,此時程式設計師需要自定義異常加以描述。

自定義異常實現:

建立一個自定義異常類繼承Exception類或其子類

public class AgeException extends Exception {

    public AgeException() {
    }

    public AgeException(String message) {
        super(message);
    }
}

自定義異常類使用:

建立一個Person類,在setAge方法中丟擲自定義異常。

public class Person {

    private String name;

    private int age;
    
    public Person() {
    }

    public Person(String name, int age) throws AgeException {
        setName(name);
        setAge(age);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) throws AgeException {
        if (age > 0 && age < 200) {
            this.age = age;
        } else {
            throw new AgeException("年齡不合理哦")
        }
    }
}

執行主方法檢視結果:

public class Test {

    public static void main(String[] args) {

        try {
            Person person = new Person("Bob",210);
        } catch (AgeException e) {
            e.printStackTrace();
        }
    }
}

輸出結果為:
AgeException: 年齡不合理哦
at Person.setAge(Person.java:36)
at Person.<init>(Person.java:17)
at Test.main(Test.java:13)

三、總結

一個異常類結構體系圖

三個異常處理方法

一個自定義異常