2020/4/10 12時頃追記
いきなりで申し訳ないが、不足があることに気がついた。
簡単に書くと、実行した時に出るエラーの説明が不足している。
エラーが出た後の見方は合っているはずなので、一旦加筆まではそこだけ見て欲しい。
修正したら、再度通知させていただこう。
2020/4/11 9:45追記
不足分を別記事として投稿した。
この記事で実行時エラーと書いてある部分について、そちらで補足しているので、よかったら併せてご覧いただきたい。
【備忘録】Javaの実行時に発生するエラーについて | Shino’s Mind Archive
JavaのSwing備忘録の記事で、エラーが出たよということを書いたと思う。
以下の記事だ。
【備忘録】Java Swingの動作 -イベントディスパッチスレッド- | Shino’s Mind Archive
…ふと思ってしまったのだが、このエラーについては全然解説していなかった。
というか、実は私はここがものすごく苦手だ。厳密に言うと次回書く内容。
なので、今回改めて勉強するので、その結果を備忘録としてまとめていこうと思う。
2部構成に分け、前半はエラーの種類やそれぞれの具体例を書く。
後半では、特に実行時エラーに焦点を当てて、その対応に関する処理を書いていこう。
なお、あくまで勉強結果なので、誤りが含まれている可能性がある。
その場合は、ご指摘頂けると幸いだ。
エラーの種類
エラーには、大きく分けて2種類ある。
- コンパイルエラー
- 実行時エラー
まず最初は、この区別をしっかりつけることが重要になる。
それぞれの解説…の前に、Javaの実行方法をおさらいしておこう。
Javaの実行方法
Javaでソースコードを書いた後、以下の二つの処理が必要になる。
- コンパイル
- 実行
まず、私たちが書いたソースコードを、機械が読んで処理できるような形に変換するコンパイルという処理を行う。
コマンドで言えば、javac ソースコード名
というものだ。
その後、実際に実行を行う。コマンドはjava クラス名
。
二つの分類は、このどちらで発生するエラーか、という違いだ。
なお、eclipse等のツールを使用している場合、いきなり実行すると思う。
これは、コンパイルが自動で行われているためだ。
書いていると、赤の波線でエラーを示してくれるところがあると思う。
それが、コンパイルエラー。
それに対し、実行するとコンソールに赤文字で色々書かれる場合もある。
こっちは実行時エラー。
では、これらのエラーの詳細を見ていこう。
コンパイルエラー
これは、簡単に言ってしまえば文法の誤りだ。
コンパイル時には、この文法チェックも同時に行われている。
ちょっと、あえてこのエラーを起こしてみよう。
public class SampleClass {
public static void main (String[] args) {
System.out.prnitln("Hello World!");
}
}
3行目、本来System.out.println
であるところを、System.out.prnitln
としてみた。
printの、iとnを入れ替えてある。
これでコンパイルすると、以下のような表示が出てくる。
(実行コマンド)> javac .\SampleClass.java
.\SampleClass.java:3: エラー: シンボルを見つけられません
System.out.prnitln("Hello World!");
^
シンボル: メソッド prnitln(String)
場所: タイプPrintStreamの変数 out
エラー1個
これは、System.out
というものの中に、prnitln
なんてもんはないよというエラーだ。
この詳細をちょっと見ていこう。
まず2行目、先頭はどのファイルでエラーが発見されたかを表している。
今回で言えば、SampleClass.javaというファイルの3行目で発生している。
その後ろは、エラーメッセージ。
これを見れば、大体どんなエラーかがわかる。
今回、シンボルが見つかんないよと言われていて、これは取り出そうとしている処理や変数がないことを表している。
で、次の行がかなり重要だ。
3, 4行目で、2行目で示された行のどこでエラーが発生しているかを表してくれている。
今回、System.out
の次にチェックがついている。
つまり、このあたりが怪しいぞと言ってくれているのだ。
その下は、今回存在しないメソッドを参照しようとしているので、その呼び出そうとしているメソッドの詳細と、それをどこから探そうとしたかまで書いてくれている。
最後に、エラーが検出された件数まで出してくれているのだ。
かなりの情報を提示してくれているのが分かると思う。
環境によっては英語で出てくると思うが、それでも書いてある内容は同じ。
冷静に、一個ずつ見ていけば何がまずかったのかわかるはずだ。
もう一個、エラーを起こしてみよう。
上では使用を間違えてみたが、今度は定義を間違えてみる。
public class SampleClass {
public static void main (String[] args) {
printHello();
}
static printHello () {
System.out.println("Hello World!");
}
}
今度は、メソッド宣言時に戻り値の型を忘れてみた。
そうすると、コンパイル時に以下のような表示が出てくる。
(実行コマンド)> javac .\SampleClass.java
.\SampleClass.java:6: エラー: 無効なメソッド宣言です。戻り値の型が必要です。
static printHello () {
^
エラー1個
今度はダイレクトに問題を指摘してくれた。
もう書いてある通りだ。しかもどこに何を入れるかも書いてくれてある。
このように、コンパイルエラーは文法的に問題があるものを指摘してくれる。
これが出たら、その指摘された箇所を見直してみよう。
実行時エラー
次はこちらだ。
これは、文法的な誤りはないため、文法をチェックするコンパイルでは見抜けない。
実行して、初めておかしい箇所が出てくるのだ。
例えば、以下のようなプログラムで見ていこう。
public class SampleClass {
public static void main (String[] args) {
int[] array = new int[5];
array[5] = 10;
}
}
5つの要素を持つ配列array
を用意して、その5番目の要素へアクセスしている。
配列の要素は、0番目からカウントするので、5個の要素の場合は0番目から4番目までしか用意されていない。
でも、その状態で5番目にアクセスしてしまっているとしよう。
そうすると、まずコンパイルではエラーが出ない。
その後、実行のタイミングで以下のようなエラーが出てくる。
(実行コマンド)> javac .\SampleClass.java
(表示なし)
(実行コマンド)> java SampleClass
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at SampleClass.main(SampleClass.java:4)
なんか英語でめっちゃ文章が出てきた。でも、落ち着いてほしい。
一個一個読み解いていけば、そんなに難しいことは書いていない。
まず4行目。先頭から見ていこう。
最初に、Exception in thread "main"
とある。
これは、mainスレッドという処理の中でエラーが出たよということを表している。
スレッドというものは処理の流れで、実行時にまず一つmainというスレッドが生成される。
その他、プログラム中で新しく別の処理の流れを作ることもできるのだが、詳細は控えよう。
分かる人は、そのスレッドをここで判断できるよということを覚えておいて欲しい。
分からない人は、基本mainと書かれているはずだ。
ただ、以前書いたように、GUI表示を行う場合には、別のスレッドになる可能性がある。
その詳細は以前の記事を参照して欲しい。
で、同じ行の後半、java.lang.ArrayIndexOutOfBoundsException: 5
とある。
これが、どんなエラーが発生したかを表すものだ。
今回、Array Index Out Of Bounds Exceptionとある。
Arrayなので配列、Index Out Of Boundsなので、インデックスの範囲外となる。
つまり、配列に対して範囲外の要素へアクセスしようとしてるよというエラーだ。
ご丁寧に、その後にいくつでアクセスしようとしているかまで表示してくれている。
その下、at以降で、どこでそれが発生したかを書いてくれている。
今回、SampleClass.main(SampleClass.java:4)
となっている。
先頭はクラス名、次がメソッド名なので、SampleClassというクラスのmainというメソッドで発生していることが分かる。
更に、後ろの括弧はソースコードにおける位置だ。
SampleClass.javaというソースコードの4行目…問題の個所になっている。
こんなふうに、こちらもどんなエラーが、どこで発生したかを示してくれているのだ。
なお、別のメソッドから呼ばれていると、それが下に連なって出てくる。
つまり、どんな処理の流れで出たかまで見ることができるのだ。
こちらももう一つ、エラーを出してみよう。
public class SampleClass {
public static void main (String[] args) {
String str = null;
System.out.println(str.length());
}
}
今度は、文字列変数str
にnullを入れて、その文字数を表示しようとしている。
で、これを実行してみよう。以下のようになる。
(実行コマンド)> java SampleClass
Exception in thread "main" java.lang.NullPointerException
at SampleClass.main(SampleClass.java:4)
Javaをやっている人なら一度は聞いたことがあるだろう。
NullPointerExceptionというやつだ。
これは、中身の入っていないオブジェクトに対して、その中にアクセスしようとした場合に発生する。
ちょっと復習で、Javaの文字列String型は、参照型だった。
つまり、String型変数にはその実体を指すポインタ、住所が入っている。
通常は、その住所先を見に行って、そこから値や処理を取り出してくる。
で、nullというのは、何も指さないことを表している。
つまり、見に行く先がないよ、というエラーなのだ。
両者の違いをもうちょっと具体的に
ここまで、両方のエラーについて解説してきた。
だが、まだイマイチ掴めていない方もいらっしゃるだろう。
というわけで、配列の要素範囲外へのアクセスを例に、もうちょっと違いを見ていこう。
ソースコードを再掲しておこう。
public class SampleClass {
public static void main (String[] args) {
int[] array = new int[5];
array[5] = 10;
}
}
これをコンパイルするとエラーなし、実行するとArrayIndexOutOfBoundsExceptionが発生していた。
これで、なぜコンパイルエラーが出ないかを見てみる。
まず、配列への要素アクセスの文法は以下のようになっている。
配列変数名[整数]
これを見てもらえれば分かると思うが、文法上は、添え字はあくまで整数だとしか決められていない。
その整数は、例えばint型変数も入れることができる。
で、そのint型変数が入っていた場合、コンパイル時はその中身が分からないのだ。
ちょっとわかりやすくするために、以下のように組み替えてみよう。
public class SampleClass {
public static void main (String[] args) {
errorTest(0);
errorTest(1);
errorTest(2);
errorTest(3);
errorTest(4);
errorTest(5);
}
static void errorTest (int index) {
int[] array = new int[5];
array[index] = 10;
}
}
まずコンパイルでやることは、文法のチェックだった。
3行目から8行目では、errorTest
という関数に、数字一つを引数として渡している。
これは、文法的には問題ない。11行目でそう定義しているからだ。
で、その11行目以降では、数字を一つ受け取って、その添え字に10を代入している。
この中だけ見ても、文法上の誤りはない。
先に配列を定義して、文法に沿った形で要素にアクセスしているからだ。
で、コンパイル時はどこから、どんな数字を渡されるかが分からないので、その範囲が正しいかまでは見ることができない。
実際に実行されると、3~7行目までは添え字の範囲内に収まっているので、エラーは出ない。
しかし、8行目で呼び出される時には、範囲外の5が渡される。
そして、13行目でその5番目の要素にアクセスしようとしている。
そこで初めて、範囲外のアクセスだと分かり、実行時エラーとして出力される。
これが、コンパイルエラーと実行時エラーの違いだ。
まとめ:コンパイルエラーと実行時エラー
今回は、コンパイルエラーと実行時エラーの違いを見てきた。
コンパイルエラーとは文法上の誤りで、その名の通りコンパイル時にエラーが発生する。
実行時エラーは、文法は正しいが処理としておかしい場合にエラーが発生する。
どちらにせよ、どの箇所で、どんなエラーが発生しているかは確認することができる。
で、コンパイルエラーは出たら文法を直すので最終的には発生しなくなる。
しかし、実行時エラーはどうやっても発生の可能性を潰しきることはほぼできない。
そこで、そのエラーが出たらどうするか、という書き方が用意されている。
それが、例外処理というやつだ。
次回は、その例外処理の書き方等を解説していこう。
更新情報はTwitter、Facebookにて告知している。
どちらも、ページ下部の各アイコンから確認できるので、よかったら覗いていってほしい。
それでは。
コメント