【納得Java】try-catch文のthrowとthrowsの使い分け

例外処理(try-catch)って実行時に発生するエラーに対して、記述した処理が実行されるようにするために使われますよね。

例外処理の例として真っ先に思い付くのが0(ゼロ)での割り算や、ファイル操作です。

ファイルからデータを読み込む処理を記述したいけれども、読み込みたいファイルがそもそもない場合を想定しておかなければなりません。

でも、例外処理にはthrowを使った処理throwsを使った処理があって、似た用語なので使い分けに迷ったり、例外が重なる場合はどうすればいいのか分からなくて困ることってありませんか?

この記事では、例外処理について

  • throwの使い方
  • throwsの使い方
  • throwsを使う場合の入れ子とは
  • 例外の種類に応じた処理の分け方
  • などの基本から応用的な使い方についても解説していきます。今回は例外処理について、使い方をわかりやすく解説します!

    throwとは

    throw例外を意図的に起こし、例外処理を実行する場合に使われます。

    意図的に例外を起こすとは、ある条件と一致する場合に例外を発生させ、エラーメッセージを返すということです。

    throwでexceptionが発生する場合

    それでは、サンプルコードでthrowの使い方をみていきましょう。除算(割り算)において、0(ゼロ)で割る場合の例外処理の例です。

    このサンプルコードでは、throwを使ってIllegalArgumentExceptionの例外を発生させています。

    public class Main {
     
        public static void main(String[] args) {
            // ゼロでの除算
            div(0);
        }
     
        static void div(int i){
            if (i != 0) {
                System.out.println("除算の結果は " + (3 / i) + " です");
            } else {
                throw new IllegalArgumentException("除算の分母がゼロで不正です");
            }
        }
    
    }

    実行結果:

    Exception in thread "main" java.lang.IllegalArgumentException: 除算の分母がゼロで不正です
        at Main.div(Main.java:13)
        at Main.main(Main.java:6)

    throwsとは

    次にthrowsについて解説します。throwsは例外が発生した場合、呼び出し元に例外処理を投げることができます。

    例外処理を記述する際には2つの方法があります。

    throwsで呼び出し元に例外処理を投げるか、try-catch構文を用いるかのどちらかです。try-catchを用いる場合はメソッドを使ったその場で例外処理を実装することができます。

    throwsを用いる場合は呼び出し元に処理を投げるので、呼び出し元の処理によってはプログラム全体の処理が停止することもあります。try-catchを用いる場合は例外処理を実施後、それ以降の処理も実行されます。

    throwsの使い方

    それではthrowsの使い方について、サンプルコードをみていきましょう。

    throwsを使う場合

    まずは、throwsでメソッドの呼び出し元に例外を投げる場合です。サンプルコードで確認しましょう。

    なお、読み込む対象となるtest.txtファイルはあえて用意をせず、FileNotFoundExceptionの例外を発生させています。

    import java.io.FileNotFoundException;
    import java.io.FileReader;
     
    public class Main {
    
        public static void main(String[] args) {
            String fileName = "test.txt";
     
            try {
                throwsSample(fileName);
            } catch (FileNotFoundException e) {
                System.out.println(fileName + "を読み込みませんでした");
            }
            System.out.println("処理が終了しました");
        }
     
        public static void throwsSample(String fileName) throws FileNotFoundException {
            FileReader r = new FileReader(fileName);
            System.out.println(fileName + "を読み込みました");
        }
    
    }

    実行結果:

    test.txtを読み込みませんでした
    処理が終了しました

    このサンプルコードでは、throwsSampleメソッドでFileReaderクラスを使用しているのでエラー処理を実施する必要があります。

    例外処理はthrowsを使って呼び出し元のクラスに投げています。

    この場合、throwsで呼び出し元のクラスに例外処理を投げた後の処理は中止され、例外処理をmainメソッドでcatchしてその後処理をしています。

    try-catchを使う場合

    ちなみに、throwsを使わずにtry-catch構文を用いる場合は次のサンプルコードのようになります。

    import java.io.FileNotFoundException;
    import java.io.FileReader;
     
    public class Main {
     
        public static void main(String[] args)  {
     
     
            String fileName = "test.txt";
            try {
                FileReader r = new FileReader(fileName);
                System.out.println(fileName + "を読み込みました");
            } catch (FileNotFoundException e) {
                System.out.println(fileName + "を読み込みませんでした");
            }
            System.out.println("処理が終了しました");
        }
     
    }

    実行結果:

    test.txtを読み込みませんでした
    処理が終了しました

    この場合もthrowsを使う場合と同じようにcatchで例外処理を受けた後の処理も実行され、「処理が終了しました」と表示されています。

    入れ子でthrowsを使う場合

    例外処理が必要な処理を含むメソッドを使用する場合、そのメソッドを使用する場所でも例外処理を記述する必要があります。

    この場合、throwsを入れ子で使用することになります。

    メソッドを使用する場所での例外処理はメソッドの呼び出し元にまず投げられ、メソッド内の例外処理がさらに呼び出し元に投げられるということになります。

    こちらもサンプルコードで確認していきましょう。

    import java.io.FileNotFoundException;
    import java.io.FileReader;
     
    public class Main {
    
        public static void main(String[] args) {
            String fileName = "test.txt";
     
            try {
                throwsSample1(fileName);
            } catch (FileNotFoundException e) {
                System.out.println(fileName + "を読み込みませんでした");
            }
            System.out.println("処理が終了しました");
        }
     
        public static void throwsSample1(String fileName) throws FileNotFoundException {
            throwsSample2(fileName);
        }
        
        public static void throwsSample2(String fileName) throws FileNotFoundException {
            FileReader r = new FileReader(fileName);
            System.out.println(fileName + "を読み込みました");
        }
    
    }

    実行結果:

    test.txtを読み込みませんでした
    処理が終了しました

    throwsは呼び出し元のメソッドに例外処理を投げます。

    このサンプルコードでは、まずthrowsSample2メソッドで発生したFileNotFoundExceptionの例外処理をthrowsSample1に投げています。

    そこからさらにmainメソッドに例外を投げてその例外をcatchしているということになります。

    例外の種類に応じて処理を分ける場合

    例外の種類に応じて処理を分ける場合、処理を記述する順番に注意をする必要があります。hrowsを使う場合では呼び出し元のクラスに例外処理を投げ処理が終了となり、その後の処理が行われないからです。

    例外の種類が複数ある場合は、throwsの後にそれぞれの呼び出し元を「,」(カンマ)で区切って記述します。

    それではサンプルコードで確認していきましょう。

    import java.io.FileNotFoundException;
    import java.io.FileReader;
     
    public class Main {
    
        public static void main(String[] args) {
            String fileName = "test.txt";
     
            try {
                throwsSample(fileName);
            } catch (FileNotFoundException | ArithmeticException e) {
                if (e instanceof FileNotFoundException)
                    System.out.println(fileName + "は読み込めませんでした");
                if (e instanceof ArithmeticException)
                    System.out.println("除算の分母がゼロで不正です");
            }
            System.out.println("処理が終了しました");
        }
     
        public static void throwsSample(String fileName) throws FileNotFoundException, ArithmeticException {
            // FileNotFoundExceptionクラスの例外発生
            FileReader r = new FileReader(fileName);
            System.out.println(fileName + "を読み込みました");
     
            // ArithmeticExceptionクラスの例外発生
            System.out.println("除算の結果は " + (3 / 0) + " です");
        }
    
    }

    test.txtファイルが存在しない場合の実行結果:

    test.txtは読み込めませんでした
    処理が終了しました

    test.txtファイルが存在する場合の実行結果:

    test.txtを読み込みました
    除算の分母がゼロで不正です
    処理が終了しました

    このサンプルコードでは、2つの例外が発生する可能性があります。

    先に記述した例外が発生した場合はその例外処理が行われ、後の処理は実行されていません。先に記述した処理に例外が発生しなければ、後の処理も実行することができます。

    このように記述する順番によって、処理が分けられる場合とそうでない場合があるので注意しましょう!

    このサンプルコードでは、catch以降を「catch (FileNotFoundException | ArithmeticException e)」のように記述しました。Java7からこのような簡潔な記述ができるようになりました。ただし、親子関係にあるクラスをこのように並べて記述することはできません。

    FileNotFoundExceptionクラスIOExceptionクラスのサブクラスですので、この2つのクラスをこのサンプルコードのように並べて記述することはできませんので注意してください。

    try catch文の使い方総まとめ

    この記事では紹介しきれなかったtry catch文のいろいろな使い方を次の記事にまとめているので、ぜひ確認してください!

    【Java】try-catchで例外処理を実装しよう!Exceptionクラスの使い方
    更新日 : 2019年6月8日

    try-catch-finally文の使い方

    try-catch文にはfinallyを使って例外の発生にかかわらず必ず行う処理を記述することができます。

    try-catch-finally文の使い方については次の記事にまとめているので、ぜひ確認してください!

    【Java入門】try-catch-finallyの使い方
    更新日 : 2019年1月28日

    まとめ

    ここでは、例外処理のthrowとthrowsの使い分けについて説明しました。

    冒頭にも書きましたが、エラー処理で実行が止まってはトラブルの原因になる可能性がありますので、例外処理はしっかりとマスターするようにしましょう!

    とは言え、習い始めの頃はなかなか慣れないかもしれませんので、使いこなせることができるようにこの記事を何度も参考にして下さいね!

    LINEで送る
    Pocket

    SEからWebエンジニアへ転職した理由

    侍エンジニア塾卒業生の小池さんは、以前は社内SEとして約5年ほど勤務していました。しかし業務内容は社内のヘルプデスク対応など、プログラムを書く仕事は全くなかったそうです。

    SEながらプログラムを書けない現状に「将来仕事がなくなるんじゃないか」と不安を感じ、プログラミング学習を決意。

    弊社スクールで学習し、無事ベンチャー企業のプログラマーとして転職に成功しました。そんな小池さんの学習法や転職体験談を伺いましたので、是非ご覧ください。

    「プログラミングができないSEは仕事がなくなる」不安を感じたSEが未経験から転職成功するまで
    更新日 : 2019年10月7日

    書いた人

    長野 透

    長野 透

    熊本在住のフリープログラマ兼ライターです。C/C++/C#、Java、Python、HTML/CSS、PHPを使ってプログラミングをしています。専門は画像処理で最近は機械学習、ディープラーニングにはまっています。幅広くやってきた経験を活かしてポイントをわかりやすくお伝えしようと思います。
    お問合せはこちらでも受け付けています。
    [email protected]

    おすすめコンテンツ

    あなたにぴったりなプログラミング学習プランを無料で診断!

    プログラミング学習の効率を劇的に上げる学習メソッドを解説