Report#6

課題
11/15付講義資料と今回の講義資料中のサンプルプログラムについて考察せよ。
講義資料サンプルプログラム考察については、static変数以降とする。

1,static変数
2,コンストラクタ
3,staticイニシャライズ
4,メソッドのオーバーロード
5,アクセス制御
6,継承
7,super
8,this
9,abstract
10,interface
感想・反省、参考文献

1.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

考察

class Sum {
  static int total;
・int型static変数totalを宣言。値を共有させる。
  Sum() {
・コンストラクタ(クラス名と同名)。
    total = 0;
・0クリア。
  }
  void add(int x) {
・メソッドaddを定義。
  total += 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に引数10を与えて実行。
    obj1.add(20);
    obj2.add(30);
・オブジェクトobj2のaddに引数30を与えて実行。
    obj2.add(40);
    System.out.println("obj1.total = " + obj1.total);
・オブジェクトobj1のtotalを表示。
    System.out.println("obj2.total = " + obj2.total);
  }
}

・static変数(クラス変数とも呼びます)とは、そのクラスのすべてのオブ ジェクトから共有される変数です。つまり、クラスそのものに結びついている変数です。静的変数を宣言するには、staticキーワードを付加します。

■静的変数の宣言
 static type[データ型] varName1[変数の名前];
 

・複数の静的変数を1行で宣言することもできます。

 static type[データ型] varName1[変数の名前], varName2, ... varNameN;
 

・複数の変数の宣言と初期化を1行で行うこともできます。

・静的変数は、クラスがメモリにロードされる時点で、概定置によって初期化 されます。boolean型の変数には偽が代入され、数値の変数には0が代入されま す。オブジェクト参照として機能する変数にはnullが代入されます。

・変数の宣言時に値を代入することもできます。

 static type[データ型] varName1[変数の名前] = expr1[式, 値];
 

・複数の変数の宣言と初期化を1行で行うこともできます。

 static type[データ型] varName1[変数の名前], varName2 = expr1[式, 値],
... varNameN;
 

・変数varName2には、式expr2の値が初期値として代入されます。それ以外 の変 数は概定置によって初期化されます。

・ここではオブジェクトobj1の変数totalとオブジェクトobj2の変数totalは 一つ のメモリ領域を共有している。つまりオブジェクトobj1、obj2のそれぞれ のtotalに引数を与えて実行するということは、結局一つの変数totalの領域に たいして行われるということになるので、obj1.totalとobj2.totalは同じ値を 示します。

・ちなみにもし上のプログラムでstaticという修飾子を抜いて実行すると次の ように出力されます。

  obj1.total = 30
  obj2.total = 70
 

2.コンストラクタ
■サンプルプログラム
  class Sum {
    int total;
    Sum() {
      total = 0;
    }
    Sum(int x) {
      total = x;
    }
    void add(int x) {
      total += x;
    }
  }

  class ConstrcSample {
    public static void main(String args[]) {
      Sum obj1 = new Sum();
      obj1.add(10);
      obj1.add(20);
      System.out.println("sum1 = " + obj1.total);
      Sum obj2 = new Sum(100);
      obj2.add(10);
      obj2.add(20);
      System.out.println("sum2 = " + obj2.total);
    }
  }
 

■実行結果
  sum1 = 30
  sum2 = 130
 

考察

class Sum {
・クラスSumを定義。
  int total;
・クラス変数totalの宣言。
  Sum() {
・コンストラクタ 引数なし の定義。
    total = 0;
・total = 0で初期化。
  }
  Sum(int x) {
・コンストラクタ 引数x を定義。
    total = x;
・total = xで初期化。
  }
  void add(int x) {
・addメソッド 引数x を定義。
    total += x;
  }
}
     Sum obj1 = new Sum();
・クラスSumのオブジェクトobj1 引数なし の生成。
     obj1.add(10);
     obj1.add(20);
・obj1の
     Sum obj2 = new Sum(100);
・クラスSumのオブジェクトobj2 引数100 の生成。
     obj2.add(10);
     obj2.add(20);
・obj2のadd(10)、add(20)を実行。
・クラスのオブジェクトは、作成するのと同時にオブジェクトを初期化しなけ ればならないことがよくあります。そのため、Javaにはクラスのコンストラク タを定義する機能が用意されています。コンストラクタとは、特定のクラスの オブジェクトを作成して初期化する特殊なメソッドのことです。コンストラク タの名前はクラス名と同じです。コンストラクタでは引数を受け取ることがで きます。

     Sum obj1 = new Sum();
 

・引数なしのオブジェクトを生成すると・・・

  Sum() {
    total = 0;
  }
 


・この引数なしのコンストラクタが定義され、total=0と初期化される。

     Sum obj2 = new Sum(100);
 

・引数xのコンストラクタを生成すると・・・

  Sum(int x) {
    total = x;
  }
 

この引数xのコンストラクタが定義され、total=xと初期化される。 ・またコンストラクタに引数を与えるかどうかだけでなく、異なる種類の引数 を与えたり、複数の引数を与えて定義することもできる。これを『オーバーロード する』という。ただしコンストラクタは返し値をもつことはできない。

3.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キーワードを付加します。

・静的メソッドはクラスに対して作用します。クラスの特定のオブジェクトに 作用するものではありません。

・コンストラクタよりも早く処理したい場合や、一度だけ処理を行いたい場 合 などに用います。

4.メソッドのオーバーロード
■サンプルプログラム
  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
 

・メソッドのオーバーロードとは、名前が同じでシグネチャが異なるメソッ ド が複数ある ことです。シグネチャとはメソッド名とそのパラメータのデータ型のリスト を 合わせたものです。オブジェクトに与える引数の数やデータ型の違いによっ て 定義されるメソッドが選ばれます。

      obj.Disp();
 

が実行されると・・・

    void Disp() {
        System.out.println("Nothing");
    }
 

      obj.Disp(1);
 

が実行されると・・・

    void Disp(int x) {
        System.out.println(x);
    }
 

      obj.Disp(1, 2);
 

が実行されると・・・

    void Disp(int x, int y) {
        System.out.println(x + y);
    }
 

と、名前が同じで、シグネチャの異なるメソッドが定義される。

5.アクセス制御

・変数を宣言するときの修飾子の中には、アクセス制御するものがある。

・privateを用いて、変数の参加を不可にするプログラム例
  class keisan {
    static int tanka;
    private float rate;                    // private変数rateの宣言
    keisan() {                             // コンストラクタ
        tanka = 1000;
        rate  = 0.05f;
    }
    int 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
 

・このようなアクセス修飾子を設定することにより、プログラムの色々な部 分 に対する誤用や異常事態の発生を抑制できる効果があり、また、プログラム の メンテナンス性を向上できると考えられます。パッケージと種々のアクセス修 飾子の関係で、アクセス可否の範囲はどのようになるかを下の表にまとめまし た。

アクセスする場所 public protected 無指定 private
そのクラス内
同一パッケージにある、そのクラスのサブクラス内 ×
同一パッケージにある一般のクラス内 ×
別のパッケージにある、そのクラスのサブクラス内 × ×
別のパッケージにある一般のクラス内 × × ×

6.継承
・既存のクラス(スーパークラス)を元に、新しいクラス(サブクラス)を 作 成することができる。

・サブクラスの中でスーパークラスの変数やメソッドが継承され、利用可能 と なる。

 class Display {
    void Disp() {
        System.out.println("Hello!");
    }
}

class Succeed extends Display {
    public static void main(String args[]) {
        Succeed obj = new Succeed();
        obj.Disp();
    }
}
 

■実行結果

    Hello!
 

■解説
class Display {
・スーパークラス Display の定義。
    void Disp() {
・メソッドDispの定義。
        System.out.println("Hello!");
    }
}
class Succeed extends Display {
・スーパークラスDisplayを継承したサブクラスSucceedの定義。
    public static void main(String args[]) {
        Succeed obj = new Succeed();
・オブジェクトの生成。
        obj.Disp();
・スーパークラスから継承したメソッドを利用。

・このオブジェクト生成に注目!

        Succeed obj = new Succeed();
 

・もしサブクラスSucceedがスーパークラスDisplayを継承していなければ、 スー パークラスのDisplayのオブジェクトを生成しなければならないはずです。しか しスーパークラスDisplayを継承してるここではサブクラスであるSucceedの オ ブジェクトを生成しています。

・スーパークラスを継承した場合でも、サブクラス内でスーパークラスと同 じ 名前のメソッドを定義し直すことができます。

・例えば、スーパークラスにいくつかのメソッドを定義し、サブクラスでこ の 一部を定義し直して利用することができます。

・これをメソッドのオーバーライドといいます。

・オーバーライドした場合は、後で定義したほうを優先します。

■サンプルプログラム
class Display {
    void Disp() {
        System.out.println("Hello!");
    }
}

class Override extends Display {
    void Disp() {
        System.out.println("Hello! Java!!");
    }
    public static void main(String args[]) {
        Override obj = new Override();
        obj.Disp();
    }
}
 

■実行結果
    Hello! Java!!
 

■解説 ・上のプログラムのサブクラスに下のメソッドのオーバーライドさせている。
    void Disp() {
        System.out.println("Hello! Java!!");
    }
 

・サブクラスにDispメソッドが加えられたため、スーパークラスで定義されて いたDispメソッドがサブクラスのDispメソッドにオーバーライドされている。 これによりDispメソッドが実行されると、スーパークラスのではなくサブクラ スのDispメソッドが実行される。

7.super
・オーバーロードした定義の内側からスーパークラスのメソッドを呼び出すに はsuperを使う。

・superは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!!
 

・ひとつ上のプログラムのOverrideクラスのDispメソッドに、super.Disp();を 加えたもの。
    void Disp() {
        super.Disp();
        System.out.println("Hello! Java!!");
    }
 

・スーパークラスDisplayのDispメソッドを、オーバーライドしたサブクラスの Dispメソッドの内側から呼び出しています。よってここではスーパークラス、サ ブクラスの順にDispが実行されて上の実行結果を出力します。

8.this
・thisはカレントクラスを参照するときに利用する。

・例えば、クラス変数と同クラス内のメソッド変数が同じ名前であるとき、ク ラス変数は 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 Keisan {             // サブクラス
ThisSample1
    public static void main(String args[]) {    // メソッド
        ThisSample1 obj = new ThisSample1();    // オブジェクト生成
        obj.add(10, 20);                        // add 実行
    }
}
 

■実行結果
    w1 = 30  w2 = 120
 

・w2 の右辺の this.x 、this はクラスそのものを表し、この場合 this.x で クラス Keisan の変数xを表す。
・これに対して、w1 の右辺の x はローカルな変数、このメソッド内の変数 x を表す。

・サンプルプログラム
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 Keisan {           // サブクラス
ThisSample2
    public static void main(String args[]) {  // メソッド
        Keisan obj1 = new Keisan(200, 5);     // 引数が二つのコンスラクタ実行
        Keisan obj2 = new Keisan(200);        // 引数が一つのコンストラクタ実行
    }
}
 
    Keisan = 1000
    Keisan = 200
 

・このサンプルプログラムでは、コンストラクタから同じクラスの別のコンス トラクタを明示的に呼び出すためにthisキーワードを使っています。2番目の コンストラクタが、thisキーワードを使って1番目のコンストラクタを呼び出 しています。

9.abstract
・抽象クラス(abstractクラス)は、一つまたは複数のサブクラスによって実 装される機能を宣言するために使われます。抽象クラスのメリットは、クラス の機能さえ定義すれば、その機能をどのように実現するかまで定義しなくてす むことです。したがって、サブクラスでは、同じ目的を達成するために複数の 実装を使うことができます。
■サンプルプログラム
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
 

・abstract 宣言された抽象メソッドをもつクラスは、abstract を付けて抽象ク ラスにする。
・抽象クラス内の抽象メソッドは戻り値と引数のみを定義する。
・従って、抽象クラスはオブジェクトを生成することはできない。
・具体的に定義する場合は、サブクラスを派生させてサブクラス内に抽象メソッ ドをオーバーライドし定義する。
・抽象クラスは、インスタンス変数やメソッドのコードを部分的に含み、かつ抽 象的なメソッドを含んだクラスを定義するときに利用する。
・抽象クラスをインスタンス化しようとすると、コンパイルエラーになります。 抽象クラスのメソッドには、何も処理が含まれていないことがよくあります。 メソッドそれ自体をabstractで宣言することもあります。このような抽象メソッ ドの宣言は、外枠だけで中身がありません。

10.interface
・Javaにおけるクラス継承では、スーパークラスを一つしか指定できません。 これはプログラム構造に無用の複雑さを招く恐れがあるからだと考えられます。 しかし一方では、クラス作成の際、特定のスーパークラスには無い何らかの情 報を別の所から取り込みたい場合も生じます。そこで必要になってくるのがこ のinterfaceです。

■サンプルプログラム
public interface PreDefine {                    // インタフェース
PreDefine
    int add(int x, int y);                      // 抽象メソッド add
    int sub(int x, int y);                      // 抽象メソッド sub
}

class InterfaceSample implements 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
 

・インターフェースの宣言は、基本的に次のような形式です。
intfModifier interface intfName {
  varModfier1 type1 varName1 = value1;
  varModfier2 type1 varName2 = value2;
  varModfier3 type1 varName3 = value3;
    ・
    ・
    ・
  varModfierN typeN varNameN = valueN;
  mthModfier1 rtype1 mthName1(Params1);
  mthModfier2 rtype2 mthName1(Params2);
    ・
    ・
    ・
  mthModfier1 rtype1 mthName1(Params1);
 

・intfModifierは、インターフェースのアクセス権を規定する修飾子(省略可 能)を意味します。ここにpublicを指定すると、そのインターフェースは他の パッケージ内のコードから使用できるようになります。修飾子を省略すると、 そのインターフェースにアクセスできるのは同じパッケージ内のコードだけに なります。

・interfaceキーワードは、intfNameという名のインターフェースを宣言するこ とを意味しています。インターフェース名は、Javaの命令規則に従う必要があ ります。

・変数varName1〜varNameNは、見た通りにそのまま組み込まれます。それぞれ のデータ型はtype1〜typeNです。インターフェース宣言では、変数に定数を代 入しなければなりません。また変数に省略可能な修飾子(前述の例では varModifier1〜varModfierN)を指定することもできます。ただし、インター フェース変数は特に明示しなくてもpublic、static、finalとして扱われます。 そのため、変数の修飾子はまず使われません。public、static、finalの変数は 定数になります。

・次のように、複数の変数を一行で宣言することができます。
   varModifier type varName1 = value1,・・・varNameN = valueN;
 

・mthName1〜mthNameNメソッドは、見たとおりにそのまんま組み込まれていま す。省略可能な修飾子(mthModifier1〜mthModifierN)を指定することもでき ます。ただし、特に明示しなくてもインターフェースメソッドはpublic、 abstractとして扱われます。そのため、メソッドの修飾子を使う場面はほとん どないでしょう。メソッドの戻り値の型はrthpe1〜rthpeNで、その省略可能な パラメータリストはparams1〜paramsNです。

・クラスには一つまたは複数のインターフェースを実装できます。インターフェー スを実装するには、まずクラス定義でimplementsキーワードを使って、実装対 象のインターフェースを指定します。次にインターフェースで指定されている 全てのメソッドをクラスに実装します。implementsキーワードの構文は、次の 通りです。

clsModifiers class clsName extends superName implements intfList
{
  //ここにインターフェースを実装する。
   ・
   ・
   ・
}
 

・clsModifiersは、前章で説明したクラス修飾子を意味します。またclsNameは クラス名です。当然ですが、extendsキーワードは必須ではありません。 extendsを指定する場合は、superNameの位置にそのスーパークラス名を指定し ます。implementsキーワードは、このクラスに一つまたは複数のインターフェー スを実装することを意味します。実装するインターフェースの名前はカンマ( , )で区切ってintfListの位置に指定します。

・クラスにインターフェースを実装する場合、そのインターフェースで宣言さ れているメソッドをすべて実装しなければなりません。そうしなければ、コン パイルエラーが発生します。

反省・感想、参考文献

反省・感想
 もっと普段からがんばればよかったと思いました。

参考文献
 独習Java 第2版     ジョゼフ・オニール 著
                トップスタジオ 訳
                武藤 健志 監修
 新Java言語入門ビギナー編  林 晴比古 著