JavaRush /Java-Blog /Random-DE /Analyse von Fragen und Antworten aus Interviews für Java-...

Analyse von Fragen und Antworten aus Interviews für Java-Entwickler. Teil 14

Veröffentlicht in der Gruppe Random-DE
Feuerwerk! Die Welt ist ständig in Bewegung und wir sind ständig in Bewegung. Um ein Java-Entwickler zu werden, reichte es bisher aus, ein wenig Java-Syntax zu kennen, und der Rest würde kommen. Im Laufe der Zeit ist der Wissensstand, der erforderlich ist, um Java-Entwickler zu werden, erheblich gestiegen, ebenso wie der Wettbewerb, der die untere Grenze der erforderlichen Kenntnisse immer weiter nach oben drückt. Wenn Sie wirklich Entwickler werden möchten, müssen Sie dies als selbstverständlich betrachten und sich gründlich vorbereiten, um sich von den Anfängern wie Ihnen abzuheben. Was wir heute tun werden, nämlich die Analyse der über 250Analyse von Fragen und Antworten aus Interviews für Java-Entwickler.  Teil 14 - 1 Fragen fortzusetzen . In früheren Artikeln haben wir alle Fragen der Junior-Ebene untersucht, und heute werden wir uns mit Fragen der Mittelebene befassen. Obwohl ich anmerke, dass es sich nicht zu 100 % um Fragen der mittleren Ebene handelt, können Sie die meisten davon bei einem Vorstellungsgespräch auf der unteren Ebene treffen, da bei solchen Vorstellungsgesprächen eine detaillierte Prüfung Ihrer theoretischen Grundlagen stattfindet, während dies bei einem mittleren Schüler der Fall ist Die Fragen konzentrieren sich mehr darauf, seine Erfahrungen zu untersuchen. Aber ohne weitere Umschweife, fangen wir an. Analyse von Fragen und Antworten aus Interviews für Java-Entwickler.  Teil 14 - 2

Mitte

Sind üblich

1. Welche Vor- und Nachteile hat OOP im Vergleich zur prozeduralen/funktionalen Programmierung?

Diese Frage gab es in der Analyse der Fragen an Juinior, und dementsprechend habe ich sie bereits beantwortet. Suchen Sie nach dieser Frage und ihrer Antwort in diesem Teil des Artikels, Fragen 16 und 17.

2. Wie unterscheidet sich die Aggregation von der Zusammensetzung?

In OOP gibt es mehrere Arten der Interaktion zwischen Objekten, die unter dem allgemeinen Konzept der „Has-A-Beziehung“ zusammengefasst sind. Diese Beziehung zeigt an, dass ein Objekt eine Komponente eines anderen Objekts ist. Gleichzeitig gibt es zwei Untertypen dieser Beziehung: Zusammensetzung – ein Objekt erstellt ein anderes Objekt und die Lebensdauer eines anderen Objekts hängt von der Lebensdauer des Erstellers ab. Aggregation – ein Objekt erhält während des Erstellungsprozesses einen Link (Zeiger) zu einem anderen Objekt (in diesem Fall hängt die Lebensdauer des anderen Objekts nicht von der Lebensdauer des Erstellers ab). Schauen wir uns zum besseren Verständnis ein konkretes Beispiel an. Wir haben eine bestimmte Fahrzeugklasse – Car , die wiederum interne Felder vom Typ – Engine und eine Liste von Passagieren – List<Passenger> , sowie eine Methode zum Starten der Bewegung – 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());
   }
 }
}
In diesem Fall ist die Komposition die Verbindung zwischen Car und Engine , da die Leistung des Autos direkt von der Anwesenheit des Engine-Objekts abhängt, denn wenn engine = null , erhalten wir eine NullPointerException . Eine Engine wiederum kann nicht ohne Maschine existieren (warum brauchen wir eine Engine ohne Maschine?) und kann nicht gleichzeitig zu mehreren Maschinen gehören. Das heißt, wenn wir das Car- Objekt löschen, gibt es keine Verweise mehr auf das Engine- Objekt und es wird bald vom Garbage Collector gelöscht . Wie Sie sehen, ist diese Beziehung sehr streng (stark). Aggregation ist die Verbindung zwischen Car und Passenger , da die Leistung von Car in keiner Weise von Objekten des Typs Passenger und deren Anzahl abhängt . Sie können entweder das Auto verlassen – removePassengerByIndex(Long index) oder neue eingeben – addPassenger(Passengerpassagier) , trotzdem funktioniert das Auto weiterhin ordnungsgemäß. Im Gegenzug können Passenger- Objekte ohne ein Car- Objekt existieren . Wie Sie wissen, ist dies eine viel schwächere Verbindung, als wir in der Komposition sehen. Analyse von Fragen und Antworten aus Interviews für Java-Entwickler.  Teil 14 - 3Aber das ist noch nicht alles: Ein Objekt, das durch Aggregation mit einem anderen verbunden ist, kann zum gleichen Zeitpunkt auch eine bestimmte Verbindung mit anderen Objekten haben. Beispielsweise sind Sie als Java-Student zum gleichen Zeitpunkt in Englisch-, OOP- und Logarithmenkursen eingeschrieben, sind aber gleichzeitig kein unbedingt notwendiger Teil davon, ohne den ein normales Funktionieren unmöglich ist (z. B ein Lehrer).

