スッキリわかるJava入門 第8章 インスタンスとクラス(練習問題)
第8章 インスタンスとクラス(練習問題)
練習8(8-1~8-4)
public class Cleric { // 名前 String name; // HP int hp = 50; // 最大HP final int MAX_HP = 50; // MP int mp = 10; // 最大MP final int MAX_MP = 10; // 魔法_セルフエイド public void selfAid() { System.out.println(name + "は魔法「セルフエイド」を唱えた!"); // MPを5消費 this.mp -= 5; // HPを最大HPまで回復 this.hp = this.MAX_HP; System.out.println("最大HPまで回復した."); } // 祈る public int pray(int sec) { System.out.println(name + "は、" + sec + "秒祈った!"); // ランダムで0~2ポイント補正 int correction = new java.util.Random().nextInt(3); System.out.println("** 補正ポイント:" + correction); // MP回復量(仮) int tempAidPoint = sec + correction; // 最大MPより回復しないよう制御 //(MP回復量(仮)またはMP減少値の最小値を採用) int aidPoint = Math.min(tempAidPoint, this.MAX_MP - this.mp); System.out.println("** 回復前のMP:" + this.mp); // MP回復 this.mp += aidPoint; System.out.println("MPが" + aidPoint + "ポイント回復した."); System.out.println("** 回復後のMP:" + this.mp); return aidPoint; } }
public class Main { public static void main (String[] args) { Cleric cleric = new Cleric(); // 名前 cleric.name = "聖職者A"; // 魔法_セルフエイド cleric.selfAid(); // 4秒祈る cleric.pray(4); } }
実行結果①:MP回復量(仮)< MP減少値(4 < 5)
C:\work\ex84>java Main
聖職者Aは魔法「セルフエイド」を唱えた!
最大HPまで回復した.
聖職者Aは、4秒祈った!
** 補正ポイント:0
** 回復前のMP:5
MPが4ポイント回復した.
** 回復後のMP:9
実行結果②:MP回復量(仮)= MP減少値(5 = 5)
C:\work\ex84>java Main
聖職者Aは魔法「セルフエイド」を唱えた!
最大HPまで回復した.
聖職者Aは、4秒祈った!
** 補正ポイント:1
** 回復前のMP:5
MPが5ポイント回復した.
** 回復後のMP:10
実行結果②:MP回復量(仮)> MP減少値(6 > 5)
C:\work\ex84>java Main
聖職者Aは魔法「セルフエイド」を唱えた!
最大HPまで回復した.
聖職者Aは、4秒祈った!
** 補正ポイント:2
** 回復前のMP:5
MPが5ポイント回復した.
** 回復後のMP:10
スッキリわかるJava入門 第8章 インスタンスとクラス②
第8章 インスタンスとクラス②
クラス定義により可能になる2つのこと
クラス型変数とは
クラス型変数が必要な理由
インスタンスの生成方法
- クラス名 変数名 = new クラス名();
public class Main { public static void main(String[] args) { // 1.勇者を生成 Hero h = new Hero(); } }
- 実際にインスタンスを生成しているのは、右辺の「new Hero()」という部分で、どれをHero変数に代入している。
インスタンスのフィールド利用
- 変数名.フィールド名 = 値;
public class Main { public static void main(String[] args) { // 1.勇者を生成 Hero h = new Hero(); // 2.フィールドに初期値をセット h.name = "ミナト"; h.hp = 100; System.out.println("勇者" + h.name + "を生み出しました!"); } }
インスタンスのメソッド呼び出し
- 3.勇者のメソッドを呼び出してゆく を参照
public class Main { public static void main(String[] args) { // 1.勇者を生成 Hero h = new Hero(); // 2.フィールドに初期値をセット h.name = "ミナト"; h.hp = 100; System.out.println("勇者" + h.name + "を生み出しました!"); // 3.勇者のメソッドを呼び出してゆく h.sit(5); h.slip(); h.sit(25); h.run(); } }
- 着目するのは、mainメソッドの内容が「まるで冒険物語のシナリオ台本みたい」でわかりやすい点。
- オブジェクト指向でなければ、mainメソッドの中に、いつくものHP計算処理や繰り返しSystem.out.println()を呼び出していた。
オブジェクト指向のクラスは現実世界と繋がっている
- 6章までのクラス(HelloWorldなど)も「文法的には正しいクラス」だが、「クラスを何のために用いるか」という思想が伴っているか否かに決定的な違いがある。
- 6章までのクラスは、「メソッドが大きくなってきた」「機能単位でクラスを分ける」など、開発者の都合や機能の単位などでクラスやメソッドを作ってきた。
- Heroクラスは、「オブジェクト指向」という明確な思想に基づいてクラスやメソッドが作られている。
- オブジェクト指向を意識したプログラム開発とは「現実世界の人や物、出来事をクラスに置き換えていく」作業にほかならない。
- 現実に似せて作り、現実に似せて動かしていく、これがオブジェクト指向の根本的な思想。
スッキリわかるJava入門 第8章 インスタンスとクラス①
第8章 インスタンスとクラス①
オブジェクトを生み出す手順
クラスとオブジェクトが別である理由
- 「オブジェクトを大量に作る必要がある状況」を創造する。
- たとえば、銀行で「口座」オブジェクトが1,000個必要な場合、それぞれに対して「属性として、残高・名義人・開設日があって…」という定義を繰り返すと、プログラマの作業は膨大になる。
- クラスに「属性として、残高・名義人・開設日があって…」という定義をした「口座クラス」を1度作っておけば、このクラスから100個でも1,000個でも必要なだけオブジェクトを生み出す事ができる。
- クラスは、プラモデル工場にある「金型」と同じと考えれば良い。元となる金型を1個作っておいて、そこにプラスチックを流し込んで、同じプラモデルを大量に製造する。
- 改めて意識するのは、クラスとオブジェクトはまったく違うものであるという点。
- プログラムの動作時に仮想世界の中で活躍するのは「オブジェクトだけ」であって、その金型であるクラスが仮想世界で活動することは基本的にはない。
オブジェクトという用語のあいまいさ
プログラムに登場する2種類のクラス
登場人物クラスの作り方
勇者 |
名前 HP |
戦う 逃げる 座る 転ぶ 眠る |
- あるクラスの設計内容を上から「クラス名」「属性」「操作」の一覧として並べる書き方は、クラス図(class diagram)という設計図の書き方ルールに準じたもの。
- クラス図は世界共通の設計図の書き方であるUML(Unified Modeling Language)で定められている図の1つ。
public class Hero { String name; int hp; public void attack() {/* … */} public void sleep() {/* … */} public void sit(int sec) {/* … */} public void slip() {/* … */} public void run() {/* … */} }
属性の宣言方法
- 属性(名前、HP)についてプログラムで使用する変数の名前と型を考える。
- 属性を宣言するにはクラスブロックの中に変数宣言を記述する。
- クラスブロック内に宣言された変数を、Javaでは特にフィールド(field)と言う。
public class Hero { String name; // 名前の宣言 int hp; // HPの宣言 }
属性の初期値指定と定数フィールド
- フィールド宣言時に代入も同時に行うよう記述しておくと、そのフィールドの初期値を設定することができる。
public class Matango { int hp; int level = 10; }
- フィールド宣言の先頭にfinalをつけると、値を書き換えることのできない定数フィールドになる。
- 定数フィールドは一目でそれと分かるように大文字で記述することが推奨される。
public class Matango { int hp; final int LEVEL = 10; }
操作の宣言方法
- 「操作」をプログラミングするには、まず「操作の名前」「操作するときに必要な情報の一覧」「操作の結果として指示元に返す情報」「処理内容」の4つを考える必要がある。
【名前】 sleep
【必要情報】なし
【結果】 なし
【処理内容】眠った後はHPが100に回復する
public class Hero { String name; int hp; void sleep() { this.hp = 100; System.out.println(this.name + "は、眠って回復した!"); } }
- thisとは特別に準備された変数で、「自分自身のインスタンス」を意味している。
- 同じクラス内のフィールドへのアクセスの場合、「this.」を省略しても動作する。
- しかし、ローカル変数や引数にも同じhpという名前の変数があった場合、そちらが優先されてしまうなど予想外の動作になる可能性がある。
- フィールドを用いるときは明示的にthisを付けるようにする。
クラス名とメンバ名のルール
- クラス名、フィールド名、メソッド名はそれぞれ、基本的に識別子のルールを満たしていれば自由に決めることができる。しかし、実際には次のような慣例に従い、名前を付けることが望ましいとされる。
クラス名 | 名詞 | 単語の頭が大文字 | Hero、MonsterInfo |
---|---|---|---|
フィールド名 | 名詞 | 最初以外の単語の頭が大文字 | level、itemList |
メソッド名 | 動詞 | 最初以外の単語の頭が大文字 | attack、findWeakPoint |
クラス定義のまとめ
勇者 |
名前 HP |
眠る 座る 転ぶ 逃げる |
- 「属性」「操作」の一覧をクラス図にまとめ、それをJavaコードに置き換え「フィールド」と「メソッド」として記述していく。
public class Hero { String name; int hp; public void sleep() { this.hp = 100; System.out.println(this.name + "は、眠って回復した!"); } public void sit(int sec) { this.hp += sec; System.out.println(this.name + "は、" + sec + "秒座った!"); System.out.println("HPが" + sec + "ポイント回復した"); } public void slip() { this.hp -= 5; System.out.println(this.name + "は、転んだ!"); System.out.println("5のダメージ!"); } public void run() { System.out.println(this.name + "は、逃げ出した!"); System.out.println("GAMEOVER"); System.out.println("最終HPは" + this.hp + "でした"); } }
スッキリわかるJava入門 第7章 オブジェクト指向をはじめよう②
第7章 オブジェクト指向をはじめよう②
サッカーで考えるオブジェクト指向
- 監督(プログラマ)である自分は、コンピュータに的確に指示を出し、目的どおりに動かさなければならない。
- 手続き型で選手に指示を出す場合、選手に逐一、指示を出さなければならない。
- オブジェクト指向型で選手に指示を出す場合、選手の役割と責任を事前に割り当てれば、後は選手自身が自分の役割を果たすために行動する。
- 監督(プログラマ)である自分は、一人一人の選手を部品と考え、それぞれの責務(役割・責任)を事前に割付けたクラスとして作る。
- 試合が始まったら、監督(プログラマ)のすることは、ほとんどない。
- 仮想世界には、それぞれの選手オブジェクトが生みだされ、後は選手オブジェクト自身が自分の役割を果たしながら他のオブジェクトと連携して動いてくれる。
- 監督(プログラマ)である自分は、それぞれの選手(=クラス・オブジェクト)に、「この状況下で、どう行動すべきか」という責務をあらかじめプログラミングしているため、試合中にそれぞれの選手の一挙手一投足まで指示する必要はなくなる。
- 責務の割り付け:オブジェクト指向プログラミングでは、プログラマはそれぞれの部品に「責務」をプログラムとして書き込む。
オブジェクトの姿
- 「サッカー選手」「口座」「受付」など、仮想世界で動くそれぞれのオブジェクトは、すべて何らかの責務を仮想世界の神様たるプログラマから与えられる。
- 「サッカー選手」オブジェクトは「ボールを受けたら前に走る」「シュートする」など、あらかじめ設定された役割を果たす行動責任を負っている。
- ATMの「受付」オブジェクトは、振り込み依頼を受け付けたら「口座」オブジェクトが管理する2つの口座間でお金を移動し、その結果を「印刷係」オブジェクトに渡してATM利用控えの印刷を依頼するという流れが受付の行動責任。
- 「口座」オブジェクトは行動責任を負っていないが、「残高をしっかり覚えておく」という情報保持責任を負っている。
- このような「情報保持」と「行動」の責任を果たすために、それぞれのオブジェクトは「属性」と「操作」を持っている。
- 【属性】その登場人物に関する情報を覚えておく箱
- 【操作】その登場人物が行う行動や動作の手順
- 勇者オブジェクトは、自分の名前やHPを覚えておかなければならない(=情報保持責任)。
- もし「戦え」と命令されれば勇敢に目の前の敵と戦い、「眠れ」と命令されれば眠って自分のHPを回復させる責任(=行動責任)がある。
【属性】 ・名前=ミナト ←勇者の名前。 ・HP =100 ←ヒットポイント(生命力)。これが0になったらGAMEOVER。 【操作】 ・戦う ←目の前の敵を殴る。 ・逃げる ←戦闘を終了する。 ・眠る ←HPが100に回復する。 ・座る ←指定した秒数だけ座る。座った秒数だけHPが回復する。
- そのオブジェクトがどんな属性や操作を持つかは、プログラマが部品を作成する際に決定する。
- そのため現実世界の登場人物をよく観察し、どのような属性を持ち、どのような操作ができるかを忠実に再現する。
- お化けキノコは重要でないモンスターなので名前は不要と考え、「名前」属性は持っていない。
- このモンスターは「催眠ガス」という技が使えるという設定なので、その操作を持っている。
【属性】 ・HP =35 ←ヒットポイント(生命力)。これが0になったらGAMEOVER。 ・レベル=10 ←倒したら得られるレベル。 【操作】 ・戦う ←目の前の敵を殴る。 ・逃げる ←戦闘を終了する。 ・催眠ガス ←相手を眠らせる。
オブジェクトの振るまいと相互作用
- 勇者やお化けキノコは複数の操作を持っている。
- プログラムのmainメソッドやほかのオブジェクトから、それらオブジェクトの操作を呼び出す(=行動指示を送る)ことができる。
- mainメソッドから勇者に「座れ」と指示を送れば、勇者は仮想世界内で座って自分のHP属性を回復させる動きをする。なぜなら勇者には、その行動責任があるから。
- お化けキノコに対して「逃げろ」と指示を送れば、仮想世界内のお化けキノコは逃げだして戦闘が終わる。
- あるオブジェクトから別のオブジェクトへの操作の指示を送ることも可能。たとえば、mainメソッドからお化けキノコに「催眠ガス」という指示を送ると、お化けキノコは勇者が持っている操作の中から「眠る」を呼び出す。すると勇者は眠ってしまう。
- オブジェクトは別のオブジェクトが持つ操作を呼び出すだけではなく、他のオブジェクトの属性を取得したり書き換えたりもできる。たとえば、mainメソッドから勇者「戦う」という指示を送ると勇者は戦い、結果としてお化けキノコのHP属性を書き換えて減らす動作をする。
オブジェクト指向の3大機能
- Javaのようなオブジェクト指向言語(Object Oriented Programming Language=オブジェクト指向の考え方に沿ってプログラムが作りやすい配慮がなされているプログラミング言語)には、開発者が「より便利に」「より安全に」現実世界を模倣できるよう、文法などに専用の機能が準備されている。
- それがオブジェクト指向の3大機能。
- カプセル化:属性や操作を、一部の相手からは利用禁止にする機能。
- たとえば、現実世界では、剣が勇者に「眠れ」という指示を出すことは、まずあり得ない。
- 「眠る」操作は、剣オブジェクトから呼べないようにしておいた方が安全。
- 継承:過去に作った部品を流用し、新しい部品を簡単に作れる機能。
- すでに「勇者」という部品があれば、空を飛べる「スーパー勇者」は簡単に開発できる。
- 多態性:似ている2つの部品を「同じようなもの」と見なし、「いい加減」に利用できる機能。
- 「お化けキノコ」と「オオコウモリ」では厳密には斬りつけ方が微妙に異なるはず。
- 違いを気にせず、どちらも「同じようなもの」と見なし、「戦う」操作で攻撃できる。
スッキリわかるJava入門 第7章 オブジェクト指向をはじめよう①
第7章 オブジェクト指向をはじめよう①
オブジェクト指向を学ぶコツ
オブジェクト指向の定義
オブジェクト指向のメリット
- 根底にある目的は「人間に把握できるプログラム開発を実現する」というもの。
- プログラムを容易に変更しやすくなる(柔軟性・保守性の向上)。
- プログラムの一部を簡単に転用できる(再利用性の向上)。
- 「マスターできたら嬉しい!」「絶対マスターしたい!」と心底思えるようなメリットは、「ラクして、楽しく、良いもの」を作れるようになること。
オブジェクト指向と現実世界
- そもそも開発するプログラムやシステムは、「現実世界における何らかの活動を自動化するためのもの」。
- プログラムやシステムは、現実世界のある活動を人間に代わって機械にやらせるために作られるものであって、現実世界とは無関係に単独で存在しているものはほとんどない。
手続き型プログラミングとの違い
- 6章までは、手続き型プログラミング(procedural programming)と呼ばれ、プログラムの先頭から順番に命令として記述していく方法。
- オブジェクト指向で開発を行う場合、プログラマはいきなりコードを書き始めることはしない。
- プログラムで実現しようとする部分の「現実世界」を観察しイメージ図(設計図)を描く。
- 着目すべきは、設計図はITの知識がない普通の人に見せても理解できる「現実世界における構図」そのものである点。
- 設計図の中の登場人物や物の1つ1つを部品ととらえ、それを「クラス」というJavaにおける部品で記述していく。
- オブジェクト指向による部品化のルールは、現実世界にでてくる登場人物の単位で、プログラムをクラスに分割すること。
開発時に作るクラス、実行時に動くオブジェクト
オブジェクト指向におけるプログラマの役割
- オブジェクト指向プログラミングにおいて、プログラマはまるで「Java仮想世界における神様」のような存在。
- 仮想世界にどんな登場人物や物を生み出し、それらをどのように連携させるかを決め、それぞれの部品を作っていく立場だから。
- 例えば「複数の倉庫にある書籍をネットで販売するプログラムを作る」なら、本の販売業務を観察した上で「必要なもの(オブジェクト)は『本』『倉庫』『顧客』『購入記録』…」と判断し、設計図を書いていく。
- 手続き型のプログラマのように「コンピュータが一行一行、何を実行するかという手順定める」のではなく、「オブジェクトをどう作るか、どのように連携させるか」を第一に意識しながら開発していく。
- このことが「オブジェクト指向プログラミング」という名前の由来になっている。
- 「~指向」というのは、「~を大切にした…」「~を中心にと捉えた…」という意味。
オブジェクト指向の本質
- なぜオブジェクト指向の考え方を使うと、大規模で複雑なプログラムも把握でき、その結果、ラクして楽しくいいものを作れるのか?
- 私たち人間が慣れ親しみ、よく把握している現実世界をマネして作られたプログラムもまた、私たち人間にとって把握しやすいものだから
- オブジェクト指向には以下のメリットもある。
- ①プログラム開発時に「頭を捻り、発想して作る」必要はない。現実をお手本に、それをマネして作ればいい。
- ②現実世界の登場人物に変化があった場合、対応する部品(クラス)を修正、交換すれば簡単にプログラムを修正できる。
- このようなメリットは、「現実世界をマネる」からこそ生まれてくる。
- 現実世界の登場人物たちを、コンピュータの中の仮想世界の中にオブジェクトとして再現し、現実世界と同じように連携して動くようにプログラムを作ることこそがオブジェクト指向の本質。
- オブジェクト指向の本質は、現実世界の登場人物とその振る舞いを、コンピュータ内の仮想世界で再現すること。
スッキリわかるJava入門 第6章 複数のクラスを用いた開発(練習問題)
第6章 複数のクラスを用いた開発(練習問題)
練習6-1(6-3)
import comment.Zenhan; public class Main { public static void main (String[] args) throws Exception { // 前半 Zenhan.doWarusa(); Zenhan.doTogame(); // 後半 comment.Kouhan.CallDeae(); comment.Kouhan.showMondokoro(); } }
package comment; public class Zenhan { public static void doWarusa() { System.out.println("きなこでござる。食えませんがの。"); } public static void doTogame() { System.out.println("この老いぼれの目はごまかせませんぞ。"); } }
package comment; public class Kouhan { public static void CallDeae() { System.out.println("えぇい、こしゃくな。くせ者だ!であえい!"); } public static void showMondokoro() throws Exception { System.out.println("飛車さん、角さん。もういいでしょう。"); System.out.println("この紋所が目にはいらぬか!"); // 3秒間待つ Thread.sleep(3000); // もう一度、とがめる Zenhan.doTogame(); } }
練習6-2
フォルダ配置
C:\WORK\EX64 │ Main.class │ └─comment Kouhan.class Zenhan.class
実行結果
C:\work\ex64>java Main
きなこでござる。食えませんがの。
この老いぼれの目はごまかせませんぞ。
えぇい、こしゃくな。くせ者だ!であえい!
飛車さん、角さん。もういいでしょう。
この紋所が目にはいらぬか!
この老いぼれの目はごまかせませんぞ。
練習6-4
フォルダ配置
C:\WORK\EX64 │ Main.class │ └─comment Kouhan.class Zenhan.class
実行結果
C:\Users>java Main
きなこでござる。食えませんがの。
この老いぼれの目はごまかせませんぞ。
えぇい、こしゃくな。くせ者だ!であえい!
飛車さん、角さん。もういいでしょう。
この紋所が目にはいらぬか!