※当サイトの記事には、広告・プロモーションが含まれます。

Java言語セミナーを受講して 7章

今回も、Java言語セミナーを受講しての続きになります。

staticとは

static とは 修飾子 の一つであり Java の キーワード の一つです。

  • staticフィールド(変数)= クラスフィールド
  • staticメソッド = クラスメソッド
  • static内部クラス(ネスト)
  • static イニシャライザ(初期子化)
  • static import

の使い方ができるそうです。

(※今回は、staticフィールドとstaticメソッドしか扱っていません。) 

 

static変数とstaticメソッド

static を使用するとクラスメソッド、クラスフィールドを宣言することができます。static を使用したものはクラス領域の定義になるそうです。

どういうことかというと、Javaではプログラムが実行されると、メモリー上にスタックとヒープという領域が確保されます。

メモリ構造のイメージ図

f:id:ts0818:20160220165721p:plain

このうち、staticキーワードが指定されたものは、JavaVMのクラスローダーというもので読み込まれたクラスファイルのデータになるのでPermanent Generation領域に格納されます。

 

  Java SE8でPermanent領域が無くなった件については下記サイトへ

Java8のHotSpotVMからPermanent領域が消えた理由とその影響 | ギークを目指して

 

実際にプログラムでstaticの特徴を見ていきたいと思います。

 

class Bicycle04{

  int intstanceVal;
  static int staticVal;
  
  void methodA(){
    System.out.println("インスタンスメソッド");
  }
  
  static void methodB(){
    System.out.println("スタティックメソッド");  
  }
  
}

 

class sample_test21{

  public static void main(String[] args){
  
    Bicycle04 bike01 = new Bicycle04();
    Bicycle04 bike02 = new Bicycle04();
    Bicycle04 bike03 = new Bicycle04();
    Bicycle04 bike04 = new Bicycle04();
  
  }

}

staticなもののイメージ図

f:id:ts0818:20160213105350p:plain

複数オブジェクトを生成しても、staticなものは1箇所の領域に収まります。

そのため、インスタンス化せずに呼び出すことが可能です。

呼び出すには、『クラス名.変数名』、『クラス名.メソッド名』と記述します。

Javaでは、クラスで定義された変数・メソッドをメンバと呼ぶようです。

メンバの種類

  メンバ変数 メンバメソッド
インスタンスメンバ インスタンス変数
(非static変数)
インスタンスメソッド
(非staticメソッド)
staticメンバ static変数 staticメソッド

メンバ間のアクセスにはルールがあり、

  • クラス内で定義したインスタンスメンバは、クラス内で定義したstaticメンバに直接アクセスできます。
  • クラス内で定義したstaticメンバは、クラス内で定義したインスタンスメンバに直接アクセスすることはできません。
    アクセスする場合はインスタンス化してからアクセスします。

 

sample_test21.javaを書き換えて、static変数・メソッドとインスタンス変数・メソッドの呼び出しの比較

class sample_test21{

  public static void main(String[] args){

    System.out.println(Bicycle04.instanceval);     // NG
    System.out.println(Bicycle04.staticval);     // OK
    Bicycle04.methodA();      // NG
    Bicycle04.methodB();      // OK
    
    Bicycle04 bike = new Bicycle04();
    System.outo.println(bike.instanceVal); // OK
    System.out.println(bike.staticVal);    // OK
    bike04.methodA();  // OK
    bike04.methodB();  // OK
  }
  
}

 

同一クラス内でのstatic変数・メソッドとインスタンス変数・メソッドの呼び出しの比較

class Bicycle05{
  int instanceVal;       // インスタンス変数
  static int staticVal;  // static変数
  
  int methodA(){
      return instanceVal;  //① OK
  }
  
  int methodB(){
      return staticVal;    //② OK
  }
  
  static int methodC(){
      return instanceVal;  //③ NG
  }
  
  static int methodD(){
      return staticVal;    //④ OK
  }
  
  static int methodE(){
      Bicycle05 bike = new Bicycle05();  //⑤ OK
      return obj.instanceVal;
  }
  
}
    

①は、インスタンスメソッド → インスタンス変数なのでOK。

②は、インスタンスメソッド → static変数なのでOK。

③は、staticメソッド → インスタンス変数なのでコンパイルエラーでNG。

④は、staticメソッド → static変数なのでOK。

⑤は、staticメソッド内で、自クラスをインスタンス化し、変数obj.インスタンス変数でアクセスしてるのでOK。

 

アクセス修飾子

Java言語では、クラス、コンストラクタ、メンバ変数、メソッドに対し、他のクラスからのアクセスを許可するか/しないか などをアクセス修飾子を使うことで指定できます。

アクセス修飾子

f:id:ts0818:20160217143354p:plain

public修飾子とprivate修飾子の例

class Bicycle{

//インスタンス変数にprivate修飾子を指定
  private int id;
//コンストラクタにpublic修飾子を指定
  public Bicycle(int i){
    id = i;
  }
//メソッドにpublic修飾子を指定
  public int getId(){
    return id;
  }

}
class sample_test22{
  
  public staitc void main(String[] args){
  
      Bicycle bike = new Bicycle(100);
  
      //private指定されたメンバは、他クラスからアクセス不可
      //System out.println("private指定のインスタンス変数へアクセス:" + emp.id);
      
      //public指定されたメンバは、他クラスからアクセス可
      System.out.println("public指定のメソッドへアクセス:" + bike.getId());
  
  }

}

sample_test22.javaの8行目は、private修飾子を指定したインスタンス変数にアクセスしようとしているので、コメント(//)を外すとコンパイルエラーになります。

 

アクセス修飾子の推奨ルール

オブジェクト指向において、Fields(state)= プロパティ(属性=変数)の集まりとMethods(behavior)= メソッド(振る舞い = 操作)を一体化して表現することをカプセル化というようです。

カプセル化されたクラスにおいて、実データであるインスタンス変数が他クラスから無闇矢鱈に変更されるのを防ぐために、

  • インスタンス変数は、アクセスをprivateにする(private修飾子を指定する)。
  • メソッドは、アクセスをpublicにする(public修飾子を指定する)。

のようにするのが望ましいようです。

1つのソースファイル(.javaファイル)に、複数のクラスを定義可能ですが、publicなクラスは1ソースファイルにつき、1つしか記述できないようです。

publicなクラスを指定した場合は、ソースファイル(.javaファイル)名はpublicなクラス名と同じにする必要があります。

ガベージコレクタ

Java言語では、作成されたプログラムはJVMJava Virtual Machine = Java仮想マシン)上で実行されます。

JVMはプログラムの実行の制御、メモリ管理などを行っています。 

メモリ領域の解放を行っているのが、JVM上で稼働しているガベージコレクタと呼ばれるプログラムです。

コードの書き方によってメモリ不足が発生した場合、必要に応じてオブジェクトをガベージコレクタの対象にさせることが可能です。

ガベージコレクタとデータの流れのイメージ図(Java EE7)

f:id:ts0818:20160217164813p:plain

 

オブジェクトをガベージコレクタの対象にする

オブジェクトはどこかから参照されている間は、ガベージコレクタの対象になることはありません。

プログラムの中で『この変数はオブジェクトを参照しない』ということを表現するには、変数に null を代入します。

 

オブジェクトをガベージコレクタの対象にするイメージ図

f:id:ts0818:20160217171448p:plain

 

オーバーライド(override)

再定義。継承関係のある親子クラスにおいて、親クラス(スーパークラス)のメソッドと同じメソッドを子クラス(サブクラス)に書いて、親クラススーパークラスのメソッドを上書きすることです。

メソッド名引数リストが全く同じであること、戻り値は同じかもしくはその戻り値の型がサブクラスであればオーバーライドとみなされます

アクセス修飾子は、スーパークラスと同じか、それより公開範囲の広いものにする必要があります

 

オーバーライドのイメージ図

f:id:ts0818:20160217184117p:plain

オーバーライドの例

//スーパークラス    
class Super{

  public void print(String s){
 
    System.out.println("Super print :" + s);
    
  }
  
  public void method(){ }
  
}
  
//サブクラス
class Sub extends Super{
 
  public void print(String s){
    
    s = "渡された文字列は" + s + " です。";
    System.out.println("Sub print :" + s);
    
  }
  //void method(){ }  //コンパイルエラー

}

//実行のためのクラス
class Sample_test24{
  
  public static void main(String[] args){
  
    Super s1 = new Super();
    s1.print("Java");        //4行目、スーパークラスのメソッドが呼び出される
    Sub.s2 = new Sub();
    s2.print("Java");        //17行目、サブクラスのメソッドが呼び出される
  
  }
  
}

32行目~33行目は、スーパークラスインスタンス化し、print()メソッドを呼び出しています。34行目~35行目では、サブクラスをインスタンス化し、print()メソッド(オーバーライドされた)を呼び出しています。 

 

final修飾子

final修飾子は、変数につけて定数を定義すること以外にも、クラスやメソッドにも適用できます。 

final修飾子の効果

  • 変数に指定した場合、定数になります。
  • メソッドに指定した場合、サブクラスでのメソッドのオーバーライドができなくなります。
  • クラスに指定した場合、そのクラスをもとにサブクラスを定義できなくなります。

final修飾子のイメージ図

f:id:ts0818:20160217195405p:plain

 

class SuperA{}                   //スーパークラスA
final class SuperB{}             //スーパークラスB
class SuperC{      
  void print(){}
}                                //スーパークラスC
class SuperD{
  final void print(){}
}                                //スーパークラスD


class SubA extends SuperA{}      //サブクラスA  // OK
//class SubB extends SuperB{}    //サブクラスB  // NG コンパイルエラー
class SuperC extends SuperC{}    //サブクラスC  // OK
//class SubD extends SuperD{}    //サブクラスD  // NG コンパイルエラー

11行目は、final修飾子を指定したクラスからサブクラスを定義しようとしているのでNGで、コメント(//)を外すとコンパイルエラーになります。

13行目は、fainal修飾子を指定したメソッドをオーバーライドしようとしているためNGで、コメント(//)を外すとコンパイルエラーになります。 

 

thisキーワード

thisは自分自身(自オブジェクト)を表すキーワードです。

自オブジェクトが保持する変数、メソッド、コンストラクタを明示的に指定する際に使います。

thisを介して変数を使用する

this.変数名とすると自オブジェクトが持つ変数という意味になります。

thisキーワードを変数に指定したイメージ図

f:id:ts0818:20160217203426p:plain

thisを介してコンストラクタを呼び出し 

呼びだされたコンストラクタの中から、自クラス内で定義した別のコンストラクタを呼び出すことができます。this( ) と記述します。

this( )はコンストラクタ定義の先頭に記述する必要があります

 

this( )のイメージ図

f:id:ts0818:20160217210022p:plain

それぞれのコンストラクタの処理の流れを確認 

class Foo{
  
  String s; int i;
  public Foo(){ this.("Hello"); }
  public Foo(String s){this(s, 100); }
  public Foo(String s, int i){
  
    this.s = s; this.i = i;
    System.out.println(this.s + " : " + this.i);
  
  }
  
}
class sample26{

  public static void main(String[] args){
  
      Foo f1 = new Foo();            // case 1
  
      Foo f2 = new Foo("Hey");       // case 2
      
      Foo f3 = new Foo("Bye", 200);  // case 3
      
  }

}

case 1 「Foo f1 = new Foo( );」では、

4行目 → 5行目 → 6行目 → 7行目 → 8行目 と処理されます。

case 2 「Foo f2 = new Foo("Hey");」では、

5行目 → 6行目 → 7行目 → 8行目 と処理されます。

case 3 「Foo f3 = new Foo("Bey", 200);」では、

6行目 → 7行目 → 8行目 と処理されます。

 

superキーワード

superキーワードは自オブジェクトから見てスーパークラスのオブジェクトを実現する際に使用します。 

superキーワードも、変数、メソッド、コンストラクタに指定することができます。

 

superを介してメソッドを呼び出す

サブクラスから明示的にスーパークラスのメソッドを呼び出したい場合は、「super.メソッド名」を使用します。

通常は、サブクラスからスーパークラスのメソッドをそのまま呼び出すことができるようですが、メソッドをオーバーライドしている場合で、明示的にスーパークラスで定義したほうのメソッドを呼び出したい場合に使用します。

class Super{

  int num;
  public void methodA(){
    num += 100;
  }
  public void print(){
    System.out.println("num値" + num);
  }

}

class Sub extends Super{

  public static void methodA(){
    num += 500;  
  }
  public void methodB(int num){
    methodA();        //15行目が呼び出される
    print();          //スーパークラスのメソッド呼び出し
    super.methodA();   //4行目が呼び出される
    print();          //スーパークラスのメソッド呼び出し
  }

}

class sample_test27{

  public static void main(String[] args){
    Sub s = new Sub();
    s.methodB(0);
  }

}
    

30行目でSubクラスをインスタンス化し、31行目でmethodB()を呼び出しています。

19行目ではmethodA()を呼び出していますが、Superクラスで定義したメソッドをSubクラスでオーバーライドしてるため、15行目が呼びだされます。

そのため20行目のprint()メソッドによる出力は500となります。

しかし、21行目でsuper.methodA()としてるため、4行目が呼びだされ、600と出力されます。

 

superを介してコンストラクタを呼び出す

継承関係のあるクラスをインスタンス化すると、必ずスーパークラスのコンストラクタが実行されてから、サブクラスのコンストラクタが実行されます。

class Super{
  
  public Super(){
    System.out.println("Super()");
  }
  public Super(int a){
    System.out.println("Super(int a)");
  }
  
}


class Sub extends Super{

  public Sub(){
    System.out.println("Sub()");  
  }
  public Sub(int a){
    System.out.println("Sub(int a)");
  }

}


class sample_test28{

  public static void main(String[] args){
    Sub s1 = new Sub();
    Sub s2 = new Sub(10);
  }

}

28行目でSubクラスの引数を持たないコンストラクタを呼び出していますが、 15行目の実行前に3行目が実行されます。

29行目では、Subクラスの引数を持つコンストラクタを呼び出していますが、同じく3行目が実行されてから18行目が実行されます。

サブクラスをインスタンス化すると、まずスーパークラスのコンストラクタが呼び出されます。

29行目で引数のあるコンストラクタを呼び出していますが、スーパークラス側で呼び出されるコンストラクタは引数を持たないものです。

これは、プログラマが明示的に指定しないと、引数を持たないスーパークラスのコンストラクタ、super( ) が呼び出される仕組みになっているからのようです。

sample_test28.javaのイメージ図

f:id:ts0818:20160217222120p:plain

(※全てのクラスは暗黙的に、Javaライブライで提供されるjava.lang.Objectクラスをスーパークラスに持ちます。)

 

ここまでの内容は下記書籍を参考にさせていただいています。

ラクル認定資格教科書 Javaプログラマ Bronze SE 7/8 単行本(ソフトカバー) – 2015/10/10

f:id:ts0818:20160202070628j:plain

 

次回、Java言語セミナーを受講して 最終章になりますが、その前にRuby on Railsの記事が間にはさまります。

興味あるかたは是非是非、読んでみてください。 

今回はこのへんで。