レポート#5






課題

講義資料オブジェクト指向プログラムJavaI&II の中のサンプルプログラムについて考察せよ。
講義資料サンプルプログラム考察については、ローカル変数以降 interface までとする。




ローカル変数とクラス変数

参考プログラム

class Display {
    int n;                                 
        void Disp() {
        int i;                            
        for (i = 1; i <= n; i++)
            System.out.print(i + " ");
    }
}
    class VarSample {
        public static void main(String args[]) {            
            Display obj = new Display();       
            obj.n = 10;                       
            obj.Disp();                       
        }
    }
	

考察
 ローカル変数・・・ローカル変数はブロック内、又はfor文の中で宣言された一時的な変数のことで、
	     そのブロック内またはfor文内でのみ有効でその処理が終わると保持していた値は消えてしまう。
	
 クラス変数・・・・クラスに対して固有の値を持ち、メソッドなどに影響されない。
class Sample {
    int n;
    void Disp() {
        int i;                            
        for (i = 1; i <= n; i++)
            System.out.print(i + " ");
    }
    void Disp2() {
        for (i = n; i >= 1; i--)
            System.out.print(i + " ");
    }
}
class Demo {
    public static void main(String args[]) {                 
        Sample obj = new Sample();       
        obj.n = 10;                       
        obj.Disp();                       
        obj.Disp2();
    }
}

 上のプログラムのDisp2という違うメソッドではDispで宣言したiは使えず、
 コンパイルしようとしてもエラーがおこります。
 コンパイルするためには、Disp2 内で新たにiを宣言するか、i自体をクラス変数にすることでできます。


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


class Sample {
    static int total;
    Sample() {                        
        total = 0;                        
    }
    void add(int x) {
        total += x;
    }
}
  
class Demo {
    public static void main(String args[]) {
        Sample obj1 = new Sample();      
        Sample obj2 = new Sample();      
        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

考察
 上の2つのプログラムを比べてみると、宣言した変数にstaticをつけるかつけないかで
 実行結果の値が違っていることがわかる。

 static変数を用いていない1つ目のプログラムではオブジェクトを複数生成すると、
 そのそれぞれに領域が確保されます。
 それでobj1.totalとobj2.totalの値が変わっています。

 static変数を用いた2つ目のプログラムではオブジェクトを複数生成しても値が共有される
 ので、obj1.totalとobj2.totalの値が同じになります。


コンストラクタ

参考プログラム

class Sum {
    int total;
    Sum() {                              // コンストラクタ 引数無し
      total = 10;
    }
    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 = 40
sum2 = 130

考察
 クラス名と同じメソッドをコンストラクタといい、オブジェクトの初期化処理に利用できます。
 上のプログラムでは引数のあるのとないのをコンストラクタと用いて、
 オブジェクト生成時に数を渡さないときは引数なしの方の処理がされて、( total=10 )
 生成時に数を渡す時は引数がある方の処理がされています。( total=x )
 それで実行結果はSum1は10+30で40、Sum2は100+30で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
construct
construct

考察
 staticイニシャライズはクラスが呼び出された時にコンストラクタより早く一度だけ実行される。
 上のプログラムでは最初のオブジェクト生成時にstaticイニシャライズを先に処理して、
 その後コンストラクタを処理。( initialize と construct 表示 )
 staticイニシャライズは一度しか処理されないので次のオブジェクト生成時では
 コンストラクタのみを処理。( construct 表示 )


オーバーロード

参考プログラム
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)を実行
    }
  } 
 

実行結果
Nothing
1
3

考察
 メソッドのオーバーロードとはクラス内に同じ名前のメソッドを定義すること。
 その際には各メソッドの引数の数や型が異なってないといけない。
 上の場合は引数の数が異なっている。
[Masaki-HIGA:~/java-rep/rep#5/5-5] j03043% cat Sample.java

class Sample {
    void Disp() {                 
        System.out.println("Nothing");
    }
    void Disp(int x) {                 
        System.out.println(x);
    }
    void Disp(double x) {             
        System.out.println(x*2);
    }
}
  
class Demo {
    public static void main(String args[]) {     
        Sample obj = new Sample();  
        obj.Disp();           
        obj.Disp(1);                    
        obj.Disp(4);   
    }
}

-------------------------------------------------------
[Masaki-HIGA:~/java-rep/rep#5/5-5] j03043% java Demo
Nothing
1
4


[Masaki-HIGA:~/java-rep/rep#5/5-5] j03043% cat Sample2.java

class Sample2 {
    void Disp() {                          
        System.out.println("Nothing");
    }
    void Disp(int x) {                     
        System.out.println(x);
    }
    void Disp(double x) {
        System.out.println(x*2);
    }
}
  
class Demo2 {
    public static void main(String args[]) {     
        Sample2 obj = new Sample2();    
        obj.Disp();                     
        obj.Disp(1);                    
        obj.Disp(3.2);
    }
}

-------------------------------------------------------
[Masaki-HIGA:~/java-rep/rep#5/5-5] j03043% java Demo2
Nothing
1
6.4



 上のプログラムでは引数の数は同じだけど型が違う。
 数が一緒だとメソッドを呼び出す時に渡す値が適した方のDispが呼び出される。
 float と double でやると整数の時はfloatの方、少数の時は1.0でもdoubleの方が呼ばれる。


アクセス制御

参考プログラム
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

考察
 privateはアクセス制御修飾子と呼ばれ、名前のまんまアクセスを制御します。
 他にも public , protected がある。
 privateは定義されたクラス内からのみアクセスが可能です。
 上のプログラムで rate は PrvSampleクラス では参照できないのでコメント文じゃなく
 するとコンパイルエラーがおこる。


継承

参考プログラム
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 {                               
    void Disp() {                              
        System.out.println("Hello!");
    }
}

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


-----------------------------------------------
Hi! Java!!



考察

 継承とは、新しいクラスが既存のクラスのメンバ変数やメソッドを引き継ぐことをいう。
 コンストラクタは継承されない。
 もとからあるクラスをスーパークラス、新しく作るクラスをサブクラスという。
 サブクラス中でスーパークラスのメソッドと同じ名前、引数をもつメソッドを宣言すると、
 内容が上書きされる。これをオーバーライドという。
 右上のプログラムでは上書きされてるのでHello!とは出てない。


super

参考プログラム
[Masaki-HIGA:~/java-rep/rep#5/5-7] j03043% cat Sample.java

class Sample {
    int x;
    void Disp() {
        System.out.println("abcd");
        x = 1;
    }
}

class superSample extends Sample {
    void Disp() {
        System.out.println("vwxy");
        int x = 2;
        super.Disp();                                                           
        System.out.println("x = " + x);
        System.out.println("x = " + super.x);
    }
    public static void main(String[] args) {
        superSample obj = new superSample();
        obj.Disp();
    }
}


実行結果
[Masaki-HIGA:~/java-rep/rep#5/5-7] j03043% java superSample
vwxy
abcd
x = 2
x = 1

考察
 superはサブクラス内でオーバーライドされたスーパークラスのメソッドを呼び出す時につかいます。
 これによってオーバーライドされる前のメソッドが呼び出されます。
 上のプログラムでも Disp はサブクラスで上書きされてますが super を使って
 上書きされる前の Disp を呼び出しています。
 しかし実行結果からもわかるように super で呼び出してもまた上書きされる訳でなく
 xを表示させるとサブクラスで上書きした値がでます。
 スーパークラスでのxを呼び出すにはまたsuperを使うことでできます。


this

参考プログラム
class 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 {              
    public static void main(String args[]) {    
        ThisSample1 obj = new ThisSample1();    
        obj.add(10, 20);                        
    }
}

-------------------------------------
w1 = 30  w2 = 120


[Masaki-HIGA:~/java-rep/rep#5/5-8] j03043% cat Sample.java 

class Sample {
    Sample(int x, int y) {
        System.out.println("Sample = " + (x*y));
    }
    Sample(int x) {
        this(x,2);
        System.out.println("Bye");
    }
}

class Demo {
    public static void main(String[] args) {
        Sample obj1 = new Sample(100,5);
        Sample obj2 = new Sample(200);
    }
}

---------------------------------------
[Masaki-HIGA:~/java-rep/rep#5/5-8] j03043% java Demo
Sample = 500
Sample = 400
Bye


考察
 クラス変数とローカル変数が同じ名前の時、thisを付けることでそれを使い分けることができる。
 左上のプログラムではクラス変数xに100代入し、その後メソッド中でローカル変数x,yを定義。
 addを実行する際、10と20の値を渡しているが、w2ではクラス変数のxとyを足しているので、
 100+20で120が出力されている。

 右上のプログラムでは引数が2つと1つのコンストラクタを作り、2つの方では渡された変数同士をかけたものを出力させる。
 引数が1つのコンストラクタではthisを使うことにより2つの方と同じ処理が呼ばれる。(yは自動的に2となる。)
 その後Byeが出力されている。


abstract

参考プログラム

[Masaki-HIGA:~/java-rep/rep#5/5-9] j03043% cat Sample.java

abstract class Sample {
    abstract int add(int x, int y);
    public int sub(int x, int y) {
        return x-y;
    }
}
class abSam extends Sample {
    int add(int x, int y) {
        return x*y;
    }
}
class abSam2 extends Sample {
    int add(int x, int y) {
        return x/y;
    }
}
class Demo {
    public static void main(String[] args) {
        abSam sam1 = new abSam();
        abSam2 sam2 = new abSam2();
        System.out.println("sam1.add(10,2) = " + sam1.add(10,2));
        System.out.println("sam1.sub(10,2) = " + sam1.sub(10,2));
        System.out.println("sam2.add(10,2) = " + sam2.add(10,2));
        System.out.println("sam2.sub(10,2) = " + sam2.sub(10,2));
    }
}

------------------------------------
[Masaki-HIGA:~/java-rep/rep#5/5-9] j03043% java Demo
sam1.add(10,2) = 20
sam1.sub(10,2) = 8
sam2.add(10,2) = 5
sam2.sub(10,2) = 8


考察
 クラスやメソッド宣言時に abstract を付けることで抽象クラス、抽象メソッドを宣言できる。
 抽象クラスはオブジェクトを生成することができない。
 抽象クラスでは抽象メソッドとそうでないメソッドのどちらがあってもいい。
 抽象メソッドのあるクラスは抽象クラスでないといけない。
 抽象メソッドは実装がなく、サブクラスで継承して実装する。

 上のプログラムでは抽象クラス Sample に抽象メソッドの add とそうでないメソッド sub を作り、
 Sample を abSam と abSam2 に継承した後、抽象メソッドをオーバーライドして中身を定義しています。
 abSam では2つの値をかけるように、abSam2 では2つの値を割るように定義しています。
 sub は抽象メソッドではなく、オーバーライドもしてないのでオブジェクトを生成後呼び出しても
 スーパークラスで定義したまんま2つの値を引いた結果がでます。


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 

考察
 インターフェイスは abstract と違い、抽象メソッドのみから構成される。
 インターフェイスを実装する時は implement を用いる、また複数実装することもできる。
 
 上のプログラムではインターフェイス PreDefine 内に抽象メソッド add と sub を作り、
 InterfaceSampleクラスでPreDefineを実装して各抽象メソッドの内容を具体的に定義しています。



感想・反省
 このレポートをやってる時一番に思ったのがもっと早くやっとけばよかったな〜と思いました。
 理由は期末テストにこのレポートでやった範囲が出た時に「何じゃそりゃ?」って思って調べてるうちに
 時間が過ぎてったからです。
 あとこれ今回のレポートの中で一番めんどいんじゃないかと思いました。