JavaRush /Java-Blog /Random-DE /Die 50 wichtigsten Fragen und Antworten zu Java Core-Inte...

Die 50 wichtigsten Fragen und Antworten zu Java Core-Interviews. Teil 1

Veröffentlicht in der Gruppe Random-DE
Hallo zusammen, meine Damen und Herren Software-Ingenieure! Lassen Sie uns über Interviewfragen sprechen. Worauf Sie sich vorbereiten müssen und was Sie wissen müssen. Dies ist ein guter Grund, diese Punkte zu wiederholen oder von Grund auf zu studieren. Die 50 wichtigsten Fragen und Antworten zu Java Core-Interviews.  Teil 1 - 1Ich habe eine ziemlich umfangreiche Sammlung häufig gestellter Fragen zu OOP, Java-Syntax, Ausnahmen in Java, Sammlungen und Multithreading, die ich der Einfachheit halber in mehrere Teile unterteilen werde. Wichtig:Wir sprechen hier nur von Java-Versionen bis 8. Alle Neuerungen ab 9, 10, 11, 12, 13 werden hier nicht berücksichtigt. Alle Ideen/Kommentare zur Verbesserung der Antworten sind willkommen . Viel Spaß beim Lesen, los geht’s!

Java-Interview: OOP-Fragen

1. Welche Funktionen bietet Java?

Antwort:
  1. OOP-Konzepte:

    1. Objektorientierung;
    2. Nachlass;
    3. Verkapselung;
    4. Polymorphismus;
    5. Abstraktion.
  2. Plattformübergreifend: Ein Java-Programm kann ohne Änderungen auf jeder Plattform ausgeführt werden. Sie benötigen lediglich eine installierte JVM (Java Virtual Machine).

  3. Hohe Leistung: JIT (Just In Time Compiler) ermöglicht hohe Leistung. JIT wandelt den Bytecode in Maschinencode um und dann beginnt die JVM mit der Ausführung.

  4. Multithreading: Ein Ausführungsthread, bekannt als Thread. Die JVM erstellt einen Thread namens main thread. Ein Programmierer kann mehrere Threads erstellen, indem er von der Thread-Klasse erbt oder eine Schnittstelle implementiert Runnable.

2. Was ist Vererbung?

Vererbung bedeutet, dass eine Klasse eine andere Klasse erben („ erweitern “) kann . Auf diese Weise können Sie Code aus der Klasse, von der Sie erben, wiederverwenden. Die vorhandene Klasse heißt superclass, die erstellte Klasse heißt subclass. Sie sagen auch parentund child.
public class Animal {
   private int age;
}

public class Dog extends Animal {

}
wo Animalist parent, und Dog- child.

3. Was ist Kapselung?

Diese Frage wird häufig in Interviews mit Java-Entwicklern gestellt. Bei der Kapselung wird die Implementierung mithilfe von Zugriffsmodifikatoren sowie Gettern und Settern ausgeblendet. Dies geschieht, um den Zugriff für die externe Nutzung an den Stellen zu sperren, an denen die Entwickler dies für notwendig erachten. Ein zugängliches Beispiel aus dem Leben ist ein Auto. Wir haben keinen direkten Zugriff auf den Motorbetrieb. Für uns besteht die Aufgabe darin, den Schlüssel ins Zündschloss zu stecken und den Motor anzulassen. Und welche Prozesse unter der Haube ablaufen, geht uns nichts an. Darüber hinaus kann unser Eingriff in diese Aktivität zu einer unvorhersehbaren Situation führen, aufgrund derer wir das Auto kaputt machen und uns selbst verletzen können. Genau das Gleiche passiert beim Programmieren. Auf Wikipedia gut beschrieben . Es gibt auch einen Artikel über Kapselung auf JavaRush .

4. Was ist Polymorphismus?

