Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Monday, November 17, 2008

I will start at the Paderborn Center for Parallel Computing

With high probability, I will start as research assistent (aka PhD student) at the Paderborn Center for Parallel Computing.

There I will research in the area of storage systems, especially on the topic "data deduplication". It is really nice to be able to continue and complete the work of my master thesis.

Sunday, October 12, 2008

Java Anwendung braucht sehr viel Speicher!

Ich wußte ja immer, dass Java etwas mehr Hauptspeicher benötigt als z.B. C/C++-Programme, aber dieses Programm will schon etwas sehr viel:

javamem.tiff

Wednesday, June 11, 2008

Zusammenarbeit Java und Python

Einer der Vorteile von Python ist die gute Integration von C-Bibliotheken z.B. um einzelne Funktionen aus Performancegründen auszulagern. Die Zusammenarbeit mit Java ist viel schwieriger. Es gibt mehrere Gründe, warum eine bessere Zusammenarbeit wünschenswert ist. Zum einen sind die sehr umfangreichen Bibliotheken ein riesiger Vorteil der Java-Welt, auf der anderen Seite könnte man eine Verbindung mit Java-Legacy-Anwendungen aus Python heraus herstellen wollen.

Es gibt mit Jython eine Python-Implementierung auf der JVM, die natürlich sehr einfach mit Java-Bibliotheken und -Programmen zusammenarbeiten kann. Jython hat in letzter Zeit einige Fortschritte erzielt, aber die Implementierung ist immer noch Jahre hinter der aktuellen Entwicklung der Sprache hinterher. In vielen Situationen scheidet Jython als Alternative einfach aus.

Eine Lösung, die ich seit einigen Wochen verwende, ist JPype. JPype erlaubt aus normalen (C-)Python heraus Zugriff auf Java-Bibliotheken und integriert diese gar nicht schlecht. Ein Beispiel: import jpype jpype.startJVM(jpype.getDefaultJVMPath(), "-Djava.class.path=%s" % classpath) java = jype.JPackage("java") de = jype.JPackage("de") contact = de.dirkmeister.example.Contact(surname = "Meister") contact.mail = "XYZ@XYZ.de" contact.sendMail("Test-Mail") java.lang.System.out.println("Mail sent") Die Integrationg geht soweit, dass Java’s hashCode() Methode als __hash__, equals() als __eq__ und toString() als __str__ verwendet werden, so dass man quasi kaum merkt, ob ein Objekt nur ein Python- oder ein “Java”-Objekt ist. Selbst Getter- und Setter werden als Python-Properties integriert und sogar Exceptions werden transparent umgesetzt. Auch Swing-Anwendungen sollen einfach möglich sein.

Nachdem die Hürde der Installation überwunden hat, gestaltet ich die Arbeit erstaunlich flüssig und problemlos.

Ich verwende JPype um in Python auf das Hadoop Distributed File System (HDFS) zu zugreifen und dabei die volle FileSystem-API auf eine pythonifizierte Art zu verwenden z.B. sollen die Stream-Objekte als “File-like Objects” integriert werden. Den Code will ich in ein paar Wochen auf hier vorstellen.

Saturday, June 07, 2008

Google TechTalk: Fujaba-Storydiagramme

Fujaba-Storydriven Modeling (SDM) sind ein an der Uni Paderborn entwickelter Ansatz für General-Purpose-, visuelle, rein-modelbasierte Softwareentwicklung. Mit General-Purpose meine ich, dass es eine Modelsyntax für alle Domainen gibt, also kein unterschiedliches Meta-Modell für unterschiedliche Domains (DSL-Ansatz) mit jeweils problem-spezifischen Code-Generatoren. Das Story in SDM hat auch keinen Zusammenhang mit den Userstories in XP, ganz bestimmt nicht. SDM haben wir in der Vorlesung "Modellbasierte Softwareentwicklung" behandelt.

Gefallen hat mir der Ansatz nie: Zu unkompakt, komplex und unübersichtlich war mir immer die Syntax. Alle Beispiele aus der Vorlesung habe ich in Python und Java nachprogrammiert. Und Python war immer wesentlich lesbarer und kürzer als die Storydiagramme, Java auch in einigen Fällen. Der aus Storydiagrammen generierte Java-Code spottet jeder Beschreibung. Es wurde zwar in der Vorlesung behauptet, dass erfahrende Java-Entwickler Storydiagramme genau so in Code übersetzen würden, aber dann bin ich wohl nicht "erfahren".

Für mich waren Storydiagramme nicht das Silver Bullet, für das einige diese Modellierungssprache offensichtlich halten. Auch insgesamt bin ich von rein-modellbasierter Softwareentwicklung (das berühmte Zitat "Programmierer sind dumm" stammt auch aus der Vorlesung) nicht wirklich überzeugt.

Aber es gibt offensichtlich doch Leute, die sich dafür interessieren.
Der "Erfinder" Alfred Zundorf hat letzte Woche einen Google TechTalk gehalten:

Friday, May 30, 2008

Born Brave Developer

brave-developer.jpg

Keine Ahnung, wo es ursprünglich herkommt. Fatal Exception war der früheste Eintrag mit dem Foto, dass ich gefunden habe.

Via JRoller-Shared Memory-Blog

Thursday, May 15, 2008

Interview zu "Effective Java - Second Edition"

InfoQ hat ein Interview mit Josh Bloch über „Effective Java – Second Edition" und ein Probekapitel des neuen Buches veröffentlicht.

Etwas Balsam für meine Seele - nachdem ich ein bestimmtes Open-Source Projekt nach ca. einem Jahr nun endgültig frustiert verlassen habe - ist folgender Rat:

InfoQ: What are the core principles and key takeaway points that Effective Java aims to convey to the reader? Joshua Bloch: Same as it ever was: Always strive to write simple, clear, and correct programs. It is penny wise and pound foolish to do otherwise. Style does matter. It pays real dividends in terms of correctness, usability, robustness, and maintainability. Also, it's way more fun to write good programs than bad ones.
Auf der Webseite des Publishers ist der 8. Mai als Veröffentlichungdatum eingetragen. Bei amazon.de steht immer noch 28. Juni. So lange kann es also nicht mehr dauern bis das Buch bei mir ankommt.

Tuesday, March 11, 2008

Jolt Award: Beautiful Code and Guice

Gerade bei der Java Posse gehört:

Das Buch Beautiful Code, dass ich vor ein paar Wochen empfohlen habe, hat einen Jolt Award 2008 gewonnen. Cool.

Wunderbar und höchst verdient: Das Dependency Injection Tool Guice hat ebenfalls einen Jolt Award gewonnen.

Durch Guice hatte ich die Idee für das neue Konfigurationssystem (das alte war im Grunde unbrauchbar) von Shox. Zuerst wollte ich Guice direkt verwenden, aber weil der Fokus doch stärker auf Konfiguration von Attributen lag, Dependency Injection dabei nur ein angenehmer Nebeneffekt war und ein paar Überlegungen mehr ist es dann doch zu dem @ShoxParameter System gekommen. Guice ist richtig, richtig cool.

Tuesday, January 08, 2008

Java Urban Legend: "Java kann kein Multi-Core"

Es ist immer wieder erstaunlich, welche Urban Legends sich über Java halten.

"Java ist langsam, zu langsam" ist davon noch der absolute Klassiker. Aktuelles Beispiel sind die Kommentare in "TWiT 126" vor ein paar Wochen.

So langsam sollte sich diese Legende doch wirklich in Luft aufgelöst haben, oder? Naja, wenn nicht, dann sollte man schnell Brian Goetz Kolumne "Java theory and practice" lesen. Insbesondere Urban performance legends und Urban performance legends, revisted. Dort heisst es zum Beispiel:

You don't have to search through too many blogs or Slashdot postings to find confidently worded statements like "Garbage collection will never be as efficient as direct memory management." And, in a way, those statements are right -- dynamic memory management is not as fast -- it's often considerably faster. The malloc/free approach deals with blocks of memory one at a time, whereas the garbage collection approach tends to deal with memory management in large batches, yielding more opportunities for optimization (at the cost of some loss in predictability).

Eine andere Legende ist, dass Java keine Multi-Prozessoren ausnutzen kann, weil es nur User-Level-Threads hat. Mir war bis heute nicht klar, dass diese Legende wirklich jemand glaubt. Aber dann hat mich ein Kommolitone, der eigentlich weiß was er tut, eines besseren belehrt.

Vermittelt wird diese Legende offenbar durch eigentlich gute Autoren wie Tanenbaum. Wenn man nach "Java User-Level" bei Google sucht, dann findet man Dutzende von Betriebssystem-Kursen, in denen genau dies gelehrt wird z.B. Uni Dortmund, Uni Mannheim und selbst, Oh Schreck, Uni Paderborn. Da muß ich wohl in KMS mal geschlafen haben. Selbst in der Wikipedia ist dies nicht eindeutig beantwortet.

Offenbar ist der Hintergrund für die Legende, dass viele immer noch bei Java 1.1. hängengeblieben sind. Dort gab es (nur) so genannte Green Threads, dies waren Javas User-Level-Threads.
Aber diese Beschränkung wurde schon mit 1.2 aufgehoben. Also vor mehr als 10 Jahren!

In "Programmieren mit Java" heisst es:

Derzeit werden Java-Threads folgendermaßen unter Windows, Solaris und Linux implementiert:
  • Unter Windows NT/2000/XP werden Java-Threads auf Threads im Betriebssystems abgebildet, die innerhalb des Virtual-Machine-Prozesses ablaufen. Die Anzahl der gestarteten Prozesse und Threads kann auf dieser Plattform gut im Task-Manager beobachtet werden. Beim Starten eines Java-Programms sieht man, dass sich die Anzahl der Prozesse um Eins erhöht, die Anzahl der Threads nimmt dagegen stärker zu. Dies ist auch dann der Fall, wenn das Programm selbst keine eigenen Threads erzeugt, weil die Virtual Machine eigene System-Threads für Aufgaben wie Garbage Collection startet.
  • Bei Solaris werden seit JDK-Version 1.2 die nativen Threads eingesetzt. In den Vorversionen machte die Solaris-Virtual Machine von Green Threads Gebrauch.
  • Unter Linux werden Threads seit JDK-Version 1.3 mit abgespalteten Tochterprozessen realisiert. Zuvor wurden auch hier Green Threads verwendet. Dadurch, dass für Java-Threads komplette Prozesse erzeugt werden, kann die Erzeugung einer großen Anzahl neuer Threads länger dauern als bei Betriebssystemen, auf denen native Threads eingesetzt werden.
Ein Blick in die Aktivitätsanzeige oder "top" hätte die Behauptung also widerlegen können.

Ans Extrem treibt es zum Beispiel die Firma "Azul Systems", die Werbung macht mit "World's Most Powerful Platform for Java Computing: Massive Capacity: 768 Processors and 768 GB of Memory".

Also: Ich hoffe, ich kann zumindest bei den Lesern dieses Blogs diese Legende aus der Welt schaffen.

Bei Autoren/Forschern aus dem Systemsoftware-Bereich bin ich schon lange sehr vorsichtig, wenn diese sich zu Software-Engineering-Themen äußern. So sehr die Autoren ihren eigenen Kram in- und auswendig kennen, so viel Mist über SE ist manchmal auch enthalten. Bestes Beispiel: "Reliable Distributed Systems". Teilweise recht grausam. Ein Paper, dass schön Distributed Computing und Software Engineering verbindet ist z.B. dieses hier.

Saturday, December 22, 2007

Artikel über Unittesting (in .NET)

Als ich noch vor zwei Wochen bei dem Wort "JUnit" angeguckt wurde wie ein Alien und nachdem etwas schief gegangen ist hämische Bemerkungen kamen, habe ich wieder bemerkt, dass Unittesting von den meisten Uni-Absolventen immer noch nicht wirklich wahrgenommen wird.

Da kommt es mir gerade gelegen, dass Karl Sequin diese Woche einen ausführlichen Artikel über die Unittesting veröffentlicht hat. Die von ihm vorgestellte Toolchain ist zwar auf .NET ausgerichtet, aber ersetze NUnit durch JUnit und RickoMock durch jMock und auch der Toolchain-Abschnitt ist weiterhin grundsätzlich gültig:

Sein Fazit ist:

But after my first project [using unit testing], I learnt a lot about what did and didn't work. One thing that immediately became clear was how much cleaner my code became. I quickly came to realize that if something was hard to test and I rewrote it to make it more testable, the entire code became more readable, better decoupled and overall easier to work with. The best advice I can give is to start small, experiment with a variety of techniques, don't be afraid to fail and learn from your mistakes. And of course, don't wait until your project is complete to unit test – write them as you go!

Der Artikel ist Teil der Serie "Foundations of Programming", die insgesamt sehr lesenswert ist.

Sunday, December 09, 2007

Ahlblog: Der Wert kleiner Klassen

Eine Schande, dass ich das "Ahlblog" von Martin Ahlborg nicht schön früher erwähnt und verlinkt habe. Die kleinen Programmiertipps und seine Gebote für Programmierer sind schon lange eine Erwähnung wert gewesen.

Herausgepickt habe ich mir hier den Artikel "Klein aber Fein - Der Wert kleiner Klassen" vom 3. September. In diesem Artikel beschreibt der Autor die Daumenregel, dass Klassen nicht zu groß werden sollten.

Klassen können [...] zu groß werden, weil sie borgmäßig Aufgaben von anderen Klassen assimilieren. In vertikaler Richtung kann eine Klasse beispielsweise gleichermaßen Highlevel- und Lowlevel-Funktionen haben. Dieses Problem findet man oft bei Kommunikationsschnittstellen. Da gibt es dann eine Klasse, die sowohl die ganzen Lowlevel-Kommunikationsdetails kapselt als auch Funktionen, die eigentlich ins Datenmodell gehören. In horizontaler Richtung kann eine Klasse Funktionalität implementieren, die eigentlich auf mehrere Klassen aufgeteilt werden sollte. Man kann dies gut erkennen, wenn man einen Teil der Klasse entfernen kann, ohne den Rest kaputt zu machen.
Was bringen mir eigentlich kleine Klassen? Nun, hauptsächlich wird man flexibler. Programme lassen sich leichter erweitern, da sich Erweiterungen auf die Implementation neuer Klassen und hier und da ein paar kleinen Codeänderungen beschränken. Ebenso erhöht sich die Wiederverwendbarkeit des Codes. Dadurch kommen weniger Codewiederholungen vor, eine der Hauptursachen für so richtig fiese Bugs.
Kleine Klassen lassen sich auch besser testen. Große Klassen tendieren dazu, viel Funktionalität intern zu kapseln. Es gibt keine Möglichkeit diese Funktionen direkt zu testen. Nutzt diese Klasse jedoch andere Klassen, die jeweils einen Teilbereich der Funktionen implementieren, dann lässt sich dieses Konglomerat besser testen, weil ich damit quasi Testschnittstellen geschaffen habe. Ich kann jetzt interne Funktionen testen, ohne dass ich Raum und Zeit verbiegen muss.

Meiner Erfahrung nach, ist die Regel, dass eine Klasse nur reine Aufgabe haben sollte, die entscheidendere und passendere Daumenregel. Eine Folge dieser Regel sind dann meist kleinere Klassen. Deshalb störe ich mich auch etwas an dem Hinweis auf 400 Lines of Code (aber auch der Autor sieht es nur als ungefähren Wert). Dennoch ein gut geschriebener, lesenswerter Artikel wie (ich befürchte ich wiederhole mich) viele Artikel in dem Blog.
"Head First Design Pattern" nennt diese Regel "Eine Klasse sollte nur einen Grund haben sich zu ändern", aber im Endeffekt läuft es auf das Gleiche hinaus.

Friday, October 26, 2007

Bad News: Kein Java 6 in Leopard

Von Blue’s Blog gestiftete Leopard-Konsolenausgabe:

Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_13-b05-237)
Java HotSpot(TM) Client VM (build 1.5.0_13-119, mixed mode, sharing)

Apple hat keine neue Java-Version zusammen mit Leopard veröffentlicht.

Naja, irgendwie war damit zu rechnen, weil Java 6 schon nicht in der 300 Einträe umfassenden Neuerungenliste von Leopard aufgetaucht war, aber enttäuschend ist es schon. Java 6 ist nun nicht brandneu (schon über ein Jahr) und bald werden wohl auch die ersten Projekt auftauchen, die Java 6 only sind (z.B. evtl. Glassfish 3).

Saturday, October 06, 2007

Double != double

In Java existieren von je her für alle primitiven Typen wie int, double entsprechende Wrapperklassen. Für int ist dies die Klasse java.lang.Integer. double hat java.lang.Double als Gegenstück.

Die Verwendung von Wertdatentypen in Sammlungsklassen wie ArrayList oder HashMap war immer etwas beschwerlich. Die Collections-Klassen können nämlich nur mit echten Objekten umgehen. Wie man an dem nachfolgenden Beispiel sieht, muß man beim Einfügen und Herausholen eine manuelle Konvertierung - Boxing - vom primitiven Typ in das Wrapperobjekt durchführen:

int i = 99; //value to add
List list = new ArrayList();
list.add(Integer.valueOf(i));
[...]
int j = list.get(0).intValue();

Mit Java 1.5 wurde neben den Generics (oben schon gesehen) auch das so genannte Auto-Boxing eingeführt. Damit wird, wenn notwendig, automatisch eine Konvertierung zwischen primitiven Typen und den entsprechenden Wrapperklassen vorgenommen. Das gleiche Beispiel sieht unter Java 1.5 so aus:

int i = 99;
List list = new ArrayList();
list.add(i);
[...]
int j = list.get(0);

Der Code ist offensichtlich wesentlich aufgeräumter, aber Auto-Boxing ist primär "Syntactic Sugar", weil die notwendigen Methodenaufrufe (und Casts) immer noch ausgeführt werden.
Sie müssen nur nicht mehr explizit niedergeschrieben werden.

Auch in vielen anderen Fällen kann man mit den Objekten auf den ersten Blick so rechnen wie mit den Werten. Dies ermöglicht zum Beispiel diesen (realen) Code:

public final Double getDuration() {
if (duration == 0.0) {
duration = syncDuration + headerLength * secPerByte;
duration += enclosedPacket.getDuration();
}
return duration;
}

Konzeptionell sähe die Methode bei der Ausführung so aus:

public final Double getDuration() {
if (duration == 0.0) {
duration = syncDuration + headerLength * secPerByte;
duration += enclosedPacket.getDuration().doubleValue();
}
return Double.valueOf(duration);
}

Dieses Beispiel zeigt aber auch warum man stets zwischen Double und double unterscheiden sollte, auch wenn es so aussieht, als ob man den Unterschied weitgehend ignorieren könnte.

Performance

Ein Profiling der Anwendung, aus der dieses Codestück stammt, hat ergeben, dass die Anwendung 38% der Zeit in der Methode Double.valueOf verbringt und 14% der Zeit in Double.doubleValue.
Die Aufrufe sind nicht direkt erfolgt, sondern diese Methoden werden "unsichtbar" durch das Autoboxing erzeugt. [*]

Die Performance der Anwendung wurde halbiert auf Grund des Unterschiedes zwischen Double und double - Ein Buchstabe, Große Wirkung!

Nur weil man die Methodenaufrufe im Vergleich zu Java 1.4 nicht mehr sieht, sind sie trotzdem nicht weg. Der Unterschied zwischen Objektdatentyp und Werttyp ist nicht aufgehoben, sondern nur die Konvertierung ist einfacher. Die Methoden Double.valueOf und Double.doubleValue sind absolut triviale Methoden, aber es kann sich aufsummieren. In diesem Fall auf 50% der gesamtes Laufzeit. [**]

NullPointerExceptions

NullPointerExceptions (NPE) sind schon in normalen Fällen eine böse Sache, weil sie keinen wirklichen Hinweis darauf geben, was die wahre Ursache für den Fehler ist. Oftmals liegt der Fehler, der zu einer NullPointerException führt, an einer ganz anderen Stelle im Code als der Ursprung der Exception. Nicht umsonst liest man oft den Rat Nullen gar nicht erst in den Code zu lassen.

Noch schlimmer sind NullPointerExceptions, die durch Autoboxing entstehen.
Was passiert bei folgenden Code:

Integer obj;
[a lot of lines]
int i = obj * 10;

Richtig. Es wird eine NullPointerException geworfen, da obj nicht auf eine Referenz gesezt wurde. Wäre obj vom Typ int, so wäre es perfekt legaler Code bei dem i = 0 wäre.
Jeder Versuch ein auf null gesetztes Nummernobjekt automatisch zu konvertieren, endet in einer NPE.
Während es bei normalen NPE noch den Dereferenzierungsoperator "." als Hinweis gibt, sind Autoboxing-NPE quasi unsichtbar.

Mit Nummernobjekten ist es auch aufwändiger über die Korrektheit zu argumentieren. Der mögliche Zustandsraum ist um diesen elenden null-Fall größer als bei Werttypen. Man muß stets überlegen, ob das Objekt an der Codestelle null sein kann und wie darauf reagiert werden muß z.B. durch eine extra Prüfung auf != null oder eine inhaltlich sinnvolle Exception wie IllegalArgumentException.

Die Möglichkeit eine Variable auf null zu setzen fügt dem Zustandsraum eine zusätzliche Äquivalenzklasse hinzu, über die man extra nachdenken und die man i.d.R. mit eigenen Testfällen testen muß.

Beachtet man diesen wichtigen Unterschied nicht, so führt dies schnell zu ziemlich fragilen, unrobusten Programmen.

Vergleich zu C#

C# hat hier das Glück des Zweitgeborenen. Es konnte aus den Ungereimtheiten von Java lernen und dort wurde die Problematik gelöst, in dem int, long, etc. weitgehend normale Objekte sind und so auch in Collections eingefügt werden können. Der wichtigste Unterschied zu normalen Objekten ist, dass man die Wertobjekte nicht auf Null setzen kann. Mit .NET 2.0 wurde diese Möglichkeit durch eine Spracherweiterung (int?, double?, ...) nachgeliefert.

double ist dort nur ein Alias für den Frameworktyp System.Double. Vielleicht ist dies auch der Grund für den performance-hungrigen Code oben. Unter C# würde es dort kein Problem geben. Möglicherweise war der Entwickler eher in der C#-Welt zu Hause.

Fazit

Die Nummbernobjekte und Auto-Boxing sollten primär in Zusammenhang mit den Collections-Klassen oder ähnlichen Anwendungsfällen verwendet werden.
Im Zweifel kann man die Nummernobjekte auch verwendet, wenn man ausdrückten muß, dass ein Wert nicht gesetzt wurde (null).

Oftmals ist dies auch keine so gute Idee, aber es ist doch auf jeden Fall besser als Double.NaN und normalen Vergleichoperating für diesen Zweck einzusetzen (wie in der gleichen Anwendung an anderer Stelle gesehen).

Für alle anderen Fälle insbesondere in Berechnungen sollte man eher die Werttypen verwenden.
Die Dokumentationsseite der Java-API sagt dazu auch:

So when should you use autoboxing and unboxing? Use them only when there is an "impedance mismatch" between reference types and primitives, for example, when you have to put numerical values into a collection. It is not appropriate to use autoboxing and unboxing for scientific computing, or other performance-sensitive numerical code.

An Integer is not a substitute for an int; autoboxing and unboxing blur the distinction between primitive types and reference types, but they do not eliminate it.

1) Es gab noch ein paar Nummernobjekte in der Anwendung (, die bis auf eine Ausnahme alle besser Werte gewesen wären. 2) Ich bin der erste der zugeben würde, dass die Duration-Berechnung auch unabhängig von der Verwendung des Double-Objektes unglücklich entworfen und implementiert ist 3) Es ist nicht allein dieser Codestück für das Profilresultat verantwortlich, aber es war das zentrale Problem.

* Dieses Beispiel ist in meinen Augen auch ein Musterbeispiel für den Optimierungsgrundregel, dass man - wenn möglich - auf Basis von Messungen optimieren sollte. Diese kleine unscheinbare Methode war das absolute Performancebottleneck. Nur durch "scharfes Hinsehen" hätte man dies niemals herausfinden können.

Thursday, August 09, 2007

Buchtipp: Java Puzzlers

In "Java Puzzlers - Traps, Pitfalls, and Corner Cases" stellen Joshua Bloch und Neal Gafter, zwei herausragende Personen der Java-Welt, 95 "Java Puzzle" vor. Dies sind kleine Codestücke, bei denen der Leser herausfinden soll, was der Code macht. In der Regel tut er nicht, das was von ihm erwartet wird.

Anschließend stellen die Autoren stets vor, was der Code wirklich macht und gibt Tipps, wie man die Probleme vermeiden kann. Die Puzzle drehen sich um verschiedene Bereiche von Java u.a. die Probleme von automatischen Casts, Generics, Autoboxing (beliebte Quelle für NullPointerExceptions), Klasseninitialisierung und Exceptionhandling. Aber auch die Java-Bibliothken werden angesprochen.

Die Puzzle und die Auslösungen sind wirklich extrem verzwickt und wären ideal für einen sadistischen GP1-Tutor. Das Lesen hat einfach Spaß gemacht.

In einem zwei Monate altem Artikel von mir habe ich vermutet, dass der Gebrauchswert von "Java Puzzler" geringer ist als der des Buches "Effective Java".
Dem ist wohl auch so, aber "Java Puzzlers" ist nicht nur nett zu lesen, sondern kann tatsächlich eine hilfreiche Lektüre sein.

