プログラム言語 例外処理

概要

・例外(エラー)を補足
・処理を止めることなくエラーの内容を呼び出し元サブルーチンへ返す
・サブルーチン間では例外用の戻り値や引数を設計する必要はない。

従来のエラー処理との比較

error1

従来のエラー処理

①A: Bを呼び出す
②B: Cを呼び出す
 ※エラー発生
③C: エラーコード判定。Bにエラーコードを返す処理
④B: エラーコード判定。Aに受け取ったエラーコードを返す処理

A: 受け取ったエラーコードに応じた処理
問題点
エラーコード判定処理及び、返す処理を複数のサブルーチンに書かなくてはならない。
書き忘れ、記述ミス、サブルーチンの追加、エラーコードの増減・変更等に応じてサブルーチンのエラー処理を修正する必要がある。

改善されたエラー処理

①A: Bを呼び出す
②B: Cを呼び出す
 ※エラー発生
③C: Bに「例外」を渡す
④B: Aに「例外」を渡す

A: 受け取った「例外」に応じた処理
問題点
・「例外」を呼び出し元のサブルーチンにそのまま返すだけ。
記述はサブルーチン間で共通。修正の必要なし。

使用例

トランザクションありの場合
use Exception;
use Illuminate\Support\Facades\DB;

DB::beginTransaction();
try {
 〜
 DB::commit();
} catch (\Exception $e) {
 DB::rollBack();
 throw $e;
}

