ソフトウェアエンジニアの皆さん、こんにちは!面接の質問について話しましょう。準備すべきこと、知っておくべきことについて。これは、これらの点を繰り返したり、最初から勉強したりする絶好の理由です。 OOP、Java 構文、Java の例外、コレクション、マルチスレッドに関するよくある質問を集めた広範なコレクションを用意しています。便宜上、いくつかの部分に分けます。 重要:ここでは、Java バージョン 8 までについてのみ説明します。9、10、11、12、13 のすべての革新性はここでは考慮されません。回答を改善する方法に関するアイデアやコメントは大歓迎です。楽しく読んで、さあ行きましょう!
Java インタビュー: OOP の質問
1. Java にはどのような機能がありますか?
答え:-
OOP の概念:
- オブジェクト指向。
- 継承;
- カプセル化。
- 多態性。
- 抽象化。
-
クロスプラットフォーム: Java プログラムは、変更を加えることなく、どのプラットフォームでも実行できます。必要なのは、インストールされた JVM (Java 仮想マシン) だけです。
-
高いパフォーマンス: JIT(Just In Time コンパイラー) により高いパフォーマンスを実現します。JIT はバイトコードをマシンコードに変換し、JVM が実行を開始します。
- マルチスレッド:として知られる実行スレッド
Thread
。JVM は というスレッドを作成しますmain thread
。プログラマは、Thread クラスから継承するか、インターフェイスを実装することによって、複数のスレッドを作成できますRunnable
。
2. 継承とは何ですか?
継承とは、あるクラスが別のクラスを継承 (「拡張」) できることを意味します。こうすることで、継承元のクラスのコードを再利用できます。既存のクラスは と呼ばれsuperclass
、作成されるクラスは と呼ばれますsubclass
。parent
やとも言いますchild
。
public class Animal {
private int age;
}
public class Dog extends Animal {
}
はどこにAnimal
ありparent
、そしてDog
- ですchild
。
3. カプセル化とは何ですか?
この質問は Java 開発者のインタビューでよく出ます。カプセル化とは、ゲッターとセッターを使用してアクセス修飾子を使用して実装を隠蔽することです。これは、開発者が必要と判断した場所での外部使用のためのアクセスを閉じるために行われます。身近な例としては車が挙げられます。私たちはエンジンの動作に直接アクセスすることはできません。私たちにとっての仕事は、キーをイグニッションに差し込んでエンジンを始動することです。そして、内部でどのようなプロセスが行われるかは、私たちには関係ありません。さらに、この活動への介入は予期せぬ状況を引き起こす可能性があり、それにより車が壊れたり、自分自身に危害を加えたりする可能性があります。まったく同じことがプログラミングでも起こります。Wikipediaに詳しく説明されています。JavaRushにはカプセル化に関する記事もあります。4. ポリモーフィズムとは何ですか?
ポリモーフィズムとは、オブジェクトの特定のタイプに関する情報がなくても、同じインターフェイスでオブジェクトを同様に使用できるプログラムの機能です。よく言われるように、1 つのインターフェイスに多数の実装があります。ポリモーフィズムを使用すると、共通の動作に基づいてさまざまなタイプのオブジェクトを組み合わせて使用できます。たとえば、Animal クラスがあり、これには Dog と Cat という 2 つの子孫があります。汎用の Animal クラスには、音を出すというすべてのクラスに共通の動作があります。Animal クラスのすべての子孫をまとめて「音を出す」メソッドを実行する必要がある場合、ポリモーフィズムの可能性を使用します。これは次のようになります。List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
したがって、ポリモーフィズムが役に立ちます。さらに、これはポリモーフィック (オーバーロード) メソッドにも当てはまります。 ポリモーフィズムの使用の実践
面接の質問 - Java 構文
5. Java のコンストラクターとは何ですか?
次の特性が有効です。- 新しいオブジェクトが作成されると、プログラムは適切なコンストラクターを使用して作成します。
- コンストラクターはメソッドのようなものです。その特徴は、戻り要素 (void を含む) がなく、その名前がクラスの名前と同じであることです。
- コンストラクターが明示的に記述されていない場合は、空のコンストラクターが自動的に作成されます。
- コンストラクターはオーバーライドできます。
- パラメーター付きのコンストラクターが作成されたが、パラメーターなしのコンストラクターも必要な場合は、自動的に作成されないため、別途作成する必要があります。
6. Object から継承しない 2 つのクラスはどれですか?
挑発に騙されないでください。そのようなクラスは存在しません。すべてのクラスは、直接または先祖を通じて Object クラスから継承されます。7. ローカル変数とは何ですか?
Java 開発者のインタビュー中によく聞かれたもう 1 つの質問。ローカル変数はメソッド内で定義され、メソッドが実行される瞬間まで存在する変数です。実行が終了すると、ローカル変数は存在しなくなります。main() メソッドで helloMessage ローカル変数を使用するプログラムを次に示します。public static void main(String[] args) {
String helloMessage;
helloMessage = "Hello, World!";
System.out.println(helloMessage);
}
8. インスタンス変数とは何ですか?
インスタンス変数はクラス内で定義される変数で、オブジェクトが存在する瞬間まで存在します。例としては、2 つの変数 nectarCapacity と maxNectarCapacity を持つ Bee クラスがあります。public class Bee {
/**
* Current nectar capacity
*/
private double nectarCapacity;
/**
* Maximal nectar that can take bee.
*/
private double maxNectarCapacity = 20.0;
...
}
9. アクセス修飾子とは何ですか?
アクセス修飾子は、クラス、メソッド、変数へのアクセスをカスタマイズできるツールです。アクセスが増加した順に並べた次の修飾子があります。private
- メソッド、フィールド、コンストラクターに使用されます。アクセス レベルは、それが宣言されているクラスのみです。package-private(default)
- 授業に使用できます。クラス、メソッド、変数、コンストラクターが宣言されている特定のパッケージ内でのみアクセスします。protected
package-private
—修飾子を持つクラスから継承するクラスに対する +と同じアクセスprotected
。public
- 授業にも使用されます。アプリケーション全体にわたるフルアクセス。
10. メソッドのオーバーライドとは何ですか?
メソッドのオーバーライドは、子が親クラスの動作を変更したいときに発生します。親メソッドの内容を実行したい場合は、子メソッドで super.methodName() のような構造を使用できます。これにより、親メソッドの作業が実行され、その後ロジックが追加されます。満たすべき要件:- メソッドのシグネチャは同じである必要があります。
- 戻り値は同じである必要があります。
11. メソッド シグネチャとは何ですか?
メソッド シグネチャは、メソッドの名前とメソッドが受け入れる引数のセットです。メソッド シグネチャは、メソッドをオーバーロードするときのメソッドの一意の識別子です。12. メソッドのオーバーロードとは何ですか?
メソッドのオーバーロードはポリモーフィズムのプロパティであり、メソッド シグネチャを変更することで、同じアクションに対して異なるメソッドを作成できます。- 同じメソッド名。
- 異なる議論。
- 異なる戻り値の型がある可能性があります。
add()
of をArrayList
次のようにオーバーロードでき、入力される引数に応じて異なる方法で加算を実行します。
add(Object o)
- 単純にオブジェクトを追加します。add(int index, Object o)
— オブジェクトを特定のインデックスに追加します。add(Collection<Object> c)
— オブジェクトのリストを追加します。add(int index, Collection<Object> c)
— 特定のインデックスから始まるオブジェクトのリストを追加します。
13. インターフェースとは何ですか?
Java では多重継承が実装されていないため、この問題を克服するために、私たちが知っているインターフェースが追加されました ;) 長い間、インターフェースにはメソッドが実装されていないだけでした。この回答の一部として、それらについて説明します。例えば:
public interface Animal {
void makeSound();
void eat();
void sleep();
}
ここからいくつかのニュアンスがわかります。
- インターフェース内のすべてのメソッドはパブリックかつ抽象です。
- すべての変数は public static Final です。
- クラスはそれらを継承 (拡張) するのではなく、実装 (実装) します。さらに、インターフェイスは好きなだけ実装できます。
- インターフェイスを実装するクラスは、インターフェイスが持つすべてのメソッドの実装を提供する必要があります。
public class Cat implements Animal {
public void makeSound() {
// method implementation
}
public void eat() {
// implementation
}
public void sleep() {
// implementation
}
}
14. インターフェースのデフォルトメソッドとは何ですか?
次に、デフォルトのメソッドについて説明します。何のために、誰のために?これらのメソッドは、すべてを「あなたのものと私たちのもの」にするために追加されました。私は何を話しているのでしょうか?はい、一方ではラムダやストリーム API などの新しい機能を追加する必要がありましたが、他方では Java の特徴である下位互換性を残す必要がありました。これを行うには、既製のソリューションをインターフェースに導入する必要がありました。これが、デフォルトのメソッドが私たちに与えられた経緯です。つまり、デフォルト メソッドは、キーワード を持つインターフェイスに実装されたメソッドですdefault
。たとえば、stream()
でよく知られている方法ですCollection
。チェックしてみてください、このインターフェイスは見た目ほど単純ではありません ;)。または、同様によく知られてforEach()
いるIterable
. また、デフォルトのメソッドが追加されるまでは存在しませんでした。ちなみに、これについてはJavaRushでも読むことができます。
15. では、2 つの同一のデフォルト メソッドを継承するにはどうすればよいでしょうか?
デフォルトの方法についての以前の回答に基づいて、別の質問をすることができます。インターフェイスにメソッドを実装できる場合、理論的には同じメソッドで 2 つのインターフェイスを実装できますが、これを行うにはどうすればよいでしょうか? 同じメソッドを使用する 2 つの異なるインターフェイスがあります。interface A {
default void foo() {
System.out.println("Foo A");
}
}
interface B {
default void foo() {
System.out.println("Foo B");
}
}
これら 2 つのインターフェイスを実装するクラスがあります。不確実性を避けてコードをコンパイルするには、foo()
クラス内のメソッドをオーバーライドする必要があります。クラス内のいずれかのインターフェイスのC
メソッドを呼び出すだけで済みます。または、クラス内のメソッドを呼び出すだけです。しかし、特定のインターフェイス メソッドを選択するにはどうすればよいでしょうか? これには次のような構造があります。 foo()
A
B
А
В
A.super.foo()
public class C implements A, B {
@Override
public void foo() {
A.super.foo();
}
}
または:
public class C implements A, B {
@Override
public void foo() {
B.super.foo();
}
}
したがって、foo()
クラスメソッドは、インターフェイスのC
デフォルト メソッドまたはインターフェイスのメソッドのいずれかを使用します。 foo()
A
foo()
B
16. 抽象メソッドと抽象クラスとは何ですか?
Java には、抽象クラスとメソッドを表すために使用される予約語がありますabstract
。まず、いくつかの定義があります。abstract
抽象メソッドは、抽象クラスのキーワードを実装せずに作成されるメソッドです。つまり、これはインターフェースのようなメソッドですが、次のようにキーワードを追加するだけです。
public abstract void foo();
abstract
抽象クラスは、次の単語 も含むクラスです。
public abstract class A {
}
抽象クラスにはいくつかの機能があります。
- オブジェクトをそれに基づいて作成することはできません。
- 抽象メソッドを持つことができます。
- 抽象メソッドを持たない場合もあります。
17. 文字列、文字列ビルダー、文字列バッファの違いは何ですか?
値はString
定数文字列プールに保存されます。行が作成されると、このプールに表示されます。そしてそれを削除することはできなくなります。例えば:
String name = "book";
...変数は文字列プールを参照します。 定数文字列プール 変数名を別の値に設定すると、次のようになります。
name = "pen";
定数文字列プール したがって、これら 2 つの値はそこに残ります。 文字列バッファ:
- 値は
String
スタックに保存されます。値が変更された場合、新しい値は古い値に置き換えられます。 String Buffer
同期されているためスレッドセーフです。- スレッドの安全性のため、動作速度にはまだ不十分な点が多くあります。
StringBuffer name = "book";
name の値が変更されるとすぐに、スタック上の値も変更されます。 StringBuilder とまったく同じですがStringBuffer
、スレッド セーフではない点が異なります。したがって、その速度は明らかに よりも高速ですStringBuffer
。
18. 抽象クラスとインターフェイスの違いは何ですか?
抽象クラス:- 抽象クラスにはデフォルトのコンストラクターがあります。この抽象クラスの子が作成されるたびに呼び出されます。
- 抽象メソッドと非抽象メソッドの両方が含まれています。一般に、抽象メソッドは含まれていない可能性がありますが、依然として抽象クラスです。
- 抽象クラスを継承するクラスは、抽象メソッドのみを実装する必要があります。
- 抽象クラスにはインスタンス変数を含めることができます (質問 #5 を参照)。
- コンストラクターがないため初期化できません。
- 抽象メソッドのみを追加する必要があります (デフォルトのメソッドはカウントしません)。
- インターフェイスを実装するクラスは、すべてのメソッドを実装する必要があります (デフォルトのメソッドは除きます)。
- インターフェイスには定数のみを含めることができます。
19. 配列内の要素へのアクセスに O(1) がかかるのはなぜですか?
この質問は文字通り前回のインタビューからのものです。後で知ったのですが、この質問はその人の考え方を知るために行われるそうです。この知識には実際的な意味がほとんどないことは明らかです。この事実を知っているだけで十分です。まず、O(1) は、演算が一定時間で行われる場合のアルゴリズムの時間計算量の指定であることを明確にする必要があります。つまり、この指定が最も高速に実行されます。この質問に答えるには、配列について何を知っているかを理解する必要があります。配列 を作成するにはint
、次のように記述する必要があります。
int[] intArray = new int[100];
この記録からいくつかの結論を導き出すことができます。
- 配列を作成するとき、その型がわかります。型がわかっていれば、配列の各セルのサイズが明らかです。
- 配列のサイズはわかっています。
ArrayList 内のオブジェクトにアクセスするときに O(1) を取得するにはどうすればよいですか?
この質問は前の質問の直後に続きます。確かに、配列を操作し、そこにプリミティブがある場合、その型の作成時にそのサイズがどのくらいであるかが事前にわかります。しかし、図のようなスキームがあり、 タイプ A の要素を含むコレクションを作成し、異なる実装 (B、C、D) を追加したい場合はどうなるでしょうか。List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
この状況では、各オブジェクトが異なり、追加フィールドが異なる (または完全に異なる) 可能性があるため、各セルのサイズをどのように理解すればよいでしょうか。何をするか?ここでは、混乱と混乱を招くような方法で質問が提起されています。実際、コレクションにはオブジェクトは格納されず、これらのオブジェクトへのリンクのみが格納されることがわかります。そして、すべてのリンクは同じサイズであることがわかります。したがって、ここでのスペースのカウントは、前の質問と同じように機能します。
21. オートボックス化とアンボックス化
歴史的背景:オートボックス化と自動アンボックス化は、JDK 5 の主な革新の 1 つです。 オートボックス化は、プリミティブ型から適切なラッパー クラスに自動的に変換するプロセスです。 自動アンボックス化- 自動ボックス化とまったく逆のことを行い、ラッパー クラスをプリミティブに変換します。ただし、ラッパー値がある場合はnull
、解凍中に例外がスローされます。NullPointerException
。
一致するプリミティブ - ラッパー
原生的 | ラッパークラス |
---|---|
ブール値 | ブール値 |
整数 | 整数 |
バイト | バイト |
チャー | キャラクター |
浮く | 浮く |
長さ | 長さ |
短い | 短い |
ダブル | ダブル |
自動パッキングが発生します:
-
プリミティブにラッパー クラスへの参照を割り当てる場合:
Java 5 より前:
//manual packaging or how it was BEFORE Java 5. public void boxingBeforeJava5() { Boolean booleanBox = new Boolean(true); Integer intBox = new Integer(3); // and so on to other types } после Java 5: //automatic packaging or how it became in Java 5. public void boxingJava5() { Boolean booleanBox = true; Integer intBox = 3; // and so on to other types }
-
ラッパーを期待するメソッドに引数としてプリミティブを渡す場合:
public void exampleOfAutoboxing() { long age = 3; setAge(age); } public void setAge(Long age) { this.age = age; }
自動解凍が行われます。
-
プリミティブ変数をラッパー クラスに代入すると、次のようになります。
//before Java 5: int intValue = new Integer(4).intValue(); double doubleValue = new Double(2.3).doubleValue(); char c = new Character((char) 3).charValue(); boolean b = Boolean.TRUE.booleanValue(); //and after JDK 5: int intValue = new Integer(4); double doubleValue = new Double(2.3); char c = new Character((char) 3); boolean b = Boolean.TRUE;
-
算術演算を伴う場合。これらはプリミティブ型にのみ適用されます。そのためには、プリミティブをアンボックス化する必要があります。
// Before Java 5 Integer integerBox1 = new Integer(1); Integer integerBox2 = new Integer(2); // for comparison it was necessary to do this: integerBox1.intValue() > integerBox2.intValue() //в Java 5 integerBox1 > integerBox2
-
対応するプリミティブを受け入れるメソッドのラッパーに渡される場合:
public void exampleOfAutoboxing() { Long age = new Long(3); setAge(age); } public void setAge(long age) { this.age = age; }
22. 最後のキーワードとその使用場所は何ですか?
キーワードはfinal
変数、メソッド、クラスに使用できます。
- Final 変数を別のオブジェクトに再代入することはできません。
- 最終クラスは不毛です)) 継承者を持つことはできません。
- Final メソッドを祖先でオーバーライドすることはできません。
最終変数
;Java では、変数を作成してそれに値を割り当てる 2 つの方法があります。- 変数を宣言して、後で初期化することができます。
- 変数を宣言して、すぐに割り当てることができます。
public class FinalExample {
//final static variable, which is immediately initialized:
final static String FINAL_EXAMPLE_NAME = "I'm likely final one";
//final is a variable that is not initialized, but will only work if
//initialize this in the constructor:
final long creationTime;
public FinalExample() {
this.creationTime = System.currentTimeMillis();
}
public static void main(String[] args) {
FinalExample finalExample = new FinalExample();
System.out.println(finalExample.creationTime);
// final field FinalExample.FINAL_EXAMPLE_NAME cannot be assigned
// FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";
// final field Config.creationTime cannot be assigned
// finalExample.creationTime = 1L;
}
}
Final 変数は定数と見なすことができますか?
最終変数に新しい値を割り当てることはできないため、これらは定数変数であるように見えます。しかし、これは一見しただけです。変数が参照するデータ型が の場合immutable
、はい、それは定数です。ただし、データ型mutable
が可変の場合は、メソッドと変数を使用して、変数が参照するオブジェクトの値を変更することができますfinal
。この場合、それを定数と呼ぶことはできません。したがって、この例では、最終変数の一部は実際には定数ですが、一部は定数ではなく、変更できることを示しています。
public class FinalExample {
//immutable final variables:
final static String FINAL_EXAMPLE_NAME = "I'm likely final one";
final static Integer FINAL_EXAMPLE_COUNT = 10;
// mutable filter variables
final List<String> addresses = new ArrayList();
final StringBuilder finalStringBuilder = new StringBuilder("constant?");
}
ローカル最終変数
final
メソッド内で変数が作成されると、それはlocal final
変数と呼ばれます。
public class FinalExample {
public static void main(String[] args) {
// This is how you can
final int minAgeForDriveCar = 18;
// or you can do it this way, in the foreach loop:
for (final String arg : args) {
System.out.println(arg);
}
}
}
ループの反復が完了すると、毎回新しい変数が作成されるため、final
拡張ループで キーワードを使用できます。ただし、これは通常の for ループには当てはまらないため、以下のコードはコンパイル時エラーをスローします。 for
for
// final local changed j cannot be assigned
for (final int i = 0; i < args.length; i ++) {
System.out.println(args[i]);
}
最終クラス
として宣言されたクラスを拡張することはできませんfinal
。簡単に言えば、このクラスから継承できるクラスはありません。final
JDK のクラスの好例はですString
。不変クラスを作成する最初のステップは、そのクラスを としてマークしてfinal
、拡張できないようにすることです。
public final class FinalExample {
}
// Compilation error here
class WantsToInheritFinalClass extends FinalExample {
}
最終的なメソッド
メソッドが「final」とマークされている場合、それは「final メソッド」と呼ばれます (論理的ですよね?)。Final メソッドは子孫クラスでオーバーライドできません。ちなみに、Object クラスのメソッド wait() と Notice() は最終的なものであるため、オーバーライドする機会はありません。public class FinalExample {
public final String generateAddress() {
return "Some address";
}
}
class ChildOfFinalExample extends FinalExample {
// compile error here
@Override
public String generateAddress() {
return "My OWN Address";
}
}
Javaでfinalを使用する方法と場所
- いくつかのクラスレベルの定数を定義するには、final キーワードを使用します。
- オブジェクトを変更したくない場合は、オブジェクトの最終変数を作成します。たとえば、ログ記録の目的で使用できるオブジェクト固有のプロパティ。
- クラスを拡張したくない場合は、それをfinalとしてマークします。
- immutable< クラスを作成する必要がある場合は、それを Final にする必要があります。
- メソッドの実装がその子孫で変更されないようにしたい場合は、メソッドを として指定します
final
。これは、実装が変更されないようにするために非常に重要です。
23. 可変不変とは何ですか?
可変
可変とは、作成後に状態や変数を変更できるオブジェクトです。たとえば、StringBuilder、StringBuffer などのクラスです。例:public class MutableExample {
private String address;
public MutableExample(String address) {
this.address = address;
}
public String getAddress() {
return address;
}
// this setter can change the name field
public void setAddress(String address) {
this.address = address;
}
public static void main(String[] args) {
MutableExample obj = new MutableExample("first address");
System.out.println(obj.getAddress());
// update the name field, so this is a mutable object
obj.setAddress("Updated address");
System.out.println(obj.getAddress());
}
}
不変
不変とは、オブジェクトの作成後に状態や変数を変更できないオブジェクトです。HashMap にとって優れたキーではないのはなぜですか?) たとえば、String、Integer、Double などです。例:// make this class final so no one can change it
public final class ImmutableExample {
private String address;
ImmutableExample (String address) {
this.address = address;
}
public String getAddress() {
return address;
}
//remove the setter
public static void main(String[] args) {
ImmutableExample obj = new ImmutableExample("old address");
System.out.println(obj.getAddress());
// Therefore, do not change this field in any way, so this is an immutable object
// obj.setName("new address");
// System.out.println(obj.getName());
}
}
24. 不変クラスの書き方は?
可変オブジェクトと不変オブジェクトが何であるかを理解したら、次の疑問は自然です - それをどのように書くか? 不変の不変クラスを作成するには、次の簡単な手順に従う必要があります。- クラスを最終的にします。
- すべてのフィールドをプライベートにして、それらのゲッターのみを作成します。もちろん、セッターは必要ありません。
- すべての可変フィールドを Final にして、値を 1 回だけ設定できるようにします。
- コンストラクターを介してすべてのフィールドを初期化し、ディープ コピーを実行します (つまり、オブジェクト自体、その変数、変数の変数などをコピーします)。
- ゲッターで可変変数オブジェクトのクローンを作成し、実際のオブジェクトへの参照ではなく、値のコピーのみを返します。
/**
* An example of creating an immutable object.
*/
public final class FinalClassExample {
private final int age;
private final String name;
private final HashMap<String, String> addresses;
public int getAge() {
return age;
}
public String getName() {
return name;
}
/**
* Clone the object before returning it.
*/
public HashMap<String, String> getAddresses() {
return (HashMap<String, String>) addresses.clone();
}
/**
* In the constructor, deep copy the mutable objects.
*/
public FinalClassExample(int age, String name, HashMap<String, String> addresses) {
System.out.println("Performing a deep copy in the constructor");
this.age = age;
this.name = name;
HashMap<String, String> temporaryMap = new HashMap<>();
String key;
Iterator<String> iterator = addresses.keySet().iterator();
while (iterator.hasNext()) {
key = iterator.next();
temporaryMap.put(key, addresses.get(key));
}
this.addresses = temporaryMap;
}
}
GO TO FULL VERSION