In einem aktuellen Universitätsprojekt ist man gleich in mehreren Fällen auf die im Buch präsentierten Fallstricke "reingefallen",z.B. auf Puzzle 1 (Oddity), Puzzle 29 (Bride of Looper), Puzzle 57 (What’s in a Name?) und Puzzle 58 (Making a Hash of It).

Als Fazit kann man sagen, dass es nicht den gleichen praktischen Wert hat wie "Effective Java", aber mit dem Tipps und dem Wissen aus dem Buch wird man nicht so leicht über die Randbereiche von Java stolpern bzw. man wird eher erkennen, wo die Randbereiche liegen. Nun kann man leicht sagen, dass in gut geschriebener Software die aller meisten Probleme aus den Puzzles nicht auftauchen. Die meisten Puzzles sind schon ziemlich obskur, so dass die meisten Entwickler den Code sowieso umschreiben würden. Aber die Hinweise sind auch in der Realität wichtig (siehe oben) auch weil nicht jeder Code perfekt geschrieben ist. Wenn man Code vorgesetzt bekommt, der nicht gut geschrieben ist und weitgehend ungetestet, dann können die Hinweise Gold wert sein.

Die gute Nachricht ist, dass viele der Randfälle aus dem Buch auch durch das statische Analysetool FindBugs (eigentlich mal einen eigenen Artikel wert) gefunden werden. Um ehrlich zu sein, habe ich die oben erwähnten Bugs nicht durch Java Puzzlers sondern durch FindBugs entdeckt.

Wer in Java Puzzler hineinlesen möchte, kann die Probekapitel auf javapuzzlers.com lesen. So interessant wie das Buch sind auch die jährlichen Puzzlevorträe auf der JavaOne-Konferenz, die online ansehen gewerden können. Bei Sun gibt es die Talks als Audiopräsentation mit Transskript und den Slides (2007, 2006) , den 2007-Vorträ gibt es auch bei Google-Video (Joshua Block zusammen mit FindBugs-Autor Bill Pugh)

Fazit: Kein Muss-Lesestoff, aber durchaus interessant, lehrreich und gleichzeitig Fun. Zumindest wenn man nicht absolut allergisch auf die Bezeichnung "Geek" reagiert.

Sunday, August 05, 2007

Null-Parameter und Rückgabe von null in Java

Ein guter Artikel über "null" als akzeptierter Methodenparameter und den Rückgabewert "null" in Java:

I am not a fan of methods that accept null and I can find very few reasons for ever wanting to return null. Once a null gets into your code it can cause havoc, which is best discovered as soon as possible during development as it either indicates a serious problem or a buggy method returning null under certain circumstances (usually instead of an empty Collection).

How many times have you had to rewrite a simple equals call to allow for a null?

Etwas arg philosopisch und in der Praxis nicht einzuhalten, aber trotzdem ein guter Rat. Auch sollte man das Null-Muster (ps) in diesem Zusammenhang nicht vergessen.

Ich hab immer die Regel des Eclipseprojektes gemocht. "Null" ist dort - wenn ich mich richtig erinnere - als Eingabeparameter ungültig, es sei denn der Fall wurde in den Javadoc-Kommentaren explizit erlaubt.

Sunday, July 29, 2007

Java Exception Handling Antipattern

Der Artikel "Exception-Handling Antipattern" auf java.net erklärt wie Exceptions in Java verwendet und nicht verwendet werden sollten.

Der Artikel ist für jeden Java-Entwickler lesenswert. Die in dem Artikel genannten Antimuster sind:

  • Log and Throw
  • Throwing Exception
  • Throwing the Kitchen Sink
  • Catching Exception
  • Destrutice Wrapping
  • Log and Return Null
  • Catch and Ignore
  • Throw from within Finally
  • Multi-Line Log Messages
  • Unsupported Operating Returning Null
  • Ignoring InterruptedException
  • Relying on getCause()

Die Regeln sollten eigentlich (weitgehend) Common Sense sein, aber (nach leidvoller Erfahrung der letzten Monate) bin ich der Meinung, dass man kann selbst Common Sense nicht oft genug wiederholen.

