1. プロパティ: ゲッターとセッター
大規模なプロジェクトが数十人のプログラマーによって同時に開発されている場合、クラス フィールドに格納されているデータの処理方法が異なると問題が発生することがよくあります。
もしかしたら、クラスのドキュメントを詳しく調べていない人もいるかもしれません。あるいは、すべてのケースが説明されているわけではないのかもしれません。その結果、オブジェクトの内部データが「破損」し、オブジェクトが無効になる状況が頻繁に発生します。
このような状況を回避するために、Java ではすべてのクラス フィールドをプライベートにするのが通例です。クラスのメソッドのみがクラスの変数を変更できます。他のクラスのメソッドは変数に直接アクセスできません。
他のクラスが自分のクラスのオブジェクト内のデータを取得または変更できるようにしたい場合は、get メソッドと set メソッドという 2 つのメソッドをクラスに追加する必要があります。例:
コード | ノート |
---|---|
|
private name フィールドコンストラクターを介したフィールドの初期化 getName() — このメソッドは name フィールドの値を返しますsetName() — このメソッドは name フィールドの値を変更します |
他のクラスは、name フィールドの値を直接変更できません。getName()
name フィールドの値を取得する必要がある場合は、オブジェクトのメソッドを呼び出す必要がありますPerson
。setName()
一部のコードで名前フィールドの値を変更したい場合は、オブジェクトのメソッドを呼び出す必要がありますPerson
。
このgetName()
メソッドは「名前フィールドのゲッター」とも呼ばれ、メソッドは「名前フィールドのセッターsetName()
」とも呼ばれます。
これは非常に一般的なアプローチです。すべての Java コードの 80 ~ 90% では、クラス内にパブリック変数が存在することはありません。代わりに、変数は宣言private
(またはprotected
) され、各変数にはパブリックのゲッターとセッターが含まれます。
このアプローチではコードが長くなりますが、信頼性は高くなります。
クラス変数に直接アクセスすることは、黄色の二重線を通過して車の向きを変えるようなものです。そのほうが簡単で早いですが、全員がそれを行うと、全員にとって状況が悪化します。
x
点 ( 、 )を記述するクラスを作成するとしますy
。初心者プログラマーがそれを行う方法は次のとおりです。
class Point
{
public int x;
public int y;
}
経験豊富な Java プログラマーが行う方法は次のとおりです。
コード |
---|
|
コードが長くなりましたか?間違いなく。
ただし、パラメーターの検証をゲッターとセッターに追加することはできます。たとえば、x
と がy
常にゼロより大きい (またはゼロ以上である) ことを確認できます。例:
コード | ノート |
---|---|
|
2. オブジェクトの有効期間
オブジェクトが演算子を使用して作成されることはすでにご存知ですnew
が、オブジェクトはどのように削除されるのでしょうか? それらは永遠に存在するわけではありません。それにはメモリが足りません。
C++ などの多くのプログラミング言語には、delete
オブジェクトを削除するための特別な演算子があります。しかし、これは Java ではどのように機能するのでしょうか?
Java では、すべての配置が少し異なります。Java には削除演算子がありません。これは、Java ではオブジェクトが削除されないという意味ですか? いや、もちろん削除されますよ。そうしないと、Java アプリケーションはすぐにメモリ不足になり、プログラムが何か月も中断せずに実行されるという話は成り立ちません。
Java では、オブジェクトの削除は完全に自動化されています。Java マシン自体がオブジェクトの削除を処理します。このプロセスはガベージ コレクションと呼ばれ、ガベージを収集するメカニズムはガベージ コレクター( GC ) と呼ばれます。
では、Java マシンはオブジェクトを削除するタイミングをどのようにして知るのでしょうか?
ガベージ コレクターは、すべてのオブジェクトを「到達可能」と「到達不能」に分割します。オブジェクトへの参照が少なくとも 1 つある場合、そのオブジェクトは到達可能であるとみなされます。オブジェクトを参照する変数がない場合、そのオブジェクトは到達不能とみなされ、ガベージとして宣言されます。これは、オブジェクトを削除できることを意味します。
Java では、既存のオブジェクトへの参照を作成することはできません。すでに存在する参照を割り当てることのみが可能です。オブジェクトへの参照をすべて消去すると、そのオブジェクトは永久に失われます。
循環参照
単純な反例に遭遇するまでは、このロジックは素晴らしく聞こえます。相互に参照する (相互への参照を保存する) 2 つのオブジェクトがあるとします。他のオブジェクトは、これらのオブジェクトへの参照を保存しません。
これらのオブジェクトはコードからアクセスできませんが、引き続き参照されます。
これが、ガベージ コレクターがオブジェクトを「参照される」と「参照されない」ではなく、到達可能なものと到達できないものに分割する理由です。
到達可能なオブジェクト
まず、100% 生存しているオブジェクトが到達可能リストに追加されます。たとえば、現在のスレッド ( Thread.current()
) またはコンソールの InputStream ( System.in
) です。
次に、到達可能なオブジェクトのリストが拡張され、到達可能なオブジェクトの最初のセットによって参照されるオブジェクトが含まれます。次に、この拡張されたセットによって参照されるオブジェクトが含まれるように再度拡張されます。
つまり、相互に参照するだけのオブジェクトがいくつかあるが、到達可能なオブジェクトからそれらに到達する方法がない場合、それらのオブジェクトはガベージとみなされ、削除されます。
3. ガベージコレクション
メモリの断片化
オブジェクトの削除に関連するもう 1 つの重要な点は、メモリの断片化です。オブジェクトを頻繁に作成および削除すると、すぐにメモリが大幅に断片化され、占有されているメモリ領域と占有されていないメモリ領域が点在するようになります。
その結果、空きメモリの大きな部分がないため、大きなオブジェクト (たとえば、100 万個の要素を持つ配列) を作成できない状況に簡単に陥ります。言い換えれば、空きメモリはたとえ大量であっても存在する可能性がありますが、空きメモリの大きな連続ブロックが存在しない可能性があります。
メモリの最適化 (デフラグ)
Java マシンは、この問題を特定の方法で解決します。次のようになります。
記憶は 2 つの部分に分かれています。すべてのオブジェクトはメモリの半分だけで作成 (および削除) されます。メモリの穴をクリーンアップする段階になると、前半のすべてのオブジェクトが後半にコピーされます。ただし、穴がないように隣同士にコピーされます。
プロセスは大まかに次のようになります。
ステップ 1: オブジェクトの作成後
ステップ2:「穴」の出現
ステップ3:「穴」をなくす
そのため、オブジェクトを削除する必要はありません。Java マシンは、到達可能なすべてのオブジェクトを新しい場所にコピーするだけで、オブジェクトが格納されていたメモリ領域全体を解放します。
GO TO FULL VERSION