Was sagt eigentlich die Nielsen Norman Group zum iOS 7 Design?

Gemisches, wie man bei http://www.nngroup.com/articles/ios-7/ nachlesen kann. Eher ein negativer Grundton.

Is iOS 7 Fatally Flawed? The short answer is no. Simply because there is no such thing as fatally flawed designs: we can always learn from mistakes. What’s surprising is that we don’t Is iOS 7 Fatally Flawed?learn from someone else’s mistakes: Apple ignored some of the hurdles that Microsoft experienced with flat design and swipe ambiguity in Windows 8. We still have to see whether Apple’s strong design guidelines will protect most app designers from not getting lost in the flat 2D world. Early experience with applications redesigned for iOS 7 is fairly negative: several have worse usability than their iOS 6 versions.

NetBeans IDE 7.4 mit JDK 8 und mehr

Das konnte ich gerade bei http://www.theserverside.com/discussions/thread.tss?thread_id=77744 lesen:

  • HTML5 features available in Java EE and PHP projects
  • Cordova application development
  • Support for Android and iOS devices and emulators
  • Preview support for JDK 8 features
  • Initial editing support for Knockout, AngularJS, and ExtJS frameworks

Mehr unter https://netbeans.org/community/releases/74/ und Download bei https://netbeans.org/downloads/.

JavaFX + CDI (Weld)

Put in the class path:

The challenge is to bring FXMLLoader and CDI together, because JavaFX is creating naked objects by itself, now they have to be „CDI-aware“.

package tutego.fx;

import java.nio.charset.*;
import javafx.fxml.FXMLLoader;
import javafx.util.Callback;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;

public class FXMLLoaderProducer
{
  @Inject
  Instance<Object> instance;

  @Produces
  public FXMLLoader createLoader()
  {
    return new FXMLLoader( null, null, null, new Callback<Class<?>, Object>() {
      @Override public Object call( Class<?> param ) {
        return instance.select( param ).get();
      }
    }, StandardCharsets.UTF_8 );
  }
}

That was the hardest part.

The first regular class has the unique main(String[]) method and it’s a JavaFX application. It starts Weld, the CDI container.

package tutego.fx;

import java.io.IOException;
import javafx.application.Application;
import javafx.stage.Stage;
import org.jboss.weld.environment.se.*;

public class Main extends Application
{
  private Weld weld;

  public static void main( String[] args )
  {
    Application.launch( args );
  }

  @Override
  public void init()
  {
    weld = new Weld();
  }

  @Override
  public void start( Stage stage ) throws IOException
  {
    weld.initialize().instance().select( FxMain.class ).get().start( stage, getParameters() );
  }

  @Override
  public void stop()
  {
    weld.shutdown();
  }
}

Weld delegates to the FxMain class, the first CDI-enabled class:

package tutego.fx;

import java.io.*;
import javafx.application.Application.Parameters;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.stage.Stage;
import javax.inject.Inject;

public class FxMain
{
  @Inject
  private FXMLLoader fxmlLoader;

  public void start( Stage stage, Parameters parameters ) throws IOException
  {
    try ( InputStream fxml = RandomController.class.getResourceAsStream( "/random.fxml" ) ) {
      Parent root = (Parent) fxmlLoader.load( fxml );
      stage.setScene( new Scene( root ) );
      stage.show();
    }
  }
}

The injected FXMLLoader now has to load the FXML-file random.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="tutego.fx.RandomController">
  <children>
    <Button onAction="#onButtonClick" text="Next random" />
    <Label id="text" fx:id="label" />
  </children>
</VBox>

In the FXML-file there is a reference to the FX Controller. JavaFX has to load it and can make the injections with the help of our very first class. A regular service is getting injected into the controller:

package tutego.fx;

import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javax.inject.Inject;

public class RandomController
{
  @FXML
  private Label label;

  @Inject
  private RandomService randomService;

  @FXML
  public void onButtonClick()
  {
    label.setText( "Random " + randomService.nextInt() );
  }
}

The service itself is a simple singleton:

package tutego.fx;

import java.util.Random;
import javax.inject.Singleton;

@Singleton
public class RandomService
{
  private Random rnd = new Random();

  public int nextInt()
  {
    return rnd.nextInt();
  }
}

Thats it!

PS: When you start, dont forget to put a (even blank) beans.xml in META-INF.

Oracle Java SE 7u45 Update

Neu ist: A JDK for Linux ARM is also available in this release. Das kann man auf der Download-Seite auch ablesen:

