Fragen zu Java-Klassenstruktur und -initialisierung

1. Einführung

Klassenstruktur und Initialisierung sind die Grundlagen, mit denen jeder Java-Programmierer vertraut sein sollte. Dieser Artikel enthält Antworten auf einige der Interviewfragen zu dem Thema, auf das Sie möglicherweise stoßen.

Q1. Beschreiben Sie die Bedeutung des Schlüsselworts final , wenn Sie auf eine Klasse, eine Methode, ein Feld oder eine lokale Variable angewendet werden.

Das final -Schlüsselwort hat mehrere unterschiedliche Bedeutungen, wenn es auf verschiedene Sprachkonstrukte angewendet wird:

  • Eine final -Klasse ist eine Klasse, die nicht untergeordnet werden kann

  • Eine final -Methode ist eine Methode, die in Unterklassen nicht überschrieben werden kann

  • Ein final -Feld ist ein Feld, das im Feld initialisiert werden muss

Konstruktor oder Initialisierungsblock und kann danach nicht mehr geändert werden ** Eine final -Variable ist eine Variable, die zugewiesen werden kann (und muss)

nur einmal vergeben und wird danach nie mehr geändert

Q2. Was ist eine default -Methode?

Vor Java 8 konnten Schnittstellen nur abstrakte Methoden haben, d. H.

Methoden ohne Körper. Beginnend mit Java 8 können Schnittstellenmethoden eine Standardimplementierung haben. Wenn eine implementierende Klasse diese Methode nicht überschreibt, wird die Standardimplementierung verwendet. Solche Methoden sind in geeigneter Weise mit einem default -Schlüsselwort gekennzeichnet.

Einer der wichtigsten Anwendungsfälle einer default -Methode ist das Hinzufügen einer Methode zu einer vorhandenen Schnittstelle. Wenn Sie diese Schnittstellenmethode nicht als default markieren, werden alle vorhandenen Implementierungen dieser Schnittstelle beschädigt. Durch das Hinzufügen einer Methode mit einer default -Implementierung wird die binäre Kompatibilität von älterem Code mit der neuen Version dieser Schnittstelle sichergestellt.

Ein gutes Beispiel dafür ist die Schnittstelle Iterator , mit der eine Klasse ein Ziel der for-each-Schleife sein kann. Diese Schnittstelle erschien zuerst in Java 5, erhielt jedoch in Java 8 zwei zusätzliche Methoden, forEach und spliterator . Sie sind als Standardmethoden bei Implementierungen definiert und beeinträchtigen daher nicht die Abwärtskompatibilität:

public interface Iterable<T> {

    Iterator<T> iterator();

    default void forEach(Consumer<? super T> action) {/**  ** /}

    default Spliterator<T> spliterator() {/**  ** /}
}

Q3. Was sind static -Klassenmitglieder?

Static -Felder und -Methoden einer Klasse sind nicht an eine bestimmte Instanz einer Klasse gebunden. Sie sind stattdessen an das Klassenobjekt selbst gebunden.

Der Aufruf einer static -Methode oder der Adressierung eines static -Feldes wird zur Kompilierzeit aufgelöst, da im Gegensatz zu den Methoden und Feldern der Instanz keine Referenz durchlaufen werden muss, um ein tatsächliches Objekt zu bestimmen, auf das wir uns beziehen.

Q4. Darf eine Klasse als abstrakt deklariert werden, wenn sie keine abstrakten Mitglieder hat? Was könnte der Zweck einer solchen Klasse sein?

Ja, eine Klasse kann als abstract deklariert werden, auch wenn sie keine abstract -Member enthält. Als abstrakte Klasse kann sie nicht instanziiert werden, sie kann jedoch als Stammobjekt einer Hierarchie dienen und stellt Methoden bereit, die für ihre Implementierungen nützlich sind.

Q5. Was ist Konstruktorketten?

Die Verkettung von Konstruktoren ist eine Möglichkeit, die Objektkonstruktion zu vereinfachen, indem mehrere Konstruktoren bereitgestellt werden, die sich nacheinander aufrufen.

Der spezifischste Konstruktor kann alle möglichen Argumente annehmen und für die detaillierteste Objektkonfiguration verwendet werden. Ein weniger spezifischer Konstruktor kann den spezifischeren Konstruktor aufrufen, indem er einige seiner Argumente mit Standardwerten bereitstellt. Ganz oben in der Kette könnte ein Konstruktor ohne Argumente ein Objekt mit Standardwerten instanziieren.

Hier ist ein Beispiel mit einer Klasse, die einen Rabatt in Prozenten modelliert, die innerhalb einer bestimmten Anzahl von Tagen verfügbar sind. Die Standardwerte von 10% und 2 Tagen werden verwendet, wenn wir sie bei Verwendung eines No-Arg-Konstruktors nicht angeben:

public class Discount {

    private int percent;

    private int days;

    public Discount() {
        this(10);
    }

    public Discount(int percent) {
        this(percent, 2);
    }

    public Discount(int percent, int days) {
        this.percent = percent;
        this.days = days;
    }

}

F6. Was ist das Überschreiben und Überladen von Methoden? Wie unterscheiden sie sich?

Das Überschreiben einer Methode wird in einer Unterklasse durchgeführt, wenn Sie eine Methode mit derselben Signatur wie in der Superklasse definieren. Dadurch kann die Laufzeitumgebung eine Methode auswählen, abhängig vom tatsächlichen Objekttyp, für den Sie die Methode aufrufen. Die Methoden toString , equals und hashCode werden in Unterklassen häufig überschrieben.

Das Überladen einer Methode findet in derselben Klasse statt. Überladen tritt auf, wenn Sie eine Methode mit demselben Namen, jedoch mit unterschiedlichen Typen oder mit unterschiedlichen Argumenten erstellen. Auf diese Weise können Sie einen bestimmten Code ausführen, je nachdem, welche Argumenttypen Sie angeben, während der Name der Methode gleich bleibt.

Hier ist ein Beispiel für die Überladung in der abstrakten Klasse java.io.Writer .

Die folgenden Methoden haben beide den Namen write , aber eine davon erhält ein int , während eine andere ein char -Array erhält.

public abstract class Writer {

    public void write(int c) throws IOException {
       //...
    }

    public void write(char cbuf[]) throws IOException {
       //...
    }

}

Q7. Können Sie eine static -Methode überschreiben?

Nein, das geht nicht. Per Definition können Sie eine Methode nur überschreiben, wenn ihre Implementierung zur Laufzeit durch den Typ der tatsächlichen Instanz bestimmt wird (ein Prozess, der als dynamische Methodensuche bezeichnet wird). Die Implementierung der static -Methode wird zur Kompilierzeit anhand des Typs der Referenz bestimmt, sodass das Überschreiben ohnehin nicht sinnvoll ist. Sie können zwar eine static -Methode mit derselben Signatur wie in der Superklasse zur Unterklasse hinzufügen, dies ist jedoch technisch nicht überschreibend.

F8. Was ist eine unveränderliche Klasse und wie können Sie eine erstellen?

Eine Instanz einer unveränderlichen Klasse kann nach ihrer Erstellung nicht mehr geändert werden.

Unter Ändern verstehen wir, den Zustand zu verändern, indem die Werte der Felder der Instanz geändert werden. Unveränderliche Klassen haben viele Vorteile: Sie sind Thread-sicher und es ist viel einfacher, über sie nachzudenken, wenn Sie keinen veränderlichen Zustand haben.

Um eine Klasse unveränderlich zu machen, sollten Sie Folgendes sicherstellen:

  • Alle Felder müssen als private und final deklariert werden. das führt dazu

Sie sollten im Konstruktor initialisiert und nicht geändert werden schon seit; ** Die Klasse sollte keine Setter oder andere Methoden haben, die den Wert verändern

Werte der Felder; ** Alle Felder der Klasse, die über den Konstruktor übergeben wurden, sollten entweder

Unveränderlich sein, oder ihre Werte sollten vor dem Feld kopiert werden Initialisierung (oder sonst könnten wir den Status dieser Klasse durch ändern An diesen Werten festhalten und ändern); ** Die Methoden der Klasse sollten nicht überschreibbar sein. entweder alle Methoden

sollte final sein oder der Konstruktor sollte private sein und nur über die static factory-Methode aufgerufen werden

Q9. Wie vergleichen Sie zwei enum -Werte: mit equals () oder mit == ?

Eigentlich können Sie beides verwenden. Die enum -Werte sind Objekte, daher können sie mit equals () verglichen werden, sie werden jedoch auch als static -Konstanten unter der Haube implementiert. Sie können sie also auch mit == vergleichen.

Dies ist hauptsächlich eine Frage des Code-Stils. Wenn Sie jedoch den Zeichenbereich sparen möchten (und möglicherweise einen nicht mehr benötigten Methodenaufruf überspringen möchten), sollten Sie die Enumerationen mit == vergleichen.

Q10. Was ist ein Initialisierungsblock? Was ist ein static -Initialisierungsblock?

Ein Initialisierungsblock ist ein geschweiften Klammerblock im Klassenbereich, der während der Instanzerstellung ausgeführt wird. Sie können damit Felder initialisieren, die etwas komplexer sind als Inline-Initialisierungs-Einzeiler.

Tatsächlich kopiert der Compiler diesen Block einfach in jeden Konstruktor. Daher ist es eine gute Möglichkeit, allgemeinen Code aus allen Konstruktoren zu extrahieren.

Ein statischer Initialisierungsblock ist ein geschweiften Codeblock mit dem Modifikator static davor. Es wird einmal während des Klassenladens ausgeführt und kann zum Initialisieren statischer Felder oder für einige Nebenwirkungen verwendet werden.

Q11. Was ist eine Markerschnittstelle? Was sind die bemerkenswerten Beispiele für Markerschnittstellen in Java?

Eine Marker-Schnittstelle ist eine Schnittstelle ohne Methoden. Sie wird normalerweise von einer Klasse implementiert oder von einer anderen Schnittstelle erweitert, um eine bestimmte Eigenschaft zu kennzeichnen. Die bekanntesten Markerschnittstellen in der Standard-Java-Bibliothek sind folgende:

  • Serializable wird verwendet, um explizit anzugeben, dass diese Klasse sein kann

serialisiert; ** Cloneable erlaubt das Klonen von Objekten mit der clone -Methode (ohne

Cloneable -Schnittstelle in Position, wirft diese Methode ein CloneNotSupportedException ); ** Remote wird in RMI verwendet, um eine Schnittstelle anzugeben, zu der die Methoden gehören könnten

aus der Ferne angerufen.

Q12. Was ist ein Singleton und wie kann es in Java implementiert werden?

Singleton ist ein Muster objektorientierter Programmierung. Eine Singleton-Klasse darf nur eine Instanz haben, die normalerweise global sichtbar und zugänglich ist.

Es gibt mehrere Möglichkeiten, ein Singleton in Java zu erstellen. Das folgende ist das einfachste Beispiel mit einem static -Feld, das direkt vor Ort initialisiert wird. Die Initialisierung ist Thread-sicher, da static -Felder garantiert Thread-sicher initialisiert werden. Der Konstruktor ist private , daher kann der äußere Code nicht mehr als eine Instanz der Klasse erstellen.

public class SingletonExample {

    private static SingletonExample instance = new SingletonExample();

    private SingletonExample() {}

    public static SingletonExample getInstance() {
        return instance;
    }
}

Dieser Ansatz könnte jedoch einen gravierenden Nachteil haben - die Instanz würde beim ersten Zugriff auf diese Klasse instanziiert. Wenn die Initialisierung dieser Klasse eine schwere Operation ist, möchten wir sie wahrscheinlich verschieben, bis die Instanz tatsächlich benötigt wird (möglicherweise nie), aber gleichzeitig Thread-sicher halten. In diesem Fall sollten wir eine Technik verwenden, die als doppelt geprüftes Sperren bezeichnet wird.

Q13. Was ist ein var-arg? Was sind die Einschränkungen für ein var-arg? Wie können Sie es innerhalb des Methodenkörpers verwenden?

Var-arg ist ein Argument mit variabler Länge für eine Methode. Eine Methode darf nur ein var-arg haben und muss in der Liste der Argumente an letzter Stelle stehen. Sie wird als Typname gefolgt von Auslassungspunkten und Argumentnamen angegeben. Innerhalb des Methodenkörpers wird ein var-arg als Array des angegebenen Typs verwendet.

Hier ein Beispiel aus der Standardbibliothek - die Collections.addAll -Methode, die eine Auflistung und eine variable Anzahl von Elementen empfängt und alle Elemente zur Auflistung hinzufügt:

public static <T> boolean addAll(
  Collection<? super T> c, T... elements) {
    boolean result = false;
    for (T element : elements)
        result |= c.add(element);
    return result;
}

Q14. Können Sie auf eine überschriebene Methode einer Superklasse zugreifen? Können Sie auf eine überschriebene Methode einer Supersuperklasse auf ähnliche Weise zugreifen?

Um auf eine überschriebene Methode einer Oberklasse zuzugreifen, können Sie das Schlüsselwort super verwenden. Sie haben jedoch keine ähnliche Möglichkeit, auf die überschriebene Methode einer Supersuperklasse zuzugreifen.

Als Beispiel aus der Standardbibliothek erweitert die LinkedHashMap -Klasse HashMap und verwendet zumeist ihre Funktionalität wieder, indem sie eine verknüpfte Liste über ihre Werte hinzufügt, um die Iterationsreihenfolge zu erhalten. LinkedHashMap verwendet die clear -Methode seiner Superklasse erneut und löscht dann Kopf- und Schwanzreferenzen der verknüpften Liste:

public void clear() {
    super.clear();
    head = tail = null;
}

Nächster "

  • "** Bisherige