JavaSilverに向けて⑦ インスタンスとメソッドとフィールド
・クラスとインスタンス
オブジェクト指向プログラミングでは、クラスを定義し、クラスからインスタンスを生成して、インスタンスが動作する事でプログラムを動作させる。
クラスは、設計図のようなもの。実体を生成するために定義された概念のようなもの。
インスタンスとは、クラスという設計図を基に作成した実体。
・プリミティブ型と参照型
ー プリミティブ型
boolean
char Unicodeのコードを表す16ビットの整数。
byte 整数(8ビット)
short 整数(16ビット)
int 整数(16ビット)
long 整数(32ビット)
float 実数(32ビット)
double 実数(64ビット)
ー 参照型
Stringや配列等が参照型である。
クラス型ともいい、クラスによる型の事。クラス型の変数は、クラスの実体を格納する。
・ガベージコレクション
Javaオブジェクトに対するメモリ領域の割り当てや開放をJVMが自動的に行う。これをガベージコレクションという。
プログラムの起動時や実行中にJavaオブジェクトが生成される。この時、JVMの内部で新しいJavaオブジェクトが作成されると、「ヒープ」と呼ばれるメモリ領域にオブジェクトを格納するための領域が割り当てられている。
JVMに指定されているヒープのサイズを超えるJavaオブジェクトが生成された場合、OutOfMemoryErrorというエラーが出る。
JVMは、どこからも参照されなくなった不要なオブジェクトを見つけ出し、そのメモリ領域を自動的に開放する。JVM内部では、このガベージコレクションが独立したスレッドとして定期的に動作している。
JVMがガベージコレクションを実行している間は、他の全てのスレッドの実行が止まる事に注意が必要。
・ガベージコレクションが発生するタイミング
ガベージコレクションの対象は、どこからも参照されなくなったインスタンスである。インスタンスへの参照が外れるタイミングとしては、以下のものがある。
ー 変数にnullを代入する時
ー インスタンスへの参照を保持している変数に他のインスタンスへの参照を代入した時
・staticなフィールド
ー どこからでもアクセスできる
staticなフィールドには、基本的にはプログラムの実行中はどこでも使えるという特徴がある。
ローカル変数はアクセスできる範囲がきまっているが、staticで宣言すると、どこからでもアクセスする事ができる。
ー インスタンスを生成しなくても使える
インスタンスフィールドとstaticフィールドとの違いである。
インスタンスフィールドは、インスタンスを生成しないと使えない。インスタンスに紐づいている変数である。
staticフィールドは、インスタンスを生成しなくても使える。クラスに紐づいている変数である。
・staticフィールドの宣言
フィールドをstaticフィールドにするには、変数宣言において型の前にstaticをつけるだけで宣言ができる。
static int staticSample = 3;
インスタンスフィールドとstaticフィールド間でも変数名が重複するとコンパイルエラーとなる。
int sample = 5;
static int sample = 5;
//コンパイルエラー
また、ローカル変数をstaticにしてもコンパイルエラーとなる。
class Sample {
public void method() {
static int x = 10; //コンパイルエラー
}
}
・staticフィールドの使い方
インスタンスフィールドとstaticフィールドでは使い方も以下のように違う。
インスタンスフィールドでは、インスタンスの変数名.インスタンスフィールド名。
staticフィールドでは、クラス名.staticフィールド名
*サンプル
class StaticFieldSample4 {
int instanceField = 1;
static int staticField = 2;
void method() {
System.out.println(staticField);
}
}
class StaticFieldSample5 {
public static void main(String[] args) {
StaticFieldSample4 sample1 = new StaticFieldSample4();
System.out.println(sample1.instanceField); //1
StaticFieldSample4 sample2 = new StaticFieldSample4();
sample2.instanceField = 100;
System.out.println(sample1.instanceField); //1
System.out.println(sample2.instanceField); //100
System.out.println(StaticFieldSample4.instanceField); //コンパイルエラー
System.out.println(StaticFieldSample4.staticField); //2
}
}
・staticフィールドの初期化の仕方
ー 宣言時に値を設定する
ー staticイニシャライザで値を設定する
初期化した際の初期値のルールは、インスタンスフィールドと同じ。
数値型は0、boolean型はfalse、参照型はnullとなる。
宣言でstatic化する他に、staticイニシャライザで初期化するという事も可能。
staticイニシャライザはブロックなので、長い処理も書ける。「static」の後ろにブロックを置き、ブロックの中に処理を書く。
*staticイニシャライザのサンプル
class StaticFieldSample11 {
static List<String> staticListField;
static final String staticStringField;
static {
staticListField = new ArrayList<>();
staticListField.add(“value1”);
staticListField.add(“value2”);
staticStringField = “String”;
}
}
・staticフィールドの活用例
ー 定数
final修飾子を使い、定数としてstaticフィールドを使うというのが最もポピュラーな使い方。
public static final int a = 1;
ー ロック(排他制御)用の変数
クラスから生成された全インスタンスでの排他制御を行う際のロック用オブジェクトとして、staticフィールドを使う事がある。
static宣言しなければ、別のインスタンスでの実行ができない。そのために、排他制御の際はstaticを使用する。
*排他制御としてのstaticのサンプル
class StaticFieldSample {
private static final Object LOCK = new Object();
void method() {
synchronized(LOCK) {
//クラスの全インスタンスで同期させて実行させたい処理
}
}
}
ー インスタンスが1つだけである事を保証したい時
インスタンスが1つである事を保証したい時がある。その時Singletonというデザインパターンを使うが、staticフィールドの活用事例として有名である。
staticフィールドに自分のクラスのインスタンスを持ち、コンストラクタはprivateとして外部からアクセスできないようにする。また、インスタンスを取得するためのstaticメソッドを作り、その中だけでインスタンスを生成すれば、Singletonパターンとなる。
*サンプル
class StaticFieldSample {
private static StaticFieldSample instance;
private StaticFieldSample() {
}
static StaticFieldSample getInstance() {
if (instance == null) {
instance = new StaticFieldSample();
}
return instance;
}
}
ー インスタンスを使い回したい場合、インスタンスの生成を制限したい場合にstaticを使う。
以下はInteger.valueOf(int)のソースコードである。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.chache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
….略
}
・staticのその他の特徴
ー staticなフィールドとメソッドは、クラスのロード後、すぐに行われる。
ー staticなフィールドとメソッドは、staticではないフィールドとメソッドにアクセスする事ができない。
反対に、staticではないフィールドとメソッドからstaticなフィールドとメソッドにアクセスする事はできる。
ー staticなフィールドとメソッドからstaticではないフィールドとメソッドに対してアクセスしようとすると、コンパイルエラーとなる。
参照(https://engineer-club.jp/java-static-field)