Linux ARM v6/v7 Hard Float ABI
67.67 MB  
jdk-7u45-linux-arm-vfp-hflt.tar.gz

Linux ARM v6/v7 Soft Float ABI
67.68 MB  
jdk-7u45-linux-arm-vfp-sflt.tar.gz

Linux x86
115.62 MB  
jdk-7u45-linux-i586.rpm

Linux x86
132.9 MB  
jdk-7u45-linux-i586.tar.gz

Linux x64
116.91 MB  
jdk-7u45-linux-x64.rpm

Linux x64
131.7 MB  
jdk-7u45-linux-x64.tar.gz

Mac OS X x64
183.84 MB  
jdk-7u45-macosx-x64.dmg

Solaris x86 (SVR4 package)
139.93 MB  
jdk-7u45-solaris-i586.tar.Z

Solaris x86
95.02 MB  
jdk-7u45-solaris-i586.tar.gz

Solaris x64 (SVR4 package)
24.6 MB  
jdk-7u45-solaris-x64.tar.Z

Solaris x64
16.23 MB  
jdk-7u45-solaris-x64.tar.gz

Solaris SPARC (SVR4 package)
139.38 MB  
jdk-7u45-solaris-sparc.tar.Z

Solaris SPARC
98.17 MB  
jdk-7u45-solaris-sparc.tar.gz

Solaris SPARC 64-bit (SVR4 package)
23.91 MB  
jdk-7u45-solaris-sparcv9.tar.Z

Solaris SPARC 64-bit
18.26 MB  
jdk-7u45-solaris-sparcv9.tar.gz

Windows x86
123.49 MB  
jdk-7u45-windows-i586.exe

Windows x64
125.31 MB  
jdk-7u45-windows-x64.exe

Singleton in Java

Ein Singleton ist eine Klasse, von der es in einer Anwendung nur ein Exemplar gibt. Nützlich ist das für Dinge, die es nur genau einmal in einer Applikation geben soll, und davon gib es einige Beispiele:

· Eine grafische Anwendung hat nur ein Fenster.

· Eine Konsolenanwendung hat nur je einen Eingabe-/Ausgabestrom.

· Alle Druckaufträge wandern in eine Drucker-Warteschlage.

Unbestreitbar ist, dass es einmalige Objekte gibt, variantenreich ist jedoch der Weg dahin. Im Prinzip lässt sich unterscheiden zwischen einem Ansatz, bei dem

a) ein Framework sich um den einmaligen Aufbau des Objekts kümmert und dann auf Anfrage das Objekt liefert oder

b) wir selbst in Java-Code ein Singleton realisieren.

Die bessere Lösung ist ein Framework zu nutzen, namentlich CDI, Guice, Spring, Java EE, doch Java SE enthält keines davon, weswegen wir zur Demonstration den expliziten Weg gehen.

Die technischen Realisierungen sind vielseitig; in Java bieten sich zur Realisierung von Singletons Aufzählungen (enum) und normale Klassen an. In Folgendem wollen wir ein Szenario annehmen, bei dem eine Anwendung zentral auf Konfigurationsdaten zurückgreifen möchte.

Singletons über Aufzählungen

Eine guter Weg für Singletons bieten Aufzählungen – auf den ersten Blick scheint ein enum nicht dafür gemacht, denn eine Aufzählung impliziert ja irgendwie mehr als ein Element – doch die Eigenschaften vom enum sind perfekt für ein Singleton. Die Idee dabei ist, genau ein Element anzubieten, gerne INSTANCE genannt, was letztendlich ein Exemplar der Aufzählungskasse wird, und die normalen Methoden:

public enum Configuration {

INSTANCE;

private Properties props = new Properties( System.getProperties() );

public String getVersion() {

return "1.2";

}

public String getUserDir() {

return props.getProperty( "user.dir" );

}

}

Der Typ Configuration deklariert neben der später öffentlichen statischen Variable INSTANCE auch noch eine interne Variable props, die von der Aufzählung genutzt werden kann, um dort Zustände abzulegen oder zu erfragen. Wir machen das im Beispiel nur lesend über getUserDir().

Ein Nutzer greift wie üblich auf die enum-Eigenschaften zu:

System.out.println( Configuration.INSTANCE.getVersion() ); // 1.2

System.out.println( Configuration.INSTANCE.getUserDir() ); // C:\Users\…

Singletons über Klassen

Ein alternativer Weg – und der übliche vor Java 5 – arbeit mit einer Klasse und privatem Konstruktor, zusammen mit einer statischen Anfrage-Methode, die das Objekt liefert:

public class Configuration2 {

private static final Configuration2 INSTANCE = new Configuration2();

public final static Configuration2 getInstance() {

return INSTANCE;

}

private Configuration2() {

}

private Properties props = new Properties( System.getProperties() );

public String getVersion() {

return "1.2";

}

public String getUserDir() {

return props.getProperty( "user.dir" );

}

}

Interessant sind einmal der private Konstruktor und zum anderen die statische Anfrage-Methode getInstance(). Wenn ein Konstruktor privat ist, bedeutet das noch lange nicht, dass keine Exemplare mehr erzeugt werden können. Ein privater Konstruktor besagt nur, dass er von außen nicht sichtbar ist – aber die Klasse selbst kann ihn ebenso wie private Methoden »sehen« und zur Objekterzeugung nutzen. Objektmethoden kommen dafür nicht in Frage, da ähnlich wie beim Henne-Ei-Problem ja vorher ein Objekt nötig wäre. Es bleiben somit die statischen Methoden als Erzeuger. Und das ist das, was wir wollen: Keine Exemplare von außen, nur von innen. Und da die statische Variable INSTANCE ja genau ein Objekt referenziert, kann die statische Methode diese Referenz nach außen geben.

Die Nutzung der zweiten Variante ist nicht sonderlich unterschiedlich, hat aber wohl eine andere Syntax, sodass ein Refactoring von einer Lösung in die andere Codeänderungen nach sich zieht:

System.out.println( Configuration2.getInstance().getVersion() ); // 1.2

System.out.println( Configuration2.getInstance().getUserDir() ); // C:\Users\…

Oftmals findet sich in Implementierungen eines Singletons noch eine Optimierung, in dem erst in getInstance() das Exemplar aufgebaut wird. Dazu muss aber noch die Methode mit synchronized ausgezeichnet werden, was vor nebenläufigen Zugriffen schützt, sodass nur ein Thread die Methode betreten kann und ein potenziell anderer Thread so lange warten muss, bis der erste Thread die Methode wieder verlassen hat.

GUI-Builder für JavaFX und Swing

Mit einem GUI-Builder lassen sich grafische Oberflächen über ein grafisches Werkzeug einfach aufbauen. In der Regel bietet ein GUI-Bilder eine Zeichenfläche und eine Symbolleiste mit Komponenten, die per Drag and Drop angeordnet werden. Zentral bei dem Ansatz ist das WYSIWYG-Prinzip (What You See Is What You Get), dass nämlich im Designschritt schon abzulesen ist, wie die fertige Oberfläche aussieht.

Ein GUI-Builder erzeugt eine Repräsentation der grafischen Oberfläche, die im Prinzip auch von Hand zu erstellen wäre – allerdings ist der Aufwand sehr groß und für jeden nachvollziehbar, der schon einmal in HMTL eine neue Tabellenspalte hinzugeführt hat. Es gibt immer wieder Diskussion über das Für und Wider doch ist es wie mit allen Tools: richtig eingesetzt kann ein GUI-Builder viel Arbeit sparen.

GUI-Builder für JavaFX

Der JavaFX Scene Builder ist ein Werkzeug von Oracle und nennt sich selbst „A Visual Layout Tool for JavaFX Applications“. Er ist kein Teil vom JDK, sondern muss unter http://www.oracle.com/technetwork/java/javafx/tools/index.html bezogen und installiert werden. Danach stehen komfortable Werkzeuge zum Entwurf von Oberflächen und deren Verschönerung mit CSS nichts im Wege.

NetBeans bietet von Haus aus Unterstützung im Entwurf grafischer Oberflächen, denn ein GUI-Bilder ist integriert, und eine Zusatzinstallation ist nicht nötig. Das gibt uns direkte Möglichkeiten, Swing und auch JavaFX spielerisch zu erfahren.[1]

Für Eclipse gibt es keinen speziellen GUI-Builder, das ist auch eigentlich gar nicht nötig, denn der Scene Builder kann in Eclipse integriert werden.[2] Zwar kein direkter WYSIWYG-Editor, aber immerhin ein Werkzeug im Umgang mit XML ist das quelloffenes Eclipse-Plugin e(fx)clipse unter http://efxclipse.org/.

GUI-Builder für Swing

