これまで「オブジェクト」という概念を使用してきましたが、オブジェクトがメモリに格納される具体的な方法については説明していませんでした。この議論は「オブジェクト参照」という重要な概念につながります。
オブジェクト参照
以前に定義した Human クラスを引き続き使用し、Test クラスを用意します。
次のようにコードをコピーします。
パブリッククラステスト
{
public static void main(String[] args)
{
人間 aperson = 新しい人間(160);
}
}
クラス人間
{
/**
* コンストラクター
*/
public Human(int h)
{
this.height = h;
}
/**
*アクセサ
*/
public int getHeight()
{
this.height を返します。
}
/**
* ミューテーター
*/
public void 成長高さ(int h)
{
この高さ = この高さ + h;
}
プライベート int 高さ;
}
クラスを外部から呼び出して、上記の Test クラスなどのオブジェクトを作成できます。
次のようにコードをコピーします。
人間 aperson = 新しい人間(160);
Humanクラスのオブジェクトapersonが作成されます。
上記は非常に単純なステートメントですが、詳しく説明する必要があることがたくさんあります。
1. まず、等号の右側を見てください。 new はメモリ内にオブジェクト用のスペースを開きます。具体的には、new はメモリのヒープ上にオブジェクト用のスペースを開きます。この空間には、オブジェクトのデータとメソッドが格納されます。
2. 等号の左側に注目してください。 aperson は Human オブジェクトを指します。これはオブジェクト参照と呼ばれます。実際、aperson はオブジェクトそのものではありませんが、オブジェクトへのポインタに似ています。 aperson はメモリ内のスタックに存在します。
3. 等号を使用して値を割り当てると、右側の new によってヒープ内に作成されたオブジェクトのアドレスがオブジェクト参照に割り当てられます。
ここでいうメモリとは、JVM(Java Virtual Machine)によって仮想化されたJavaプロセスのメモリ空間を指します。メモリ内のヒープとスタックの概念については、「Linux のプログラムからプロセスへ」を参照してください。
スタックはヒープよりも高速に読み取ることができますが、スタックに格納されるデータは有効範囲によって制限されます。 C言語では関数呼び出しが終了すると、対応するスタックフレームが削除され、スタックフレーム上に格納されていたパラメータや自動変数は消滅します。 Java のスタックにも同じ制限が適用され、メソッド呼び出しが終了すると、メソッドによってスタックに保存されたデータがクリアされます。 Java では、すべての (通常の) オブジェクトがヒープに格納されます。したがって、 new キーワードの完全な意味は、ヒープ上にオブジェクトを作成することです。
int や double などのプリミティブ型のオブジェクトはスタックに格納されます。基本型を宣言する場合、new は必要ありません。宣言されると、Java はプリミティブ型のデータをスタックに直接保存します。したがって、基本型の変数名は参照ではなくデータそのものを表します。
参照とオブジェクトの関係は、凧と人間のようなものです。私たちが空を見るとき(プログラムに書かれています)、見えるのは凧(参考)ですが、その凧に相当するのは人(物体)です。
参照とオブジェクトの分離 参照はオブジェクトを指します。
参照とオブジェクトは分離されていますが、reference.method() を介してオブジェクト メソッドにアクセスするなど、オブジェクトへのすべてのアクセスは参照の「ドア」を通過する必要があります。 Java では、参照をスキップしてオブジェクトに直接触れることはできません。別の例として、オブジェクト a のデータ メンバーが通常のオブジェクト b の場合、a のデータ メンバーはオブジェクト b への参照を保存します (基本型変数の場合、a のデータ メンバーは基本型変数自体を保存します)。 。
Javaでは参照がポインタの役割を果たしますが、C言語のようにポインタの値に1を加算するなど、ポインタの値を直接変更することはできません。オブジェクトに対する操作は参照を通じてのみ実行できます。この設計により、ポインターが引き起こす可能性のある多くのエラーが回避されます。
参照の割り当て
参照を別の参照に割り当てると、実際にはオブジェクトのアドレスがコピーされます。どちらの参照も同じオブジェクトを指します。たとえば、dummyPerson=aperson; とすると次のようになります。
オブジェクトは複数の参照を持つことができます (1 人が複数の凧を揚げることができます)。プログラムが 1 つの参照を通じてオブジェクトを変更すると、その変更は他の参照からも表示されます。次の Test クラスを使用して、実際の効果をテストできます。
次のようにコードをコピーします。
パブリッククラステスト
{
public static void main(String[] args)
{
人間 aperson = 新しい人間(160);
人間のダミー人物 = a人;
System.out.println(dummyperson.getHeight());
aパーソン.growHeight(20);
System.out.println(dummyperson.getHeight());
}
}
aperson に対する変更は、dummyperson に影響します。これら 2 つの参照は、実際には同じオブジェクトを指します。
したがって、参照を別の参照に代入しても、オブジェクト自体はコピーされません。オブジェクトをコピーするための他のメカニズムを見つけなければなりません。
ガベージコレクション
メソッド呼び出しが終了すると、参照型変数とプリミティブ型変数はクリアされます。オブジェクトはヒープ上に存在するため、メソッド呼び出しが終了してもオブジェクトが占有しているメモリはクリアされません。プロセス空間は、作成中のオブジェクトですぐにいっぱいになる場合があります。 Java には、メモリ領域を再利用するために使用されなくなったオブジェクトを消去するためのガベージ コレクション メカニズムが組み込まれています。
ガベージ コレクションの基本原理は、オブジェクトを指す参照が存在する場合、そのオブジェクトはリサイクルされず、オブジェクトを指す参照が存在しない場合、そのオブジェクトはクリアされるということです。占有しているスペースは再利用されます。
上図は、ある瞬間における JVM のメモリ状態を想定しています。 Human オブジェクトには 3 つの参照があります。スタックからの aperson と dummyperson、および別のオブジェクトのデータ メンバーである President です。 Club オブジェクトには参照がありません。このときガベージコレクションを開始すると、クラブオブジェクトは空になり、クラブオブジェクトからのヒューマンオブジェクトの参照(社長)も削除されます。
ガベージ コレクションは Java の重要なメカニズムであり、Java の動作効率に直接影響します。詳細については後ほど説明します。
パラメータの受け渡し
参照とオブジェクトの概念を分離すると、Java メソッドのパラメータ受け渡しメカニズムは実際には非常に明確になります。Java パラメータの受け渡しは値によるものです。つまり、パラメータを渡すと、メソッドはパラメータのコピーを取得します。
実際、渡すパラメータの 1 つは基本型の変数で、もう 1 つはオブジェクトへの参照です。
プリミティブ型変数の値渡しとは、変数自体がコピーされて Java メソッドに渡されることを意味します。 Java メソッドによる変数の変更は、元の変数には影響しません。
値渡しとは、オブジェクトのアドレスがコピーされて Java メソッドに渡されることを意味します。この参照に基づく Java メソッド アクセスはオブジェクトに影響を与えます。
ここで言及する価値のある別の状況があります。メソッド内で new を使用してオブジェクトを作成し、そのオブジェクトへの参照を返します。戻り値が参照によって受け取られた場合、オブジェクトの参照は 0 ではないため、オブジェクトはまだ存在しており、ガベージ コレクションされません。
要約する
新しい
参照、オブジェクト
ガベージコレクションの対象となる条件
パラメータ: 値によって渡されます