Hier finden Sie eine Liste von 10 Java -Codierungspraktiken, die subtiler sind als Josh Blochs effektive Java -Regeln. Im Vergleich zu Josh Blochs Liste, die leicht zu erlernen ist und sich auf tägliche Situationen konzentriert, enthält diese Liste Situationen, in denen ungewöhnliche Dinge in API/SPI -Design beteiligt sind, die möglicherweise einen großen Einfluss haben.
Ich habe diese beim Schreiben und Pflegen von JOOQ (SQL modellierte interne DSL in Java) begegnet. Als interne DSL fordert JOOQ Java -Compiler und Generika in größtem Maße heraus und kombiniert Generika, veränderliche Parameter und Überlastungen, die Josh Bloch möglicherweise nicht für eine so breite API empfiehlt.
Lassen Sie mich 10 subtile Best Practices für die Java -Codierung mit Ihnen teilen:
1. Erinnern Sie sich an den Destruktor von C ++
Erinnerst du dich an den Destruktor von C ++? Erinnerst du dich nicht? Dann haben Sie wirklich Glück, weil Sie die Speicherlecks aufgrund des Speicheres nicht debuggen müssen, nachdem die Löschung der Objekte nicht veröffentlicht wurde. Vielen Dank an Sun/Oracle für den Mechanismus für die Müllsammlung!
Trotzdem bietet der Destruktor eine interessante Funktion. Es versteht die inverse Zuteilungsreihenfolge zum freien Speicher. Denken Sie daran, dass dies bei Java der Fall ist, wenn Sie die Klassen -Destructor -Syntax betreiben:
Es gibt verschiedene andere Anwendungsfälle. Hier ist ein konkretes Beispiel für die Implementierung einiger SPI von Event -Hörern:
@Overridepublic void preevent (eventContext e) {Super.beforevent (e); // Supercode vor meinem Code} @Overridepublic void attode (eventContext e) {// Super Code nach meinem Code Super.afterEvent (e);} Das berüchtigte Problem des Philosophen ist ein weiteres gutes Beispiel dafür, warum es wichtig ist. Fragen zu Philosophen Dining finden Sie unter den Link:
http://adit.io/posts/2013-05-11- the-dining-philosophers-problem-with-ron-swanson.html
Regel : Wenn Sie vorher/nachher verwenden, die Semantik zuweisen/freien/zurückgeben, um Logik zu implementieren, überlegen Sie, ob Sie nach/kostenlosen/zurückgegebenen Vorgängen in umgekehrter Reihenfolge ausgeführt werden sollen.
Stellen Sie Kunden SPI -Methoden zur Verfügung, die es ihnen leicht machen, in Ihre Bibliothek/Ihren Code ein benutzerdefiniertes Verhalten zu verleihen. Achten Sie darauf, dass Ihre SPI -Evolutionsurteile Sie möglicherweise verwirren und Sie denken, dass Sie (nicht) zusätzliche Parameter benötigen. Natürlich sollten Funktionen nicht zu früh hinzugefügt werden. Sobald Sie Ihren SPI veröffentlichen, werden Sie Ihr SPI, sobald Sie sich für die Semantic Versioning beschließen, wirklich einen dummen einzelnen Parameter hinzugefügt, wenn Sie feststellen, dass Sie in einigen Fällen möglicherweise einen anderen Parameter benötigen:
Interface EventListener {// Bad void Message (String -Nachricht);}Was ist, wenn Sie eine Nachrichten -ID und Nachrichtenquelle benötigen? Die API -Evolution hindert Sie daran, den oben genannten Typen Parameter hinzuzufügen. Natürlich können Sie mit Java 8 eine Verteidigungsmethode hinzufügen, die Ihre schlechten Designentscheidungen in den frühen Tagen "verteidigt":
Interface EventListener {// schlechte Standard -void -Nachricht (String -Nachricht) {message (message, null, null); } // Besser? void meldung (String -Nachricht, Ganzzahl -ID, MessageSource Quelle);} Beachten Sie, dass die Verteidigungsmethode leider den endgültigen Modifikator nicht verwenden kann.
Es wäre jedoch viel besser, Kontextobjekte (oder Parameterobjekte) zu verwenden, als Ihre SPI mit vielen Methoden zu verschmutzen.
Schnittstelle messageContext {String message (); Ganzzahl id (); MessageSource Source ();} Interface EventListener {// fantastisch! void meldung (MessageContext -Kontext);} Sie können die MessageContext -API leichter entwickeln als Eventlistner SPI, da nur wenige Benutzer sie implementieren.
Regel : Wenn ein SPI angegeben wird, sollten Sie ein Kontext/Parameterobjekt verwenden, anstatt Methoden mit festen Parametern zu schreiben.
Hinweis : Es ist auch eine gute Idee, die Ergebnisse durch einen dedizierten Messageresult -Typ auszutauschen, der mit der Builder -API konstruiert werden kann. Dies wird die Flexibilität der SPI -Evolution erheblich erhöhen.
Swing -Programmierer drücken normalerweise nur ein paar Abkürzungsschlüssel, um Hunderte oder Tausende von anonymen Klassen zu erzeugen. In den meisten Fällen schadet es nicht, dies zu tun, solange die Schnittstelle befolgt wird und der SPI -Subtyp -Lebenszyklus nicht verletzt wird. Verwenden Sie jedoch aus einem einfachen Grund keine anonymen, lokalen oder internen Klassen häufig - sie speichern Verweise auf externe Klassen. Denn egal wohin sie gehen, externe Kategorien müssen folgen. Wenn der externe Betrieb einer lokalen Klasse beispielsweise unangemessen ist, wird der gesamte Objektdiagramm subtile Änderungen erfährt, was zu Leckagen von Speicher verursacht wird.
Regel: Bevor Sie anonyme, lokale oder interne Klassen schreiben, denken Sie bitte zweimal darüber nach, ob Sie sie in statische oder gewöhnliche Klassen auf höchstem Niveau umwandeln können, um ihre Objekte auf eine Domäne auf äußerer Ebene zurückzugeben.
HINWEIS: Verwenden Sie doppelte lockige Klammern, um einfache Objekte zu initialisieren:
New HashMap <String, String> () {{put ("1", "a"); put ("2", "b");}}Diese Methode verwendet den in der JLS §8.6 -Spezifikation beschriebenen Instanzinitialisierer. Es sieht auf der Oberfläche gut aus, wird aber tatsächlich nicht empfohlen. Wenn Sie ein völlig unabhängiges HashMap -Objekt verwenden, wird die Instanz nicht immer Verweise auf externe Objekte enthalten. Darüber hinaus ermöglicht dies dem Klassenlader mehr Klassen.
Das Tempo von Java8 nähert sich. Mit Java 8 bringt Lambda -Ausdrücke, ob es Ihnen gefällt oder nicht. Obwohl es Ihren API -Benutzern es mag, sollten Sie besser sicherstellen, dass sie es so oft wie möglich verwenden können. Wenn Ihre API also nicht einfache "Skalar" -Typen wie INT, Long, String und Datum empfängt, erhalten Sie Ihre API so oft wie möglich.
Was ist Sam? SAM ist eine einzelne abstrakte Methode [Typ]. Auch als Funktionsschnittstelle bezeichnet, wird es bald als @FunctionAnInterface kommentiert. Dies entspricht Regel 2, EventListener ist eigentlich ein Sam. Der beste SAM hat nur einen Parameter, da dies das Schreiben von Lambda -Ausdrücken weiter vereinfacht. Stellen Sie sich vor, Sie schreiben
listener.add (c -> system.out.println (c.message ()));
Ersetzen
listeners.add (new EventListener () {@Override public void message (messageContext c) {System.out.println (C.Message ())); }});Stellen Sie sich vor, Sie behandeln XML in Joox. Joox enthält viel Sam:
$ (Dokument) // Elemente mit einer ID .Find (c -> $ (c) .id ()!
Regeln: Seien Sie nett zu Ihren API -Benutzern, schreiben Sie von nun an SAM/Function -Schnittstellen.
Hinweis: Es gibt viele interessante Blogs über Java8 Lambda -Ausdrücke und verbesserte Sammlungs -API:
Ich habe 1 oder 2 Artikel über Java Nulls geschrieben und auch die Einführung neuer optionaler Klassen in Java 8 erläutert. Aus akademischer oder praktischer Sicht sind diese Themen sehr interessant.
Obwohl Null und NullPointerexception in dieser Phase immer noch ein Fehler in Java sind, können Sie dennoch eine API entwerfen, die keine Probleme hat. Beim Entwerfen von APIs sollten Sie es vermeiden, Null so weit wie möglich zurückzugeben, da Ihre Benutzer die Methode in einer Kette aufrufen können:
initialise (someargument) .calculate (Daten) .Dispatch ();
Wie aus dem obigen Code ersichtlich ist, sollte keine Methode NULL zurückgeben. In der Tat wird die Verwendung von NULL normalerweise als vergleichbare Heterogenität angesehen. Die Bibliothek wie JQuery oder Joox hat Null auf iterierbaren Objekten vollständig aufgegeben.
Null wird normalerweise bei der verzögerten Initialisierung verwendet. In vielen Fällen sollte auch eine verzögerte Initialisierung vermieden werden, ohne die Leistung stark zu beeinflussen. Wenn die beteiligte Datenstruktur zu groß ist, sollte die Verzögerungsinitialisierung mit Vorsicht verwendet werden.
Regel: Methoden sollten vermeiden, Null zurückzugeben, wann immer sie sind. Null wird nur zur Darstellung der Semantik von "nicht initialisiert" oder "nicht existiert" verwendet.
Obwohl es in einigen Fällen in Ordnung ist, eine Methode mit einem Nullwert zurückzugeben, geben Sie niemals ein leeres Array oder eine leere Sammlung zurück! Bitte beachten Sie die Methode java.io.file.list (). Sie ist so gestaltet:
Diese Methode gibt eine Reihe von Zeichenfolgen für alle Dateien oder Verzeichnisse im angegebenen Verzeichnis zurück. Wenn das Verzeichnis leer ist, ist das zurückgegebene Array leer. Null zurückgeben, wenn der angegebene Pfad nicht vorhanden ist oder ein E/A -Fehler auftritt.
Daher wird diese Methode normalerweise so verwendet:
Dateiverzeichnis = // ... if (verzeichnis.isdirectory ()) {string [] list = diritees.list (); if (list! = null) {für (String -Datei: Liste) {// ...}}}Denken Sie, dass Null -Check notwendig ist? Die meisten E/A -Operationen erzeugen IOExceptions, aber diese Methode gibt nur NULL zurück. Null kann keine E/A -Fehlermeldungen speichern. Daher hat ein solches Design die folgenden drei Mängel:
Wenn Sie sich das Problem aus der Perspektive von Sammlungen ansehen, ist ein leeres Array oder eine leere Sammlung die beste Implementierung von "nicht existiert". Die Rückgabe eines leeren Arrays oder einer Sammlung ist von geringer praktischer Bedeutung, es sei denn, es wird zur verzögerten Initialisierung verwendet.
Regel: Das zurückgegebene Array oder die zurückgegebene Sammlung sollte nicht null sein.
Der Vorteil von HTTP ist ein Stauro. Alle relevanten Staaten werden in jeder Anfrage und Antwort übertragen. Dies ist die Essenz der Ruhe Benennung: Zustandsübertragung (Repräsentationsstatusübertragung). Es ist auch toll, dies in Java zu tun. Denken Sie aus der Perspektive von Regel 2 darüber nach, wenn die Methode das Statusparameterobjekt empfängt. Wenn der Staat durch ein solches Objekt übertragen wird, anstatt den Staat von außen zu betreiben, werden die Dinge einfacher. Nehmen Sie JDBC als Beispiel. Das folgende Beispiel liest einen Cursor aus einem gespeicherten Programm.
CALLABLESTATEMENT S = connection.preeRecall ("{? = ...}"); // verbose Manipulation des Aussagezustands: S. RegisterOutParameter (1, Cursor); ssetstring (2, "abc"); S. execute ();Dies macht die JDBC -API so seltsam. Jedes Objekt ist staatlich und schwer zu bedienen. Insbesondere gibt es zwei Hauptprobleme:
Regeln: Weitere Implementierungen im funktionalen Stil. Übertragungszustand durch Methodenparameter. Sehr wenige operative Objektzustände.
Dies ist eine relativ einfache Möglichkeit zu bedienen. In relativ komplexen Objektsystemen können Sie erhebliche Leistungsverbesserungen erzielen, solange Sie zuerst die gleichen Beurteilungen in der Equals () -Methode aller Objekte fällen:
@Overridepublic Boolean Equals (Object Other) {if (this == other) return true; // Andere Logik für Gleichheitsurteilungen ...}Beachten Sie, dass andere Kurzschlussüberprüfungen möglicherweise Nullwertprüfungen beinhalten, daher sollten sie auch hinzugefügt werden:
@Overridepublic Boolean Equals (Object Other) {if (this == other) return true; if (other == null) return false; // Rest der Gleichstellungslogik ...}Regel: Verwenden Sie Kurzschaltungen in allen Ihren Equals () -Methoden, um die Leistung zu verbessern.
Einige Menschen können damit nicht einverstanden sind, da die Verhandlung der Methode gegen das Finale der Gewohnheit der Java -Entwickler widerspricht. Wenn Sie jedoch die vollständige Kontrolle über den Code haben, ist es sicherlich korrekt, die Methode standardmäßig zu gestalten:
Dies ist besonders für statische Methoden geeignet, in diesem Fall "Overlay" (tatsächlich Okklusion) funktioniert kaum. Ich bin kürzlich auf ein Beispiel für eine sehr schlechte statische Methode in Apache Tika gestoßen. Schauen Sie sich an:
TikainputStream erweitert TaggeDInputStream, um seine statische Get () -Methode mit einer relativ anderen Implementierung zu maskieren.
Im Gegensatz zu herkömmlichen Methoden können statische Methoden nicht miteinander überschrieben werden, da der Aufruf zum Kompilierungszeit an den Aufruf der statischen Methode gebunden ist. Wenn Sie Pech haben, können Sie versehentlich die falsche Methode erhalten.
Regel: Wenn Sie die vollständige Kontrolle über Ihre API haben, stellen Sie so viele Methoden wie möglich für die endgültige Auswahl an.
In besonderen Anlässen können Sie die Variable-Parameter-Methode "Accept-All" verwenden, um ein Objekt zu empfangen ... Parameter:
void AcceptAll (Objekt ... alle);
Das Schreiben einer solchen Methode bringt dem Java -Ökosystem ein kleines JavaScript -Gefühl. Natürlich möchten Sie den tatsächlichen Typ basierend auf der tatsächlichen Situation wie String einschränken. Da Sie nicht zu viel einschränken möchten, denken Sie vielleicht eine gute Idee, Objekt durch generisches T zu ersetzen:
void Acceptall (t ... alle);
Aber nicht. T wird immer als Objekt abgeleitet. Tatsächlich denken Sie möglicherweise nur, dass Generika in den oben genannten Methoden nicht verwendet werden können. Noch wichtiger ist, Sie könnten denken, Sie können die obige Methode überladen, aber Sie können nicht:
void AcceptAll (t ... alle); void AcceptAll (String -Nachricht, t ... alle);
Dies sieht so aus, als ob Sie optional eine String -Nachricht an die Methode übergeben können. Aber was passiert mit diesem Anruf?
AcceptAll ("Nachricht", 123, "ABC");Der Compiler färbt sich als <? Erweitert serialisierbar und vergleichbar <? >>, was den Anruf unklar macht!
Wenn Sie also eine "Akzeptanz" -Signatur haben (auch wenn es ein Generika ist), können Sie sie niemals überladen. API -Verbraucher lassen den Compiler nur "versehentlich" die "richtige" Methode auswählen, wenn sie Glück haben. Es ist jedoch auch möglich, die Methode der Akzeptanz zu verwenden oder keine Methode aufzurufen.
Regel : Vermeiden Sie nach Möglichkeit "Akzeptieren" -Sypaturen. Wenn nicht, überladen Sie eine solche Methode nicht.
Java ist ein Tier. Im Gegensatz zu anderen idealistischeren Sprachen entwickelte es sich langsam zu dem, was es heute ist. Dies mag eine gute Sache sein, denn bei der Geschwindigkeit der Java -Entwicklung gibt es bereits Hunderte von Warnungen, und diese Warnungen können nur durch jahrelange Erfahrung erfasst werden.
Seien Sie gespannt auf weitere der zehn besten Listen zu diesem Thema!
Original -Link: JOOQ -Übersetzung: ImportNew.com - Sieben
Übersetzungslink: http://www.importnew.com/10138.html
[Bitte behalten Sie die ursprüngliche Quelle, den Übersetzer und den Übersetzungslink beim Nachdruck. ]