Während NetBeans für Swing gute Unterstützung mitbringt, ist bei Eclipse standardmäßig kein GUI-Builder integriert. Es gilt also, ein Plugin nachzuinstallieren. In den letzten Jahren kamen und gingen verschiedene GUI-Builder, aber letztendlich hat sich der WindowsBuilder (https://developers.google.com/java-dev-tools/wbpro/) von Google als De-facto-Standard etabliert. Über den Update-Mechanismus von Eclipse wird er installiert. Eine Installationsanleitung findet sich auf der Webseite. Neben Swing nimmt der WindowsBuilder gleich noch GWT, SWT und XWT (Eclipse XML Window Toolkit) mit.


[1] Didaktiker nennen das »exploratives Lernen«.

[2] http://docs.oracle.com/javafx/scenebuilder/1/use_java_ides/sb-with-eclipse.htm

Deklarative und programmierte Oberflächen: Swing und JavaFX im Vergleich

Grundsätzlich können grafische Oberflächen über eine Programm-API aufgebaut werden oder in einer deklarativen Beschreibung spezifiziert werden.

· Programmierte Oberflächen: Der traditionelle Bau von grafischen Oberflächen in Java weist die Besonderheit auf, dass das Design der Oberfläche in Java-Code gegossen werden muss. Jede Komponente muss mit new erzeugt werden und mithilfe eines Layouts explizit angeordnet werden. Komplexe Oberflächen bestehen dann aus fast unwartbaren Mengen von Programmcode zum Aufbau der Komponenten, zum Setzen der Eigenschaften und Platzierung auf dem Container. Die Änderung des Layouts ist natürlich sehr schwierig, da mitunter auch für kleinste Änderungen viel Quellcode bewegt wird. In der Versionsverwaltung sieht das mitunter schrecklich aus.

· Deklarative Oberflächen stehen im Gegensatz zu den programmierten Oberflächen. Bei ihnen ist die Beschreibung des Layouts und die Anordnung der Komponenten nicht in Java ausprogrammiert, sondern in einer externen Ressourcen-Datei beschrieben. Das Format kann etwa XML sein, und spiegelt wieder, wie das Objektgeflecht aussieht. Eine Ablaufumgebung liest die Ressourcen-Datei und übersetzt die Deklarationen in ein Geflecht von GUI-Komponenten. Im Hauptspeicher steht dann am Ende das gleiche wie bei der programmierten GUI: ein Objekt-Graph.

Das andere Ufer: Microsoft erkannte ebenfalls die Notwendigkeit einer deklarativen Beschreibung von Oberflächen und nutzt intensiv XAML (Extensible Application Markup Language). Gleichzeitig gibt es leistungsfähige Tools und Designer für XAML. Die Firma Soyatec versucht sich mit eFace an einer Java-Realisierung (http://www.soyatec.com/eface/).

Gui-Beschreibungen in JavaFX

JavaFX unterstützt beide Möglichkeiten zum Aufbau von grafischen Oberflächen. Zum einen ist da die klassische API, die Knoten in einen Baum hängt, viel interessanter ist aber der deklarative Ansatz, der sehr schön Präsentation und Logik trennt. JavaFX selbst bietet eine Beschreibung auf XML-Basis, genannt FXML. XML ist selbst hierarchisch, kann also die grundlegende hierarchische Gliederung einer GUI in Containern und Komponenten sehr gut abbilden.

Neben FXML gibt es weitere proprietäre Beschreibungen und Mischformen. Eine davon ist FXGraph vom Projekt e(fx)clipse (http://www.eclipse.org/efxclipse/), einer JavaFX-Unterstützung in Eclipse. Die Beschreibung ist eine DSL[1] und definiert den Objekt-Graphen, der im Hintergrund in FXML umgesetzt wird. FXGraph ist kompakter als FXML und erinnert entfernt an JSON. Auch kann die JavaFX-API in alternativen Sprachen angesprochen werden, JavaScript und weitere Skriptsprachen wie Groovy (und der Hilfe von GoovyFX[2]) oder Scala (zusammen mit ScalaFX[3]) zeigen interessante Wege auf. Allerdings mischt sich dann doch wieder schnell die Deklaration der GUI mit Logik, was die Trennung zwischen Präsentation und Logik aufweicht. Es ist guter Stil, die Beschreibung der Benutzerschnittstelle und der Logik zu trennen, um auch Tests leichter zu realisieren.

Deklarative GUI-Beschreibungen für Swing?

Für AWT und Swing hat sich für deklarative Oberflächen in den letzten Jahren kein Standard gebildet, und Oberflächen werden heute noch so programmiert wie vor 15 Jahren. Dass Swing-Oberflächen immer programmiert werden müssen hält auf, auch wenn ein GUI-Builder heutzutage die Schmerzen minimiert. Über die WYSIWYG-Oberfläche wird in der Regel das Layout mit allen Komponenten zusammengeklickt und im Hintergrund erzeugt der GUI-Builder den Programmcode. Für die Laufzeitumgebung hat sich also nichts verändert, aber für uns schon.

Um auch in Swing in die Richtung von deklarativen Oberflächen zu kommen, gibt es unterschiedliche Open-Source-Lösungen, da Oracle nichts im Angebot hat.

· Swixml (http://www.swixml.org/) nutzt das XML-Format zur Beschreibung von GUIs und bildet jede Swing-Klassen auf ein XML-Element ab. Später nutzt Swixml dann SAX und JDOM, um die XML-Datei einzulesen und zu repräsentieren und um zur Laufzeit eine Swing-Komponentenbaum aufzubauen. Die Folien unter http://www.swixml.org/slides.html geben einen Einblick in die Möglichkeiten. Seit Mitte 2011 wird Swixml nicht mehr erweitert.

· Eine weitere Lösung zur deklarativen Beschreibung von Swing-Oberflächen bietet der Swing JavaBuilder (http://code.google.com/p/javabuilders/). Die Open-Source-Bibliothek steht unter der Apache-Lizenz und nutzt statt XML das kompaktere YAML-Format, dessen Schreibweise noch weiter verkürzt wurde. Das letzte Release stammt von Ende 2011, eine Weiterentwicklung ist unwahrscheinlich.

Die Inaktivität lässt entweder so erklären dass die Produkte perfekt sind, oder sich Entwickler mit den klassischen Code-generierenden GUI-Buildern anfreunden konnten, oder die Tools mit dem Aufkommen von JavaFX einfach unattraktiv werden.


[1] Eine domain-specific-language (DSL) ist eine „Spezialsprache“, die ein exklusives Format für einen klar abgegrenzten Anwendungsfall definiert.

[2] http://groovyfx.org/

[3] https://code.google.com/p/scalafx/

Geschichte von JavaFX: JavaFX 1, JavaFX 2, JavaFX 8

Ursprünglich wollte Sun/Oracle JavaFX als Flash-Ersatz im Internet positionieren, doch dafür ist die Kombination HTML5 + CSS3 + JavaScript zu attraktiv. JavaFX ist in erster Linie eine großartige GUI-Bibliothek für klassische Client-Anwendungen, die langsam auch auf mobile Endgeräte vorrückt. So nahm die Entwicklung auch unterschiedliche Richtungen an.

JavaFX ist schon sehr lange in Entwicklung und viele interne Swing-Entwickler wurden bei Sun/Oracle auf das Projekt angesetzt – umgekehrt passierte bei AWT/Swing nicht mehr viel, Bugfixes kommen aber immer noch brav. Im Jahr 2007 wurde JavaFX auf der SunOne-Konferenz vorgestellt, Ende 2008 erschien das Release JavaFX 1.0 zusammen mit der Programmiersprache JavaFX Script. Die besondere Sprache machte es einfach möglich, hierarchische Objektgrafen aufzubauen, und bot eine nette Syntax für Object-Binding, sodass Zustände synchronisiert werden konnten.

Im Oktober 2011 erschien JavaFX 2.0 mit vielen Neuerungen und auch Änderungen. So wurde JavaFX Script entfernt, denn Oracle wollte keine weitere Programmiersprache aufbauen, sondern eine pure Java API, die Entwickler dann von unterschiedlichen existierenden Skriptsprachen ansprechen können. Das ist sicherlich eine gute Entscheidung, denn unter JavaScript und Groovy[1] sieht das sehr schlank aus, fast wie mit JavaFX Script. Mit dem Update auf JavaFX 2.0 ändert sich auch die API, sodass alter Code angefasst werden musste. Heute ist JavaFX 1.x ist veraltet, genauso wie die Literatur.

JavaFX entwickelte sich immer mehr als Alternative zu Swing/AWT und so integrierte Oracle im August 2012 das Java FX 2.2 SDK und die Runtime in das JDK 7u6 and JRE 7u6. Der Schritt war ungewöhnlich, denn so große Ergänzungen waren bisher im JRE/JDK noch nie gemacht worden. Neben der Integration bewegte sich auch das ehemals geschlossene JavaFX in Richtung Open-Source und mündete in der Implementierung OpenJFX. Mit dem OpenJDK und OpenJFX lässt sich ein komplett freies Java-System mit GUI-Stack unter der GPL bauen, was strikte Linux-Distributionen freuen wird. Die Offenheit führte schon zu sehr spannenden Experimenten, etwa einer Java-Version für iOS.

Mit Java 8 zieht auch JavaFX 8 fest in die Distribution ein, der nächste Sprung mit 3D-Unterstützung.


[1] http://groovyfx.org/

Best Practice: Teste eine Web-Anwendung in jedem Browser

Negativbeispiel vom Vorzeigeunternehmen Ryanair beim Versuch online einzuchecken und die Board-Karte zu beziehen. Hier im IE:

ryanair

Beim gleichen Vorgang in Chrome:

Segments - Ryanair.com

Also gelingt es in beiden Browsern nicht, die Board-Karte zu bekommen! Die Antwort vom Kundendienst:

Bezugnehmend auf Ihre aktuelle Korrespondenz moechten wir Sie bitten auf unsere Webseite die neueste Version Ihres Browsers benutzen. Auβerdem empfehlen wir Ihnen den Browser Verlauf und die Cookies zu löschen.

Ehr ungewöhnlich, dass man den neusten Browser verwenden soll, viele Unternehmen zwingen sich selbst noch zu so alten Trümmern wie dem IE 6. Und der selbst-aktualisierende Chrome soll nicht aktuell sein? Es ist eher ein Cookie-Problem, was aber nicht beim Anwender liegt, sondern klar bei den Web-Entwicklern. Weil Ryanair also schlechte IT-Produkte hat, müssen Benutzer zu allen möglichen Tricks greifen, um das Problem zu lösen, und wenn das nicht gelingt, auch noch bei der kostenpflichten Hotline anrufen. So macht man auch Geld …

Test-Werkzeuge wie Selenium helfen.

Best Practice: Behebe technische Fehler schnell und professionell

Was im Grunde offensichtlich ist, wird von Ryanair beflissen auf die lange Bank geschoben. Vor ein paar Tagen hatte ich schon das schlechte Fehlerhanding kritisiert, diese Fehlermeldung ist noch übler:

PaymentSportsChange - Ryanair.com

Wenn man einen Dienst anbietet, hat man:

  • Fehler schnell zu beheben (dieser Fehler kommt seit mehren Tagen beim Versuch ein Gepäckstück hinzuzufügen),
  • dem Benutzer in der jeweiligen Landessprache zuerst die Fehlermeldung zu präsentieren (diese große Webseite hat neun lokalisierte Fehlermeldungen untereinander, die Italiener müssen also ganz nach untern scrollen),
  • hinter dem Link “Bitte warten Sie 10 Sekunden und wenn Sie nicht automatisch zurückgeleitet werden, klicken Sie hier.” auch eine Aktion verbinden, und nicht als nutzloses “<a href="#" id="A2">klicken Sie hier</a>” realisieren (JavaScript gibt es auch nicht, sonst könnte man das ja noch verstehen),
  • die Fehlermeldung in das Standard-Template einbetten und keine rohe HTML-Seite präsentieren.

Fazit: Eine schlechte Webseite und schlampige Programmierung kostet dem Interessenten nur Zeit (und damit Geld, war er über die günstigen Flüge ja eigentlich sparen wollte) und natürlich auch dem Unternehmen.

PS: Wer dieses Problem hat, sollte seine Cookies löschen bzw. ausnahmsweise einen anderen Browser nutzen. Als Chrome-Nutzer konnte ich im IE mein Gepäckstück hinzufügen. Die Entwickler von Ryanair habe also irgendwie ein Cookie-Problem.

IdentityHashMap

Es gibt eine besondere Datenstruktur mit dem Namen IdentityHashMap, die statt der internen equals(…)-Vergleiche einen Identitätsvergleich mit == durchführt. Die Implementierung ist selten im Einsatz, kann aber im Bereich der Performance-Optimierung eine interessante Rolle übernehmen und auch das Problem lösen, wenn in der Map denn absichtlich Objekte enthalten sein sollen, die equals-gleich, aber nicht identisch sind. Es lässt sich auch so sehen: IdentityHashMap ist attraktiv, wenn als Schlüssel Objekte zum Einsatz kommen, bei denen Gleichheit und Identität dasselbe bedeuten.

Hinweis: An Integer-Objekten in einer IdentityHashMap zeigt sich genau der Unterschied zur klassischen Map, wie einer HashMap. Nehmen wir

Integer key1 = 1;
Integer key2 = 1;
Integer key3 = 1000;
Integer key4 = 1000;

dann sind wegen dem Autoboxing und wegen dem internen Cache key1 == key2, aber key3 != key4 (die Integer-Klasse cached standardmäßig Ganzzahlen im Wertebereich eines byte). Anfragen mit equals-gleichen Integer-Objekten sind in der HashMap üblich, laufen aber bei IdentityHashMap ins Leere, da es unmöglich ist, zum Beispiel später über Integer.value(1000) ein genau identisches Integer-Objekte aufzubauen, sodass es als Schlüssel im IdentityHashMap „passt“ und der Identitätsvergleich wahr wird.

Algorithmen für Rastergrafik, 2. Einfache Füllalgorithmen

In der Computergrafik sind Objekte, die durch Linien und Punkte abgegrenzt sind selten. Vielmehr bekommen die Flächen eine Farbe oder ein Überzug von einer Textur. Ein ausgefülltes Objekt kann durch zwei Techniken entstehen. Zunächst einmal kann es sofort so konstruiert werden, dass es gefüllt erscheint, beispielsweise ergeben viele gleichförmige Linien ein Rechteck. Aber Objekte können nachträglich gefüllt werden. Dazu sind Begrenzungen nötig. Ein einfacher Algorithmus, der sich auf die Informationen des Bildschirmspeichers verlässt ist Seed-Fill. Die Idee von Seed-Fill ist einfach: Wir starten bei einem bekannten Punkt — dem Seed (zu deutsch Korn) — und geben dem Nachbarpixel die gleiche Füllfarbe, wenn dieser nicht die Begrenzungsfarbe besitzt. Der Algorithmus ist schnell rekursiv definiert. Wenn wir auf den Bildschirmspeicher zugreifen, so umschreiben wir dies mit einem Zugriff auf das Feld pixelArray. Und boundaryValue bezeichnet die begrenzende Farbe und fillValue die zu füllende Farbe:

void seedFill( int x, int y )
{
 if ( ( pixelArray[x,y] != boundaryValue ) && ( pixelArray[x,y] != fillValue ) ) {
  setPixel( x, y, fillValue );
  seedFill( x+1, y );
  seedFill( x-1, y);
  seedFill( x+1, y+1 );
  seedFill( x-1, y-1 );
 }
}

Die rekursive Implementierung krankt an dem Problem, dass der interne Stack sehr schnell ansteigt. Auch besitzt diese Implementierung den Nachteil, dass für jeden zu setzenden Punkt immer der Bildschirmspeicher ausgelesen wird und mehrere gleiche Punkte auf den Stack kommen und zurückgewiesen werden.

Eine mögliche Verbesserung erhalten wir dadurch, dass wir die direkten Rekursion eliminieren. Dadurch wird dieser Algorithmus aber nicht schneller, da das setzen eines Pixels vier Stack-Operationen nach sich zieht. Außerdem ist eine Implementierung wie die folgende schon deswegen nicht schneller, da bei der Stack-Implementierung der Objekt-Overhead mit sich gezogen wird. Daher dient folgender Algorithmus lediglich zur Anschauung, wie Flood-Fill mit einem Stack umgesetzt werden kann.

void seedFill( int xSeed, int ySeed )
{
 Stack s = new Stack();
 floodValue = pixelArray[xSeed, ySeed];
 s.push( xSeed );
 s.push( ySeed );
 while ( s.notEmpty() )
 {
  y = s.pop();
  x = s.pop();
  setPixel( x, y );
  if ( pixelArray[x+1, y] == floodValue ) {
   s.push( x+1 ); s.push( y );
  }
  if ( pixelArray[x-1, y] == floodValue ) {
   s.push( x-1 ); s.push( y );
  }
  if ( pixelArray[x, y+1] == floodValue ) {
   s.push( x ); s.push( y+1 );
  }
  if ( pixelArray[x, y-1] == floodValue ) {
   s.push( x ); s.push( y-1 );
 }
}

Überlegen wir, wodurch diese Implementierung ineffizient wird. Ein Grund haben wir schon erschlossen: jeder gesetzte Punkt wird mehrmals überprüft. Nehmen wir einen Punkt (x,y) heraus. Dann wird (x+1,y) auf den Stapel gesetzt aber mit diesen Koordinaten wird wiederum (x+1-1,y) überprüft. Eine verbesserte Variante vom Seed-Fill müsste sich daher einfach die Richtung merken und daraufhin die Rückrichtung nicht mehr gehen. Anstelle der Methode seedFill(…) treten nun vier Implementierungen an, für jede Richtung eine Funktion. Zur Demonstration sei seedFillLeft(…) aufgeführt.

seedFillLeft( int x, int y )
{
 if ( pixelArray[x, y] != boundaryValue ) {
  setPixel( x, y, fillValue );
  seedFillLeft( x+1, y );
  seedFillUp( x-1, y);
  seedFillDown( x+1, y+1 );
}

Wollen wir noch schnellere Füllalgorithmen benutzen, so kommen wir mit diesem Ansatz nicht weiter. Mit einer modifizierten Version des Scan-Line-Algorithmus ist dies aber zu schaffen.

Thema der Woche: Performant gedacht?

Diskutiere folgende Fragen:

Frage

richtig

falsch

Native Compiler können viel kompakteren Maschinencode erstellen.

   

n+n ist schneller als 2*n.

   

Anweisungen wie n/2, n/4, n*2 sollte man immer durch Verschieben nachbilden.

   

Für eine switch-Anweisung kennt der Compiler zwei unterschiedliche Umsetzungen in Bytecode.

   

Schleifen soll man immer runterzählen. Das ist viel schneller.

   

Über Weak-Reference verwaltet Objekte werden früher freigegeben, als über Soft-Reference verwaltete Objekte.

   

Synchronisierte Methoden sind nicht viel langsamer im Aufruf als nicht-synchronisierte Methoden.

   

Wenn man schnellen Zugriff auf ein Attribut haben muss, dann würde man auf die get()-Funktion verzichten und das Attribut zum Lesen freigeben. Das ist zwar kein Gutes Design, aber im Zugriff viel schneller.

   

Je tiefer die Vererbungshierarchie ist, desto langsamer ist eine Objekterzeugung.

   

Statische Eigenschaften sind im Zugriff immer viel schneller als Objekteigenschaften.

   

Wenn man intern eine Liste als Datenstruktur benutzt, man möchte diese aber sicher nach außen weitergeben (etwa durch getListe()), dann muss man die Liste immer kopieren.

   

Felder beliebiger Größe zu kopieren ist mit System.arraycopy() immer am schnellsten.

   

Ein String-Objekt aus einem StringBuffer zu erstellen kostet noch einmal Speicher für die gesamte Zeichenkette.

   

""+ganzzahl ist in nettes Idiom zur Konvertierung von Ganzzahlen in Strings, was auch in der Performance nicht schlecht ist.

   

intern() ist eine nützliche Funktion, die mehr Vorteile als Nachteile mit sich bringt.

   

charAt() von String ist schneller als charAt() von StringBuffer.

   

substring() erzeugt ein neues Zeichenfeld-Objekt, das genau den ausgeschnittenen Teil speichert.

   

Liest eine Schleife Zeilen aus einer Datei aus, so kann ein StringTokenizer gut benutzt werden, um die Zeilen zu untersuchen.

   

StringBuffer ist von der Performance ausgezeichnet.

   

Es spielt keine große Rolle, ob man einer HashMap eine Initialgröße gibt oder nicht.

   

Eine Menge von Wahrheitswerten sind in einem boolean-Array gut aufgehoben.

   

Ein BufferedOutputStream um einen beliebigen Stream bringt in jedem Fall einen Geschwindigkeitsvorteil.

   

Eine ArrayList kann viel kompakter serialisiert werden als eine LinkedList.

   

Mit java.nio kann man Dateien viel schneller kopieren als ohne.

   

Ein Server sollte eine Anfrage immer mit einem neuen Thread beantworten.

   

SELECT * FROM Tabelle ist unproblematisch.

   

Es ist egal, ob ich einen ResultSet mit getXXX(String Spaltenname) oder getXXX(int Spaltennummer) abfrage.

   

Für mehrere Anfragen an eine Datenbank ist immer ein PreparedStatement einzusetzen.

   

Eine JTable holt sich beim Scrollen jedes Element neu aus dem Model.

   

Wenn man SwingWorker durch FoxTrot ersetzt, gewinnt man noch einmal Geschwindigkeit bei Swing-Programmen.

   

Ein Obfuscator führt bei Applets zu einer Minimierung der Übertragungszeit.