Polymorphismus ist die Fähigkeit eines Programms, Objekte mit derselben Schnittstelle identisch zu verwenden, ohne Informationen über den spezifischen Typ dieses Objekts zu benötigen. Wie man so schön sagt: eine Schnittstelle – viele Implementierungen. Mit Polymorphismus können Sie verschiedene Arten von Objekten basierend auf ihrem gemeinsamen Verhalten kombinieren und verwenden. Zum Beispiel haben wir eine Klasse „Tier“, die zwei Nachkommen hat – Hund und Katze. Die generische Animal-Klasse hat ein gemeinsames Verhalten für alle: ein Geräusch machen. Für den Fall, dass wir alle Nachkommen der Klasse Animal zusammenstellen und die Methode „make a sound“ ausführen müssen, nutzen wir die Möglichkeiten des Polymorphismus. So wird es aussehen:
List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
Polymorphismus hilft uns also. Darüber hinaus gilt dies auch für polymorphe (überladene) Methoden. Praxis der Verwendung von Polymorphismus

Interviewfragen – Java-Syntax

5. Was ist ein Konstruktor in Java?

Es gelten folgende Merkmale:
  1. Wenn ein neues Objekt erstellt wird, verwendet das Programm dazu den entsprechenden Konstruktor.
  2. Ein Konstruktor ist wie eine Methode. Seine Besonderheit besteht darin, dass es kein zurückgegebenes Element (einschließlich void) gibt und sein Name mit dem Namen der Klasse identisch ist.
  3. Wenn kein Konstruktor explizit geschrieben wird, wird automatisch ein leerer Konstruktor erstellt.
  4. Der Konstruktor kann überschrieben werden.
  5. Wenn ein Konstruktor mit Parametern erstellt wurde, Sie aber auch einen ohne Parameter benötigen, müssen Sie diesen separat schreiben, da er nicht automatisch erstellt wird.

6. Welche beiden Klassen erben nicht von Object?

Lassen Sie sich nicht von Provokationen täuschen, es gibt keine solchen Klassen: Alle Klassen werden direkt oder über Vorfahren von der Object-Klasse geerbt!

7. Was ist eine lokale Variable?

Eine weitere beliebte Frage während eines Java-Entwicklerinterviews. Eine lokale Variable ist eine Variable, die innerhalb einer Methode definiert wird und bis zur Ausführung der Methode existiert. Sobald die Ausführung beendet ist, hört die lokale Variable auf zu existieren. Hier ist ein Programm, das die lokale Variable helloMessage in der Methode main() verwendet:
public static void main(String[] args) {
   String helloMessage;
   helloMessage = "Hello, World!";
   System.out.println(helloMessage);
}

8. Was ist eine Instanzvariable?

Eine Instanzvariable ist eine Variable, die innerhalb einer Klasse definiert wird und solange existiert, bis das Objekt existiert. Ein Beispiel ist die Bee-Klasse, die zwei Variablen nectarCapacity und maxNectarCapacity hat:
public class Bee {

   /**
    * Current nectar capacity
    */
   private double nectarCapacity;

   /**
    * Maximal nectar that can take bee.
    */
   private double maxNectarCapacity = 20.0;

  ...
}

9. Was sind Zugriffsmodifikatoren?

Zugriffsmodifikatoren sind ein Tool, mit dem Sie den Zugriff auf Klassen, Methoden und Variablen anpassen können. Es gibt die folgenden Modifikatoren, geordnet nach zunehmendem Zugriff:
  1. private- wird für Methoden, Felder und Konstruktoren verwendet. Die Zugriffsebene ist nur die Klasse, innerhalb derer sie deklariert ist.
  2. package-private(default)- kann für den Unterricht verwendet werden. Zugriff nur in einem bestimmten Paket, in dem eine Klasse, Methode, Variable oder ein Konstruktor deklariert ist.
  3. protected– derselbe Zugriff wie package-private+ für die Klassen, die von einer Klasse mit dem Modifikator erben protected.
  4. public- Wird auch für den Unterricht verwendet. Voller Zugriff während der gesamten Anwendung.
  5. Die 50 wichtigsten Fragen und Antworten zu Java Core-Interviews.  Teil 1 - 2

10. Was sind überschreibende Methoden?

Das Überschreiben von Methoden erfolgt, wenn das untergeordnete Element das Verhalten der übergeordneten Klasse ändern möchte. Wenn Sie möchten, dass der Inhalt der übergeordneten Methode ausgeführt wird, können Sie in der untergeordneten Methode eine Konstruktion wie super.methodName() verwenden, die die Arbeit der übergeordneten Methode erledigt und erst dann Logik hinzufügt. Zu erfüllende Anforderungen:
  • die Methodensignatur muss gleich sein;
  • Der Rückgabewert sollte derselbe sein.

11. Was ist eine Methodensignatur?

Die 50 wichtigsten Fragen und Antworten zu Java Core-Interviews.  Teil 1 - 3Eine Methodensignatur besteht aus dem Namen der Methode und den Argumenten, die die Methode akzeptiert. Eine Methodensignatur ist eine eindeutige Kennung für eine Methode beim Überladen von Methoden.

12. Was ist Methodenüberladung?

Das Überladen von Methoden ist eine Eigenschaft des Polymorphismus, bei dem Sie durch Ändern der Methodensignatur verschiedene Methoden für dieselben Aktionen erstellen können:
  • gleicher Methodenname;
  • verschiedene Argumente;
  • Möglicherweise gibt es einen anderen Rückgabetyp.
add()Dasselbe kann beispielsweise ArrayListwie folgt überladen werden und führt die Addition abhängig von den eingehenden Argumenten auf unterschiedliche Weise aus:
  • add(Object o)- fügt einfach ein Objekt hinzu;
  • add(int index, Object o)– fügt ein Objekt zu einem bestimmten Index hinzu;
  • add(Collection<Object> c)– fügt eine Liste von Objekten hinzu;
  • add(int index, Collection<Object> c)– Fügt eine Liste von Objekten hinzu, beginnend mit einem bestimmten Index.

13. Was ist eine Schnittstelle?

Mehrfachvererbung ist in Java nicht implementiert. Um dieses Problem zu lösen, wurden Schnittstellen, wie wir sie kennen, hinzugefügt ;) Lange Zeit verfügten Schnittstellen nur über Methoden, ohne diese zu implementieren. Im Rahmen dieser Antwort werden wir darüber sprechen. Zum Beispiel:

public interface Animal {
   void makeSound();
   void eat();
   void sleep();
}
Daraus ergeben sich einige Nuancen:
  • alle Methoden in der Schnittstelle sind öffentlich und abstrakt;
  • Alle Variablen sind öffentliches statisches Finale;
  • Klassen erben sie nicht (extends), sondern implementieren sie (implementiert). Darüber hinaus können Sie beliebig viele Schnittstellen implementieren.
  • Klassen, die eine Schnittstelle implementieren, müssen Implementierungen aller Methoden bereitstellen, über die die Schnittstelle verfügt.
So:
public class Cat implements Animal {
   public void makeSound() {
       // Methodenimplementierung
   }

   public void eat() {
       // Implementierung
   }

   public void sleep() {
       // Implementierung
   }
}

14. Was ist die Standardmethode in der Schnittstelle?

Lassen Sie uns nun über Standardmethoden sprechen. Für was, für wen? Diese Methoden wurden hinzugefügt, um alles „sowohl Ihr als auch unser“ zu machen. Wovon rede ich? Ja, einerseits mussten neue Funktionen hinzugefügt werden: Lambdas, Stream-API, andererseits musste das verlassen werden, wofür Java berühmt ist – die Abwärtskompatibilität. Dazu war es notwendig, vorgefertigte Lösungen in die Schnittstellen einzuführen. So kamen Standardmethoden zu uns. Das heißt, die Standardmethode ist eine in der Schnittstelle implementierte Methode mit dem Schlüsselwort default. Zum Beispiel die bekannte Methode stream()im Collection. Probieren Sie es aus, diese Schnittstelle ist nicht so einfach, wie es scheint ;). Oder auch eine ebenso bekannte Methode forEach()aus dem Iterable. Es existierte auch nicht, bis Standardmethoden hinzugefügt wurden. Dies können Sie übrigens auch auf JavaRush nachlesen .

15. Wie erbt man dann zwei identische Standardmethoden?

Basierend auf der vorherigen Antwort zur Standardmethode können Sie eine weitere Frage stellen. Wenn Sie Methoden in Schnittstellen implementieren können, können Sie theoretisch zwei Schnittstellen mit derselben Methode implementieren. Und wie geht das? Es gibt zwei verschiedene Schnittstellen mit derselben Methode:
interface A {
   default void foo() {
       System.out.println("Foo A");
   }
}

interface B {
   default void foo() {
       System.out.println("Foo B");
   }
}
Und es gibt eine Klasse, die diese beiden Schnittstellen implementiert. foo()Um Unsicherheiten zu vermeiden und den Code zu kompilieren, müssen wir die Methode in der Klasse überschreiben Cund können einfach eine Methode foo()einer der darin enthaltenen Schnittstellen aufrufen – Aoder B. Aber wie wählt man eine bestimmte Schnittstellenmethode aus Аoder В? Dafür gibt es eine Struktur wie diese A.super.foo():
public class C implements A, B {
   @Override
   public void foo() {
       A.super.foo();
   }
}
oder:
public class C implements A, B {
   @Override
   public void foo() {
       B.super.foo();
   }
}
Daher verwendet eine foo()Klassenmethode Centweder die Standardmethode foo()der Schnittstelle Aoder eine Methode foo()der Schnittstelle B.

16. Was sind abstrakte Methoden und Klassen?

Java verfügt über ein reserviertes Wort abstract, das zur Bezeichnung abstrakter Klassen und Methoden verwendet wird. Zunächst einige Definitionen. Eine abstrakte Methode ist eine Methode, die ohne Implementierung mit einem Schlüsselwort abstractin einer abstrakten Klasse erstellt wird. Das heißt, dies ist eine Methode wie in der Schnittstelle, nur mit der Hinzufügung eines Schlüsselworts, zum Beispiel:
public abstract void foo();
Eine abstrakte Klasse ist eine Klasse, die auch abstractdas Wort hat:
public abstract class A {

}
Eine abstrakte Klasse verfügt über mehrere Funktionen:
  • ein Objekt kann nicht auf seiner Grundlage erstellt werden;
  • es kann abstrakte Methoden haben;
  • Es verfügt möglicherweise nicht über abstrakte Methoden.
Abstrakte Klassen werden benötigt, um eine Art Abstraktion zu verallgemeinern (entschuldigen Sie die Tautologie), die im wirklichen Leben nicht existiert, aber viele allgemeine Verhaltensweisen und Zustände (d. h. Methoden und Variablen) enthält. Es gibt mehr als genug Beispiele aus dem Leben. Alles ist um uns herum. Es könnte „Tier“, „Auto“, „geometrische Figur“ usw. sein.

17. Was ist der Unterschied zwischen String, String Builder und String Buffer?

Die Werte Stringwerden in einem konstanten String-Pool gespeichert. Sobald eine Zeile erstellt wurde, wird sie in diesem Pool angezeigt. Und es wird nicht möglich sein, es zu löschen. Zum Beispiel:
String name = "book";
...die Variable verweist auf den String-Pool. Konstanter String-Pool. Die 50 wichtigsten Fragen und Antworten zu Java Core-Interviews.  Teil 1 - 4 Wenn Sie den Variablennamen auf einen anderen Wert setzen, erhalten Sie Folgendes:
name = "pen";
Konstanter String-Pool Die 50 wichtigsten Fragen und Antworten zu Java Core-Interviews.  Teil 1 - 5Diese beiden Werte bleiben also dort. String-Puffer:
  • Werte Stringwerden auf dem Stapel gespeichert. Wenn der Wert geändert wird, wird der neue Wert durch den alten ersetzt.
  • String Buffersynchronisiert und daher threadsicher;
  • Aufgrund der Thread-Sicherheit lässt die Arbeitsgeschwindigkeit zu wünschen übrig.
Beispiel:
StringBuffer name = "book";
Die 50 wichtigsten Fragen und Antworten zu Java Core-Interviews.  Teil 1 - 6Sobald sich der Wert von name ändert, ändert sich auch der Wert auf dem Stapel: Die 50 wichtigsten Fragen und Antworten zu Java Core-Interviews.  Teil 1 - 7StringBuilder Genau das Gleiche wie StringBuffer, nur dass es nicht Thread-sicher ist. Daher ist seine Geschwindigkeit deutlich höher als in StringBuffer.

18. Was ist der Unterschied zwischen einer abstrakten Klasse und einer Schnittstelle?

Abstrakte Klasse:
  • abstrakte Klassen haben einen Standardkonstruktor; es wird jedes Mal aufgerufen, wenn ein untergeordnetes Element dieser abstrakten Klasse erstellt wird;
  • enthält sowohl abstrakte als auch nicht abstrakte Methoden. Im Großen und Ganzen enthält es möglicherweise keine abstrakten Methoden, ist aber dennoch eine abstrakte Klasse.
  • eine Klasse, die von einer abstrakten Klasse erbt, darf nur abstrakte Methoden implementieren;
  • Eine abstrakte Klasse kann eine Instanzvariable enthalten (siehe Frage Nr. 5).
Schnittstelle:
  • hat keinen Konstruktor und kann nicht initialisiert werden;
  • es sollten nur abstrakte Methoden hinzugefügt werden (Standardmethoden nicht mitgezählt);
  • Klassen, die eine Schnittstelle implementieren, müssen alle Methoden implementieren (Standardmethoden nicht mitgezählt);
  • Schnittstellen können nur Konstanten enthalten.

19. Warum benötigt der Zugriff auf ein Element in einem Array O(1)?

Diese Frage stammt buchstäblich aus dem letzten Interview. Wie ich später erfuhr, wird diese Frage gestellt, um zu sehen, wie eine Person denkt. Es ist klar, dass dieses Wissen wenig praktische Bedeutung hat: Es reicht aus, diese Tatsache zu kennen. Zunächst müssen wir klarstellen, dass O(1) eine Bezeichnung für die zeitliche Komplexität eines Algorithmus ist , wenn die Operation in konstanter Zeit stattfindet. Das heißt, diese Bezeichnung ist die schnellste Ausführung. Um diese Frage zu beantworten, müssen wir verstehen, was wir über Arrays wissen. Um ein Array zu erstellen int, müssen wir Folgendes schreiben:
int[] intArray = new int[100];
Aus dieser Aufzeichnung lassen sich mehrere Schlussfolgerungen ziehen:
  1. Beim Erstellen eines Arrays ist dessen Typ bekannt. Wenn der Typ bekannt ist, ist klar, welche Größe jede Zelle des Arrays haben wird.
  2. Es ist bekannt, wie groß das Array sein wird.
Daraus folgt: Um zu verstehen, in welche Zelle geschrieben werden soll, müssen Sie lediglich berechnen, in welchen Speicherbereich geschrieben werden soll. Für ein Auto könnte es nicht einfacher sein. Die Maschine verfügt über einen Anfangsspeicher, eine Anzahl von Elementen und eine einzelne Zellengröße. Daraus ist klar, dass der Aufnahmeraum gleich der Startposition des Arrays + der Größe der Zelle multipliziert mit ihrer Größe ist.

Wie erhält man O(1) beim Zugriff auf Objekte in einer ArrayList?

Diese Frage folgt unmittelbar auf die vorherige. Wenn wir mit einem Array arbeiten und dort Grundelemente vorhanden sind, wissen wir zwar im Voraus, wie groß dieser Typ ist, wenn er erstellt wird. Was aber, wenn es ein Schema wie das im Bild gibt: Die 50 wichtigsten Fragen und Antworten zu Java Core-Interviews.  Teil 1 - 8und wir möchten eine Sammlung mit Elementen vom Typ A erstellen und verschiedene Implementierungen hinzufügen – B, C, D:
List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
Wie können Sie in dieser Situation verstehen, wie groß jede Zelle sein wird, da jedes Objekt anders ist und möglicherweise unterschiedliche zusätzliche Felder hat (oder völlig unterschiedlich sein kann)? Was zu tun ist? Hier wird die Frage so gestellt, dass sie verwirrt und verwirrt. Wir wissen, dass die Sammlung tatsächlich keine Objekte speichert, sondern nur Links zu diesen Objekten. Und alle Links haben die gleiche Größe, und das ist bekannt. Das Zählen des Leerzeichens funktioniert hier also genauso wie in der vorherigen Frage.

21. Autoboxing und Unboxing

Historischer Hintergrund: Autoboxing und Autounboxing sind eine der Hauptinnovationen von JDK 5. Autoboxing ist der Prozess der automatischen Konvertierung von einem primitiven Typ in die entsprechende Wrapper-Klasse. Auto-Unboxing – macht genau das Gegenteil von Auto-Boxing – wandelt eine Wrapper-Klasse in ein Primitiv um. Wenn jedoch ein Wrapper-Wert vorhanden ist null, wird beim Entpacken eine Ausnahme ausgelöst NullPointerException.

Passendes Grundelement – ​​Wrapper

Primitive Wrapper-Klasse
Boolescher Wert Boolescher Wert
int Ganze Zahl
Byte Byte
verkohlen Charakter
schweben Schweben
lang Lang
kurz Kurz
doppelt Doppelt

Autopacking findet statt:

  • wenn einem Grundelement ein Verweis auf die Wrapper-Klasse zugewiesen wird:

    VOR Java 5:

    //manuelles Packen oder wie es VOR Java 5 war.
    public void boxingBeforeJava5() {
       Boolean booleanBox = new Boolean(true);
       Integer intBox = new Integer(3);
       // und so weiter zu anderen Typen
    }
    
    после Java 5:
    //Automatisches Paketieren oder wie es in Java 5 wurde.
    public void boxingJava5() {
       Boolean booleanBox = true;
       Integer intBox = 3;
       // und so weiter zu anderen Typen
    }
  • wenn ein Grundelement als Argument an eine Methode übergeben wird, die einen Wrapper erwartet:

    public void exampleOfAutoboxing() {
       long age = 3;
       setAge(age);
    }
    
    public void setAge(Long age) {
       this.age = age;
    }

Das automatische Entpacken erfolgt:

  • wenn wir der Wrapper-Klasse eine primitive Variable zuweisen:

    //vor 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();
    
    //und nach JDK 5:
    int intValue = new Integer(4);
    double doubleValue = new Double(2.3);
    char c = new Character((char) 3);
    boolean b = Boolean.TRUE;
  • In Fällen mit arithmetischen Operationen. Sie gelten nur für primitive Typen. Dazu müssen Sie das Primitiv auspacken.

    // Vor Java 5
    Integer integerBox1 = new Integer(1);
    Integer integerBox2 = new Integer(2);
    
    // Zum Vergleich musste Folgendes durchgeführt werden:
    integerBox1.intValue() > integerBox2.intValue()
    
    //в Java 5
    integerBox1 > integerBox2
  • bei Übergabe an einen Wrapper in einer Methode, die das entsprechende Grundelement akzeptiert:

    public void exampleOfAutoboxing() {
       Long age = new Long(3);
       setAge(age);
    }
    
    public void setAge(long age) {
       this.age = age;
    }

22. Was ist das letzte Schlüsselwort und wo wird es verwendet?

Das Schlüsselwort finalkann für Variablen, Methoden und Klassen verwendet werden.
  1. Eine endgültige Variable kann nicht einem anderen Objekt zugewiesen werden.
  2. Die letzte Klasse ist unfruchtbar)) kann keine Erben haben.
  3. Die endgültige Methode kann für einen Vorfahren nicht überschrieben werden.
Wir haben das Top abgedeckt, jetzt wollen wir es genauer besprechen.

endgültige Variablen

;Java bietet uns zwei Möglichkeiten, eine Variable zu erstellen und ihr einen Wert zuzuweisen:
  1. Sie können eine Variable deklarieren und später initialisieren.
  2. Sie können eine Variable deklarieren und sofort zuweisen.
Beispiel für die Verwendung der Endvariablen für diese Fälle:
public class FinalExample {

   //letzte statische Variable, die sofort initialisiert wird:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";

   //final ist eine Variable, die nicht initialisiert ist, aber nur funktioniert, wenn
   //initialisiere dies im Konstruktor:
   final long creationTime;

   public FinalExample() {
       this.creationTime = System.currentTimeMillis();
   }

   public static void main(String[] args) {
       FinalExample finalExample = new FinalExample();
       System.out.println(finalExample.creationTime);

       // letztes Feld FinalExample.FINAL_EXAMPLE_NAME kann nicht zugewiesen werden
//    FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";

       // letztes Feld Config.creationTime kann nicht zugewiesen werden
//    finalExample.creationTime = 1L;
   }
}

Kann die Endvariable als Konstante betrachtet werden?

Da wir einer endgültigen Variablen keinen neuen Wert zuweisen können, scheint es sich hierbei um konstante Variablen zu handeln. Aber das ist nur auf den ersten Blick. Wenn der Datentyp, auf den sich die Variable bezieht, ist immutable, dann handelt es sich ja um eine Konstante. Wenn der Datentyp jedoch mutableveränderbar ist, ist es mithilfe von Methoden und Variablen möglich, den Wert des Objekts zu ändern, auf das finalsich die Variable bezieht. In diesem Fall kann sie nicht als Konstante bezeichnet werden. Das Beispiel zeigt also, dass einige der endgültigen Variablen tatsächlich Konstanten sind, andere jedoch nicht, und sie können geändert werden.
public class FinalExample {

   // unveränderliche Endvariablen:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";
   final static Integer FINAL_EXAMPLE_COUNT  = 10;

   // veränderbare Filtervariablen
   final List<String> addresses = new ArrayList();
   final StringBuilder finalStringBuilder = new StringBuilder("constant?");
}

Lokale Endvariablen

Wenn finaleine Variable innerhalb einer Methode erstellt wird, wird sie local finalals Variable bezeichnet:
public class FinalExample {

   public static void main(String[] args) {
       // So geht's
       final int minAgeForDriveCar = 18;

       // oder Sie können es so machen, in der foreach-Schleife:
       for (final String arg : args) {
           System.out.println(arg);
       }
   }

}
Wir können das Schlüsselwort finalin einer erweiterten Schleife verwenden for, da nach Abschluss einer Iteration der Schleife forjedes Mal eine neue Variable erstellt wird. Dies gilt jedoch nicht für eine normale for-Schleife, sodass der folgende Code einen Fehler bei der Kompilierung auslöst.
// final lokal geändert j kann nicht zugewiesen werden
for (final int i = 0; i < args.length; i ++) {
   System.out.println(args[i]);
}

Abschlussklasse

Sie können eine als deklarierte Klasse nicht erweitern final. Einfach ausgedrückt: Keine Klasse kann von dieser erben. Ein großartiges Beispiel finalfür eine Klasse im JDK ist String. Der erste Schritt zum Erstellen einer unveränderlichen Klasse besteht darin, sie als zu markieren final, damit sie nicht erweitert werden kann:
public final class FinalExample {
}

// Kompilierungsfehler hier
class WantsToInheritFinalClass extends FinalExample {
}

Endgültige Methoden

Wenn eine Methode als final markiert ist, wird sie als finale Methode bezeichnet (logisch, oder?). Die Final-Methode kann in einer Nachfolgeklasse nicht überschrieben werden. Übrigens sind die Methoden in der Object-Klasse – wait() und notify() – endgültig, sodass wir keine Möglichkeit haben, sie zu überschreiben.
public class FinalExample {
   public final String generateAddress() {
       return "Some address";
   }
}

class ChildOfFinalExample extends FinalExample {

   // Kompilierungsfehler hier
   @Override
   public String generateAddress() {
       return "My OWN Address";
   }
}

Wie und wo man final in Java verwendet

  • Verwenden Sie das Schlüsselwort final, um einige Konstanten auf Klassenebene zu definieren.
  • Erstellen Sie endgültige Variablen für Objekte, wenn Sie nicht möchten, dass sie geändert werden. Zum Beispiel objektspezifische Eigenschaften, die wir für Protokollierungszwecke verwenden können;
  • Wenn Sie nicht möchten, dass der Kurs verlängert wird, markieren Sie ihn als endgültig.
  • Wenn Sie eine unveränderliche Klasse erstellen müssen, müssen Sie sie endgültig machen.
  • Wenn Sie möchten, dass sich die Implementierung einer Methode in ihren Nachkommen nicht ändert, bezeichnen Sie die Methode als final. Dies ist sehr wichtig, um sicherzustellen, dass sich die Implementierung nicht ändert.

23. Was ist veränderlich unveränderlich?

Veränderlich

Veränderbar sind Objekte, deren Zustände und Variablen nach der Erstellung geändert werden können. Zum Beispiel Klassen wie StringBuilder, StringBuffer. Beispiel:
public class MutableExample {

   private String address;

   public MutableExample(String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   // Dieser Setter kann das Namensfeld ändern
   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());

       // Aktualisiere das Namensfeld, sodass dies ein veränderbares Objekt ist
       obj.setAddress("Updated address");
       System.out.println(obj.getAddress());
   }
}

Unveränderlich

Unveränderlich sind Objekte, deren Zustände und Variablen nach der Erstellung des Objekts nicht geändert werden können. Warum nicht ein toller Schlüssel für eine HashMap, oder?) Zum Beispiel String, Integer, Double und so weiter. Beispiel:
// Diese Klasse endgültig machen, damit niemand sie ändern kann
public final class ImmutableExample {

   private String address;

   ImmutableExample (String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   //Entferne den Setter

   public static void main(String[] args) {

       ImmutableExample obj = new ImmutableExample("old address");
       System.out.println(obj.getAddress());

       // Ändern Sie dieses Feld daher in keiner Weise, es handelt sich also um ein unveränderliches Objekt
       // obj.setName("new address");
       // System.out.println(obj.getName());

   }
}

24. Wie schreibe ich eine unveränderliche Klasse?

Nachdem Sie herausgefunden haben, was veränderliche und unveränderliche Objekte sind, stellt sich natürlich die nächste Frage: Wie schreibt man es? Um eine unveränderliche unveränderliche Klasse zu schreiben, müssen Sie einfache Schritte ausführen:
  • Machen Sie die Klasse zum Abschluss.
  • Machen Sie alle Felder privat und erstellen Sie nur Getter für sie. Setter sind natürlich nicht erforderlich.
  • Machen Sie alle veränderlichen Felder endgültig, sodass der Wert nur einmal festgelegt werden kann.
  • Initialisieren Sie alle Felder über den Konstruktor und führen Sie eine tiefe Kopie durch (d. h. das Objekt selbst, seine Variablen, Variablen von Variablen usw. kopieren).
  • Klonen Sie veränderliche Variablenobjekte in Gettern, um nur Kopien von Werten und keine Verweise auf tatsächliche Objekte zurückzugeben.
Beispiel:
/**
* Ein Beispiel für die Erstellung eines unveränderlichen Objekts.
*/
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;
   }

   /**
    * Klonen Sie das Objekt, bevor Sie es zurückgeben.
    */
   public HashMap<String, String> getAddresses() {
       return (HashMap<String, String>) addresses.clone();
   }

   /**
    * Kopieren Sie im Konstruktor die veränderlichen Objekte tief.
    */
   public FinalClassExample(int age, String name, HashMap<String, String> addresses) {
       System.out.println(„Durchführen einer tiefen Kopie im Konstruktor“);
       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;
   }
}
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION