アルゴリズムとプログラミング (Algorithms and Programming) 第10回:インスタンス変数、クラス変数 •インスタンス変数とクラス変数 •インスタンスメソッドとクラスメソッド •ローカル変数とスコープ •変数の寿命、ガーベージコレクション 講義資料等: http://www.pe.titech.ac.jp/~watanabe/lecture/ap/index-j.html インスタンス変数 class Point { // 点クラス double x; // x座標 double y; // y座標 } Point p1 = new Point(); Point p2 = new Point(); p1とp2はクラスは同じだが、 別のインスタンスであり、それ ぞれ個別に変数double x, double yを持つ これまで出てきた変数は、 インスタンスごとにメモリ領 域が個別に割り当てられ、 別々に読み書きができた インスタンス変数 Point: p1 Point: p2 x: 1.5 y: 2.0 x: 0.2 y: 0.0 お互い無関係に 読み書き可能 クラス変数 同じクラスのどのインスタンスからでも、共 通の1つの変数(=メモリ領域)を読み書き したい場合がある p1からクラス変数の値を 変更すると、p2から見ても 値が変更されている!! クラス変数 実体は1つ Point: p1 Point: p2 x: 1.5 y: 2.0 count : x: 0.2 y: 0.0 count : count : 2.0 クラス変数 クラス全体で 共有する変数 クラス変数の宣言 class Point { // 点クラス double x; // x座標 double y; // y座標 public static int count; } static修飾子 クラス変数へのアクセス方法: オブジェクト変数名.クラス変数名 クラス名.クラス変数名 クラス変数の宣言と利用の例 class Point { // 点クラス double x; // x座標 double y; // y座標 //クラス変数の定義と初期化(static修飾子の有無で出力結果を比較せよ) public static int count = 0; Point( double a, double b ){ コンストラクタ x = a; y = b; count++; } } class SampleAP0801 { public static void main(String[] args){ // インスタンスが生成される前からクラス変数は存在する! System.out.println("count=" + Point.count); count=0 Point p1 = new Point(1.0,2.0); System.out.println("count=" + p1.count); Point p2 = new Point(3.0,4.0); count=1 System.out.println("count=" + p2.count); count=2 } } クラスメソッド クラス単位で共有されるメソッド インスタンス変数にアクセス不可 インスタンスメソッドの呼び出し不可 クラスメソッドは、クラス内で定義されてい るのでクラスのメンバーといえるが、実体 はインスタンスの外に存在する オブジェクト(実体) インスタンス1 インスタンス変数 インスタンスメソッド クラス変数 クラスメソッド クラス定義 インスタンス2 static static インスタンス3 クラスで共有 static static アクセス class Point { // 点クラス double x; // x座標 インスタンス変数 double y; // y座標 //クラス変数、クラスメソッドの定義 クラス変数 public static int count = 0; クラスメソッド public static void printValue(){ System.out.println( "value:" + count ); } クラスメソッド public static void printValue(int i){ System.out.println( "value:" + i ); } クラスメソッドの中からインスタンス変数で } あるx,yを参照することはできない! class SampleAP0802 { public static void main(String[] args){ // インスタンスが生成される前からクラス変数、クラスメソッドは存在する! Point.count = 5 ; value:5 Point.printValue(); Point.printValue(6); value:6 Point o = new Point(); o.printValue(7); value:7 } } main()はクラスメソッドだった! class ユーザ定義 { public static void main(String[] args){ 処理; } } main()メソッドが実行される約束 になっている どこで宣言された変数か? 変数の3つの分類 宣言される場所 •インスタンス変数 •クラス変数 •ローカル変数 クラス内 メソッド内 ローカル変数が見える 範囲(スコープ)と寿命 中カッコ{}で囲まれたブロックがスコープの目印 class SampleAP0803 { public static void main(String[] args){ int x = 0; for( int i = 1; i <= 10; i++) { x = x + i; } System.out.println("total = " + x ); } } 変数iの値をこのforブロックの外から参照することはできない 変数xの値はforブロック内からでも参照可能 変数の寿命 使い終わった変数は破棄され、使用 していたメモリが解放される (ガベージコレクション) ローカル変数の寿命:ブロックの最初にメモリが確保さ れ、ブロックの終わりで破棄される インスタンス変数の寿命:newで生成されてから、 最後に参照された後、Java VM が判断したタイミ ング クラス変数の寿命:プログラムの開始から終了まで バッティングしても有効 ローカル変数 SampleAP0804.java class X { static int i = 100; クラス変数 int j = 200; インスタンス変数 void printValue(){ System.out.println("i= System.out.println("j= int i = 123; ローカル変数 int j = 456; System.out.println("i= System.out.println("j= } } " + i ); " + j ); " + i ); " + j ); SampleAP0804.java続き class SampleAP0804 { public static void main(String[] args){ X o = new X(); o.printValue(); } } 実行結果 i= j= i= j= 100 200 123 456 ローカル変数 クラス変数・インスタンス変数 変数名がバッティングした場合は ローカル変数が優先される バッティングしないように名前を付けるのが望ましい 値を変更しない変数(定数)の宣言 final修飾子 class X { static final int MAX = 100; final static int MIN = -100; final int DEFAULT = 10; 値を変更しないのなら、インスタンスご とにこの定数のためにメモリを確保す } るのは無駄といえる→普通はクラス変 数にする(static) class SampleAP0805 { public static void main(String[] args){ System.out.println("value= " + X.MAX); System.out.println("value= " + X.MIN); X o = new X(); System.out.println( "value= " + o.DEFAULT); } } まとめ •インスタンス変数とクラス変数 •インスタンスメソッドとクラスメソッド •ローカル変数とスコープ •変数の寿命、ガーベージコレクション •final修飾子 アルゴリズムとプログラミング (Algorithms and Programming) 第11回:継承の実装 •継承の実装: extends キーワード コンストラクタ •継承とメソッドのオーバーライド,可視性 •抽象クラス (abstractキーワード) 講義資料等: http://www.pe.titech.ac.jp/~watanabe/lecture/ap/index-j.html スーパークラス 継承の実装 Vehicle(乗り物) - speed: double # changeSpeed(s:double) # getSpeed(): double サブクラス Plane - altitude: double # takeOff () # land () サブクラスは、スーパークラス の全てのフィールドとメソッドを 含んでいる。 (実際にアクセスできるかは、 アクセス修飾子に従う) 継承の実装:extendsキーワード スーパークラス Vehicle(乗り物) - speed: double # changeSpeed(s:double) # getSpeed(): double サブクラス Plane - altitude: double # takeOff () # land () class Plane extends Vehicle { private double altitude; protected void takeOff() { 処理(スーパークラスのメソッドも呼べる); } protected void land() { 処理; } } スーパークラスへのアクセス スーパークラス Vehicle(乗り物) - speed: double # changeSpeed(s:double) # getSpeed(): double サブクラス Plane - altitude: double # takeOff () # land () public static void main(略) { Plane o = new Plane(); o.changeSpeed(0.0); for(int i=0;i<=300;i+=50){ o.changeSpeed(i); o.takeOff(); } System.out.println("離陸!"); } } 全てのクラスのスーパークラス Objectクラス extendsキーワードが 無い全てのクラスは、自 動的にObjectクラスの サブクラスとなる。従って、 全てのクラスはObject クラスのフィールドとメ ソッドを暗黙的に継承し ている。 Objectクラス Vehicle(乗り物) - speed: double # changeSpeed(s:double) # getSpeed(): double Plane - altitude: double # takeOff () # land () 継承とコンストラクタ サブクラスのコンストラクタを呼び出すと.. 1. まずスーパクラスのコンストラクタが実行される (何も指定しなければデフォルトコンストラクタが呼ばれる.引数は無し) 2. 次にサブクラスのコンストラクタが実行される サブクラスのコンストラクタ(例) SampleAP0901.java class int X() x } } class int Y() y } } X { x; { = 100; Y extends X { y; ここにsuper();が省略 { = 200; されていると考える サンプルプログラム(続き) SampleAP0901.java続き class SampleAP0901 { public static void main(String[] args) { Y o = new Y(); System.out.println("x= " + o.x ); System.out.println("y= " + o.y ); } } 実行結果 x= 100 y= 200 親のコンストラクタを指定する場合 Superキーワード SampleAP0902.java class X { int x; X(int a) { x = a; } } class Y extends X { int y; Y(int a, int b) { super(a); y = b; } } (thisキーワードとの併用は不可) コンストラクタでsuperを使うときは 最初の行でしか使えない! SampleAP0902.java続き class SampleAP0902 { public static void main(String[] args) { Y o = new Y( 1, 2 ); System.out.println("x= " + o.x ); System.out.println("y= " + o.y ); } } 実行結果 x= 1 y= 2 暗黙の親コンストラクタ呼び出し SampleAP0901.java(再) class int X() x } } class int Y() y } } X { x; { = 100; Y extends X { y; ここにsuper();が省略 { = 200; されていると考える 継承を禁止するfinalキーワード final class X { int x; サブクラスの宣言を禁止! } class Y extends X { int y; } コンパイルエラー! final宣言したクラスは、extendsで サブクラスを作ることが禁止される メソッドのオーバーライド オーバーライド(override) 親クラスと子クラスのそれぞれに、 メソッド名、引数、戻り値が全て同一で 内容が異なるメソッドを定義すること 既出 オーバーロード (overload) 混同注 意! ひとつのクラス内に、メソッド名が同じ で、引数の型及び数が異なるメソッドを 定義すること オーバーライドの目的 Vehicle(エンジン付き乗り物) - speed: double # getSpeed(): double # startEngine () Plane - altitude: double # takeOff () # startEngine () もともと、サブクラスにはスー パークラスのフィールドとメ ソッドが全て自動的に引き継 がれている。 しかし、引き継がれているメ ソッドの処理内容を、サブク ラスで再定義(変更)したい場 合がある 。 オーバーライドの必要性 オーバーライドと可視性 子クラスでオーバーライドするメソッドの可視性は、 親クラスのメソッドの可視性により制限される オーバーライドされる スーパークラスのメソッド に指定されている修飾子 オーバーライドするサブクラスのメソッド public public指定が必要 protected protectedかpublicが必要 private アクセス不可につき、オーバーライド不可 無指定 private指定は不可 親クラスでprotectedが指定されているメソッドを子クラスでオーバーライドするにはprotectedかpublic指定が必要 親・子クラスにおける オブジェクト参照型変数の互換性 Javaのきまり: 型の違いに厳しいJavaですが.. •子クラスのインスタンスを指すオブジェ クト参照型変数を、親クラスの参照変数 に代入することができる。 •代入後も、インスタンスの出身が子クラ スであることを忘れずに記憶しておく仕 組みになっている。 1つのオブジェクトが、あたかも複数の型を持 つかのように見せることができる。 オブジェクト指向3原則:ポリモーフィズム (メソッドのオーバーライドと参照型変数の 互換性によって実現される) 乗り物 飛行機 ジェット機 ジェット機クラスのインスタン スを、乗り物オブジェクトとして 扱うことができる。 自動車 プロペラ機 ジェット機は飛行機でも あり、乗り物でもある ジェット機クラスのインスタン スに対する操作を、乗り物に 対する操作として記述できる このような記述の仕方に どんなメリットがあるの? 乗り物 #動力を始動する() 飛行機 自動車 #動力を始動する() ジェット機 #動力を始動する() #動力を始動する() プロペラ機 #動力を始動する() 操作名は同じだ が、実際の操作 内容はそれぞれ 異なる メソッドのオーバーライド ジェット機クラスのインスタンスを生成する :ジェット機 new ジェット機(); このインスタンスを乗り物オブジェクトと見なす (乗り物オブジェクト変数 v1 に格納する) 乗り物 v1 = new ジェット機(); 乗り物クラス ジェット機クラスのインスタンスへの参照 動力を始動する()メッセージを送る v1.動力を始動する() 乗り物オブジェクトの操作が呼び出 されるように記述されているが、自動 的にジェット機オブジェクトに対する 操作が呼び出される この記法は、ジェット機に限ら ず、プロペラ機にも自動車にも、 将来的に追加される乗り物に もそのまま変更なく使える。 上位概念(スーパークラス)オブジェクトに対する操作と して記述しておくと、サブクラスの細かい修正や追加な どに対して強いプログラムになる(保守性が高い) ジェット機クラスのインスタンスを生成する :ジェット機 new ジェット機(); このインスタンスをジェット機オブジェクト変数 j1 に格納する ジェット機 j1 = new ジェット機(); ジェット機クラス ジェット機クラスのインスタンスへの参照 動力を始動する()メッセージを送る j1.動力を始動する() この記法では、プロペラ機や自動車に変更したり、さらに は将来的に追加される乗り物に対しては、そのままでは 使えない。プログラムの記述を変更する必要がある! ポリモーフィズムの意味と効果 ポリモーフィズム(polymorphism)の意味: 1つのオブジェクトが複数の型を持つ ← 継承により実現 ポリモーフィズムの活用の仕方: 上位概念(スーパークラス)への操作としてプログラ ムを記述する その効果: サブクラスの細かい修正や追加などに対して強い (保守性が高い)プログラムになる オブジェクト指向プログラミングの有効性! 参照型変数への代入 (例) SampleAP1001.java class X { // スーパー(親)クラス int x; } class Y extends X { // サブ(子)クラス int y; } class SampleAP1001 { public static void main(String[] args){ X o = new Y();//サブクラスYのインスタンスを親クラスX } //の参照型変数へ代入できる! } メソッドのオーバーライド(例) SampleAP1002.java class Shape { //2次元図形の面積を表現するクラス double a; //一般に、面積を計算するには、縦×横や底辺×高さ/2など、 double b; //2つの辺の長さを与える必要があると考え、変数を2つ用意する Shape(double x, double y) { a = x; b = y; } double getArea() { //ここでは具体的な定義を与えないことにする。 return 0.0; //具体的な定義はサブクラスで与える。 } } SampleAP1002.java続き class Triangle extends Shape { //三角形 Triangle(double x, double y); super(x,y); // x,yとしては底辺と高さを与える } double getArea() { オーバーライド return ( a * b / 2 ); } } class Rectangle extends Shape { //四角形 Rectangle (double x, double y){ super(x,y); //x,yとしては各辺の長さを与える } double getArea() { オーバーライド return ( a * b ); } } SampleAP1002.java続き class SampleAP1002 { public static void main(String[] args) { Shape o1 = new Shape( 10.0, 10.0 ); Shape o2 = new Triangle( 10.0, 10.0 ); Shape o3 = new Rectangle( 10.0, 10.0 ); System.out.println("o1の面積は" + o1.getArea() ); System.out.println("o2の面積は" + o2.getArea() ); System.out.println("o3の面積は" + o3.getArea() ); } } 実行結果 o1の面積は0.0 o2の面積は50.0 o3の面積は100.0 引数に与えた数値はo1~o2まで 全て同じだが、それぞれオーバー ライドされたgetArea()メソッドが、 異なる処理を実行している。 抽象クラス: 抽象メソッドを持つためオブジェ クト生成できないクラス 乗り物 -スピード #スピードを変える() #スピードを表示する() 飛行機 -高度 #離陸する() #着陸する() #高度を表示する() クラス名をイタリックにすると 抽象クラス 乗り物クラスのインスタンスは 存在しないことが保証される 飛行機クラスでは、依然として スピードを表示する()操作は必須 であり、乗り物クラスは使われる。 抽象クラスの実装 親クラスではメソッド名だけ定義して、子ク ラスにおいて、オーバーライドにより具体的 な処理内容を記述する 抽象メソッド: abstractキーワード 具体的な処理内容が与えられていな いため、インスタンスの作成は不可 抽象クラスの使用例 SampleAP1003.java abstract class Shape { //抽象クラス double a; double b; Shape(double x, double y) { a = x; b = y; } //抽象メソッドとして定義(この方が自然) abstract double getArea(); } •抽象メソッドが1つでもあるとそのクラスは抽象クラス →サブクラスで具体的な処理内容を与えて使用する •抽象クラスだからといって、全てが抽象クラスである必要は無い SampleAP1003.java続き この部分はSampleAP1002.javaと同じ class Triangle extends Shape { //三角形 Triangle(double x, double y); super(x,y); // x,yとしては底辺と高さを与える } double getArea() { return ( a * b / 2 ); } } class Rectangle extends Shape { //四角形 Rectangle (double x, double y){ super(x,y); //x,yとしては各辺の長さを与える } double getArea() { return ( a * b ); } } SampleAP1003.java続き class SampleAP1003 { public static void main(String[] args) { Shape o2 = new Triangle( 10.0, 10.0 ); Shape o3 = new Rectangle( 10.0, 10.0 ); System.out.println("o2の面積は" + o2.getArea() ); System.out.println("o3の面積は" + o3.getArea() ); } } 抽象クラスのインスタンス化を削除 まとめ •継承の実装: extends キーワード •継承とコンストラクタ: superキーワード •継承の禁止: finalキーワード •継承とメソッドのオーバーライド •抽象メソッド、抽象クラス より保守性の高いプログラムの実現 世の中で既に作成され公開されている各種のJavaクラ スライブラリはこのような考え方で作成されている。適当 なクラスを継承してカスタマイズすることにより、極めて 簡単に自分独自の問題を解決することができる。
© Copyright 2024 ExpyDoc