トランザクションのみの場合(注:例外処理なし
return DB::transaction(function () use ($〜) {
 〜
});

トランザクションありの場合(例外処理あり)
try {
 return DB::transaction(function () use ($〜) {
  〜
 });
} catch (\Exception $e) {
 throw $e;
}

use Exception;
try {

 if (~) return;

} catch (\Exception $e) {
 エスケープ必要
 $msg = $e->getMessage();
 $trace = $e->getTraceAsString();

 throw new Exception($e->getMessage());
} finally {
 ~
}

例外が発生するクラスを利用する場合
public static InputStream newInputStream(~) throws IOException
{
  ~
}

パターン① throw
public static void main(String[] args) throws IOException {
 String ret = method();
}

private static String method() throws IOException {
 File file = new File(“C:\\test.txt”);
 Path path = file.toPath();
 InputStream stream = Files.newInputStream(path);
 return stream.toString();
}

パターン② try catch
public static void main(String[] args) {
 String ret = method();
}

private static String method() {
 File file = new File(“C:\\test.txt”);
 Path path = file.toPath();
 InputStream stream;
 try {
  stream = Files.newInputStream(path);
  return stream.toString();
 } catch (IOException e) {
  return null;
 }
}

パターン③ try with resources
public static void main(String[] args) {
 String ret = method();
}

private static String method() {
 File file = new File(“C:\\test.txt”);
 try (InputStream stream = Files.newInputStream(file.toPath())) {
  return stream.toString(); 
 } catch (IOException e) {
  return null;
 }
}

public void myForm_Load(object sender, EventArgs e)
{
  try
  {
    SubRoutine();
  }
  catch (Exception ex)
  {
    MessageBox.Show(text:ex.Message, caption:System.Reflection.MethodBase.GetCurrentMethod().ToString());
  }
}

private void SubRoutine()
{
  try
  {
    //例外を発生させる
    throw new System.IO.FileNotFoundException(message: “ファイル無し”);

    throw new System.Exception(message:”エラー全般”);

  }
  catch(System.IO.FileNotFoundException){
    //ファイル無し
    throw; //外へエラー情報を渡す
  }
  catch(System.IO.IOException){
    //ロック
    throw; //外へエラー情報を渡す
  }
  catch(System.UnauthorizedAccessException){
    //アクセス権限なし
    throw; //外へエラー情報を渡す
  }
  catch (System.Exception)
  {
    //その他のエラー
    throw; //外へエラー情報を渡す
  }
  finally
  {
    //エラー有無に関わらず必ず実行させる処理(各種Close処理等)
  }
}

try
{
 throw “エラー内容文字列”;
}
catch (char *str) {
 cout << str;
}

Private Sub myForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  Try

    Call subRoutine()

  Catch ex As Exception
    Call MessageBox.Show(ex.Message, Reflection.MethodBase.GetCurrentMethod.Name)
    ’SubRoutine()での例外メッセージが表示される
  End Try
End Sub

Private Sub SubRoutine()

  Dim sr As IO.StreamReader = Nothing
  Try

    Throw New IO.FileNotFoundException(“ファイルが見つかりません。”)

    Throw New Exception(“例外処理”)

  Catch ex As IO.FileNotFoundException
    ’ファイルがない
    Throw '(外の処理に例外メッセージを渡す)

  Catch ex As IO.IOException
    ’ファイルロック時
    Throw '(外の処理に例外メッセージを渡す)

  Catch ex As UnauthorizedAccessException
    ’アクセス権がない      
    Throw '(外の処理に例外メッセージを渡す)

  Catch ex As Exception
    ’その他
    Throw '(外の処理に例外メッセージを渡す)

  Finally
    ’確実にファイルを閉じる
    If Not sr Is Nothing Then
      sr.Close()
    End If
  End Try
End Sub

‘例外発生時:次の行へ移る
OnErrorResumeNext

‘例外発生時:指定行へ移動
OnErrorGoTo ERR_TRAP

  Exit Sub(Function)

ERR_TRAP:
  ’エラー番号からエラーメッセージを取得
  Call MsgBox(Error(Err.Number))

import sys

def test():
  try:
    例外を発生させる
    raise Exception(‘エラーメッセージ’)
  except Exception as err:
    補足したエラーメッセージ
    print(str(err))
    tracebackオブジェクト
    print(sys.exc_info()[0])
    print(sys.exc_info()[1])
    print(sys.exc_info()[2])
    # 例外を上に逃がす
    raise
  finally:
    print(‘end’)
  return

try:
  test()
except:
  test()内の例外を補足

function ErrTest()
{
 try {
  myMethod();
 }
 catch (e) {
  window.alert(e.message);
  // → 作成されたエラー
 } finally {
  ~
 }
}

function myMethod()
{
 try {
  throw new Error(‘作成されたエラー’);
 }
 catch (e) {
  throw e;
 }
}

エラー/チェック例外/非チェック例外

※JAVA
Throwableクラス
(1)Error
 ※電源やメモリの問題等、プログラムでは対処できない問題
  try~catchの記述は強制されない
  (書く事はできる)
(2)例外
 ※プログラムで対処できる問題
  ①Exception(検査例外)
   ※try~catchの記述が強制される例外
    try~chatchの記述忘れを防ぐ為に言語仕様として定められた例外
  ②RuntimeException(非検査例外)
   ※try~catchの記述が強制されない例外
   (書く事はできる)
以下のコーディングはコンパイルエラー
public static void main(String[] args) {
  method();
}
private static void method()
{
  String filename = “C:\test.txt”;
  FileReader reader = new FileReader(filename);
}
FileReaderクラスのメソッド(コンストラクタ)は検査例外であるFileNotFoundExceptionが発生する可能性がある。
その為、以下の対策を施す必要がある。
(1)このメソッドをtry~catchで囲む
(2)throwsで呼び出し元へ例外を渡す。
(1)public static void main(String[] args) {
  method();
}
private static void method()
{
  String filename = “C:\test.txt”;
  try {
    FileReader reader = new FileReader(filename);
  } catch (FileNotFoundException e) {
    System.out.println(e.getMessage());
  }
}
(2)private static void method() throws FileNotFoundException
{
  String filename = “C:\test.txt”;
  FileReader reader = new FileReader(filename);
}
※この場合、method()の呼び出し元ではmethod()に対して同様に、
(1)このメソッドをtry~catchで囲む
(2)throwsで呼び出し元へ例外を渡す。
の対策を施す必要がある。
public static void main(String[] args) {
  try {
    method();
  } catch (FileNotFoundException e) {
    e.getMessage();
  }
}

主な例外一覧(エラー)

java.lang.OutOfMemoryError
メモリーが不足した場合にスロー

java.lang.StackOverflowError
スタックオーバーフロー時にスロー

java.lang.ExceptionInInitializerError
staticイニシャライザーでの例外発生時にスロー
staticイニシャライザーはクラスのインスタンス作成前に処理される為、
直接JVMに例外を渡す

主な例外一覧(検査例外)

Exceptionのサブクラス

java.lang.ClassNotFoundException
クラスが見つからない場合にスロー

IOExceptionのサブクラス
java.io.FileNotFoundException
ファイルが存在しない。開けない場合にスロー

java.io.file.FileAlreadyExistsException
ファイルが既に存在している場合にスロー

java.io.file.DirectoryNotEmptyException
ディレクトリーが空でない場合にスロー

java.io.file.NoSuchFileException
ファイルが存在しない場合にスロー

主な例外一覧(非検査例外)

java.lang.RuntimeException のサブクラス

java.lang.ClassCastException
変換できないクラスへキャストしようとした場合にスロー

java.lang.IllegalArgumentException
メソッドに渡された引数が想定外の値の場合にスロー

java.lang.NullPointerException
オブジェクトの値がnullの時、そのオブジェクトのメンバ変数やメソッドを参照した場合にスロー

java.lang.IllegalStateException
メソッドの呼び出しに対してオブジェクト状態が不正(インスタンス化されていない等)

java.lang.IndexOutOfBoundsException
オブジェクト(配列、文字列等)へのアクセス(インデックス)が範囲外である場合にスロー

java.lang.StringIndexOutOfBoundsException
文字列に対して誤った文字位置を指定した場合にスロー

java.lang.NumberFormatException
文字列を数値型に変換しようとした時、文字列の形式が正しくない場合にスロー

java.lang.ArithmeticException
整数演算時、0で除算した場合にスロー

java.lang.SecurityException
セキュリティーマネージャーによってセキュリティー違反と判定された場合にスロー

アサーション

プログラマーのミスを発見する為のエラーチェック
assert

assert 1==2
例外が発生する
assert 1==1
何も起こらない

アサーションを無効化して実行する方法
python ~.py
assert命令が実行される
python -O ~.py
assert命令が無視される