第13回 制御構造としての例外
平井玄
2004/2/5
| 例外オブジェクトの生成 |
前回「エラーに対処するプログラムを書く」は予期しないエラーに対処する方法として、Javaの仕組みである例外を紹介しました。復習をかねて、指定したURLからデータを読み込んで画面に表示するプログラムを考えてみます。
| URLからデータを読み込んで画面に表示 |
import java.net.*; |
このプログラム本来の処理は、コマンドライン引数として渡されたURLからHTMLファイルを取得し、その内容を画面に表示することです。しかし、URLが引数として指定されていなかったり、指定されていても書式が間違っていたり、存在しないURLが引数として指定されるかもしれません。例えば、URLが引数として指定されていない場合の処理をプログラムの冒頭で以下のように記述してもいいはずです。
public class ShowHTML { |
この方法の問題点は、HTMLファイルを取得してその内容を画面に表示するというプログラム本来の処理が記述されているブロックに、プログラムを中断するときの処理が記述されていることです。また、プログラムの使い方はURLの書式が無効だったときにも表示するので、同じ内容の語句を表示するためのコードを別々の場所に記述することになり、効率的ではありません。そこで初めに紹介したプログラムでは、引数が指定されなかったときに「throw new MalformedURLException()」という文を実行し、MalformedURLException型のオブジェクトを発生させています。例外もオブジェクトですので、プログラマが手動で生成できるのです。このようにすることで、引数として渡されたときの処理と、URL型のオブジェクトがURLを解釈できなかったときの処理を1つのcatchブロックで処理しています。従って、このプログラム全体の大まかな流れは以下のようになります。
|
URLからファイルを読み込む本来の処理
|
|
URLが指定されていない、間違った書式のURLが引数として渡されたときの例外処理
|
|
存在しないURLが引数として渡されたときの例外処理
|
|
結果を表示する処理
|
try〜catch〜finallyという例外処理用の文が制御構造を記述する制御文としての働きを持つことは、前回簡単に説明しました。例えば、引数が指定されていなかったときに実行される「throw new MalformedURLException();」という文により、プログラムの制御は「catch( MalformedURLException e )」以下の例外処理ブロックに移ります。try〜catch〜を使うと、ちょうどif〜elseで条件分岐させるように、特定の処理ごとにブロックを分けられるわけです。さらにこのプログラムでは、取得したHTML文書も例外発生時の説明文も同じ文字列としてStringBuffer型のオブジェクトであるresultに格納しています。もちろん、例外ごとにprintln()で画面に表示することもできます。しかし、例外が起きても起きなくても最後のfinallyブロックで処理の結果を表示することにより、このプログラム全体が最終的に何かを表示することを目的にしていることが明確になったと思います。
| 例外を親メソッドに任せる |
せっかくURLからHTML文書を取得するプログラムを作ったので、以前に作ったHTMLDocumentクラスに、指定したURLからHTML文書を取得するためのメソッドを追加してみましょう。しかし、URLからHTML文書を取得するときに起こり得る例外はどうしたらいいのでしょうか。HTMLDocumentというクラスはHTML文書を格納したり加工したりするためにあるわけですから、例外発生時にHTMLDocumentのインスタンスが勝手に「例外発生」などと画面に表示するのは困ります。
そこでJavaには、メソッドの定義にthrowsというキーワードを付けることで、あるメソッド内で発生した例外をそのメソッドを呼び出した親メソッドに転送するという機能が用意されています。別のいい方をすると、例えばURLクラスのメソッドであるgetContent()が発生させるIOExceptionの例外も、getContent()が内部で処理せずに呼び出し元に転送しているからこそ、catchブロックによって捕捉しなければならないわけです。ただし、クラスのメソッドで発生した例外を呼び出し元のメソッドで捕捉しているわけですから、制御としてはちょっと複雑になります。breakやcontinueといった通常の制御文であれば、1つ外側のブロックに制御が移るだけですが、throwsによって例外が転送される場合はreturnと書かれていないのに、制御がいきなり親メソッドに移ってしまうからです。
以上を踏まえたうえでHTMLDocument型を書き直してみると、以下のようになりました。
| HTMLDocumentクラスの改良(パッケージとして登録する) |
package jp.co.atmarkit.java; |
元のHTMLDocumentクラスでは、HTML文書をString型で保持していましたが、今回はStringBuffer型で保持することにしました。また、指定したURLからHTML文書を取得するloadFromURL()というメソッドを追加しています。loadFromURL()はMalformedURLExceptionという例外を発生させますので、呼び出し元がこの例外を捕捉しなければなりません。さらに、取得したHTML文書のタグを除いたテキスト部分を取得するgetPlainText()というメソッドを用意しました。
では、さっそく新しいHTMLDocumentを使ったプログラムを書いてみましょう。
| 指定したURLのテキストを表示するプログラム |
import jp.co.atmarkit.java.HTMLDocument; |
プログラムを実行すると以下のような結果が得られます。
C:\>javac ShowTextFromURL.java |
HTML文書をURLから取得したり、HTML文書を解析してテキスト部分を取り出す機能がHTMLDocument型のメソッドとして用意されたため、プログラムはだいぶシンプルなものになりました。操作する対象となるデータと、データそのものをクラスとしてまとめてあるので、HTMLDocument型を使うプログラマは、HTML文書をどうやって取得しているのか、取得したHTML文書からどうやってテキスト部分を取り出しているのか、知っている必要はなくなりました。また、プログラムはHTML文書から取り出したテキストをセットするブロック、例外時のエラーメッセージをセットするブロック、結果を出力するブロックという3つに分割されており、何をしているのか誰が見ても理解できます。
| 例外の種類 |
例外を発生させるメソッドを使った場合はtry〜catchで捕捉しないとコンパイル時にエラーになります。従って、URL型のgetContent ()やHTMLDocument型のloadFromURL()のように、例外を発生させるメソッドを使う場合は必ずtry〜catchを使うことになりますが、はじめのうちはいちいちtryブロック内に本来の処理、catchブロックに例外時の処理を記述することが面倒に感じるものです。しかし見方を変えれば、コンパイルエラーを発生させないために、Javaプログラマはtry〜catch〜finallyを使ってプログラムを(1)本来の処理、(2)例外処理、(3)最後の処理に分割して書くように仕向けられているわけです。
ただし、すべての例外がtry〜catchで捕捉しないとコンパイル時にエラーになるわけではありません。コンパイル時にチェックされる例外と、チェックされない例外があるのです。試しに以下のプログラムをコンパイルして実行してみましょう。
| 配列の添え字はコンパイル時にチェックされない |
public class ExceptionWithoutCheck { |
実行すると以下のような結果が得られます。
C:\>javac ExceptionWithoutCheck.java |
要素の数が10個の配列に対して、インデックス10000の要素にアクセスしようとしていますので、実行時には当然ArrayIndexOutOfBoundsExceptionという例外が発生します。しかし、プログラムで例外を捕捉していないのにコンパイル時にエラーになりません。
実は、MalformedURLExceptionのように、コンパイル時に捕捉しないとエラーになる例外と、ArrayIndexOutOfBoundsExceptionのように、コンパイル時に捕捉しなくてもエラーにならない例外の違いは、第一にそれぞれの例外のスーパークラスの違いです。
すでに説明したように、例外はオブジェクトです。そして、すべての例外はjava.lang.Throwableのサブクラスのインスタンスです。これを図にすると以下のようになります。
![]() |
すべての例外のスーパークラスであるThrowableを継承しているのが、ExceptionとErrorという2つのサブクラスです。このうちMalformedURLExceptionのように、必ず捕捉しなければならない例外の直接または間接的なスーパークラスになっているのがExceptionです。ただし、ExceptionのサブクラスであるRuntimeExceptionを継承しているArrayIndexOutOfBoundsExceptionのような例外は、捕捉しなくてもコンパイル時にエラーになりません。両者の違いはやや分かりにくいのですが、大ざっぱにいうとExceptionのサブクラスのものは、ファイルが存在しない場合やURLが不正である場合など、実行時にしか判断できない種類の例外です。別のいい方をすると、Exceptionのサブクラスになっている例外は、回復し得る例外ということになります。
それに対してArrayIndexOutOfBoundsExceptionのように、RuntimeExceptionのサブクラスである例外は、プログラムを注意して作れば回避可能な例外です。また、RuntimeExceptionを継承した例外はメソッドごとにいちいち捕捉していたらキリがないので、捕捉はできるがプログラマの責任でなるべく回避すべき例外が含まれています。
一方のErrorはExceptionよりも重大な例外のスーパークラスです。例えば、Javaのバーチャルマシンが壊れたことを示すVirtualMachineErrorのように、プログラム内で捕捉しても回復のしようがない例外はErrorのサブクラスになります。
従って、なんでもtry〜catch〜finallyの例外機構を使って書けばよいというわけではありません。要素数を超える配列の要素にアクセスしないようなコードは、本来の処理の中に含めるべきなのです。catchブロックは、あくまでもプログラム本来の目的とは外れた処理、finallyブロックはいずれにしても実行すべき処理、というように明確に使い分けるようにします。
次回はいよいよ最終回です。Javaのクラスを完全にマスターするためには避けて通れない「インターフェイス(interface)」について解説します。
いまから始めるJava バックナンバー
- 第1回 Java2 SDKで学習の準備
- 第2回 Javaの変数の本質を知る
- 第3回 クラスを簡単に理解しよう
- 第4回 クラスの継承の本質を知る
- 第5回 メソッドとコンストラクタはなぜ必要?
- 第6回 Javaプログラムの制御構造を理解する
- 第7回 Javaの制御文を使いこなそう
- 第8回 メソッドの挙動を変えるオーバーライド
- 第9回 クラスのメンバに利用制限を付与するアクセス制御
- 第10回 Javaのクラスをグループ化するパッケージ
- 第11回 Javaのクラスの理解を深める
- 第12回 Javaプログラミングにおけるエラー対処
- 第13回 Javaの例外をスマートに記述する
- 最終回 Javaの抽象クラスとインターフェイス
| 「いまから始めるJava」記事一覧 |
ホワイトペーパー(TechTargetジャパン)
- Android NDKでJNIを使用してアプリを高速化するには (2010/3/17)
C/C++やOpenGL ESといったネイティブコードを使うためのNDKとJNIを紹介し、その使い方や注意点を徹底解説します - 調査の難しい「OutOfMemoryError」事例、5選 (2010/3/11)
Java開発者が避けて通れないメモリ不足エラーの基本的な問題切り分け方法と発生につながる事例、対処法を解説 - 究極の問題解析ツール、逆コンパイラJD-Eclipseとは (2010/3/8)
ライブラリ内で例外が発生! そのクラスのソースコードを調べたい!! 自動で逆コンパイルしてくれる無料Eclipseプラグインがあります - いまさら聞けない「Webサービス」の常識 (2010/2/26)
昨今では企業システムでも使われる「Webサービス」の概念やJava標準のJAX-WSを紹介しJBoss WSでサンプルを作成
|
|
スキルアップ/キャリアアップ(JOB@IT)
スポンサーからのお知らせ
- - PR -
| 「いつかは壊れるサーバ」そんな故障に 迅速で安価に手軽に対応する方法とは? New! |
| 「特権ユーザー」の事件を防げ! 万能権限を持つユーザーの管理方法とは? New! |
| 仮想環境の構築とデータ保護の特効薬?! 実績と信頼性の高いパッケージで安心運用 |
| 仮想環境のバックアップもこれまでどおり 「まるごと取ってまるごと戻す」簡単運用 |
| おばかアプリ選手権、第4弾開催中!! ムダにカッコよくてくだらない作品求ム! |
| 社内ファイルサーバを“クラウド”に統合 VPN直結「クラウド型ストレージ」を紹介 |
| その数、なんと400台以上! グループ内 サーバの「統合管理」によるメリットは? |
| 美人!? まあまあ? 気になる いやし系!! PV急増で「美人時計」がとった手段とは? |
| 進化を続ける富士通ストレージETERNUS DX 製品開発者の自信を裏付けるものとは何か |
| 運用管理の課題を“2つの観点”から分析 ユーザー満足度の高い「仮想環境」とは? |
- - PR -
お勧め求人情報

**先週の人気講座ランキング**
〜CCNA編〜
| ◆ | TomcatやJBossなどAPサーバ環境に関する 情報を集約! “業務”用APサーバ大百科 New! |
| ◆ | 一気に解説! 最新のクラスタストレージ 「RAIDを超えたストレージ基準」……など New! |
| ◆ | クラウド的ユーザー体験の変化は脅威か? 仮想化技術を使いこなす運用管理術を紹介 New! |

| ◆ | 上司や部下、部署内メンバーとの情報共有 を“ガラッ”と変えるコラボツールとは? New! |
| ◆ | おばかアプリ選手権、第4弾開催中!! ムダにカッコよくてくだらない作品求ム! |
| ◆ | 社内ファイルサーバを“クラウド”に統合 VPN直結「クラウド型ストレージ」を紹介 |

| ◆ | Twitterのアカウントはなぜ突破された? メールによる新手の攻撃手法とその対策 |
| ◆ | もう仮想化のお試しフェイズは終わりだ! Hyper-V 2.0が基幹システムも仮想化 |
| ◆ | 美人!? まあまあ? 気になる いやし系!! PV急増で「美人時計」がとった手段とは? |

| ◆ | クライアント企業から求められる人材 ⇒IT技術と経営戦略を併せ持つ「戦略家」 |
| ◆ | .NET編集長が実践する「技術情報検索術」 サンプル・コードを簡単に探す“技”は? |
| ◆ | 業務効率と情報セキュリティ対策を両立! 手間なく確実に機密情報を守る方法とは? |

| ◆ | 進化を続ける富士通ストレージETERNUS DX 製品開発者の自信を裏付けるものとは何か |
| ◆ | 運用管理の課題を“2つの観点”から分析 ユーザー満足度の高い「仮想環境」とは? |

| ◆ | 【CTC事例】約30の基幹システムを統合! 膨大なバッジジョブを制御した方法は? |
| ◆ | 仮想化すればコストは削減できるか? 仮想化に必要な「3つの視点」を解説する |
| ◆ | その数、なんと400台以上! グループ内 サーバの「統合管理」によるメリットは? |







