Javaコア
9. Java における静的バインディングと動的バインディングの違いは何ですか?
静的ポリモーフィズムと動的ポリモーフィズムについては、 この記事の質問 18ですでに回答していますので、それを読むことをお勧めします。10. インターフェイスでプライベート変数または保護された変数を使用することは可能ですか?
いいえ、あなたがすることはできません。これは、インターフェースを宣言すると、Java コンパイラーが自動的にpublic キーワードとabstractキーワードをインターフェース メソッドの前に追加し、 public キーワード、staticキーワード、finalキーワードをデータ メンバーの前に追加するためです。実際には、privateまたはprotectedを追加すると競合が発生し、コンパイラはアクセス修飾子について「修飾子 '<選択された修飾子>' はここでは許可されていません」というメッセージを表示します。インターフェイス内の変数? それを理解してみましょう:- public - このインターフェースにより、クライアントはオブジェクトと対話できるようになります。変数がパブリックでない場合、クライアントは変数にアクセスできません。
- static - インターフェイス (またはそのオブジェクト) を作成できないため、変数は静的です。
- Final - インターフェースは 100% の抽象化を達成するために使用されるため、変数は最終形式になります (変更されません)。
11. クラスローダーとは何ですか?また何に使用されますか?
クラスローダー- またはクラス ローダー - Java クラスのロードを提供します。より正確には、ロードはその子孫、つまり特定のクラスローダーによって保証されます。ClassLoader自体は抽象的です。.class ファイルがロードされるたびに (たとえば、対応するクラスのコンストラクターまたは静的メソッドを呼び出した後)、このアクションはClassLoaderクラスの子孫の 1 つによって実行されます。相続人には次の3種類があります。-
Bootstrap ClassLoaderは基本的なローダーであり、JVM レベルで実装され、JVM カーネルの一部でありネイティブ コードで記述されているため、ランタイム環境からのフィードバックがありません。このローダーは、他のすべての ClassLoader インスタンスの親として機能します。
主に、JDK 内部クラス (通常は$JAVA_HOME/jre/libディレクトリにあるrt.jarおよびその他のコア ライブラリ) のロードを担当します。プラットフォームが異なれば、このクラス ローダーの実装も異なる場合があります。 -
拡張クラスローダーは、基本ローダー クラスの子孫である拡張ローダーです。標準 Java 基本クラスの拡張機能のロードを処理します。JDK 拡張機能ディレクトリ (通常は$JAVA_HOME/lib/extまたは java.ext.dirs システム プロパティに記載されているその他のディレクトリ) からロードされます (このオプションは拡張機能のロードを制御するために使用できます)。
-
System ClassLoader は、JRE レベルで実装されたシステム ローダーで、すべてのアプリケーション レベルのクラスを JVM にロードします。クラス環境変数-classpathまたは-cpコマンド ライン オプションで見つかったファイルをロードします。
-
システム クラスローダーは、キャッシュ内でクラスを見つけようとします。
-
1.1. クラスが見つかった場合、ロードは正常に完了します。
-
1.2. クラスが見つからない場合、ロードは拡張クラスローダーに委任されます。
-
-
拡張クラスローダーは、独自のキャッシュ内でクラスを検索しようとします。
-
2.1. クラスが見つかった場合は、正常に完了します。
-
2.2. クラスが見つからない場合、ロードはブートストラップ クラスローダーに委任されます。
-
-
ブートストラップ クラスローダーは、独自のキャッシュ内でクラスを検索しようとします。
-
3.1. クラスが見つかった場合、ロードは正常に完了します。
-
3.2. クラスが見つからない場合、基礎となるブートストラップ クラスローダーはそのクラスをロードしようとします。
-
-
ロードしている場合:
-
4.1. 成功 - クラスのロードが完了しました。
-
4.2. 失敗すると、制御は拡張クラスローダーに移されます。
-
-
5. 拡張クラスローダーはクラスのロードを試みます。ロード中の場合は、次のようになります。
-
5.1. 成功 - クラスのロードが完了しました。
-
5.2. 失敗すると、制御はシステム クラスローダーに移されます。
-
-
6. システム クラスローダーはクラスのロードを試みます。ロード中の場合は、次のようになります。
-
6.1. 成功 - クラスのロードが完了しました。
-
6.2. 正常に通過しませんでした - 例外が生成されました - ClassNotFoundException。
-
12. 実行時データ領域とは何ですか?
ランタイム データ領域- JVM ランタイム データ領域。JVM は、プログラムの実行中に必要ないくつかのランタイム データ領域を定義します。それらの一部は、JVM の起動時に作成されます。その他はスレッドローカルであり、スレッドの作成時にのみ作成されます (スレッドが破棄されると破棄されます)。JVM ランタイム データ領域は次のようになります。-
PC レジスタは各スレッドに対してローカルであり、スレッドが現在実行している JVM 命令のアドレスが含まれています。
-
JVM スタックは、ローカル変数および一時的な結果のストレージとして使用されるメモリ領域です。各スレッドには独自の個別のスタックがあります。スレッドが終了するとすぐに、このスタックも破棄されます。ヒープに対するスタックの利点はパフォーマンスですが、ストレージのスケールでは確かにヒープの方が有利であることは注目に値します。
-
ネイティブ メソッド スタック - ネイティブ (非 Java) メソッドを実行するための、JVM スタックと同様のデータ要素を格納するスレッドごとのデータ領域。
-
ヒープ - 実行時に作成されるオブジェクト、クラスのメタデータ、配列などを含むストアとしてすべてのスレッドによって使用されます。この領域は、JVM の起動時に作成され、シャットダウン時に破棄されます。
-
メソッド領域 - このランタイム領域はすべてのスレッドに共通であり、JVM の起動時に作成されます。ランタイム定数プールなどの各クラスの構造、コンストラクターとメソッドのコード、メソッド データなどを保存します。
13. 不変オブジェクトとは何ですか?
この記事のこの部分の質問 14 と 15 には、この質問に対する答えがすでにあるので、時間を無駄にせずに見てください。14. String クラスの特別な点は何ですか?
分析の初めに、String の特定の機能について繰り返し説明しました(これについては別のセクションがありました)。次に、 Stringの機能を要約しましょう。-
これは Java で最も人気のあるオブジェクトであり、さまざまな目的に使用されます。使用頻度という点では原始型にも劣りません。
-
このクラスのオブジェクトは、 new キーワードを使用せずに、引用符で直接作成できます。 String str = “string”; 。
-
Stringは不変クラスです。このクラスのオブジェクトを作成するとき、そのデータは変更できません (特定の文字列に +「別の文字列」を追加すると、結果として新しい 3 番目の文字列が得られます)。String クラスの不変性により、スレッド セーフになります。
-
Stringクラスはファイナライズされている ( final修飾子がある) ため、継承できません。
-
String には、作成した文字列値をキャッシュするヒープ内のメモリ領域である独自の文字列プールがあります。このシリーズのこのパート、質問 62 では、文字列プールについて説明しました。
-
Java にはString の類似物、 StringBuilderとStringBuffer という文字列を扱うように設計されたものがありますが、変更可能であるという違いがあります。詳細については、この記事を参照してください。
15. 型共分散とは何ですか?
共分散を理解するために、例を見てみましょう。動物クラスがあるとします。public class Animal {
void voice() {
System.out.println("*тишина*");
}
}
そしてそれを拡張するいくつかのDogクラス:
public class Dog extends Animal {
@Override
public void voice() {
System.out.println("Гав, гав, гав!!!");
}
}
覚えているように、継承型のオブジェクトを親型に簡単に割り当てることができます。
Animal animal = new Dog();
これは単なるポリモーフィズムにすぎません。便利で柔軟ですよね?さて、動物のリストはどうでしょうか?一般的なAnimalを含むリストにDogオブジェクトを含むリストを与えることはできますか?
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = dogs;
この場合、犬のリストを動物のリストに割り当てる行には赤色の下線が引かれます。コンパイラはこのコードを渡しません。この割り当ては非常に論理的であるように見えますが (結局のところ、DogオブジェクトをAnimal型の変数に割り当てることができます)、それは実行できません。これは、それが許可されている場合、リストにはDogのみが含まれていると考えながら、本来Dogであることを意図していたリストにAnimalオブジェクトを含めることができるためです。そして、たとえば、get()メソッドを使用して、犬のリストからオブジェクトを取得し、それが犬であると考えて、そのオブジェクトに対してDogオブジェクトのメソッドを呼び出します。これは、 Animalにはありません。そして、ご理解のとおり、これは不可能です - エラーが発生します。しかし幸いなことに、コンパイラは、子孫のリストを親のリストに代入する際のこの論理エラーを見逃しません (またその逆も同様です)。Java では、一致するジェネリックを持つリスト変数にのみリスト オブジェクトを割り当てることができます。これを不変性といいます。もしこれができれば、それは共分散と呼ばれ、共分散と呼ばれます。つまり、共分散は、 ArrayList<Dog>型のオブジェクトをList<Animal>型の変数に設定できるかどうかです。Java では共分散がサポートされていないことがわかりましたか? たとえそれがどんなものであっても! しかし、これは独自の特別な方法で行われます。デザインは何に使用されますか? Animal を拡張します。これは、リスト オブジェクトを設定する変数のジェネリックとその子孫のジェネリックとともに配置されます。この一般的な構造は、 Animal型の子孫である任意の型が機能することを意味します( Animal型もこの一般化に当てはまります)。つまり、Animal はクラスであるだけでなく、インターフェイスにもなることができます ( extendsキーワードに騙されないでください)。前回の割り当ては次のように行うことができます。
List<Dog> dogs = new ArrayList<>();
List<? extends Animal> animals = dogs;
その結果、IDE では、コンパイラーがこの構造に関して文句を言わないことがわかります。この設計の機能を確認してみましょう。渡されたすべての動物に音を鳴らすメソッドがあるとします。
public static void animalsVoice(List<? extends Animal> animals) {
for (Animal animal : animals) {
animal.voice();
}
}
彼に犬のリストを渡してみましょう。
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
dogs.add(new Dog());
dogs.add(new Dog());
animalsVoice(dogs);
コンソールに次の出力が表示されます。
List<Dog> dogs = new ArrayList<>();
List<? extends Animal> animals = dogs;
animals.add(new Dog());
dogs.add(new Animal());
実際、最後の 2 行では、コンパイラはオブジェクトの挿入を赤色で強調表示します。これは、汎用の<?によって、どのタイプのオブジェクトのどのリストがデータを含むリストに割り当てられるのかを 100% 確信できないためです。Animal> を拡張します。 また、反変性についてもお話したいと思います。通常、この概念は常に共変性と組み合わされており、原則として共変性についても一緒に尋ねられるからです。この構成では継承型を使用するため、この概念は共分散とはやや逆です。Dogオブジェクトの祖先ではないタイプ オブジェクトのリストを割り当てることができるリストが必要だとします。ただし、具体的にどのような種類になるかは事前にはわかりません。この場合、形式?の構造は次のようになります。super Dog 。すべてのタイプが適しています。Dogクラスの祖先です。
List<Animal> animals = new ArrayList<>();
List<? super Dog> dogs = animals;
dogs.add(new Dog());
dogs.add(new Dog());
いずれの場合も、その祖先の実装されたメソッドがすべて含まれているため、このようなジェネリックを使用して、型Dog のオブジェクトをリストに安全に追加できます。ただし、タイプAnimalのオブジェクトを追加することはできません。これは、内部にこのタイプのオブジェクトが存在し、たとえばDog ではないためです。結局のところ、このリストの要素から、AnimalにはないDogクラスのメソッドをリクエストできます。この場合、コンパイルエラーが発生します。また、前のメソッドを実装したい場合は、このジェネリックを使用します。
public static void animalsVoice(List<? super Dog> dogs) {
for (Dog dog : dogs) {
dog.voice();
}
}
返されたリストにDog型のオブジェクトが含まれており、そのメソッドを自由に使用できるかどうかを確認できないため、forループ でコンパイル エラーが発生します。このリストでDogs.get(0)メソッドを呼び出すとします。- Object 型のオブジェクトを取得します。つまり、animalsVoice()メソッドが機能するには、少なくとも型データを絞り込む小さな操作を追加する必要があります。
public static void animalsVoice(List<? super Dog> dogs) {
for (Object obj : dogs) {
if (obj instanceof Dog) {
Dog dog = (Dog) obj;
dog.voice();
}
}
}
GO TO FULL VERSION