JavaRush /Java Blog /Random-JA /Java開発者へのインタビューからの質問と回答の分析。パート14
Константин
レベル 36

Java開発者へのインタビューからの質問と回答の分析。パート14

Random-JA グループに公開済み
花火!世界は常に動いており、私たちも常に動いています。以前は、Java 開発者になるには、Java 構文を少し知っていれば十分で、残りは後から学べました。時間の経過とともに、Java 開発者になるために必要な知識のレベルは大幅に増加し、競争も激化し、必要な知識の低いハードルが引き上げられ続けています。 Java開発者へのインタビューからの質問と回答の分析。 パート 14 - 1本当に開発者になりたいのであれば、それを当然のこととして考え、あなたのような初心者の中で目立つように徹底的に準備する必要があります。今日私たちが行うこと、つまり 250 以上の質問を分析し続けます。前回の記事では初級レベルの問題をすべて考察しましたが、今日は中級レベルの問題に挑戦します。これらは 100% 中級レベルの質問ではないことに注意しますが、そのほとんどは中級レベルの面接で解決できます。なぜなら、そのような面接では理論的基礎の詳細な調査が行われるからです。一方、中学生の場合は、質問は彼の経験を詳しく調べることに重点が置かれています。 Java開発者へのインタビューからの質問と回答の分析。 パート14-2しかし、これ以上苦労せずに始めましょう。

真ん中

共通しています

1. 手続き型/関数型プログラミングと比較した場合、OOP の長所と短所は何ですか?

Juinior の質問分析の中にこの質問がありましたので、すでに回答しました。この質問とその回答は、記事の この部分、質問 16 と 17で探してください。

2. 集約は合成とどう違うのですか?

OOP では、オブジェクト間の相互作用にはいくつかのタイプがあり、「Has-A Relationship」という一般概念に基づいて統合されています。この関係は、あるオブジェクトが別のオブジェクトのコンポーネントであることを示します。同時に、この関係には 2 つのサブタイプがあります。 合成- 1 つのオブジェクトが別のオブジェクトを作成し、別のオブジェクトの存続期間は作成者の存続期間に依存します。 集約- オブジェクトは構築プロセス中に別のオブジェクトへのリンク (ポインタ) を受け取ります (この場合、他のオブジェクトの存続期間は作成者の存続期間に依存しません)。理解を深めるために、具体的な例を見てみましょう。特定の車クラス - Carがあり、これにはタイプ - Engine乗客のリスト - List<Passenger>の内部フィールドがあり、移動を開始するためのメソッド - startMoving()もあります。
public class Car {

 private Engine engine;
 private List<Passenger> passengers;

 public Car(final List<Passenger> passengers) {
   this.engine = new Engine();
   this.passengers = passengers;
 }

 public void addPassenger(Passenger passenger) {
   passengers.add(passenger);
 }

 public void removePassengerByIndex(Long index) {
   passengers.remove(index);
 }

 public void startMoving() {
   engine.start();
   System.out.println("Машина начала своё движение");
   for (Passenger passenger : passengers) {
     System.out.println("В машине есть пассажир - " + passenger.getName());
   }
 }
}
この場合、CompositionはCarEngineの間の接続です。これは、車のパフォーマンスがエンジン オブジェクトの存在に直接依存するためです。Engine = nullの場合、 NullPointerExceptionを受け取ることになります。同様に、エンジンはマシンなしでは存在できません (なぜマシンなしではエンジンが必要なのでしょうか?) また、ある時点で複数のマシンに属することはできません。これは、 Carオブジェクトを削除すると、 Engineオブジェクトへの参照がなくなり、すぐにガベージ コレクターによって削除されることを意味します。ご覧のとおり、この関係は非常に厳密 (強い) です。 Carのパフォーマンスは、Passengerタイプのオブジェクトとその数にまったく依存しないため、集約はCarPassengerの間の接続です。車から離れることもできます ( removePassengerByIndex(Long Index))か、新しいものを入力する ( addPassenger(Passenger乗客))ことができますが、それにもかかわらず、車は正常に機能し続けます。また、PassengerオブジェクトはCarオブジェクトがなくても存在できます。ご理解のとおり、これは構図で見られるよりもはるかに弱い接続です。 しかし、それだけではありません。集約によって別のオブジェクトに接続されているオブジェクトは、同じ時点で他のオブジェクトと特定の接続を持つこともできます。たとえば、Java の学生であるあなたは、英語、OOP、および対数のコースに同時に登録していますが、同時に、それらのコースに非常に必要な部分ではなく、これらのコースがなければ通常の機能が不可能です (例:教師)。 Java開発者へのインタビューからの質問と回答の分析。 パート 14 - 3

