Javaの歴史
☆ Javaの歴史
JDK1.0→JDK1.1
→(大幅改良 文法の拡張やAPIの追加)J2SE1.2→J2SE1.3→J2SE1.4
→(大幅改良) J2SE5.0(1.5)→J2SE6.0(1.6)→J2SE7.0(1.7)
//大掛かりな改良だったため、1.5ではなく5.0を使うことになった
⇩
〈1.4以前のJavaでは何が使えないのか〉
① 拡張for文が利用できない
② さまざまなデータ構造を扱うAPI〈コレクションフレームワーク〉が
利用出来ない
ex ArrayList<String>
//ジェネリクス(山ガッコを用いたクラスの表記が可能)
このAPIを使うと、ArrayListに指定した型のインスタンスしか格納できないように指定出来る
これが利用出来ないので、
同じ方法を使いたい場合は以下のようにキャスト演算子を使って強制型変換をしないといけない
ArrayList list=new ArrayList( );
list.add("こんにちは");
String s=(String)list.get(10); //キャストを使い強制的に型を変換させる
③ AutoBoxing/AutoUnboxingが利用出来ない
↑基本データ型とラッパークラス間で自動的に型変換を行ってくれるクラス
これが利用出来ないので、
~Value( )メソッドやvalueOf( )メソッドを用いて明示的に相互変換を行う必要がある
④ java.util.Scannerが利用出来ない
↑画面から文字入力ができるクラス
これが利用出来ないので、
次のようにコードを記述し、画面から文字入力が出来るように指定する必要がある
import java.io*. ;
public class Main{
public static void main(String[ ] args) throws Exception{
BufferedReader br=new BufferedReader(new InputStream Reader(System.in));
System.out.println("何か入力してください");
String s=br.readLine( ); //1行読み込む
System.out.println("あなたの入力:"+s);
}
}
※ コード記述簡略化のためにここではmainメソッドにthrows Exceptionを追加しているが、実際にはtry-catchを用いた例外ハンドリングを行う必要がある
エラーの種類 〜エラーが出ないが動作がおかしい場合〜
☆ エラーは出ないが動作がおかしい
・プログラムが動き続けて終わらない
(実行すると、動き続けて止まらない)
原因 ① プログラムはユーザーからの入力を待っている状態。
java.util.Scannerなどを使ってキー入力を試みるとこの状態になる。
② プログラムは無限ループに陥っている
誤った終了条件。for文などを記述した場合に起こる。
〈プログラムの中断方法 コマンドラインプロンプトの場合〉
CTRLキーを押しながらCキーを押すと実行中のプログラムを強制終了できる
eclipseやNotBeansなどの統合開発環境などの場合、操作メニューかたプログラムを終了させる
①の場合の原因
キーボード入力待ちによってプログラムが終わらないことは正しい動作だが、
利用者が「キーボード入力を待っている」と分からないから動き続けていると勘違いしまう
解決策 利用者に対して「キーボード入力を待っている」ことが伝わるように
「◯◯を入力してください」のようなプロンプト(表示)を出すようにする
②の場合
これはプログラムミスなので、ループ終了条件を確認し、ループの中でカウンタ変数を次に進めているかを確認する
ex ifによる条件分岐が正しく動作しない
(if(str=="hello"){…}のような条件分岐を含むプログラムを実行すると、エラーは出ないものの、
ブロック内の実行がされない
原因 文字列の比較に==を使っているから
解決策 文字列の内容が等しいかどうかの判定には==演算子ではなく equals( )メソッドを使う
・金額やフォルダ名の画面表示がおかしい
ex ¥1200やc:¥notepcを出力させようとすると、エラーは出ないものの画面におかしな文字が表示される
原因 ¥記号は特殊文字
¥1200ではなく、¥¥1200にしないと¥120→P(文字コード)に置き換えられてしまい、おかしな文字が表示される
解決策 文字リテラルの中で¥文字を用いたい場合"¥¥"という表記が必要
エラーの種類 〜プログラムが実行できない場合〜
☆ プログラムが実行できない
・ 「java.lang.NoclassDefFoundError」(その1)
・「java.lang.NoclassDefFoundError」(その2)
原因 何らかの理由で"JVMが指定されたクラスのクラスファイルを見つけられない"
① 起動クラスのクラス名のつづりをタイプミス
② 起動クラスの完全限定クラス名(パッケージつきのクラス名 拡張子はなし)で
指定していない
③ クラスファイルがクラスパス以下のパッケージ階層に対応したフォルダに
配置されてない(保存場所の問題)
④ クラスパスの指定が正しく行われていない(どこにJVMがいくかが分からない)
解決策 ① 起動しようとしているクラス名をタイプミスしていないか確認
ex. Main→mainなどになっていないか
② 起動しようとしているクラスの完全限定クラス名を確認
ex. パッケージ a.b.cのMainクラスを起動
↓
③ クラスファイルが指定の場所に正しく存在するか確認する
特にパッケージに所属するクラスではファイル置き場にも注意が必要
ex. パッケージ a.b.cのMainクラス
(クラスパスのフォルダ「¥a¥b¥c¥Main.class」に
クラスファイルが存在していないといけない)
④ クラスパスが起動時または環境変数で正しく設定しているかを確認
(タイプミスしていないかも確認)
・「java.lang.NoSuchMethodError:Main」(1行目)
原因 起動しようとしたクラスに正しいmainメソッドがない。
mainメソッドを定義したつもりなのにこのメッセージが表示される場合、
細部が間違っている可能性がある
(publicやstaticをつけ忘れている。引数の型が違う。mainのつづりが違う)
解決策 起動しようとしたクラスにmainメソッドが存在することを確認
特にmainメソッドの宣言は一字一句間違っていないことを確認
・「java.lang.ArrayIndexOutOfBoundsException」(1行目)
原因 ある配列について、範囲外の添字を使ってアクセスしようとした
ex 要素3の配列 i [0][1][2]
i[3]やi[-1]にアクセスしようとするとエラー
解決策 エラー報告されている行で、添字が範囲外でないか確認する
(特にforやWhileによるループの中で配列を用いる場合は、ループの回数に注意)
ループにおける範囲外の配列にアクセスするエラーを未然に防ぐためには、
拡張for文を利用する
(拡張for文であれば、範囲内のものだけにしかアクセスしないので)
for(型 任意の変数名:配列要素名)
…配列要素から1つずつ取り出して任意の変数名に代入していく
※なお、エラーメッセージの末尾にあるコロンの右側
エラーメッセージ:3 ←「何番目の要素にアクセスしようとしたか」を示す数字が表示されるので参考にする
・「java.lang.NullPointerException」(1行目)
原因 「nullが格納されている変数(中身が何も入っていない)」に対してフィールドやメソッドを利用しようとした
ex 変数s→null
「s.toString( );」を実行→エラー
ex 「a.method( ).method2( )」のようにメソッドの呼び出しを連ねて記述
(まずaメソッドを実行してから、aメソッドで返ってきた値を元にbメソッドを実行するということ)
→a.method( )の実行結果としてnullが返ってきているので、続けたmethod2( )の呼び出しでエラーが発生している
解決策 エラー報告されている行の中でメソッドやフィールドを利用しようとしている部分(上記のa.method( )の部分)を特定し、
内容がnullでないかを確認する
☆ エラーになる行の直前の変数の内容を画面に出力することで確認出来る
もし出力結果がnullの場合、
ソースコードを逆に遡って「どこでnullになってしまったか」を特定し、修正する
・ 「java.lang.ClassCastException:~cannot be cast to~」
原因
あるインスタンスをそのインスタンスの型または親クラスの型以外へ
キャスト(強制型変換)しようとした場合にこのエラーが発生する
ex. Wizard w=(Wizard) o;
//実際のoの中身はHeroであるにも関わらず、Wizard型へキャストしようとする
※ 多くの場合は、キャストを試みた変数(上記ではo)に何型のインスタンスが入っているかを開発者が理解していないことに起因している
解決策
① キャストを試みた変数(上記の例ではo)にインスタンスを代入されている箇所まで
ソースコードを遡る
② 変数に代入したインスタンスの型(ざっくり捉えて代入した型の前は何の型だったのか)を確認する
③ キャスト(強制型変換したい先)の型を確認。
(代入したい型の中身と代入したい先の型が同じかどうか)
※ 変換先の型がインスタンスの型と同じか、その親クラスの型でなければ、
キャスト(強制型変換)は行うことができない
・ 「java.lang.ArithmeticException:/ by zero」(1行目)
原因 数学的に許されてない「0での割り算」を行った場合
ex int a=b/c; //cの中に0が入っていたのでエラー
※時にユーザーが入力した情報やファイルから読み込んだ情報を割る数として利用した場合に、
入力した値をチェックしていないとこのエラーになりやすい
解決策 どんな条件での動作でも0での除算が行われないようプログラムを修正する
エラーの種類 〜コンパイル出来ない場合〜
☆ コンパイル出来ないとき
・「return文が指定されていません」
「このメソッドは型~の結果を必ず返す必要があります」
原因 戻り値を返すように宣言されたメソッドの内部で
「戻り値を返さない可能性がある場合」
ex int型を返すメソッドの中身
if(条件式){return(;)
}←条件式が満たされない場合には値が戻されない
また、実行途中に例外が発生した場合にreturnが実行されない可能性もある。
解決策 処理中の条件分岐や例外発生の有無によらず、
常に何らかの値をreturnするようにプログラムを修正する
・「~は~でprivateアクセスされます」
「~は不可視です」
原因 カプセル化(特定のものからしかアクセスできないようにすること private/package private)により、
アクセス権限がないクラス、フィールド、メソッドを利用しようとしている
ex 同一クラスからしかアクセスできないprivate宣言されているフィールドを他クラスから利用しようとした場合
解決策 利用しようとしているクラスやメンバのアクセス修飾が正しいことを確認する
※ privateフィールドへのアクセスが必要な場合、
フィールドのアクセス修飾をpublicなどへ緩和するのではなく、
getterやsetterが既に存在しないか確認し、なければ作成を検討する
・コンストラクタを定義してもnewで利用出来ない
原因 コンストラクタの宣言に間違いがあり、コンストラクタとして見なされていない可能性がある
ex. Heroクラスに引数2つのコンストラクタ
public void Hero(String name,int hp)
//コンストラクタの宣言にも関わらず戻り値voidの記述がある
→コンストラクタ宣言が間違っているので、インスタンス化するとエラー
よくあるもの
戻り値を宣言している(コンストラクタは戻り値なし)
クラス名とコンストラクタ名に違いがある
(コンストラクタ名はクラス名と一緒でなければならない)
解決策
①コンストラクタ宣言に戻り値がついてないことを確認する(void記述もだめ)
②コンストラクタ名がクラス名と完全に一致していることを確認する
「~は~で定義されています」
原因 "戻り値だけしか違いがない"場合には、オーバーロードが利用できない
ex public void m(int a)
public int m(int a)
解決策
(オーバーロード自体を諦める)
・引数の数か型が違うものになるようにする
(戻り値だけの違いではないように修正する)
・メソッド自体に継承関係や親クラスが存在するのであれば、親クラスの型を戻り値にすることで(多態性を利用)
メソッドを2つ作るのではなく、1つに統一する
・ メソッドの呼び出しの戻り値をDate型変数に代入できない
「互喚性のない型です」「型が不一致です」
ex 前提 "クラスAのa( )メソッドはDate型の戻り値を返す"とする
→その結果をDate型変数に代入しようとするとエラー
(Date型の戻り値を設定しているにも関わらず)
原因 java.util.Date型で返されたインスタンスをjava.sql.Date型の変数に代入しようとしている可能性あり
クラスA java.util.Date がimportされている
クラスB java.sql.Date がimportされている
(importされているパッケージは同じでないといけない)
解決策 正しいimport文が記述されているかソースコードを確認
※ java.util.Dateクラス以外にも入力支援によりimport間違いしやすいクラス
・「例外~は報告されません」
「処理されない例外の型~です」
原因
前提 "メソッドA内で「チェック例外が発生する可能性のあるメソッドB」を呼び出す"
しかし、メソッドA内ではtry-catchとthowsどちらも指定されていない
(例外発生時にどのように動作をすべきかメソッドAに指定していない)
解決策 メソッドBの呼び出しをtry-catch文で囲む
例外発生時にメソッドB内では対処を行わず、呼び出し先のメソッドAに例外処理を任せる場合、
メソッドBの修飾にthrowsを追加する
・ 「シンボルを見つけられません」(その2)
「暗黙的スーパー・コンストラクタ~はデフォルト・コンストラクタについては未定義です」
原因 親クラスA….引数があるコンストラクタ
その子クラスB…コンストラクタが指定されてない
(引数なしのコンストラクタと見なされる)
子クラスBに自動で追加されるデフォルトコンストラクタsuper( )は引数がないため、
親クラスのAの引数なしコンストラクタを呼び出そうとするが、親クラスに引数なしのコンストラクタがないために、
エラーとなる
解決策 子クラスにコンストラクタを引数のある形(suoer(引数))と記述して、引数のある親コンストラクタをきちんと呼び出す
エラーの種類 エラーメッセージとその対応
☆ エラー種類
・「";"がありません」
原因 セミコロンと似ている文字を入力している可能性あり
解決策 :(コロン)やi(小文字のアイ)を入力していないか確認。また、誤って全角文字の;になってないかも確認する
・「構想解析中にファイルの終わりに移りました」
原因 ソースコード内で{ }の対応がとれてない(閉じ忘れ)
解決策 波かっこの対応を再度確認して修正する
・「class.interface、またはenumがありません。」
原因 ソースコード内でブロックの{ }の対応がとれてない(閉じすぎ)
解決策 波かっこの対応を再度確認して修正する
・「〈identifier〉がありません。」
原因 ソースコード内でpublicやstaticなどのキーワードのタイプミス
または、現在のバージョンのJDKでは利用出来ないキーワード、構文を利用している場合に発生
解決策 ① エラーが指摘されている箇所でタイプミスをしていないか確認
② もし問題ない場合、現在利用中のJDKで利用出来る構文であることを確認
・ 「¥12288は不正な文字です。」
原因 ソースコード内に全角スペースの文字が含まれている
ex. インデントにタブや全角スペース文字が含まれている可能性もあり
public…
ここ → public…
※ Webサイトなどからサンプルコードをコピー&ペーストした場合に混入することもあるので注意
¥12288….全角スペース
¥65307….全角 ;
¥65373….全角 }
¥65289…..全角 )
¥8221……全角 ”
・「シンボルが見つけられません。」
「~を~に解決できません。」
原因
①存在しないクラス名・型名・変数名・メソッド名・フィールド名などを利用している
ex String型を指定したい string(先頭を大文字ではなく小文字にしてしまっている)
変数name namae(スペルが間違っている)
② importしていないクラスを利用しようとした場合
ex java.util.ArrayListをimport文で宣言していないにも関わらず、
コードの中には「ArrayList a=new ArrayList( );」がある
→クラスが見つからないのでエラーになる
解決策 このエラーメッセージの次の行には
見つけられなかったシンボルが何であるかという情報が隠されている
ex シンボル:クラスString
① このシンボル名を確認し、
ソースコード中で確かに存在するクラス・型・フィールド・メソッド・変数を指定しているか確認する
②もしクラス名の場合はimportを忘れていないかを確認
・ 「変数は初期化されていない可能性があります。」(その1)
原因 値をまだ一度も代入していない変数を計算や画面出力に利用しようとした
ex int a;
System.out.println(a); ←aの値代入し忘れ
解決策 エラーで報告された変数に値を代入する
未然に防ぐために変数は極力必要になった時に随時宣言する
(宣言と同時に初期値を代入する)
・ 「変数は初期化されていない可能性があります。」(その2)
…..final修飾されたフィールドを持つクラスをコンパイルするとこのメッセージが表示される
原因 final修飾されたフィールドはインスタンス化が完了するまでの間に中身の値が確定しないといけない
(final修飾された値は永久に固定だから)
また、何も代入されてないfinalフィールドが存在しうる場合に発生する
(確実に値を代入しないといけないため。finalがつくと他のメソッド操作などから値を代入することはできないから)
解決策 finalフィールド宣言時には、
「final int a=10;」のように、すぐに初期値を代入する
または、コンストラクタ(自動初期値)内でaの値が確実に代入されるようにする
「staticでないメソッド~をstaticコンテキストから参照することは出来ません」
「非staticメソッド~をstatic参照出来ません」
原因 mainメソッドは静的メソッド(static)なので、静的メソッドの制約により、
同じくstaticで宣言されているメンバしか利用できない
よって、mainメソッドからstaticでないメソッドを呼び出そうとするとエラーとなる
解決策 mainメソッドから呼び出そうとしているメソッドがstaticで修飾されているか確認
もしされていなければ
① メソッドをstaticで修飾する
(これはクラスにメソッドがくっつく状態になるので、インスタンス化しなくてもメソッドが動く)
② 呼び出し先メソッドを含むクラスをインスタンス化した上でメソッドを呼び出す
・ 「クラス~はpublicであり、ファイル~で宣言しなければなりません」
「public型~はそれ独自のファイル内に定義されなければなりません」
原因 publicなクラスAを定義するファイル名は、A.javaである必要がある
それ以外のクラス名を指定するとエラーになる
解決策 クラス名.javaにクラス名を修正
エラーとは何か
☆ エラー
① 「何が悪いのか、どこが悪いのか」という指摘は、エラーメッセージに書いてある
→まずはエラーメッセージをよく読む事が大事
② 原因を理解した上で修正する
(なぜなら、原因が分からないまま修正して動いたとしても、また同じエラーに悩まされるだけだから)
③ エラーはスキルアップのチャンス
熟練プログラマは頭の中に「エラーを起こした失敗経験と、それを解決した成功経験」の記憶の引き出しをたくさん持っている
・・・似たようなエラーに悩んだ経験があるから出来る
・コンパイルエラーの読み方
>javac Main.java
Main.java:5 : 変数は初期化されていません;
↑エラー発生場所 ↑エラー本文
☆ コンパイルエラーが一度に2つ以上あった場合は、上から着実に修正していくこと
…なぜなら、最初のエラーが原因で2番目のエラーが誘発されていることが少なくないから。
・スタックトレースの読み方
>java Main
Exception in java.lang.NumberFormatException:null
at java.lang.Integer.parseInt(Unknown Source)
↑エラーが起きた直接的な要因
at sub process(sub.java:3) //sub…クラス名 process…メソッド名
↑最も怪しむべき間接要因
at Main.caller(Main.java:28)
at Main.main(Main.java:6)
☆ スタックトレースは下から上に向かって読んでいく
翻訳「Mainクラスのmain( )の内部でMainクラスのcaller( )を呼び、その内部でsubクラスのprocess( )を呼び、
その内部でIntegerクラスのparseInt( )を呼んだところ、例外が発生した」
〈解決までのプロセス〉
① まず、先頭行を見て発生した例外の種類と詳細情報を確認し、
「 何が起きたか」を把握する
今回:NumberFormatException:null→「数字の形式がおかしい(nullであった)」と推測
②「どこで起きたか」を把握するために、次の行(atから始まる最初の行)を見る
(この行に記述があるクラスおよびメソッド内で例外が発生している
「例外の直接原因となった場所」)
③その行を見ても問題が見つからなかった場合や、その行がAPIメソッドである場合は次の行を見る。
:バグを探す根本の考え方は「このメソッドを呼び出した元が悪かったのではないか」と仮定すること
よって、あらかじめAPIとして準備されているメソッドにバグがあるとは考えにくい
④直接原因となっているメソッドを呼び出している「subクラスのprocessメソッド」を例外の間接要因として着目する
(このクラスはAPIクラスではなく、自分が開発しているクラスであるため、誤ったコードが含まれている可能性が比較的高い)
⑤もし、そのコードに問題がなければ、それを呼び出している元メソッドコード(次の行)に誤りがないかを検証する
:
(この作業を繰り返していく)
スマートフォンアプリ/ウェブアプリを作るには
☆ スマートフォンアプリを作る
アプリを作るには、それぞれの機種専用の「プログラミングで利用するクラス」がSDKとして提供されており、
それら専用のクラスを用いてプログラミングを行う必要がある
☆ Webアプリケーション
利用者がブラウザから入力した情報をサーバ側のプログラムで処理する仕組み(インターネット経由でアクセス出来る)を備えたWebサイトを作る
ブラウザ → Webサーバ → データベース
← ←
Webアプリケーションの開発….サーブレットというクラスを使用
サーブレットのコードができたらコンパイルし、いくつかの設定ファイルなどと共に、Javaを動かせる機能があるWebサーバに登録する。
ex. aaajava.jp サーバにHelloServletを登録すると、世界中どこからでも「http://aaajava.jp/HelloServlet」という
アドレスにブラウザでアクセスし、HelloServlet内に書いてある内容の実行ができる
※ Java7以降
AutoClosableインタフェースを実装しているものは、
tryブロック終了後または、例外発生時に自動的にclose( )が実行され、
ファイルが閉じられるようになった
7以前のルール
try-catch文の利用は「finally内でclose( )を必ず呼ぶこと」
FileReader r=null;
try{
r=new FileReader("c¥¥test.dat");
}catch(IOException e){
:
}finally{
if(r!=null){ //rがnullではない時(nullの場合はデータ読み込みなし)
r.close( ); //ファイルを閉じる
}
⇩java7以降
「finallyなしでOK」
try{
FileReader r=new FileReader("c¥¥test.dat");
}catch(IOException e){
:
}