STUDENT_ID : 045754A NAME : Hayato.MIYAGI プログラミングIIの課題6 課題6 Report#6:11/15付講義資料と今回の講義資料中のサンプルプログラムについて考察せよ。{〜12/13(Mon)} 講義資料サンプルプログラム考察については、static変数以降とする。 static変数 class Sum { int total; Sum() { // コンストラクタ(クラス名と同名) total = 0; // 0 クリア } void add(int x) { total += x; } } class SumSample { public static void main(String args[]) { Sum obj1 = new Sum(); // クラスSumのオブジェクトobj1生成 Sum obj2 = new Sum(); // クラスSumのオブジェクトobj2生成 obj1.add(10); obj1.add(20); obj2.add(30); obj2.add(40); System.out.println("obj1.total = " + obj1.total); System.out.println("obj2.total = " + obj2.total); } } ※実行結果 obj1.total = 30 obj2.total = 70 考察 obj1.add(10)が実行された時点でobj1.totalには10が格納されている。次にobj1.add(20)が 実行されたときx = 20なので、totalには10 + 20 = 30となっている。 obj2.totalの値の出力もobj1.totalと同様にして出力されている。 つまり、totalの値は格納されたままで次の処理を促している。 しかし、obj1とobj2の変数totalは共に違うことに注意! 以上でstatic変数の動作の考察を終わります。 次はstatic変数を共有させるソースを以下に示します。 class Sum { static int total; // 値を共有させる Sum() { // コンストラクタ(クラス名と同名) total = 0; // 0 クリア } void add(int x) { total += x; } } class SumSample { public static void main(String args[]) { Sum obj1 = new Sum(); // クラスSumのオブジェクトobj1生成 Sum obj2 = new Sum(); // クラスSumのオブジェクトobj2生成 obj1.add(10); obj1.add(20); obj2.add(30); obj2.add(40); System.out.println("obj1.total = " + obj1.total); System.out.println("obj2.total = " + obj2.total); } } ※実行結果 obj1.total = 100 obj2.total = 100 考察 変数totalの値をstatic変数と指定することで別々のオブジェクトで使用 される変数totalを別々のオブジェクトで共有して使用することができるように なります。 つまり、上の例では変数totalをstaticなものとして指定しているので、obj1や obj2で変数totalの値が変化しても、そのまま値を格納して最終的に変数total の値は100になっています。 コンストラクタを用いた合計を出力プログラム class Sum { int total; Sum() { // コンストラクタ 引数無し total = 0; } Sum(int x) { // コンストラクタ 引数 x total = x; } void add(int x) { total += x; } } class ConstrcSample { public static void main(String args[]) { Sum obj1 = new Sum(); // クラスSumのオブジェクトobj1生成 obj1.add(10); obj1.add(20); System.out.println("sum1 = " + obj1.total); Sum obj2 = new Sum(100); // クラスSumのオブジェクトobj2生成 obj2.add(10); obj2.add(20); System.out.println("sum2 = " + obj2.total); } } ※実行結果 sum1 = 30 sum2 = 130 考察 obj1では引数がないのが変数totalの初期値が0に指定されているのでobj1.add() が実行されると変数totalの値は10 + 20 =30となる。 obj2では引数が100に指定されているので、変数totalの値は100に初期化される。 その後にobj2.add()が実行されると、total = 100 + 10 +20 = 130 となる。 staticイニシャライズを用いたプログラム class Disp { static { // static イニシャライズ System.out.println("initialize"); } Disp() { // コンストラクタ System.out.println("construct"); } } class StaticSample { public static void main(String args[]) { Disp obj1 = new Disp(); // オブジェクト生成時にコンストラクタを処理 Disp obj2 = new Disp(); // オブジェクト生成時にコンストラクタを処理 } } ※実行結果 initialize ※ Dispクラスのロード時に処理 construct ※ Disp.obj1 = new Disp(); が実行されるときに処理 construct ※ Disp.obj2 = new Disp(); が実行されるときに処理 考察 上の例ではstaticイニシャライズを用いることでクラスDispが呼び出されたときに staticの部分が実行されている。プログラムの中で先に処理したいプログラムがある ときは上のようにstaticイニシャライズを用いて処理することができる。このとき、 コンストラクタよりも先に処理を行うことに注意すること。 class Display { void Disp() { // メソッド Disp 引数なし System.out.println("Nothing"); } void Disp(int x) { // メソッド Disp 引数 x System.out.println(x); } void Disp(int x, int y) { // メソッド Disp 引数 x, y System.out.println(x + y); } } class OvldSample { public static void main(String args[]) { Display obj = new Display(); // クラスDisplayのオブジェクトobj生成 obj.Disp(); // オブジェクトobjのDisp()を実行 obj.Disp(1); // オブジェクトobjのDisp(1)を実行 obj.Disp(1, 2); // オブジェクトobjのDisp(1,2)を実行 } } ※実行結果 Noting 1 3 考察 クラス内でオーバーロードするときに注意することは、引数が異なっていなければ いけないということに注意。確かに、人間でも同じ名前の人同士もいるが、中身が全く 同じ人は存在しない。それと同じことだと思う。 privateを用いて、変数の参照を不可にするプログラム例 class keisan { atatic int tanka; private float rate; // private変数rateの宣言 keisan() { // コンストラクタ tanka = 1000; rate = 0.05f; } init keisan(int kazu) { int kingaku; kingaku = (int)(tanka * kazu * (1.0 + rate)); return kingaku; } } class PrvSample { public static void main(String args[]) { keisan obj1 = new keisan(); int kazu = 3; int kingaku = obj1.keisan(kazu); System.out.println("tanka = " + obj1.tanka); System.out.println("kazu = " + kazu); System.out.println("kingaku = " + kingaku); // System.out.println("rate = " + obj1.rate); //private変数のため参照不可 } } ※実行結果 tanka = 1000 kazu = 3 kingaku = 3150 考察 このプログラムは単価1000の商品を3つ買ったときの総額を消費税を含めて、出力する プログラムです。クラスkeisanでは、float型変数のrateに「private」という修飾子が 付いているので変数rateはクラスkeisanの中でしかアクセスできないことになります。 よって、クラスPrvSampleの中で変数rateを参照しようとしても、修飾子「private」に より、アクセス制御されています。 スーパークラスの元に新たにサブクラスを作成し、スーパークラスで定義した変数や メソッドを使用することができる。以下にその例を示します。 class Display { // スーパークラス Display void Disp() { // メソッド System.out.println("Hello!"); } } class Succeed extends Display { // スーパークラス Displayを継承したサブクラス Succeed public static void main(String args[]) { // メソッド Succeed obj = new Succeed(); // オブジェクト生成 obj.Disp(); // スーパークラスから継承したメソッドを利用 } } ※実行結果 Hello! 考察 サブクラスにスーパークラスを継承させるには、extendsというキーワードをクラス間に 形成しなければなりません。そうすることでスーパークラスの中身を継承させることができます。 メソッドのオーバーライド class Display { // スーパークラス Display void Disp() { // メソッド System.out.println("Hello!"); } } class Override extends Display { // サブクラス Override void Disp() { // メソッドのオーバーライド System.out.println("Hello! Java!!"); } public static void main(String args[]) { // メソッド Override obj = new Override(); // オブジェクト生成 obj.Disp(); // メソッド Disp 実行 } } ※実行結果 Hello! Java!! 考察 スーパークラスで定義したメソッドと同じメソッド名で作成したメソッドに別の動きを するようにして、サブクラスに定義してやると、実際に実行したときにはサブクラスで定義 したメソッドが実行される。これよりわかることは、スーパークラスで作成したメソッドも サブクラスで定義し直すことができるということです。 このことをメソッドのオーバーライドと言います。 キーワードsuperの使用例 class Display { // スーパークラス Display void Disp() { // メソッド System.out.println("Hello!"); } } class SuperSample extends Display { // サブクラス SuperSample void Disp() { // メソッドのオーバーライド super.Disp(); // スーパークラスの Disp 実行 System.out.println("Hello! Java!!"); } public static void main(String args[]) { // メソッド SuperSample obj = new SuperSample(); // オブジェクト生成 obj.Disp(); // サブクラスの Disp 実行 } } ※実行結果 Hello! Hello! Java!! 考察 スーパークラスで定義されたメソッドをその下にあるサブクラスで呼び出したいとき にキーワードsuperを使う。オーバーロードしたメソッドをサブクラス内で呼び出し、その 中身を書き換えることもできる。このことより、メソッド内の機能を拡張させることが できます。 キーワードthisの使い方 class Keisan { // スーパークラス Keisan int x = 100; void add(int x, int y) { // メソッド int w1 = x + y; int w2 = this.x + y; System.out.println("w1 = " + w1 + " w2 = " + w2); } } class ThisSample1 extends Display { // サブクラス ThisSample1 public static void main(String args[]) { // メソッド ThisSample1 obj = new ThisSample1(); // オブジェクト生成 obj.add(10, 20); // add 実行 } } ※実行結果 w1 = 30 w2 = 120 考察 キーワードthisを用いると、そのクラスに定義されている変数を直接使うことができる。 ここで、キーワードthisを用いるメリットは、クラス変数とそのクラスのメソッド内で定義 した変数とが同じ名前だとしても、this.同じ名前の変数名という風に記述すると、同じ変数名 でも区別して使用することができるようになります。さらに、もう一つのメリットとしては、 同じ変数名で区別して使用すると、たくさん変数を使わなくても良いので、ソースが見やすく なるということです。 次の例もキーワードthisの使い方の説明です。 class Keisan { void Keisan(int x, int y) { // 引数が二つのコンストラクタ System.out.println("Keisan = " + (x * y)); } void Keisan(int x) { // 引数が一つのコンストラクタ this(x, 1); // Keisan(x, 1)と同じ } } class ThisSample2 extends Display { // サブクラス ThisSample2 public static void main(String args[]) { // メソッド Keisan obj1 = new Keisan(200, 5); // 引数が二つのコンストラクタ実行 Keisan obj2 = new Keisan(200); // 引数が一つのコンストラクタ実行 } } ※実行結果 Keisan = 1000 Keisan = 200 考察 引数が二つのコンストラクタの他に、引数が一つのコンストラクタを呼び出すときに、 キーワードthisを用いて呼び出すことができる。結果を見てもわかるのですが、同じ名前 のコンストラクタであっても、出力された値は違っています。これはキーワードthisを 用いたことにより、同じ名前のコンストラクタ同士を区別しています。 抽象クラスの使い方 public abstract class PreDefine { // 抽象クラス PreDefine public abstract int add(int x, int y); // 抽象メソッド add public int sub(int x, int y) { // メソッド sub return x - y; } } class AbstractSample extends PreDefine { // 抽象クラス PreDefine の継承 public int add(int x, int y) { // メソッドの内容を具体的に定義 return x + y; } public static void main(String args[]) { // メソッド AbstractSample obj = new AbstractSample(); System.out.println("add = " + obj.add(100, 80)); System.out.println("sub = " + obj.sub(100, 80)); } } ※実行結果 add = 180 sub = 20 考察 抽象クラスに定義されたメソッドにはすぐに内容を持たせる必要はありません。 そのかわり、戻り値と引数を持たせる必要があります。そして、その下のサブクラス では、抽象クラスで定義したメソッドの内容を具体的に決定してあげる必要があります。 なぜなら、抽象クラスではオブジェクトが生成できないので、サブクラスに抽象クラスを 継承してあげてから間接的にオブジェクトを生成してます。 interfaceの使い方 public interface PreDefine { // インタフェース PreDefine int add(int x, int y); // 抽象メソッド add int sub(int x, int y); // 抽象メソッド sub } class InterfaceSample implement PreDefine { // PreDefine のインプリメント public int add(int x, int y) { // メソッドの内容を具体的に定義 return x + y; } public int sub(int x, int y) { // メソッドの内容を具体的に定義 return x + y; } public static void main(String args[]) { // メソッド InterfaceSample obj = new InterfaceSample(); System.out.println("add = " + obj.add(100, 80)); System.out.println("sub = " + obj.sub(100, 80)); } } ※実行結果 add = 180 sub = 20 考察 interfaceは抽象メソッドの集まりです。抽象クラスと同様に、抽象メソッドには、戻り値と 引数さえ与えてしまえば、すぐに定義部分を与える必要はありません。抽象クラスのように、 インタフェースではオブジェクトを生成することはできません。そこで、抽象クラスではサブクラス を派生させてオブジェクトを生成するのに対し、インタフェースの抽象メソッドをimplementという キーワードを用いて使用します。 インタフェースの利点は、ある「テーマ」に沿ったプログラムを多数のプログラマーが作成する ときに、共通のメソッド群として、全体のプログラムの大まかな実装部分を確認し、プログラマー の統一化が図れるところです。 感想と反省 今回の課題を通して感じたことはJava言語を使用する際に、なぜ、Javaがオブジェクト指向型言語 なのか、また少し理解することができました。それから、Java言語はプログラマーのプログラミング 時における利便性をかなりの高水準でフォローしていると思いました。ホントにJavaは使い易い言語 だと感じました。 参考文献 独習Java第2版 ジョセフ.オニール 著 トップスタジオ 訳 武藤 健志 監修