Ich würde noch drei Empfehlungen hinzufügen, die vielleicht zu offensichtlich sind, aber dennoch irgendwie in die Liste passen. Java-Pros werden wahrscheinlich (zumindest für Empfehlung 1) Gegenbeispiele finden, aber grundsätzliche sind dies gute Ratschläe.

  1. Niemals Exceptionen von Error und Untertypen werfen.
    "Error" ist in der Regel einzig und allein der Virtuell Maschine und dem SDK vorbehalten sein und sollte so nur in Ausnahmefällen von Benutzercode gefangen werden. Schon gar nicht sollte Benutzercode Error-Exceptions werfen.
    In der Dokumentation zu Error heisst es:
    <blockquote>
      <p>An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions.</p>
    </blockquote>
    
    <p>In der Regel verwendet man eine Error-Klasse für einen ganz anderen Zweck als in der Dokumentation angegeben, nur weil der Name passend klingt z.B. VerifyError für die Verifikation von Benutzereingaben. Eigene Exceptions oder RuntimeExceptions für problematische Zustände sind die oft die bessere Wahl.</p>
    
  2. System.exit() sollte nicht in catch aufgerufen werden.
    Ich gestehe zu, dass es von dieser Regel Ausnahmen geben kann, aber das Anliegen von Exceptionhandling ist, dass sich der Code um die Fehlerbehandlung kümmert, der weiß ein Fehler behandelt werden soll. Nur niemand eine Exception behandeln kann, soll das Programm beendet werden.

    <p>Wenn man System.exit() in catch aufruft, dann nehme ich Methoden weiter oben im Stack die Möglichkeit den Fehler zu behandeln. In der Regel ist dies nicht sinnvoll.</p>
    
    <p>System.exit() sollte in der Regel sowieso gemeiden werden, da z.B. alle Cleanup-Operationen nicht durchgeführt werden und der Code mit System.exit() mit einem Schlag quasi-untestbar wird.</p>
    
  3. Eine NullPointerException sollte nicht manuell geworfen werden.
    Eine NullPointerException ist eine wirklich meise Art von Exception, weil sie oft auf Fehler hinweisen, die an ganz anderen Stellen im Code verursacht wurden. Zum Beispiel sind NullPointerExceptions tief aus dem Code eines J2EE-Containers ist so das schlimmestem was passieren kann. Es ist nur schwer möglich den Grund für den Fehler zu suchen.

    <p>Wenn man den Gedanken hat durch "throw new NullPointerExcecption()" manuell eine NPE zu werfen, sollte man besser davon Abstand nehmen und eine Exceptionklasse wählen, die besser den wirklichen Fehler beschreibt z.B. IllegalArgumentException("xy is null").</p>
    

Wednesday, July 18, 2007

Vector vs. Collections.synchronizedList

Every Java developer knows (or should know), that concurrent access to a list has to be synchronized.

In Java there are two build in ways to synchronize lists directly:

  1. java.util.Vector: The original implementation of an dynamic array from the old days of Java 1.0 - Java Stone Age.
  2. the factory method Collections.synchronizedList which produces a synchonized wrapper around a list (e.g. ArrayList or LinkedList).

There is also the list implementation CopyOnWriteArrayList from the package java.util.concurrent, which I will ignore here most of the time.

When I have to synchronize the access to a list in new code, which variant should be used

Depending from the concrete usage it could be an ultimate advance of the factory method, that the implementation of the underlying list can be chosen. If Vector is used, it is obviously a bad idea to delete elements from the middle of a list on a regular basis. The O calculus strikes back.

Furthermore the performance comparison shows surprising clear differences.

At the artificial task to insert 2^17 elements sequentially into the list, Vector needs 8 percent less time than the synchronized ArrayList. The advance is caused by the fact that Vector doubles the capacity of its internal data structure every time a threshold is reached. The ArrayList increases its internal array only about 50%. As a result the ArrayList copies its data more often into new arrays.

The iteration over 2^17 elements the vector needs six times longer then the synchronized variant of the ArrayList. This huge difference is caused by the locking behavior of the classes. Vector locks every single access to next(). The synchronizedList-Implementation doesn’t lock the access over an iterator as described in the javadoc. This breaks the rule that the client should never be responsible for thread safety of a class, but it allows coarse-grained locking around the whole loop. This avoids the possibility of ConcurrentModificationExceptions. The difference between one entry and release of an critical region against the 2^17 entrances and releases, causes the surprisingly high time difference.

Of course it is also possible to lock very single access to next, e.g. when the execution of the loop body takes a longer time. With this fine-grained locking the performance would be the same as of the Vector implementation.

Another reason against vector is the ballast the class carries from the Java 1.0 time. Vector implements the List interface and offers the methods add(), remove() and so, but it also has old methods like elementAt (old get), elements (old iterator which returns an Enumeration) and insertElementAt (old add). This makes the usage not very handy.

A more subtle reason for synchronizedList is that a lot of developers use Vector all the time, even if no synchronization is needed. It is often not clear, if Vector is used with purpose or since a lack of experience or knowledge. A lot of tutorials and books don’t mention the collections api. Furthermore there are universities haven’t discovery the collections api yet.

synchronizedList documents the purpose of the code in a better way: Yes, I want synchronization. Yes, I know what I do. In very case the intention should be documented. In the javadoc and (in the ideal case) also with the Annotations from "Java Concurrency in Practice".

In my opinion Vector should be declared obsolete at least after the introduction of generics. When no synchronization is needed, a "new" list implementation should be used. In the case of multi threading a synchronized wrapper or (when the reading accesses outnumbers the write accesses) the CopyOnWriteArrayList implementation should be used. Let Vector die, its time is over.

A bit more correct: Vector locks every single access to get(), which is called by next().
[Update May, 5th, 2008: In the Javaposse Googlegroup is currently an active and interessing discussion about ArrayList vs. Vector with further details, options and surprisingly different results]

Saturday, June 16, 2007

2. Auflage von "Effective Java" in Arbeit

Bei den regelmäßigen Lesern dieses Blogs sollte ich herumgesprochen haben, dass ""Effective Java" von Joshua Bloch eines meiner Lieblingsbücher ist.

Leider ist die aktuelle Version schon von 2001 und damit aus IT-Sicht uralt. Hinweise wie der Tipp für typsichere Enums sind von der Sprachspezifikation überholt worden; Nebenläufigkeit ist heute wichtiger als vor 6 Jahren und natürlich werden auch generische Typen nicht besprochen.

Dennoch hat es auch heute noch einen Mehrwert. Deshalb fand ich es schade, dass "Effective Java" nicht mehr verkauft wird. Aber die gute Nachricht ist dieses Interview mit Bloch auf der letzten JavaOne:


Q: You are currently busy revising Effective Java. Can you give us some hints about what will be in the second edition?

A: I’m trying very hard to preserve the tone of the first edition. I’m revising all the existing items in light of the J2SE 5.0 language changes and everything I’ve learned since 2001. I’m adding a few more items here and there, plus an entire chapter on generics. Also, I’m slanting the threads chapter toward java.util.concurrent.


Für die Übergangszeit gibt es noch "Java Puzzlers", auch wenn ich vermute, dass der "Gebrauchswert" von "Puzzler" deutlich geringer sein wird.

Wednesday, June 06, 2007

Singletons: Bitte nicht als Ersatz für globalen Variablen

Vor 1 1/2 Jahren habe ich schon mal etwas über das Singleton-Pattern geschrieben. Damals ging es um die Implementierung in Java. Auch damals hatte ich schon geschreiben

Merke: Wenn Singleton (meist ist dies keine so gute Idee), dann doch bitte nur mit private-Konstruktor.

Einen Vorteil hat das Singleton-Pattern gegenüber den anderen GoF-Pattern. Es ist absolut einfach zu verstehen. Das Klassendiagramm enthält nur eine einzige Klasse und das Singleton hat nichtmal einen Clienten. Es ist in vielen Kursen (zum Beispiel auch in Modellbasierte Softwareentwicklung) eines der ersten oder das erste Pattern, dass vorgestellt wird.

Dies führt dazu, dass es zu jedem nur denkbaren Zeitpunkt eingesetzt wird. Nachdem ich mich im Moment in ein Open Source Projekt einarbeite in dem Klassen mit Singleton-Eigenschaft nicht die Ausnahme sondern die Regel sind, ist es Zeit etwas auszuführen, warum das Singleton-Pattern meist keine so gute Idee ist.

Nicht, dass es für das Singleton-Pattern keine sinnvolle Verwendung gibt, aber meine Schätzung ist, dass es in 90% der Fällen als Ersatz für globale Objekte verwendet wird anstatt für den eigentlichen Zweck. Singleton ist eine Erzeugungsmuster. Es dient dazu sicherzustellen, dass niemals mehr als eine Instanz einer Klasse existieren.

(more...)

Saturday, June 02, 2007

Mac-OS Tiger: Endlich installiert

Mal ein kleiner Rückblick in die Geschichte dieses Blogs:
12. April 2005:

Was könnte ein besserer 1. Beitrag für dirkmeister.de sein als die Nachricht, dass Mac OSX 10.4 Codename "Tiger" endlich erscheint.
Die integrierte "Suchfunktion" Spotlight und Dashboard sind echt schon zwei Schmankerl und Microsoft um mehrere Jahre voraus. *grin*
Von dem Geld, das ich mir in den Semesterferien verdient habe, kaufe ich mir vielleicht "Tiger".

Irgendwie ist da nie etwas raus geworden. Die Investition habe ich immer gescheut.
Aber gestern war es soweit: Am Vorabend der Leopard-Veröffentlichung habe ich Tiger gekauft und installiert.

Der Grund: Spotlight, Dashboard? Nein
Es ist dieser kleine Text:

    $ java -version
   java version "1.5.0_07"
   Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_07-164)
   Java HotSpot(TM) Client VM (build 1.5.0_07-87, mixed mode)

Ich brauche jetzt endgültig Java 5.0.

Thursday, May 17, 2007

Eclipse-Emma-Plugin: EclEmma

Emma ist ein Werkzeug zur Überdeckungsanalyse für Javacode. Man kann damit Basisanalysen erstellen wie gut JUnit-Tests die verschiedenen Zweige im Code überprüfen.

Doch der Einsatz der Emma-Bibliothek alleine ist ziemlich aufwendig. Man muss manuell den Code instrumentalisieren und Berichte aus den gesammelten Informationen erstellen. Mit einem guten ant-Skript ist dies kein Ding, aber für den einzelnen Entwickler/Unittester doch zu viel Aufwand.

Mit EclEmma (momentan Version 1.2.0) gibt es ein sehr gutes Eclipse-Plugin, das den ganzen Prozess extrem abkürzt. Die Coverageprüfung integriert sich als zusätzlichen Lunchmode (neben Debug und Run). Damit wird das Durchführen einer Coverageprüfung ein Kinderspiel. Der Code wird automatisch instrumentalisiert und die Resultate werden automatisch ermittelt und dargestellt.

Apropo Ergebnisse:
Bei der Anzeige der Ergebnisse zeigt sich der wahre Vorteil des Plugins. Ähnlich wie bei der Edition für Tester von Visual Studio 2005 werden die Überdeckungsinformationen direkt im Codeeditor angezeigt. Extrem übersichtlich. Man sieht direkt, wo die Tests noch nicht ausreichen.

Ich kann das Plugin nur empfehlen. Man erhält ohne großen Aufwand einen echten Mehrwert bei der Überdeckungsanalyse. Es ist nicht einmal notwendig die Projekteinstellungen zu verändern.