Ich bin ein Java-Programmierer, der neu in der Unternehmenswelt ist. Kürzlich habe ich eine Anwendung mit Groovy und Java entwickelt. Während des gesamten Codes, den ich geschrieben habe, wurde eine ganze Reihe von Statiken verwendet. Ich wurde vom leitenden technischen Los gebeten, die Anzahl der verwendeten Statiken zu reduzieren. Ich habe ungefähr gleich gegoogelt und finde, dass viele Programmierer ziemlich dagegen sind, statische Variablen zu verwenden.
Ich finde statische Variablen bequemer zu verwenden. Und ich gehe davon aus, dass sie auch effizient sind (bitte korrigieren Sie mich, wenn ich falsch liege), denn wenn ich 10.000 Aufrufe einer Funktion innerhalb einer Klasse durchführen müsste, würde ich die Methode gerne statisch machen und Class.methodCall()
stattdessen eine einfache Methode verwenden den Speicher mit 10.000 Instanzen der Klasse überladen, richtig?
Darüber hinaus reduzieren Statiken die gegenseitigen Abhängigkeiten von den anderen Teilen des Codes. Sie können als perfekte Staatsinhaber auftreten. Hinzu kommt, dass Statik in einigen Sprachen wie Smalltalk und Scala weit verbreitet ist . Warum ist diese Unterdrückung der Statik unter Programmierern (insbesondere in der Welt von Java) weit verbreitet?
PS: Bitte korrigieren Sie mich, wenn meine Annahmen zur Statik falsch sind.
Antworten:
Statische Variablen repräsentieren den globalen Zustand. Das ist schwer zu überlegen und schwer zu testen: Wenn ich eine neue Instanz eines Objekts erstelle, kann ich innerhalb von Tests über seinen neuen Status nachdenken. Wenn ich Code verwende, der statische Variablen verwendet, kann er sich in einem beliebigen Zustand befinden - und alles kann ihn ändern.
Ich könnte noch eine ganze Weile weitermachen, aber je größer das Konzept ist, desto einfacher ist es, darüber nachzudenken, je enger der Umfang von etwas ist. Wir können gut über kleine Dinge nachdenken, aber es ist schwierig, über den Zustand eines Millionen-Leitungssystems nachzudenken, wenn es keine Modularität gibt. Dies gilt übrigens für alle möglichen Dinge - nicht nur für statische Variablen.
quelle
Es ist nicht sehr objektorientiert: Ein Grund, warum Statik von manchen Menschen als "böse" angesehen werden könnte, ist, dass sie dem objektorientierten Paradigma widersprechen . Insbesondere verstößt es gegen das Prinzip, dass Daten in Objekten eingekapselt sind (die erweitert werden können, Informationen verbergen usw.). Statik, wie Sie sie beschreiben, besteht im Wesentlichen darin, sie als globale Variable zu verwenden, um Probleme wie den Umfang zu vermeiden. Globale Variablen sind jedoch eines der bestimmenden Merkmale eines prozeduralen oder imperativen Programmierparadigmas und kein Merkmal eines "guten" objektorientierten Codes. Dies bedeutet nicht, dass das prozedurale Paradigma schlecht ist, aber ich habe den Eindruck, dass Ihr Vorgesetzter erwartet, dass Sie "guten objektorientierten Code" schreiben und wirklich schreiben möchten. "
Es gibt viele Gotchyas in Java, wenn Sie anfangen, Statiken zu verwenden, die nicht immer sofort offensichtlich sind. Wenn beispielsweise zwei Kopien Ihres Programms auf derselben VM ausgeführt werden, werden sie dann den Wert der statischen Variablen ändern und sich gegenseitig beeinflussen? Oder was passiert, wenn Sie die Klasse erweitern? Können Sie das statische Element überschreiben? Geht Ihrer VM der Arbeitsspeicher aus, weil Sie über eine unglaubliche Anzahl von statischen Daten verfügen und dieser Arbeitsspeicher nicht für andere benötigte Instanzobjekte zurückgefordert werden kann?
Objektlebensdauer: Darüber hinaus hat die Statik eine Lebensdauer, die der gesamten Laufzeit des Programms entspricht. Dies bedeutet, dass der Speicher all dieser statischen Variablen auch dann nicht gesammelt werden kann, wenn Sie Ihre Klasse verwendet haben. Wenn Sie beispielsweise stattdessen Ihre Variablen nicht statisch gemacht und in Ihrer main () -Funktion eine einzelne Instanz Ihrer Klasse erstellt und Ihre Klasse dann aufgefordert haben, eine bestimmte Funktion 10.000 Mal auszuführen, sobald diese 10.000 Aufrufe ausgeführt wurden Wenn Sie Ihre Verweise auf die einzelne Instanz löschen, können alle Ihre statischen Variablen durch Müll gesammelt und wiederverwendet werden.
Verhindert bestimmte Wiederverwendungen: Außerdem können statische Methoden nicht zum Implementieren einer Schnittstelle verwendet werden, sodass statische Methoden verhindern können, dass bestimmte objektorientierte Features verwendet werden können.
Andere Optionen: Wenn Effizienz Ihr Hauptanliegen ist, gibt es möglicherweise andere bessere Möglichkeiten, um das Geschwindigkeitsproblem zu lösen, als nur den Vorteil zu berücksichtigen, dass der Aufruf normalerweise schneller als die Erstellung ist. Überlegen Sie, ob die transienten oder flüchtigen Modifikatoren irgendwo benötigt werden. Um die Inline-Fähigkeit zu erhalten, kann eine Methode als endgültig statt als statisch markiert werden. Methodenparameter und andere Variablen können als endgültig markiert werden, um bestimmte Compileroptimierungen basierend auf Annahmen darüber zu ermöglichen, was diese Variablen ändern kann. Ein Instanzobjekt kann mehrmals wiederverwendet werden, anstatt jedes Mal eine neue Instanz zu erstellen. Möglicherweise gibt es Compliler-Optimierungsschalter, die für die App im Allgemeinen aktiviert sein sollten. Möglicherweise sollte das Design so eingerichtet werden, dass die 10.000 Läufe Multithreading-fähig sind und Multiprozessor-Kerne nutzen können. Wenn Portabilität nicht ist '
Wenn Sie aus irgendeinem Grund nicht mehrere Kopien eines Objekts möchten, das Singleton-Entwurfsmuster, hat Vorteile gegenüber statischen Objekten, wie z. B. Thread-Sicherheit (vorausgesetzt, Ihr Singleton ist gut codiert), ermöglicht eine verzögerte Initialisierung, stellt sicher, dass das Objekt bei seiner Verwendung ordnungsgemäß initialisiert wurde, Unterklassifizierung, Vorteile beim Testen und Refactoring Ihres Codes, Ganz zu schweigen davon, dass es VIEL einfacher ist, den Code zu entfernen, um doppelte Instanzen zu vermeiden, wenn Sie Ihre Meinung ändern, nur eine Instanz eines Objekts zu wollen, als Ihren gesamten statischen Variablencode für die Verwendung von Instanzvariablen umzugestalten. Ich musste das schon einmal machen, es macht keinen Spaß, und am Ende müssen Sie viel mehr Klassen bearbeiten, was das Risiko erhöht, neue Fehler einzuführen ... viel besser, wenn Sie die Dinge beim ersten Mal "richtig" einrichten. auch wenn es so aussieht, als hätte es seine Nachteile. Für mich, Die Nacharbeit, die erforderlich ist, wenn Sie später entscheiden, dass Sie mehrere Kopien von etwas benötigen, ist wahrscheinlich einer der zwingendsten Gründe, um Statik so selten wie möglich zu verwenden. Und daher würde ich auch Ihrer Aussage nicht zustimmen, dass Statik die gegenseitigen Abhängigkeiten verringert. Ich denke, Sie werden am Ende Code erhalten, der stärker gekoppelt ist, wenn Sie viele Statiken haben, auf die direkt zugegriffen werden kann, anstatt ein Objekt, das "weiß, wie es geht" etwas "an sich.
quelle
synchronized
Methoden), bedeutet dies nicht, dass der aufrufende Code in Bezug auf den Singleton-Status frei von Race-Bedingungen ist.Object Lifetime
ist ein sehr wichtiger Punkt, den @jessica erwähnt hat.Das Böse ist ein subjektiver Begriff.
Sie kontrollieren die Statik nicht in Bezug auf Schöpfung und Zerstörung. Sie leben auf Geheiß des Programms beim Laden und Entladen.
Da sich die Statik in einem Bereich befindet, müssen alle Threads, die sie verwenden möchten, die Zugriffskontrolle durchlaufen, die Sie verwalten müssen. Dies bedeutet, dass Programme stärker gekoppelt sind und diese Änderung schwerer vorstellbar und zu verwalten ist (wie J Skeet sagt). Dies führt zu Problemen bei der Isolierung der Auswirkungen von Änderungen und wirkt sich somit auf die Verwaltung der Tests aus.
Dies sind die beiden Hauptprobleme, die ich mit ihnen habe.
quelle
Nein. Globale Staaten sind an sich nicht böse. Aber wir müssen Ihren Code sehen, um zu sehen, ob Sie ihn richtig verwendet haben. Es ist durchaus möglich, dass ein Neuling globale Staaten missbraucht; genauso wie er jedes Sprachmerkmal missbrauchen würde.
Globale Staaten sind absolute Notwendigkeit. Wir können globale Staaten nicht vermeiden. Wir können es nicht vermeiden, über globale Staaten nachzudenken. - Wenn wir unsere Anwendungssemantik verstehen wollen.
Menschen, die versuchen, globale Staaten deswegen loszuwerden, haben unweigerlich ein viel komplexeres System - und die globalen Staaten sind immer noch da, geschickt / idiotisch getarnt unter vielen Ebenen von Indirektionen; und wir müssen immer noch über globale Staaten nachdenken, nachdem wir alle Indirektionen ausgepackt haben.
Wie die Spring-Leute, die verschwenderisch globale Staaten in XML deklarieren und irgendwie denken, dass es überlegen ist.
@ Jon Skeet
if I create a new instance of an object
Jetzt haben Sie zwei Gründe: den Status innerhalb des Objekts und den Status der Umgebung, in der sich das Objekt befindet.quelle
Es gibt 2 Hauptprobleme mit statischen Variablen:
quelle
Wenn Sie das Schlüsselwort 'static' ohne das Schlüsselwort 'final' verwenden, sollte dies ein Signal sein, um Ihr Design sorgfältig zu prüfen. Selbst das Vorhandensein eines "Finales" ist kein Freipass, da ein veränderbares statisches Endobjekt genauso gefährlich sein kann.
Ich würde schätzen, dass ungefähr 85% der Zeit, in der ich eine "Statik" ohne "Finale" sehe, FALSCH ist. Oft finde ich seltsame Problemumgehungen, um diese Probleme zu maskieren oder zu verbergen.
Bitte erstellen Sie keine statischen Mutables. Besonders Sammlungen. Im Allgemeinen sollten Sammlungen initialisiert werden, wenn ihr enthaltendes Objekt initialisiert wird, und sollten so gestaltet sein, dass sie zurückgesetzt oder vergessen werden, wenn ihr enthaltendes Objekt vergessen wird.
Die Verwendung von Statik kann zu sehr subtilen Fehlern führen, die den Ingenieuren tagelang Schmerzen bereiten. Ich weiß, weil ich diese Bugs sowohl erstellt als auch gejagt habe.
Wenn Sie weitere Informationen wünschen, lesen Sie bitte weiter…
Warum nicht Statik verwenden?
Es gibt viele Probleme mit der Statik, einschließlich des Schreibens und Ausführens von Tests sowie subtile Fehler, die nicht sofort offensichtlich sind.
Code, der auf statischen Objekten basiert, kann nicht einfach in Einheiten getestet werden, und Statik kann (normalerweise) nicht einfach verspottet werden.
Wenn Sie Statik verwenden, ist es nicht möglich, die Implementierung der Klasse auszutauschen, um übergeordnete Komponenten zu testen. Stellen Sie sich beispielsweise ein statisches CustomerDAO vor, das Kundenobjekte zurückgibt, die es aus der Datenbank lädt. Jetzt habe ich eine Klasse CustomerFilter, die auf einige Kundenobjekte zugreifen muss. Wenn CustomerDAO statisch ist, kann ich keinen Test für CustomerFilter schreiben, ohne zuerst meine Datenbank zu initialisieren und nützliche Informationen auszufüllen.
Das Auffüllen und Initialisieren der Datenbank dauert lange. Und meiner Erfahrung nach wird sich Ihr DB-Initialisierungsframework im Laufe der Zeit ändern, was bedeutet, dass sich Daten ändern und Tests möglicherweise unterbrochen werden. Stellen Sie sich vor, Kunde 1 war früher ein VIP, aber das DB-Initialisierungsframework hat sich geändert, und jetzt ist Kunde 1 kein VIP mehr, aber Ihr Test war fest codiert, um Kunde 1 zu laden.
Ein besserer Ansatz besteht darin, ein CustomerDAO zu instanziieren und es beim Erstellen an den CustomerFilter zu übergeben. (Ein noch besserer Ansatz wäre die Verwendung von Spring oder eines anderen Inversion of Control-Frameworks.
Sobald Sie dies getan haben, können Sie schnell ein alternatives DAO in Ihrem CustomerFilterTest verspotten oder auslöschen, sodass Sie mehr Kontrolle über den Test haben.
Ohne das statische DAO ist der Test schneller (keine Datenbankinitialisierung) und zuverlässiger (da er nicht fehlschlägt, wenn sich der Datenbankinitialisierungscode ändert). In diesem Fall muss beispielsweise sichergestellt werden, dass Kunde 1 ein VIP ist und immer sein wird, was den Test betrifft.
Ausführen von Tests
Die Statik verursacht ein echtes Problem, wenn mehrere Komponententests zusammen ausgeführt werden (z. B. mit Ihrem Continuous Integration-Server). Stellen Sie sich eine statische Karte von Netzwerk-Socket-Objekten vor, die von einem Test zum anderen geöffnet bleibt. Der erste Test öffnet möglicherweise einen Socket an Port 8080, aber Sie haben vergessen, die Karte zu löschen, wenn der Test abgerissen wird. Wenn jetzt ein zweiter Test gestartet wird, stürzt er wahrscheinlich ab, wenn versucht wird, einen neuen Socket für Port 8080 zu erstellen, da der Port noch belegt ist. Stellen Sie sich außerdem vor, dass Socket-Referenzen in Ihrer statischen Sammlung nicht entfernt werden und (mit Ausnahme von WeakHashMap) niemals zur Speicherbereinigung berechtigt sind, was zu einem Speicherverlust führt.
Dies ist ein übermäßig verallgemeinertes Beispiel, aber in großen Systemen tritt dieses Problem STÄNDIG auf. Die Leute denken nicht daran, dass Unit-Tests ihre Software wiederholt in derselben JVM starten und stoppen, aber es ist ein guter Test für Ihr Software-Design, und wenn Sie Bestrebungen nach hoher Verfügbarkeit haben, müssen Sie sich dessen bewusst sein.
Diese Probleme treten häufig bei Framework-Objekten auf, z. B. bei den Ebenen DB-Zugriff, Caching, Messaging und Protokollierung. Wenn Sie Java EE oder einige der besten Frameworks verwenden, verwalten diese wahrscheinlich viel davon für Sie, aber wenn Sie wie ich mit einem Legacy-System arbeiten, haben Sie möglicherweise viele benutzerdefinierte Frameworks, um auf diese Ebenen zuzugreifen.
Wenn sich die Systemkonfiguration, die für diese Framework-Komponenten gilt, zwischen Komponententests ändert und das Unit-Test-Framework die Komponenten nicht herunterfährt und neu erstellt, können diese Änderungen nicht wirksam werden. Wenn ein Test auf diesen Änderungen beruht, schlagen sie fehl .
Auch Nicht-Framework-Komponenten sind diesem Problem ausgesetzt. Stellen Sie sich eine statische Karte namens OpenOrders vor. Sie schreiben einen Test, der einige offene Aufträge erstellt, und prüfen, ob alle im richtigen Zustand sind. Anschließend endet der Test. Ein anderer Entwickler schreibt einen zweiten Test, der die benötigten Bestellungen in die OpenOrders-Karte einfügt und dann bestätigt, dass die Anzahl der Bestellungen korrekt ist. Diese Tests werden einzeln ausgeführt und bestehen beide. Wenn sie jedoch zusammen in einer Suite ausgeführt werden, schlagen sie fehl.
Schlimmer noch, ein Fehler kann auf der Reihenfolge basieren, in der die Tests ausgeführt wurden.
In diesem Fall vermeiden Sie durch die Vermeidung von Statik das Risiko, dass Daten über Testinstanzen hinweg erhalten bleiben, und gewährleisten so eine bessere Testzuverlässigkeit.
Subtile Fehler
Wenn Sie in einer Hochverfügbarkeitsumgebung oder an einem Ort arbeiten, an dem Threads möglicherweise gestartet und gestoppt werden, kann das oben erwähnte Problem mit Unit-Test-Suites auch dann auftreten, wenn Ihr Code in der Produktion ausgeführt wird.
Wenn Sie mit Threads arbeiten, anstatt ein statisches Objekt zum Speichern von Daten zu verwenden, ist es besser, ein Objekt zu verwenden, das während der Startphase des Threads initialisiert wurde. Auf diese Weise wird jedes Mal, wenn der Thread gestartet wird, eine neue Instanz des Objekts (mit einer möglicherweise neuen Konfiguration) erstellt, und Sie vermeiden, dass Daten von einer Instanz des Threads zur nächsten Instanz durchbluten.
Wenn ein Thread stirbt, wird ein statisches Objekt nicht zurückgesetzt oder Müll gesammelt. Stellen Sie sich vor, Sie haben einen Thread namens "EmailCustomers". Wenn er gestartet wird, wird eine statische String-Sammlung mit einer Liste von E-Mail-Adressen gefüllt und anschließend jede der Adressen per E-Mail gesendet. Nehmen wir an, der Thread wird unterbrochen oder irgendwie abgebrochen, sodass Ihr Hochverfügbarkeits-Framework den Thread neu startet. Wenn der Thread gestartet wird, wird die Kundenliste neu geladen. Da die Sammlung jedoch statisch ist, wird möglicherweise die Liste der E-Mail-Adressen aus der vorherigen Sammlung beibehalten. Jetzt erhalten einige Kunden möglicherweise doppelte E-Mails.
Nebenbei: Statisches Finale
Die Verwendung von "static final" ist effektiv das Java-Äquivalent einer C # -Definition, obwohl es technische Implementierungsunterschiede gibt. AC / C ++ #define wird vor der Kompilierung vom Vorprozessor aus dem Code ausgelagert. Ein Java "statisches Finale" wird am Ende auf dem Stapel gespeichert. Auf diese Weise ähnelt es eher einer "statischen const" -Variablen in C ++ als einer #define.
Zusammenfassung
Ich hoffe, dies erklärt einige grundlegende Gründe, warum Statik problematisch ist. Wenn Sie ein modernes Java-Framework wie Java EE oder Spring usw. verwenden, treten möglicherweise nicht viele dieser Situationen auf. Wenn Sie jedoch mit einer großen Anzahl von Legacy-Code arbeiten, können diese viel häufiger auftreten.
quelle
Da hat es niemand * erwähnt: Parallelität. Statische Variablen können Sie überraschen, wenn mehrere Threads in die statische Variable lesen und schreiben. Dies ist in Webanwendungen (z. B. ASP.NET) häufig und kann einige ziemlich verrückte Fehler verursachen. Wenn Sie beispielsweise eine statische Variable haben, die von einer Seite aktualisiert wird, und die Seite "fast zur gleichen Zeit" von zwei Personen angefordert wird, erhält ein Benutzer möglicherweise das vom anderen Benutzer erwartete Ergebnis oder noch schlimmer.
Ich hoffe, Sie sind bereit, Schlösser zu verwenden und mit Konflikten umzugehen.
* Eigentlich hat Preet Sangha es erwähnt.
quelle
Einige grundlegende Vor- und Nachteile der Verwendung statischer Methoden in Java zusammenfassen:
Vorteile:
Nachteile:
quelle
Sie müssen die Notwendigkeit, Daten in ein Objekt mit einem Status zu kapseln, gegen die Notwendigkeit abwägen, das Ergebnis einer Funktion für einige Daten einfach zu berechnen.
Kapselung auch. In großen Anwendungen erzeugt die Statik in der Regel Spaghetti-Code und ermöglicht kein leichtes Refactoring oder Testen.
Die anderen Antworten liefern ebenfalls gute Gründe gegen einen übermäßigen Einsatz von Statik.
quelle
Statische Variablen werden im Allgemeinen als schlecht angesehen, da sie den globalen Zustand darstellen und daher viel schwieriger zu begründen sind. Insbesondere brechen sie die Annahmen der objektorientierten Programmierung. Bei der objektorientierten Programmierung hat jedes Objekt seinen eigenen Status, der durch Instanzvariablen (nicht statisch) dargestellt wird. Statische Variablen repräsentieren den Zustand über Instanzen hinweg, was für Unit-Tests viel schwieriger sein kann. Dies liegt hauptsächlich daran, dass es schwieriger ist, Änderungen an statischen Variablen in einem einzigen Test zu isolieren.
Abgesehen davon ist es wichtig, zwischen regulären statischen Variablen (allgemein als schlecht angesehen) und endgültigen statischen Variablen (AKA-Konstanten; nicht so schlecht) zu unterscheiden.
quelle
Meiner Meinung nach geht es kaum um Leistung, sondern um Design. Ich halte die Verwendung statischer Methoden nicht für falsch, da statische Variablen verwendet werden (aber ich denke, Sie sprechen tatsächlich von Methodenaufrufen).
Es geht einfach darum, Logik zu isolieren und ihr einen guten Platz zu geben. Manchmal rechtfertigt dies die Verwendung statischer Methoden, wofür
java.lang.Math
ein gutes Beispiel ist. Ich denke, wenn Sie die meisten Ihrer Klassen benennenXxxUtil
oderXxxhelper
Ihr Design besser überdenken sollten.quelle
Ich habe gerade einige der in den Antworten gemachten Punkte zusammengefasst. Wenn Sie etwas falsch finden, können Sie es gerne korrigieren.
Skalierung: Wir haben genau eine Instanz einer statischen Variablen pro JVM. Angenommen, wir entwickeln ein Bibliotheksverwaltungssystem und haben beschlossen, den Namen des Buches als statische Variable festzulegen, da es nur eine pro Buch gibt. Aber wenn das System wächst und wir mehrere JVMs verwenden, haben wir keine Möglichkeit herauszufinden, mit welchem Buch wir es zu tun haben?
Thread-Sicherheit: Sowohl Instanzvariable als auch statische Variable müssen gesteuert werden, wenn sie in einer Umgebung mit mehreren Threads verwendet werden. Bei einer Instanzvariablen ist jedoch kein Schutz erforderlich, es sei denn, sie wird explizit von Threads gemeinsam genutzt. Bei einer statischen Variablen wird sie jedoch immer von allen Threads im Prozess gemeinsam genutzt.
Testen: Obwohl testbares Design nicht gleich gutes Design ist, werden wir selten ein gutes Design beobachten, das nicht testbar ist. Da statische Variablen den globalen Zustand darstellen und es sehr schwierig wird, sie zu testen.
Begründung zum Status: Wenn ich eine neue Instanz einer Klasse erstelle, können wir den Status dieser Instanz begründen. Wenn sie jedoch statische Variablen enthält, kann sie sich in einem beliebigen Status befinden. Warum? Weil es möglich ist, dass die statische Variable von einer anderen Instanz geändert wurde, da die statische Variable von mehreren Instanzen gemeinsam genutzt wird.
Serialisierung: Die Serialisierung funktioniert auch nicht gut mit ihnen.
Erzeugung und Zerstörung: Die Erzeugung und Zerstörung statischer Variablen kann nicht gesteuert werden. Normalerweise werden sie beim Laden und Entladen des Programms erstellt und zerstört. Dies bedeutet, dass sie für die Speicherverwaltung schlecht sind und auch die Initialisierungszeit beim Start addieren.
Aber was ist, wenn wir sie wirklich brauchen?
Aber manchmal brauchen wir sie wirklich. Wenn wir wirklich das Bedürfnis nach vielen statischen Variablen haben, die in der gesamten Anwendung gemeinsam genutzt werden, besteht eine Option darin, das Singleton-Entwurfsmuster zu verwenden, das alle diese Variablen enthält. Oder wir können ein Objekt erstellen, das diese statische Variable enthält und weitergegeben werden kann.
Auch wenn die statische Variable als endgültig markiert ist, wird sie zu einer Konstante und der ihr einmal zugewiesene Wert kann nicht geändert werden. Es bedeutet, dass es uns vor allen Problemen bewahren wird, mit denen wir aufgrund seiner Veränderlichkeit konfrontiert sind.
quelle
Mir scheint, Sie fragen nach statischen Variablen, aber Sie weisen in Ihren Beispielen auch auf statische Methoden hin.
Statische Variablen sind nicht böse - sie werden in den meisten Fällen als globale Variablen wie Konstanten in Kombination mit dem endgültigen Modifikator verwendet, aber wie gesagt, verwenden Sie sie nicht übermäßig.
Statische Methoden, auch als Utility-Methode bezeichnet. Es ist im Allgemeinen keine schlechte Praxis, sie zu verwenden, aber die größte Sorge ist, dass sie das Testen behindern könnten .
Als Beispiel für ein großartiges Java-Projekt, das viel Statik verwendet und es richtig macht, schauen Sie sich bitte Play! Rahmen . Es gibt auch Diskussionen darüber in SO.
Statische Variablen / Methoden in Kombination mit statischem Import werden auch häufig in Bibliotheken verwendet, die die deklarative Programmierung in Java erleichtern, z . B .: Make it easy oder Hamcrest . Ohne viele statische Variablen und Methoden wäre dies nicht möglich.
Statische Variablen (und Methoden) sind also gut, aber setzen Sie sie mit Bedacht ein!
quelle
Statische Variablen verursachen vor allem Probleme mit der Datensicherheit (jederzeit geändert, jeder kann Änderungen vornehmen, direkter Zugriff ohne Objekt usw.)
Für weitere Informationen lesen Sie diesen Dank.
quelle
Es kann vorgeschlagen werden, dass Sie in den meisten Fällen, in denen Sie eine statische Variable verwenden, wirklich das Singleton-Muster verwenden möchten .
Das Problem mit globalen Zuständen ist, dass manchmal das, was in einem einfacheren Kontext als global sinnvoll ist, in einem praktischen Kontext etwas flexibler sein muss, und hier wird das Singleton-Muster nützlich.
quelle
Noch ein Grund: Zerbrechlichkeit.
Wenn Sie eine Klasse haben, erwarten die meisten Leute, dass Sie sie erstellen und nach Belieben verwenden können.
Sie können dokumentieren, dass dies nicht der Fall ist, oder sich davor schützen (Singleton- / Factory-Muster) - aber das ist zusätzliche Arbeit und daher zusätzliche Kosten. Selbst dann, in einem großen Unternehmen, besteht die Möglichkeit, dass irgendwann jemand versucht, Ihre Klasse zu nutzen, ohne all die netten Kommentare oder die Fabrik vollständig zu beachten.
Wenn Sie häufig statische Variablen verwenden, wird dies nicht funktionieren. Bugs sind teuer.
Zwischen einer Leistungsverbesserung von 0,0001% und der Robustheit gegenüber Änderungen durch potenziell ahnungslose Entwickler ist in vielen Fällen die Robustheit die gute Wahl.
quelle
Ich finde statische Variablen bequemer zu verwenden. Und ich gehe davon aus, dass sie auch effizient sind (bitte korrigieren Sie mich, wenn ich falsch liege), denn wenn ich 10.000 Aufrufe einer Funktion innerhalb einer Klasse durchführen müsste, würde ich die Methode gerne statisch machen und eine einfache class.methodCall () verwenden. darauf, anstatt den Speicher mit 10.000 Instanzen der Klasse zu überladen, richtig?
Ich verstehe, was Sie denken, aber ein einfaches Singleton-Muster macht dasselbe, ohne 10 000 Objekte instanziieren zu müssen.
statische Methoden können verwendet werden, jedoch nur für Funktionen, die sich auf die Objektdomäne beziehen und keine internen Eigenschaften des Objekts benötigen oder verwenden.
Ex:
quelle
Class.Instance
) ist kaum besser als eine statische Variable. Es ist etwas testbarer, aber immer noch viel schlimmer als Designs, bei denen Sie zufällig eine einzelne Instanz erstellen, anstatt Ihren Code unter der Annahme zu erstellen, dass es nur eine gibt.Class.Instance
), der einen veränderlichen Zustand trägt, ist schlechtes Design IMO. In diesem Fall bevorzuge ich dringend ein Design, bei dem die Singletons, die ich verwenden muss, als Parameter an die Klasse übergeben werden, die sie verwendet (normalerweise mit Hilfe von DI). Logisch unveränderliche klassische Singletons sind IMO in Ordnung.Das Thema "Statik ist böse" ist eher ein Thema des globalen Staates. Die geeignete Zeit für eine statische Variable ist, wenn sie nie mehr als einen Zustand hat. IE-Tools, auf die das gesamte Framework zugreifen sollte und die immer dieselben Ergebnisse für dieselben Methodenaufrufe zurückgeben, sind niemals "böse" wie die Statik. Zu Ihrem Kommentar:
Statik ist die ideale und effiziente Wahl für Variablen / Klassen, die sich nie ändern .
Das Problem mit dem globalen Zustand ist die inhärente Inkonsistenz, die er erzeugen kann. In der Dokumentation zu Komponententests wird dieses Problem häufig behoben, da Ihre Komponententests immer dann unvollständig und nicht "einheitlich" sind, wenn ein globaler Status vorliegt, auf den mehr als mehrere nicht verwandte Objekte zugreifen können. Wie in diesem Artikel über den globalen Zustand und Singletons erwähnt , sollte A den Zustand von B nicht beeinflussen können, wenn Objekt A und B nicht miteinander verwandt sind (da in einem Objekt nicht ausdrücklich auf ein anderes Bezug genommen wird).
Es gibt einige Ausnahmen zum Verbot des globalen Zustands in gutem Code, wie z. B. die Uhr. Zeit ist global und ändert in gewissem Sinne den Zustand von Objekten, ohne eine codierte Beziehung zu haben.
quelle
Mein $ .02 ist, dass einige dieser Antworten das Problem verwirren, anstatt zu sagen, dass "Statik schlecht ist". Ich denke, es ist besser, über Umfang und Instanzen zu sprechen.
Was ich sagen würde, ist, dass eine Statik eine "Klassen" -Variable ist - sie repräsentiert einen Wert, der von allen Instanzen dieser Klasse gemeinsam genutzt wird. Normalerweise sollte der Umfang auch so sein (geschützt oder privat für die Klasse und ihre Instanzen).
Wenn Sie vorhaben, das Verhalten auf Klassenebene zu umgehen und es anderem Code auszusetzen, ist ein Singleton möglicherweise die bessere Lösung, um zukünftige Änderungen zu unterstützen (wie von @Jessica vorgeschlagen). Dies liegt daran, dass Sie Schnittstellen auf Instanz- / Singleton-Ebene auf eine Weise verwenden können, die Sie auf Klassenebene nicht verwenden können - insbesondere bei der Vererbung.
Einige Gedanken darüber, warum ich denke, dass einige Aspekte in anderen Antworten nicht der Kern der Frage sind ...
Statik ist nicht "global". In Java wird das Scoping getrennt von static / instance gesteuert.
Parallelität ist für die Statik nicht weniger gefährlich als Instanzmethoden. Es ist immer noch ein Zustand, der geschützt werden muss. Sicher, Sie haben vielleicht 1000 Instanzen mit jeweils einer Instanzvariablen und nur einer statischen Variablen, aber wenn der Code, auf den auf eine der beiden zugegriffen wird, nicht threadsicher geschrieben ist, sind Sie immer noch geschraubt - es kann nur etwas länger dauern, bis Sie ihn realisieren .
Das Management des Lebenszyklus ist ein interessantes Argument, aber ich denke, es ist weniger wichtig. Ich verstehe nicht, warum es schwieriger ist, ein Paar von Klassenmethoden wie init () / clear () zu verwalten, als eine Singleton-Instanz zu erstellen und zu zerstören. Einige könnten sogar sagen, dass ein Singleton aufgrund von GC etwas komplizierter ist.
PS: In Bezug auf Smalltalk haben viele seiner Dialekte Klassenvariablen, aber in Smalltalk sind Klassen tatsächlich Instanzen von Metaclass, also tatsächlich Variablen in der Metaclass-Instanz. Trotzdem würde ich die gleiche Faustregel anwenden. Wenn sie instanzübergreifend für den gemeinsamen Status verwendet werden, ist dies in Ordnung. Wenn sie öffentliche Funktionen unterstützen, sollten Sie sich einen Singleton ansehen. Seufz, ich vermisse Smalltalk wirklich ...
quelle
Ihr Beitrag enthält zwei Hauptfragen.
Zunächst zu statischen Variablen. Statische Variablen sind völlig unnötig und ihre Verwendung kann leicht vermieden werden. In OOP-Sprachen im Allgemeinen und in Java im Besonderen werden Funktionsparameter als Referenz angegeben. Wenn Sie also ein Objekt an eine Funktion übergeben, übergeben Sie einen Zeiger auf das Objekt, sodass Sie seitdem keine statischen Variablen definieren müssen Sie können einen Zeiger auf das Objekt an einen beliebigen Bereich übergeben, der diese Informationen benötigt. Selbst wenn dies impliziert, dass Sie Ihren Speicher mit Zeigern füllen, stellt dies keine schlechte Leistung dar, da die tatsächlichen Speicher-Pagging-Systeme dafür optimiert sind und die Seiten, auf die durch die Zeiger verwiesen wird, die Sie an die neuen übergeben haben, im Speicher bleiben Umfang; Die Verwendung statischer Variablen kann dazu führen, dass das System die Speicherseite lädt, auf der sie gespeichert sind, wenn auf sie zugegriffen werden muss (dies geschieht, wenn die Seite längere Zeit nicht aufgerufen wurde). Eine gute Vorgehensweise besteht darin, all diese statischen Daten in einigen kleinen "Konfigurationsklassen" zusammenzufassen. Dadurch wird sichergestellt, dass das System alles auf derselben Speicherseite ablegt.
Zweitens über statische Methoden. Statische Methoden sind nicht so schlecht, können aber die Leistung schnell reduzieren. Stellen Sie sich beispielsweise eine Methode vor, die zwei Objekte einer Klasse vergleicht und einen Wert zurückgibt, der angibt, welches der Objekte größer ist (typische Vergleichsmethode). Diese Methode kann statisch sein oder nicht. Beim Aufrufen ist die nicht statische Form jedoch effizienter da nur zwei Referenzen (eine für jedes Objekt) zu den drei Referenzen aufgelöst werden müssen, die die statische Version derselben Methode lösen müssen (eine für die Klasse plus zwei, eine für jedes Objekt). Aber wie gesagt, das ist nicht so schlimm. Wenn wir uns die Matheklasse ansehen, können wir viele Mathefunktionen finden, die als statische Methoden definiert sind. Dies ist wirklich effizienter, als alle diese Methoden in die Klasse aufzunehmen, die die Zahlen definiert.
Fazit: Vermeiden Sie die Verwendung statischer Variablen und finden Sie das richtige Leistungsgleichgewicht, wenn Sie mit statischen oder nicht statischen Methoden arbeiten.
PS: Entschuldigung für mein Englisch.
quelle
An statischen Variablen an sich ist nichts auszusetzen. Es ist nur die Java-Syntax, die kaputt ist. Jede Java-Klasse definiert tatsächlich zwei Strukturen - ein Singleton-Objekt, das statische Variablen kapselt, und eine Instanz. Das Definieren beider im selben Quellblock ist absolut böse und führt zu einem Code, der schwer zu lesen ist. Scala hat das richtig gemacht.
quelle
a) Grund für Programme.
Wenn Sie ein kleines bis mittelgroßes Programm haben, in dem auf die statische Variable Global.foo zugegriffen wird, kommt der Aufruf normalerweise aus dem Nichts - es gibt keinen Pfad und daher keine Zeitleiste, wie die Variable an den Ort kommt, an dem sie sich befindet wird genutzt. Woher weiß ich nun, wer es auf seinen tatsächlichen Wert gesetzt hat? Woher weiß ich, was passiert, wenn ich es gerade ändere? Ich habe über die gesamte Quelle grep, um alle Zugriffe zu sammeln, um zu wissen, was los ist.
Wenn Sie wissen, wie Sie es verwenden, weil Sie gerade den Code geschrieben haben, ist das Problem unsichtbar. Wenn Sie jedoch versuchen, Fremdcode zu verstehen, werden Sie verstehen.
b) Brauchen Sie wirklich nur einen?
Statische Variablen verhindern häufig, dass mehrere Programme derselben Art in derselben JVM mit unterschiedlichen Werten ausgeführt werden. Sie sehen häufig keine Verwendungen voraus, bei denen mehr als eine Instanz Ihres Programms nützlich ist. Wenn sich diese jedoch weiterentwickelt oder wenn sie für andere nützlich ist, kann es zu Situationen kommen, in denen sie mehr als eine Instanz Ihres Programms starten möchten .
Nur mehr oder weniger nutzloser Code, der von vielen Menschen über einen längeren Zeitraum nicht intensiv verwendet wird, passt möglicherweise gut zu statischen Variablen.
quelle
Alles (kann :) seinen Zweck haben, wenn Sie eine Reihe von Threads haben, die Daten gemeinsam nutzen / zwischenspeichern müssen , sowie den gesamten verfügbaren Speicher (damit Sie nicht in Kontexte innerhalb einer JVM aufgeteilt werden), ist die statische Aufladung die beste Wahl
-> natürlich können Sie dies erzwingen nur eine Instanz, aber warum?
Ich finde einige der Kommentare in diesem Thread böse, nicht die Statik;)
quelle
Statische Variablen sind weder gut noch böse. Sie stellen Attribute dar, die die gesamte Klasse und keine bestimmte Instanz beschreiben. Wenn Sie einen Zähler für alle Instanzen einer bestimmten Klasse benötigen, ist eine statische Variable der richtige Ort, um den Wert zu speichern.
Probleme treten auf, wenn Sie versuchen, statische Variablen zum Speichern instanzbezogener Werte zu verwenden.
quelle
Alle obigen Antworten zeigen, warum die Statik schlecht ist. Der Grund, warum sie böse sind, ist, dass sie den falschen Eindruck erwecken, dass Sie objektorientierten Code schreiben, obwohl dies nicht der Fall ist. Das ist einfach nur böse.
quelle
Hier gibt es viele gute Antworten, die dazu beitragen,
Speicher: Statische Variablen sind so lange aktiv, wie der Klassenlader [im Allgemeinen bis zum Absterben der VM] aktiv ist. Dies gilt jedoch nur für als statisch gespeicherte Massenobjekte / Referenzen.
Modularisierung: Berücksichtigen Sie Konzepte wie IOC, DependencyInjection, Proxy usw. Alle sind vollständig gegen eng gekoppelte / statische Implementierungen.
Andere Nachteile: Gewindesicherheit, Testbarkeit
quelle
Wenn Sie eine Anwendung mit vielen Benutzern haben und ein statisches Formular definiert haben, ändert jeder Benutzer auch alle anderen Formulare anderer Benutzer.
quelle
Ich denke, dass eine übermäßige Verwendung globaler Variablen mit statischem Schlüsselwort auch zu einem Speicherverlust an einem bestimmten Punkt in der Anwendung führt
quelle
Aus meiner Sicht
static
sollten Variablen nur schreibgeschützte Daten oder Variablen sein, die durch Konventionen erstellt wurden .Zum Beispiel haben wir eine Benutzeroberfläche eines Projekts und eine Liste von Ländern, Sprachen, Benutzerrollen usw. Und wir haben eine Klasse, um diese Daten zu organisieren. Wir sind absolut sicher, dass die App ohne diese Listen nicht funktioniert. Das erste, was wir bei App Init tun, ist, diese Liste auf Updates zu überprüfen und diese Liste von der API abzurufen (falls erforderlich). Wir sind uns also einig, dass diese Daten "immer" in der App vorhanden sind. Es handelt sich praktisch nur um schreibgeschützte Daten, sodass wir uns nicht um den Status kümmern müssen. Wenn wir über diesen Fall nachdenken, möchten wir wirklich nicht viele Instanzen dieser Daten haben. Dieser Fall scheint ein perfekter Kandidat zu sein, um statisch zu sein .
quelle
Ich habe viel mit Statik gespielt und darf ich Ihnen eine etwas andere Antwort geben - oder vielleicht eine etwas andere Sichtweise?
Wenn ich Statik in einer Klasse verwendet habe (Mitglieder und Methoden beide), bemerkte ich schließlich, dass meine Klasse tatsächlich zwei Klassen ist, die die Verantwortung teilen - es gibt den "statischen" Teil, der sich sehr ähnlich wie ein Singleton verhält, und den Nicht-Teil -statischer Teil (eine normale Klasse). Soweit ich weiß, können Sie diese beiden Klassen immer vollständig trennen, indem Sie einfach alle Statiken für eine Klasse und Nicht-Statiken für die andere auswählen.
Dies geschah häufig, wenn ich eine statische Sammlung in einer Klasse hatte, die Instanzen der Klasse und einige statische Methoden zum Verwalten der Sammlung enthielt. Wenn Sie einmal darüber nachgedacht haben, ist es offensichtlich, dass Ihre Klasse nicht "nur eine Sache" tut, sondern eine Sammlung ist und etwas völlig anderes tut.
Lassen Sie uns das Problem nun ein wenig umgestalten: Wenn Sie Ihre Klasse in eine Klasse aufteilen, in der alles statisch ist, und eine andere, die nur eine "normale Klasse" ist, und die "normale Klasse" vergessen, wird Ihre Frage zu einer rein statischen Klasse gegen Singleton wird hier ausführlich behandelt (und wahrscheinlich ein Dutzend anderer Fragen).
quelle