3. Welche GoF-Muster haben Sie in der Praxis verwendet? Nenne Beispiele.

Ich habe diese Frage bereits zuvor beantwortet, daher hinterlasse ich einfach einen Link zur Analyse , siehe erste Frage. Ich habe auch einen wunderbaren Spickzettel-Artikel zu Designmustern gefunden, den ich unbedingt zur Hand haben sollte.

4. Was ist ein Proxy-Objekt? Nenne Beispiele

Ein Proxy ist ein strukturelles Entwurfsmuster, das es Ihnen ermöglicht, anstelle realer Objekte spezielle Ersatzobjekte, also Proxy-Objekte, zu ersetzen. Diese Proxy-Objekte fangen Aufrufe an das Originalobjekt ab, sodass eine gewisse Logik eingefügt werden kann, bevor oder nachdem der Aufruf an das Original übergeben wird. Analyse von Fragen und Antworten aus Interviews für Java-Entwickler.  Teil 14 - 4Beispiele für die Verwendung eines Proxy-Objekts:
  • Als Remote-Proxy – wird verwendet, wenn wir ein Remote-Objekt (ein Objekt in einem anderen Adressraum) benötigen, das lokal dargestellt werden muss. In diesem Fall übernimmt der Proxy die Verbindungserstellung, Kodierung, Dekodierung usw., während der Client ihn so verwendet, als wäre er das ursprüngliche Objekt im lokalen Raum.

  • Als virtueller Proxy – wird verwendet, wenn ein ressourcenintensives Objekt benötigt wird. In diesem Fall dient das Proxy-Objekt als so etwas wie ein Abbild eines realen Objekts, das tatsächlich noch nicht existiert. Erst wenn eine echte Anfrage (Methodenaufruf) an dieses Objekt gesendet wird, wird das ursprüngliche Objekt geladen und die Methode ausgeführt. Dieser Ansatz wird auch als verzögerte Initialisierung bezeichnet. Dies kann sehr praktisch sein, da in manchen Situationen das ursprüngliche Objekt möglicherweise nicht nützlich ist und dann keine Kosten für die Erstellung anfallen.

  • Als Sicherheits-Proxy – wird verwendet, wenn Sie den Zugriff auf ein Objekt basierend auf Client-Rechten steuern müssen. Das heißt, wenn ein Client mit fehlenden Zugriffsrechten versucht, auf das Originalobjekt zuzugreifen, wird der Proxy dies abfangen und nicht zulassen.

Schauen wir uns ein Beispiel eines virtuellen Proxys an: Wir haben eine Handler-Schnittstelle:
public interface Processor {
 void process();
}
Die Implementierung verbraucht zu viele Ressourcen, wird aber gleichzeitig möglicherweise nicht bei jedem Start der Anwendung verwendet:
public class HiperDifficultProcessor implements Processor {
 @Override
 public void process() {
   // некоторый сверхсложная обработка данных
 }
}
Proxy-Klasse:
public class HiperDifficultProcessorProxy implements Processor {
private HiperDifficultProcessor processor;

 @Override
 public void process() {
   if (processor == null) {
     processor = new HiperDifficultProcessor();
   }
   processor.process();
 }
}
Lassen Sie es uns in main ausführen :
Processor processor = new HiperDifficultProcessorProxy();
// тут тяжеловсеного оригинального ein Objektа, ещё не сущетсвует
// но при этом есть ein Objekt, который его представляет и у которого можно вызывать его методы
processor.process(); // лишь теперь, ein Objekt оригинал был создан
Ich stelle fest, dass viele Frameworks Proxying verwenden, und für Spring ist dies ein Schlüsselmuster (Spring ist innen und außen damit verbunden). Lesen Sie hier mehr über dieses Muster . Analyse von Fragen und Antworten aus Interviews für Java-Entwickler.  Teil 14 - 5

5. Welche Neuerungen wurden in Java 8 angekündigt?

