Java-Interview: OOP-Fragen
1. Welche Funktionen bietet Java?
Antwort:-
OOP-Konzepte:
- Objektorientierung;
- Nachlass;
- Verkapselung;
- Polymorphismus;
- Abstraktion.
-
Plattformübergreifend: Ein Java-Programm kann ohne Änderungen auf jeder Plattform ausgeführt werden. Sie benötigen lediglich eine installierte JVM (Java Virtual Machine).
-
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.
- Multithreading: Ein Ausführungsthread, bekannt als
Thread
. Die JVM erstellt einen Thread namensmain thread
. Ein Programmierer kann mehrere Threads erstellen, indem er von der Thread-Klasse erbt oder eine Schnittstelle implementiertRunnable
.
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ßtsuperclass
, die erstellte Klasse heißt subclass
. Sie sagen auch parent
und child
.
public class Animal {
private int age;
}
public class Dog extends Animal {
}
wo Animal
ist 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:- Wenn ein neues Objekt erstellt wird, verwendet das Programm dazu den entsprechenden Konstruktor.
- 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.
- Wenn kein Konstruktor explizit geschrieben wird, wird automatisch ein leerer Konstruktor erstellt.
- Der Konstruktor kann überschrieben werden.
- 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:private
- wird für Methoden, Felder und Konstruktoren verwendet. Die Zugriffsebene ist nur die Klasse, innerhalb derer sie deklariert ist.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.protected
– derselbe Zugriff wiepackage-private
+ für die Klassen, die von einer Klasse mit dem Modifikator erbenprotected
.public
- Wird auch für den Unterricht verwendet. Voller Zugriff während der gesamten Anwendung.
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?
Eine 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 ArrayList
wie 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.
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üsselwortdefault
. 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 C
und können einfach eine Methode foo()
einer der darin enthaltenen Schnittstellen aufrufen – A
oder 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 C
entweder die Standardmethode foo()
der Schnittstelle A
oder eine Methode foo()
der Schnittstelle B
.
16. Was sind abstrakte Methoden und Klassen?
Java verfügt über ein reserviertes Wortabstract
, 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 abstract
in 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 abstract
das 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.
17. Was ist der Unterschied zwischen String, String Builder und String Buffer?
Die WerteString
werden 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. Wenn Sie den Variablennamen auf einen anderen Wert setzen, erhalten Sie Folgendes:
name = "pen";
Konstanter String-Pool Diese beiden Werte bleiben also dort. String-Puffer:
- Werte
String
werden auf dem Stapel gespeichert. Wenn der Wert geändert wird, wird der neue Wert durch den alten ersetzt. String Buffer
synchronisiert und daher threadsicher;- Aufgrund der Thread-Sicherheit lässt die Arbeitsgeschwindigkeit zu wünschen übrig.
StringBuffer name = "book";
Sobald sich der Wert von name ändert, ändert sich auch der Wert auf dem Stapel: StringBuilder 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).
- 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 erstellenint
, müssen wir Folgendes schreiben:
int[] intArray = new int[100];
Aus dieser Aufzeichnung lassen sich mehrere Schlussfolgerungen ziehen:
- Beim Erstellen eines Arrays ist dessen Typ bekannt. Wenn der Typ bekannt ist, ist klar, welche Größe jede Zelle des Arrays haben wird.
- Es ist bekannt, wie groß das Array sein wird.
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: und 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 istnull
, 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üsselwortfinal
kann für Variablen, Methoden und Klassen verwendet werden.
- Eine endgültige Variable kann nicht einem anderen Objekt zugewiesen werden.
- Die letzte Klasse ist unfruchtbar)) kann keine Erben haben.
- Die endgültige Methode kann für einen Vorfahren nicht überschrieben werden.
endgültige Variablen
;Java bietet uns zwei Möglichkeiten, eine Variable zu erstellen und ihr einen Wert zuzuweisen:- Sie können eine Variable deklarieren und später initialisieren.
- Sie können eine Variable deklarieren und sofort zuweisen.
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, istimmutable
, dann handelt es sich ja um eine Konstante. Wenn der Datentyp jedoch mutable
veränderbar ist, ist es mithilfe von Methoden und Variablen möglich, den Wert des Objekts zu ändern, auf das final
sich 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
Wennfinal
eine Variable innerhalb einer Methode erstellt wird, wird sie local final
als 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 final
in einer erweiterten Schleife verwenden for
, da nach Abschluss einer Iteration der Schleife for
jedes 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 erweiternfinal
. Einfach ausgedrückt: Keine Klasse kann von dieser erben. Ein großartiges Beispiel final
fü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.
/**
* 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;
}
}
GO TO FULL VERSION