제이온

[Effektives Java] Item 6: Vermeide unnötige Objektgenerierung

  • Verfasst in: Koreanisch
  • Land: Alle Ländercountry-flag
  • IT

Erstellt: 2024-04-28

Erstellt: 2024-04-28 13:40

Fälle, in denen unnötige Objekte erstellt werden

Verwendung von new String()


Die Zeichenfolgen a, b und c enthalten alle die Zeichenfolge „hi“. Da die drei Zeichenfolgen jedoch auf unterschiedliche Adressen verweisen, wird für dieselben Daten unterschiedlicher Speicherplatz zugewiesen, was zu Verschwendung führt.


<span class="image-inline ck-widget" contenteditable="false"><img src="https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fa53d14aa-6abf-40f0-8441-435647d172fa%2FUntitled.png?table=block&id=f168859c-367c-4924-a4c6-5e04788fab67&spaceId=b453bd85-cb15-44b5-bf2e-580aeda8074e&width=2000&userId=80352c12-65a4-4562-9a36-2179ed0dfffb&cache=v2" alt="Untitled" style="aspect-ratio:2000/1146;" width="2000" height="1146"></span>


Daher sollte bei der Deklaration von Zeichenfolgen nicht das Schlüsselwort new verwendet werden, sondern eine Literaldeklaration.



Der obige Quellcode verwendet nur eine Instanz. Darüber hinaus wird durch die Verwendung dieser Methode sichergestellt, dass jeder Code innerhalb derselben JVM, der das Zeichenfolgenliteral „hi“ verwendet, dasselbe Objekt wiederverwendet. Dies ist auf die Besonderheit des Java Constant Pools zurückzuführen.


Verwendung von new Boolean()

Im obigen Code wird über einen Konstruktor, der eine Zeichenfolge als Parameter entgegennimmt, eine Boolean-Instanz erstellt. Boolean kann nur true oder false sein, daher ist es eine Verschwendung von Speicherplatz, jedes Mal eine Instanz zu erstellen. Daher ist es besser, die statische Factory-Methode Boolean.valueOf() zu verwenden.



Verwendung von String.matches()

Wenn die Erstellungskosten hoch sind, ist es ratsam, sie zu cachen und wiederzuverwenden. Allerdings können wir nicht immer die Kosten der von uns erstellten Objekte kennen. Angenommen, wir möchten eine Methode schreiben, die überprüft, ob eine gegebene Zeichenfolge eine gültige römische Zahl ist. In diesem Fall ist es am einfachsten, reguläre Ausdrücke zu verwenden, wie folgt:



Die Methode String.matches() hat jedoch ein Leistungsproblem. Die Pattern-Instanz für reguläre Ausdrücke, die diese Methode intern erstellt, wird einmal verwendet und dann verworfen und ist somit sofort für die Garbage Collection vorgesehen. Je häufiger der reguläre Ausdruck verwendet wird, desto höher sind die Kosten für die Erstellung und das Verwerfen derselben Pattern-Instanz. Daher ist es besser, die Pattern-Instanz im Voraus zu cachen und sie bei jedem Aufruf der Methode isRomanNumeral() wiederzuverwenden.



**Hinweis**

In allen obigen Beispielen wurden unnötige Objekte beim Caching immer als unveränderliche Objekte erstellt. Dies ist notwendig, um die Sicherheit bei der Wiederverwendung zu gewährleisten. Es gibt jedoch Fälle, in denen dies der Intuition zur Wiederverwendung von Objekten widerspricht.


Ein Adapter (Ansicht) ist ein Objekt, das die eigentliche Arbeit an ein Backend-Objekt delegiert und selbst als sekundäre Schnittstelle fungiert. Adapter müssen nur das Backend-Objekt verwalten, daher reicht es aus, für jedes Backend-Objekt nur einen Adapter zu erstellen.


Beispielsweise gibt die Methode keySet() der Map-Schnittstelle eine Set-Ansicht zurück, die alle Schlüssel des Map-Objekts enthält. Der Benutzer könnte denken, dass bei jedem Aufruf der Methode keySet() eine neue Set-Instanz erstellt wird, aber in der tatsächlichen JDK-Implementierung wird immer dieselbe veränderliche Set-Instanz zurückgegeben.


Dies liegt daran, dass die zurückgegebene Set-Instanz zwar veränderlich ist, aber alle ausgeführten Funktionen identisch sind und alle Set-Instanzen die Map-Instanz repräsentieren. Daher spielt es keine Rolle, ob keySet() mehrere Ansichtsobjekte erstellt, aber es ist auch nicht notwendig oder von Vorteil.



Wenn also die Instanz names1 geändert wird, wirkt sich dies auch auf die Instanz names2 aus.


Persönlich halte ich es jedoch für richtig, dass der Rückgabewert der Methode keySet() eine defensive Kopie verwendet und jedes Mal ein neues Objekt zurückgibt. Wenn die über die Methode keySet() empfangene Set-Instanz auch an anderer Stelle verwendet wird und Code vorhanden ist, der den Zustand dieser Instanz ändert, kann man dem aktuellen Zustand der verwendeten Set-Instanz und der Map-Instanz nicht mehr vertrauen.


Außerdem hat die wiederholte Erstellung der Set-Schnittstelle, sofern die Methode keySet() nicht übermäßig häufig verwendet wird, keinen großen Einfluss auf die Leistung. Meiner Meinung nach ist es besser, die Set-Schnittstelle als unveränderliches Objekt zu erstellen und die Wartung sicherer zu gestalten.


Autoboxing

Autoboxing ist eine Technik, bei der der Compiler bei der gemischten Verwendung von primitiven Datentypen und Wrapper-Datentypen automatisch eine gegenseitige Konvertierung durchführt. Autoboxing beseitigt die Unterscheidung zwischen primitiven Datentypen und Wrapper-Datentypen jedoch nicht vollständig, sondern verwischt sie lediglich.



Der Code hat logisch gesehen kein Problem, ist aber leistungstechnisch sehr ineffizient. Der Grund dafür liegt in den Datentypen von sum und i innerhalb der for-Schleife.


Der Datentyp von sum ist Long und der von i ist long. Das heißt, wenn der long-Typ i die Schleife durchläuft und zu sum addiert wird, wird jedes Mal eine neue Long-Instanz erstellt. Daher sollten Sie, wenn möglich, primitive Datentypen anstelle von Wrapper-Datentypen verwenden und darauf achten, dass kein unbeabsichtigtes Autoboxing stattfindet.


Was man nicht missverstehen sollte

Man sollte den Rat, die Erstellung unnötiger Objekte zu vermeiden, nicht so verstehen, dass die Kosten für die Objekterstellung hoch sind und man sie daher vermeiden sollte.


Insbesondere in modernen JVMs ist das Erstellen und Freigeben kleiner, unnötiger Objekte keine große Belastung. Daher sollten Sie, außer bei Objekten mit hohen Kosten wie Datenbankverbindungen, keine benutzerdefinierten Objektpools erstellen.


Darüber hinaus sollten Sie bedenken, dass die negativen Auswirkungen der Wiederverwendung von Objekten in Situationen, in denen eine defensive Kopie erforderlich ist, weitaus größer sind als die negativen Auswirkungen der wiederholten Erstellung unnötiger Objekte. Die negativen Auswirkungen der wiederholten Erstellung wirken sich nur auf die Form und die Leistung des Codes aus, während ein Fehler bei der defensiven Kopie direkt zu Fehlern und Sicherheitsproblemen führt.


Quelle

Kommentare0