Die Neuerungen, die Java 8 mit sich bringt, sind folgende:
  • Es wurden funktionale Schnittstellen hinzugefügt. Lesen Sie hier , was für ein Biest das ist .

  • Lambda-Ausdrücke, die eng mit funktionalen Schnittstellen verbunden sind, erfahren Sie hier mehr über ihre Verwendung .

  • Stream-API zur bequemen Verarbeitung von Datenerfassungen hinzugefügt . Weitere Informationen finden Sie hier .

  • Links zu Methoden hinzugefügt .

  • Die Methode forEach() wurde zur Iterable -Schnittstelle hinzugefügt .

  • Dem java.time- Paket wurde eine neue Datums- und Uhrzeit -API hinzugefügt . Eine detaillierte Analyse finden Sie hier .

  • Verbesserte gleichzeitige API .

  • Zum Hinzufügen einer optionalen Wrapper-Klasse , die zur korrekten Verarbeitung von Nullwerten verwendet wird, finden Sie hier einen hervorragenden Artikel zu diesem Thema .

  • Hinzufügen der Möglichkeit für Schnittstellen, statische und Standardmethoden zu verwenden (was Java im Wesentlichen näher an die Mehrfachvererbung bringt), weitere Details finden Sie hier .

  • Neue Methoden zur Klasse Collection(removeIf(), spliterator()) hinzugefügt .

  • Kleinere Verbesserungen an Java Core.

Analyse von Fragen und Antworten aus Interviews für Java-Entwickler.  Teil 14 - 6

6. Was sind hohe Kohäsion und niedrige Kopplung? Nenne Beispiele.

Hoher Zusammenhalt oder hohe Kohäsion ist das Konzept, wenn eine bestimmte Klasse Elemente enthält, die eng miteinander verbunden sind und für ihren Zweck kombiniert werden. Beispielsweise sollten alle Methoden in der User- Klasse das Benutzerverhalten darstellen. Eine Klasse weist eine geringe Kohäsion auf, wenn sie nicht verwandte Elemente enthält. Beispielsweise enthält die User- Klasse eine E-Mail-Adressvalidierungsmethode:
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() {
   // некоторая логика валидации емейла
 }
}
Die Benutzerklasse ist möglicherweise für die Speicherung der E-Mail-Adresse des Benutzers verantwortlich, jedoch nicht für deren Validierung oder den Versand der E-Mail. Um eine hohe Kohärenz zu erreichen, verschieben wir daher die Validierungsmethode in eine separate Utility-Klasse:
public class EmailUtil {
 public static boolean isValidEmail(String email) {
   // некоторая логика валидации емейла
 }
}
Und wir verwenden es nach Bedarf (zum Beispiel vor dem Speichern des Benutzers). Low Coupling oder Low Coupling ist ein Konzept, das eine geringe gegenseitige Abhängigkeit zwischen Softwaremodulen beschreibt. Interdependenz bedeutet im Wesentlichen, dass die Veränderung des einen die Veränderung des anderen erfordert. Zwei Klassen haben eine starke Kopplung (oder enge Kopplung), wenn sie eng miteinander verbunden sind. Zum Beispiel zwei konkrete Klassen, die Referenzen aufeinander speichern und die Methoden des jeweils anderen aufrufen. Lose gekoppelte Klassen sind einfacher zu entwickeln und zu warten. Da sie unabhängig voneinander sind, können sie parallel entwickelt und getestet werden. Darüber hinaus können sie geändert und aktualisiert werden, ohne dass sie sich gegenseitig beeinflussen. Schauen wir uns ein Beispiel für stark gekoppelte Klassen an. Wir haben einige Studentenklassen:
public class Student {
 private Long id;
 private String name;
 private List<Lesson> lesson;
}
Enthält eine Liste von Lektionen:
public class Lesson {
 private Long id;
 private String name;
 private List<Student> students;
}
Jede Lektion enthält einen Link zu den teilnehmenden Schülern. Unglaublich starker Halt, finden Sie nicht? Wie kann man es reduzieren? Stellen wir zunächst sicher, dass die Schüler keine Fächerliste, sondern eine Liste ihrer Kennungen haben:
public class Student {
 private Long id;
 private String name;
 private List<Long> lessonIds;
}
Zweitens muss die Unterrichtsklasse nicht über alle Schüler Bescheid wissen, also löschen wir ihre Liste ganz:
public class Lesson {
 private Long id;
 private String name;
}
Es wurde also viel einfacher und die Verbindung wurde viel schwächer, finden Sie nicht? Analyse von Fragen und Antworten aus Interviews für Java-Entwickler.  Teil 14 - 7

OOP

7. Wie kann man Mehrfachvererbung in Java implementieren?

Mehrfachvererbung ist ein Merkmal des objektorientierten Konzepts, bei dem eine Klasse Eigenschaften von mehr als einer übergeordneten Klasse erben kann. Das Problem entsteht, wenn sowohl in der Oberklasse als auch in der Unterklasse Methoden mit derselben Signatur vorhanden sind. Beim Aufruf einer Methode kann der Compiler nicht bestimmen, welche Klassenmethode aufgerufen werden soll, und selbst beim Aufruf der Klassenmethode hat diese Vorrang. Daher unterstützt Java keine Mehrfachvererbung! Aber es gibt eine Art Lücke, über die wir als nächstes sprechen werden. Wie ich bereits erwähnt habe, wurde mit der Veröffentlichung von Java 8 den Schnittstellen die Möglichkeit hinzugefügt, über Standardmethoden zu verfügen . Wenn die Klasse, die die Schnittstelle implementiert, diese Methode nicht überschreibt, wird diese Standardimplementierung verwendet (es ist nicht erforderlich, die Standardmethode zu überschreiben, z. B. durch die Implementierung einer abstrakten Methode). In diesem Fall ist es möglich, verschiedene Schnittstellen in einer Klasse zu implementieren und deren Standardmethoden zu verwenden. Schauen wir uns ein Beispiel an. Wir haben eine Flyer-Schnittstelle mit einer Standardmethode „fly()“ :
public interface Flyer {
 default void fly() {
   System.out.println("Я лечу!!!");
 }
}
Die Walker-Schnittstelle mit der Standardmethode walk() :
public interface Walker {
 default void walk() {
   System.out.println("Я хожу!!!");
 }
}
Die Swimmer-Schnittstelle mit der Methode swim() :
public interface Swimmer {
 default void swim() {
   System.out.println("Я плыву!!!");
 }
}
Nun, jetzt implementieren wir das alles in einer Entenklasse:
public class Duck implements Flyer, Swimmer, Walker {
}
Und lassen Sie uns alle Methoden unserer Ente ausführen:
Duck donald = new Duck();
donald.walk();
donald.fly();
donald.swim();
In der Konsole erhalten wir:
Ich gehe!!! Ich fliege!!! Ich schwimme!!!
Das bedeutet, dass wir die Mehrfachvererbung korrekt dargestellt haben, auch wenn es sich dabei nicht um eine solche handelt. Analyse von Fragen und Antworten aus Interviews für Java-Entwickler.  Teil 14 - 8Ich möchte außerdem anmerken, dass, wenn eine Klasse Schnittstellen mit Standardmethoden implementiert, die dieselben Methodennamen und dieselben Argumente in diesen Methoden haben, der Compiler anfangen wird, sich über Inkompatibilität zu beschweren, da er nicht versteht, welche Methode wirklich verwendet werden muss. Es gibt mehrere Auswege:
  • Benennen Sie Methoden in Schnittstellen um, damit sie sich voneinander unterscheiden.
  • Überschreiben Sie solche umstrittenen Methoden in der Implementierungsklasse.
  • Erben Sie von einer Klasse, die diese umstrittenen Methoden implementiert (dann verwendet Ihre Klasse genau deren Implementierung).

8. Was ist der Unterschied zwischen den Methoden „final“, „final“ und „finalize()“?

final ist ein Schlüsselwort, das verwendet wird, um eine Einschränkung für eine Klasse, Methode oder Variable festzulegen. Eine Einschränkung bedeutet:
  • Für eine Variable: Nach der Erstinitialisierung kann die Variable nicht neu definiert werden.
  • Bei einer Methode kann die Methode nicht in einer Unterklasse (Nachfolgeklasse) überschrieben werden.
  • Für eine Klasse: Die Klasse kann nicht vererbt werden.
„finally “ ist ein Schlüsselwort vor einem Codeblock, das bei der Behandlung von Ausnahmen in Verbindung mit einem „ try“ -Block und zusammen (oder austauschbar) mit einem „catch“-Block verwendet wird. Der Code in diesem Block wird auf jeden Fall ausgeführt, unabhängig davon, ob eine Ausnahme ausgelöst wird oder nicht. In diesem Teil des Artikels, in Frage 104, werden Ausnahmesituationen besprochen, in denen dieser Block nicht ausgeführt wird. finalize() ist eine Methode der Object- Klasse . Sie wird aufgerufen, bevor jedes Objekt vom Garbage Collector gelöscht wird. Diese Methode wird (zuletzt) ​​aufgerufen und zum Bereinigen belegter Ressourcen verwendet. Weitere Informationen zu den Methoden der Object- Klasse , die jedes Objekt erbt, finden Sie in Frage 11 in diesem Teil des Artikels. Nun, damit enden wir heute. Wir sehen uns im nächsten Teil! Analyse von Fragen und Antworten aus Interviews für Java-Entwickler.  Teil 14 - 9
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION