ソフトウェアエンジニアの皆様、こんにちは!面接の質問について話しましょう。準備すべきこと、知っておくべきことについて。これは、これらの点を初めて確認または検討するのに最適な時期です。 最終的には、OOP、Java 構文、Java 例外、コレクション、マルチスレッドに関するよくある質問を集めた広範なコレクションができました。便宜上、いくつかの部分に分割します。一度にすべてをカバーすることは困難ですが、この資料がプログラマーとしての最初の仕事を見つける準備をしている人にとって良い基礎となることを願っています。最大限の理解と記憶を維持するために、他のソースもくまなく調べることをお勧めします。さまざまな角度からアプローチすることで、コンセプトをより深く理解することができます。 重要:ここでは、バージョン 8 より前の Java についてのみ説明します。バージョン 9、10、11、12、および 13 に導入されたすべての革新については、ここでは考慮しません。回答を改善する方法に関するアイデアやコメントは大歓迎です。読書をお楽しみください。さあ行こう!
Java インタビュー: OOP に関する質問
1. Java の特徴は何ですか?
答え:-
OOP の概念:
- オブジェクト指向
- 継承
- カプセル化
- 多態性
- 抽象化
-
クロスプラットフォーム: Java プログラムは、変更を加えることなく、どのプラットフォームでも実行できます。もちろん、これには JVM (Java 仮想マシン) がインストールされている必要があります。
-
高性能:ジャストインタイム (JIT) コンパイラーにより、高いパフォーマンスが可能になります。JIT コンパイラーがバイトコードをマシンコードに変換し、JVM が実行を開始します。
- マルチスレッド: JVM は、 と呼ばれる実行スレッドを作成します
main thread
。プログラマは、Thread クラスから派生するか、Runnable
インターフェイスを実装することによって、複数のスレッドを作成できます。
2. 継承とは何ですか?
継承とは、あるクラスが別のクラスを継承できることを意味します ( extendsキーワードを使用)。これは、継承したクラスのコードを再利用できることを意味します。既存のクラスは と呼ばれsuperclass
、新しく作成されたクラスは と呼ばれますsubclass
。「parent」と「 」という用語を使用するという人もいますchild
。
public class Animal {
private int age;
}
public class Dog extends Animal {
}
は、はAnimal
です。 parent
Dog
child
3. カプセル化とは何ですか?
この質問は、Java 開発者の職の面接でよく聞かれます。カプセル化では、アクセス修飾子、ゲッター、セッターを使用して実装を隠します。これは、開発者が必要と考える外部アクセスを防ぐために行われます。現実の簡単な例としては車が挙げられます。私たちはエンジンの動作に直接アクセスすることはできません。私たちがしなければならないのは、キーをイグニッションに差し込み、エンジンをオンにすることだけです。内部で行われるプロセスは私たちには関係ありません。また、エンジンの作動を妨げると予期せぬ事態を引き起こし、車両の損傷や人身傷害につながる可能性があります。まったく同じことがプログラミングでも起こります。これはウィキペディアに詳しく説明されています。CodeGymにはカプセル化に関する記事もあります。4. ポリモーフィズムとは何ですか?
ポリモーフィズムとは、オブジェクトの特定の型に関する情報がなくても、同じインターフェイスを持つオブジェクトを同じ方法で処理するプログラムの機能です。よく言われるように、「1 つのインターフェイスで多数の実装」です。ポリモーフィズムを使用すると、共有動作に基づいてさまざまなタイプのオブジェクトを組み合わせて使用できます。たとえば、Dog と Cat という 2 つの子孫を持つ Animal クラスがあるとします。一般的な 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 開発者にとってよくある面接の質問です。ローカル変数はメソッド内で定義され、メソッドが実行されている限り存在する変数です。実行が終了すると、ローカル変数は存在しなくなります。以下は、main() メソッドで helloMessage という名前のローカル変数を使用するプログラムです。
public static void main(String[] args) {
String helloMessage;
helloMessage = "Hello, World!";
System.out.println(helloMessage);
}
8. インスタンス変数とは何ですか?
インスタンス変数は、クラス内で宣言される変数です。オブジェクトが存在する限り、それは存在します。たとえば、NectarLoad と maxNectarLoad という 2 つのインスタンス変数を持つ Bee クラスがあります。
public class Bee {
/**
* Current nectar load
*/
private double nectarLoad;
/**
* Maximum nectar that can the bee can collect.
*/
private double maxNectarLoad = 20.0;
...
}
9. アクセス修飾子とは何ですか?
アクセス修飾子は、クラス、メソッド、変数へのアクセスをカスタマイズするためのメカニズムです。次の修飾子が存在します。アクセスが増加した順にリストされています。private
— このアクセス修飾子はメソッド、フィールド、コンストラクターで使用されます。アクセスは、それらが宣言されているクラスに制限されます。package-private (default)
— これはクラスのデフォルトのアクセス レベルです。アクセスは、クラス、メソッド、変数、またはコンストラクターが宣言されている特定のパッケージに制限されます。protected
package-private
— このアクセス修飾子は、修飾子を使用してクラスを継承するクラスへのアクセスの追加と同じアクセス レベルを提供しますprotected
。public
— このアクセス レベルはクラスにも使用されます。このアクセス レベルは、アプリケーション全体に完全なアクセス権があることを意味します。
10. メソッドのオーバーライドとは何ですか?
子クラスが親クラスの動作を変更したい場合、メソッドをオーバーライドします。親メソッドの内容も実行する必要がある場合は、子メソッドで super.methodName() を使用できます。これにより、親メソッドが実行されます。その後、追加のロジックを追加できます。遵守しなければならない要件:- メソッドのシグネチャは同じである必要があります
- 戻り値は同じである必要があります
11. メソッド シグネチャとは何ですか?
メソッド シグネチャは、メソッド名とメソッドが受け取る引数の組み合わせです。メソッド シグネチャは、メソッドをオーバーロードするときのメソッドの一意の識別子です。12. メソッドのオーバーロードとは何ですか?
メソッドのオーバーロードは、メソッドのシグネチャを変更して同じアクションを実行する複数のメソッドを作成するポリモーフィズムの機能です。- 同じ名前
- 異なる議論
- さまざまな戻り値の型が存在する可能性があります
ArrayList
クラスのadd()
メソッドをオーバーロードして、入力引数に応じてさまざまな方法で追加できるようにすることができます。
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 です
- クラスはインターフェイスを継承しません (つまり、extends キーワードを使用しません)。代わりに、クラスがそれらを実装します (つまり、implements キーワードを使用します)。さらに、必要なだけインターフェイスを実装できます。
- インターフェイスを実装するクラスは、インターフェイス内のすべてのメソッドの実装を提供する必要があります。
public class Cat implements Animal {
public void makeSound() {
// Method implementation
}
public void eat() {
// Implementation
}
public void sleep() {
// Implementation
}
}
14. インターフェースのデフォルトのメソッドとは何ですか?
次に、デフォルトのメソッドについて説明します。それらは何のため?誰のためのものですか?これらのメソッドは「両手」に対応するために追加されました。私は何を話しているのでしょうか?そうですね、一方では、ラムダと Stream API という新しい機能を追加する必要がありました。その一方で、Java の特徴である下位互換性を維持する必要がありました。これを行うには、インターフェースにいくつかの新しい既製のソリューションが必要でした。これが、デフォルトのメソッドが私たちに与えられた経緯です。デフォルト メソッドは、キーワードでマークされたインターフェイスに実装されたメソッドですdefault
。たとえば、インターフェイスstream()
でよく知られているメソッドCollection
。信じてください、このインターフェースは見た目ほど単純ではありません。または、同様に有名なforEach()
方法でも、Iterable
インターフェース。また、デフォルトのメソッドが追加されるまでは存在しませんでした。ちなみに、これについては CodeGym のこちらで読むこともできます。
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 つのインターフェイスを実装するクラスがあります。しかし、インターフェイス A または B で特定のメソッドを選択するにはどうすればよいでしょうか? 次の特別な構造によりこれが可能になりますA.super.foo()
。
public class C implements A, B {
public void fooA() {
A.super.foo();
}
public void fooB() {
B.super.foo();
}
}
したがって、メソッドはインターフェイスのfooA()
デフォルトのメソッドを使用しますが、メソッドはインターフェイスのメソッドを使用します。 foo()
A
fooB()
foo()
B
16. 抽象メソッドと抽象クラスとは何ですか?
Java では、abstract
は予約語です。抽象クラスとメソッドを表すために使用されます。まず、定義が必要です。abstract
抽象メソッドは、抽象クラスに実装せずにキーワードを使用して宣言されるメソッドです。つまり、これはインターフェイスと同様のメソッドですが、次のようなキーワードが追加されています。
public abstract void foo();
抽象クラスは、次のキーワードでもマークされたクラスですabstract
。
public abstract class A {
}
抽象クラスにはいくつかの機能があります。
- 抽象クラスのオブジェクトを作成することはできません
- 抽象メソッドを含めることができます
- 抽象メソッドがない場合もあります
17. String、StringBuilder、StringBuffer の違いは何ですか?
String
値は定数文字列プールに保存されます。文字列が作成されるとすぐに、このプールに表示されます。そしてそれを削除することはできません。例えば:
String name = "book";
変数は定数文字列プールを指します。 name 変数を別の値に設定すると、次のようになります。
name = "pen";
定数文字列プールは次のようになります。 つまり、両方の値がそこに残ります。 文字列バッファ:
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 の主要な革新の一部です。 オートボックス化は、プリミティブ型から対応するラッパー クラスに自動的に変換するプロセスです。 アンボックス化はオートボックス化の正反対です。これは、ラッパー クラスをプリミティブに変換するプロセスです。ただし、ラッパーの値が の場合null
、NullPointerException
ボックス化解除中に がスローされます。
プリミティブとそれに対応するラッパー
原生的 | ラッパークラス |
---|---|
ブール値 | ブール値 |
整数 | 整数 |
バイト | バイト |
文字 | キャラクター |
浮く | 浮く |
長さ | 長さ |
短い | 短い |
ダブル | ダブル |
// オートボクシングが発生します:
-
ラッパークラスへの参照にプリミティブを割り当てる場合:
Java 5 より前:
// Manual boxing (the way it was BEFORE Java 5). public void boxingBeforeJava5() { Boolean booleanBox = new Boolean(true); Integer intBox = new Integer(3); // And so on for other types } After Java 5: // Automatic boxing (the way it became in Java 5). public void boxingJava5() { Boolean booleanBox = true; Integer intBox = 3; // And so on for 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); // A comparison used to require this: integerBox1.intValue() > integerBox2.intValue() // In Java 5 integerBox1 > integerBox2
-
ラッパー クラスのインスタンスを、対応するプリミティブを受け取るメソッドに渡す場合:
public void exampleOfAutoboxing() { Long age = new Long(3); setAge(age); } public void setAge(long age) { this.age = age; }
22. 最後のキーワードは何ですか?またどこで使用されますか?
キーワードfinal
は変数、メソッド、クラスで使用できます。
- 最終変数の値は、初期化後に変更することはできません。
- 最後のクラスは無菌です:) 子を持つことはできません。
- 最終メソッドは子孫によってオーバーライドできません。
最終変数
Java では、変数を宣言して値を割り当てる方法が 2 つあります。- 変数を宣言して、後で初期化することができます。
- 変数を宣言して、すぐに値を割り当てることができます。
public class FinalExample {
// A static final variable that is immediately initialized:
final static String FINAL_EXAMPLE_NAME = "I'm likely the final one";
// A final variable that is not initialized, but will only work if you
// initialize it 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);
// The final FinalExample.FINAL_EXAMPLE_NAME field cannot be accessed
// FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";
// The final Config.creationTime field cannot be accessed
// finalExample.creationTime = 1L;
}
}
最終変数は定数とみなせるでしょうか?
最終変数には新しい値を割り当てることができないため、これらは定数変数であるように見えます。ただし、これは一見しただけです。変数のデータ型が の場合immutable
、はい、それは定数です。ただし、データ型がmutable
、つまり変更可能な場合は、メソッドと変数を使用して、変数によって参照されるオブジェクトの値を変更することができますfinal
。このため、定数とは言えません。次の例は、一部の最終変数は実際に定数であるが、その他は変更可能であるため定数ではないことを示しています。
public class FinalExample {
// Immutable final variables
final static String FINAL_EXAMPLE_NAME = "I'm likely the final one";
final static Integer FINAL_EXAMPLE_COUNT = 10;
// Mutable final 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) {
// You can do this
final int minAgeForDriveCar = 18;
// Or you can do this, in a for-each loop:
for (final String arg : args) {
System.out.println(arg);
}
}
}
ループの各反復後に新しい変数が作成されるため、拡張された for ループで Final キーワードを使用できます。これは通常の for ループには適用されないため、コンパイル時にエラーが発生することに注意してください。
// The final local j variable 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!
class WantsToInheritFinalClass extends FinalExample {
}
最終的なメソッド
メソッドが「final」とマークされている場合、そのメソッドは「final メソッド」と呼ばれます (当然ですよね?)。Final メソッドを子クラスでオーバーライドすることはできません。ちなみに、Object クラスの wait() メソッドと Notify() メソッドは最終的なものであるため、これらをオーバーライドすることはできません。
public class FinalExample {
public final String generateAddress() {
return "Some address";
}
}
class ChildOfFinalExample extends FinalExample {
// Compilation error!
@Override
public String generateAddress() {
return "My OWN Address";
}
}
Javaでfinalを使用する方法と場所
- いくつかのクラスレベルの定数を定義するには、final キーワードを使用します。
- 変更したくないオブジェクトの最終変数を作成します。たとえば、ロギング目的で使用できるオブジェクト固有のプロパティなどです。
- クラスを拡張したくない場合は、そのクラスを最終としてマークします。
- 不変クラスを作成する必要がある場合は、それを 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());
// We are updating the name field, so this is a mutable object
obj.setAddress("Updated address");
System.out.println(obj.getAddress());
}
}
不変
不変オブジェクトとは、オブジェクトの作成後に状態や変数を変更できないオブジェクトです。HashMap にとって素晴らしいキーだと思いませんか? :) たとえば、文字列、整数、倍精度浮動小数点数などです。例:
// We'll 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;
}
// We remove the setter
public static void main(String[] args) {
ImmutableExample obj = new ImmutableExample("Old address");
System.out.println(obj.getAddress());
// There is no way to change this field, so it is an immutable object
// obj.setName("new address");
// System.out.println(obj.getName());
}
}
次のパートでは、コレクションに関する質問と回答について考えます。 GitHub の私のプロフィール Java Core に関する就職面接の質問と回答トップ 50。パート2
GO TO FULL VERSION