3. 実際にどのような GoF パターンを使用しましたか? 例を上げてください。

この質問には以前に回答したことがありますので、分析へのリンクを残しておきます。最初の質問を参照してください。また、デザイン パターンに関する素晴らしいチートシートの記事も見つけたので、手元に置いておくことを強くお勧めします。

4. プロキシ オブジェクトとは何ですか? 例を上げてください

プロキシは、実際のオブジェクトの代わりに特別な代替オブジェクト、つまりプロキシ オブジェクトを置き換えることを可能にする構造設計パターンです。これらのプロキシ オブジェクトは、元のオブジェクトへの呼び出しをインターセプトし、呼び出しが元のオブジェクトに渡される または後にいくつかのロジックを挿入できるようにします。Java開発者へのインタビューからの質問と回答の分析。 パート 14 - 4プロキシ オブジェクトの使用例:
  • リモート プロキシとして - ローカルで表現する必要があるリモート オブジェクト (別のアドレス空間内のオブジェクト) が必要な場合に使用されます。この場合、プロキシは接続の作成、エンコード、デコードなどを処理し、クライアントはローカル空間にある元のオブジェクトであるかのようにプロキシを使用します。

  • 仮想プロキシとして - リソースを大量に消費するオブジェクトが必要な場合に使用されます。この場合、プロキシオブジェクトは、実際には存在しない実オブジェクトの画像のようなものとなる。実際のリクエスト (メソッド呼び出し) がこのオブジェクトに送信されると、そのとき初めて元のオブジェクトがロードされ、メソッドが実行されます。このアプローチは遅延初期化とも呼ばれます。状況によっては元のオブジェクトが役に立たない可能性があり、その場合はオブジェクトの作成にコストがかからないため、これは非常に便利です。

  • セキュリティ プロキシとして - クライアント権限に基づいてオブジェクトへのアクセスを制御する必要がある場合に使用されます。つまり、アクセス権を持たないクライアントが元のオブジェクトにアクセスしようとすると、プロキシがそれを傍受し、許可しません。

仮想プロキシの例を見てみましょう。いくつかのハンドラー インターフェイスがあります。
public interface Processor {
 void process();
}
この実装では大量のリソースが使用されますが、同時にアプリケーションが起動されるたびにリソースが使用されるわけではありません。
public class HiperDifficultProcessor implements Processor {
 @Override
 public void process() {
   // некоторый сверхсложная обработка данных
 }
}
プロキシクラス:
public class HiperDifficultProcessorProxy implements Processor {
private HiperDifficultProcessor processor;

 @Override
 public void process() {
   if (processor == null) {
     processor = new HiperDifficultProcessor();
   }
   processor.process();
 }
}
main で実行しましょう:
Processor processor = new HiperDifficultProcessorProxy();
// тут тяжеловсеного оригинального an object, ещё не сущетсвует
// но при этом есть an object, который его представляет и у которого можно вызывать его методы
processor.process(); // лишь теперь, an object оригинал был создан
多くのフレームワークがプロキシを使用していることに注意してください。Spring ではこれが重要なパターンです (Spring は内側と外側でプロキシをステッチします)。このパターンの詳細については、こちらをご覧ください。 Java開発者へのインタビューからの質問と回答の分析。 パート 14 - 5

5. Java 8 ではどのような革新が発表されましたか?

Java 8 によってもたらされた革新は次のとおりです。
  • 機能的なインターフェイスが追加されました。これがどのような種類のものであるかについては、こちらをご覧ください。

  • ラムダ式は関数インターフェイスと密接に関連していますが、その使用法について詳しくは、こちらをご覧ください。

  • データ収集を便利に処理するためにStream APIを追加しました。詳細については、こちらをご覧ください。

  • メソッドへのリンクを追加しました。

  • forEach()メソッドがIterableインターフェイスに追加されました。

  • java.timeパッケージに新しい日付と時刻のAPIが追加されました。詳細な分析はここにあります

  • 同時実行 APIの改善。

  • null 値を正しく処理するために使用されるオプションのラッパー クラスを追加する場合、このトピックに関する優れた記事がここにあります。

  • インターフェイスに静的メソッドとデフォルトメソッドを使用する機能を追加します(これにより、本質的に Java が多重継承に近づきます)。詳細は、こちら をご覧ください。

  • Collection(removeIf(),spliterator())クラスに新しいメソッドを追加しました。

  • Java コアのマイナーな改善。

Java開発者へのインタビューからの質問と回答の分析。 パート 14 - 6

6. 高凝集性と低結合性とは何ですか? 例を上げてください。

高い凝集性または高い凝集性は、特定のクラスに、互いに密接に関連し、目的に合わせて組み合わせられた要素が含まれている場合の概念です。たとえば、Userクラスのすべてのメソッドはユーザーの動作を表す必要があります。クラスに無関係な要素が含まれている場合、そのクラスの凝集性は低くなります。たとえば、Userクラスには電子メール アドレス検証メソッドが含まれています。
public class User {
private String name;
private String email;

 public String getName() {
   return this.name;
 }

 public void setName(final String name) {
   this.name = name;
 }

 public String getEmail() {
   return this.email;
 }

 public void setEmail(final String email) {
   this.email = email;
 }

 public boolean isValidEmail() {
   // некоторая логика валидации емейла
 }
}
ユーザー クラスは、ユーザーの電子メール アドレスの保存を担当する場合がありますが、その検証や電子メールの送信は担当しません。したがって、高い一貫性を実現するために、検証メソッドを別のユーティリティ クラスに移動します。
public class EmailUtil {
 public static boolean isValidEmail(String email) {
   // некоторая логика валидации емейла
 }
}
そして、必要に応じて (たとえば、ユーザーを保存する前に) 使用します。 低結合または低結合は、ソフトウェア モジュール間の低い相互依存性を説明する概念です。基本的に、相互依存とは、一方を変更するために他方を変更する必要があるということです。2 つのクラスが密接に関連している場合、それらは強い結合 (または密結合) になります。たとえば、相互への参照を保存し、互いのメソッドを呼び出す 2 つの具象クラスが考えられます。疎結合クラスは開発と保守が容易です。これらは互いに独立しているため、並行して開発およびテストできます。さらに、それらは相互に影響を与えることなく変更および更新できます。強く結合されたクラスの例を見てみましょう。いくつかの学生クラスがあります。
public class Student {
 private Long id;
 private String name;
 private List<Lesson> lesson;
}
これにはレッスンのリストが含まれています。
public class Lesson {
 private Long id;
 private String name;
 private List<Student> students;
}
各レッスンには、出席する生徒へのリンクが含まれています。信じられないほど強力なグリップだと思いませんか?どうすれば減らすことができますか?まず、学生が科目のリストではなく、識別子のリストを持っていることを確認しましょう。
public class Student {
 private Long id;
 private String name;
 private List<Long> lessonIds;
}
次に、レッスン クラスはすべての生徒について知る必要はないので、生徒のリストを完全に削除しましょう。
public class Lesson {
 private Long id;
 private String name;
}
それで、とても簡単になりました、そして、接続ははるかに弱くなったと思いませんか? Java開発者へのインタビューからの質問と回答の分析。 パート 14 - 7

OOP

7. Java で多重継承を実装するにはどうすればよいですか?

多重継承は、クラスが複数の親クラスからプロパティを継承できるオブジェクト指向の概念の機能です。問題は、スーパークラスとサブクラスの両方に同じシグネチャを持つメソッドが存在する場合に発生します。メソッドを呼び出すとき、コンパイラは、どのクラス メソッドを呼び出す必要があるかを判断できません。また、優先するクラス メソッドを呼び出すときも同様です。したがって、Java は多重継承をサポートしていません。しかし、ある種の抜け穴があります。それについては次に説明します。前に述べたように、Java 8 のリリースにより、デフォルトのメソッドを持つ機能がインターフェースに追加されました。インターフェースを実装するクラスがこのメソッドをオーバーライドしない場合は、このデフォルトの実装が使用されます (抽象メソッドを実装するなど、デフォルトのメソッドをオーバーライドする必要はありません)。この場合、1 つのクラスに異なるインターフェイスを実装し、それらのデフォルト メソッドを使用することができます。例を見てみましょう。デフォルトのfly()メソッドを備えたいくつかのフライヤー インターフェイスがあります。
public interface Flyer {
 default void fly() {
   System.out.println("Я лечу!!!");
 }
}
デフォルトのwalk()メソッド を備えた walker インターフェイス:
public interface Walker {
 default void walk() {
   System.out.println("Я хожу!!!");
 }
}
swim()メソッド を使用したスイマー インターフェイス:
public interface Swimmer {
 default void swim() {
   System.out.println("Я плыву!!!");
 }
}
さて、これらすべてを 1 つのアヒル クラスに実装しましょう。
public class Duck implements Flyer, Swimmer, Walker {
}
そして、アヒルのすべてのメソッドを実行してみましょう。
Duck donald = new Duck();
donald.walk();
donald.fly();
donald.swim();
コンソールでは次のものを受け取ります:
囲碁!!!私は飛んでいる!!!泳いでるよ!!!
これは、実際はそうではないにもかかわらず、多重継承を正しく描写したことを意味します。 Java開発者へのインタビューからの質問と回答の分析。 パート 14 - 8また、クラスが同じメソッド名と同じ引数を持つデフォルト メソッドを備えたインターフェイスを実装している場合、コンパイラは実際にどのメソッドを使用する必要があるかを理解していないため、非互換性について警告し始めることにも注意してください。いくつかの方法があります。
  • インターフェース内のメソッドの名前を変更して、互いに異なるようにします。
  • このような物議を醸すメソッドを実装クラスでオーバーライドします。
  • これらの物議を醸すメソッドを実装するクラスから継承します (クラスはその実装を正確に使用します)。

8. Final、finally、finalize() メソッドの違いは何ですか?

Finalは、クラス、メソッド、または変数に制約を設定するために使用されるキーワードであり、制約の意味は次のとおりです。
  • 変数の場合、初期初期化後は変数を再定義できません。
  • メソッドの場合、サブクラス (後継クラス) でメソッドをオーバーライドすることはできません。
  • クラスの場合 - クラスを継承することはできません。
finallyは、コード ブロックの前にあるキーワードで、例外を処理するときにtryブロックと組み合わせたり、catch ブロックと組み合わせて (または互換的に) 使用されます。このブロック内のコードは、例外がスローされるかどうかに関係なく、どのような場合でも実行されます。この記事のこの部分の質問 104では、このブロックが実行されない例外的な状況について説明します。 Finalize()はObjectクラスのメソッドで、各オブジェクトがガベージ コレクターによって削除される前に呼び出されます。このメソッドは (最後に) 呼び出され、占有されているリソースをクリーンアップするために使用されます。すべてのオブジェクトが継承するObjectクラスのメソッドの詳細については、この記事のこの部分の質問 11を参照してください。さて、今日はこれで終わります。次のパートでお会いしましょう! Java開発者へのインタビューからの質問と回答の分析。 パート 14 - 9
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION