Klausuraufgabe: JavaServer Pages und verwandte Technologien

Am 8. Feb. 2002 unterrichtete ich noch am Bildungszentrum für informationsverarbeitende Berufe e. V. (BIB) Informatik und insbesondere Java und stellte unter anderem diese Klausur. Wie würdet ihr abschneiden?

Aufgabe 1

Ja/Nein-Fragen. Kreuze die jeweils richtige Aussage an.

Frage

Ja

Nein

Ein Assoziativspeicher kann mit einem Schlüssel mehr als nur einen Wert verbinden.

   

Auf der Server-Seite haben wir es niemals mit Socket-Objekten zu tun.

   

Sei o eine Referenz-Variable für ein beliebiges aber gesetztes Objekt. Dann ist der Ausdruck o instanceof Object immer wahr.

   

Der Konstruktor von java.io.RandomAccessFile kann eine IOException werfen, die abgefangen werden muss.

   

Die Klasse java.io.File bietet eine Funktion readLine()

   

OutputStream und InputStream sind abstrakte Klassen

   

Das Skriptlet <%= out.println(2); %> wird vom JSP-Compiler zurückgewiesen.

   

Mit dem Objekt request kommt man an die Daten, die der Server an den Browser stellt.

   

Implizite Objekte müssen vom Benutzer erst erzeugt werden.

   

Bei Optionsfeldern kann es passieren, dass Parameter gleich benannt sind.

   

Ein Formular mit der GET-Methode erzeugt eine URL mit etwa folgendem Aufbau: http://horst/test.jsp&x=12?y=100

   

Eine Java Bean ist eine Klasse, die eine spezielle Schnittstelle implementieren muss.

   

Eine Java Bean ist eine Klasse, die von keine spezielle Klasse erben muss.

   

Sei a ein Attribut vom Typ int. Dann hat die Setze-Funktion die Signatur void setA( int i ).

   

JSP bietet eine Möglichkeit, dass Formular-Einträge automatisch in eine Bean eingetragen werden.

   

Mit einem Cookie kann man Informationen auf der Server-Seite hinterlegen.

   

Zum Einbinden von java.util und java.sql schreibt man die Direktive

<%! Include"java.util", "java.sql" %>

   

Ein Webkontext in der server.xml Konfigurationsdatei von Tomcat assoziiert einen beliebigen Server mit einem physikalischen Verzeichnis.

   

Erfragt man mit request.getParameter() einen nicht existierenden Parameter, wird eine NullPointerException ausgelöst.

   

Kleine Informationseinheiten können auf der Client-Seite des Benutzers abgelegt werden.

   

Aufgabe 2

Schreibe JSP-Code, welches eine Funktion bold() deklariert. Die Methode soll eine Zeichenkette s entgegennehmen und eine Zeichenkette zurückliefen, die den übergebenen String mit <b> und </b> umrahmt. Beispiel: Aus Peter Pan wird <b>Peter Pan</b>.

 

 

 

 

 

 

Nutze die Funktion ist einem JSP-Ausdruck.

<%=                                              %>

Aufgabe 3

Gegeben ist ein Formular mit einem Textfeld. Nach dem Abschicken wird eine URL mit dem Namen http://server.org/db/klein/send.jsp?name=paulchen formuliert. Wie sieht ein Skriptlet aus, welches den existierenden Namen ausliest?

<%

String name;

 

 

%>

Nun soll die Länge des Namens (die Länge eines Strings kann man mit der Funktion length() erfragen) ausgegeben werden. Was muss man schreiben, wenn es Fehler bei der URL gibt? Beachte nicht existierende Parameter und leere Parameter. Gibt es Fehler, soll die Länge –1 ausgegeben werden.

<%

String name; int len = -1;

 

 

 

 

%>

<%=                                   %>

Sonnenfinsternis in der Schule. Die Eclipse-IDE erfolgreich im Informatikunterricht einsetzen

Viel wurde über das Für und Wider von Java im schulischen Unterricht diskutiert. Fakt ist jedoch, dass heute viele Lehrpläne Java als Programmiersprache auflisten und neue Lehrkräfte an den Universitäten mit dieser imperative objektorientierte Programmiersprache in Kontakt kommen und sich schon aus diesem Grunde heute für Java entscheiden. Steht die Entscheidung für eine Programmiersprache fest, ist die Wahl der Entwicklungsumgebung (neudeutsch IDE für „Integrated Development Environment“) nicht einfacher. Im Allgemeinen steht als Basis für die Entwicklung von Java-Programmen das Java SE von Sun; es besteht aus Compiler und Laufzeitumgebung (JVM) und einer Reihe Tools. Das Java SE – früher J2SE – steht in zwei Spielarten zum Download bereit: Java Runtime Environment (JRE) und Software Development Kit (SDK), auch JDK genannt. (Sun macht es einen mit häufigen Umbenennungen nicht einfach.) Das JDK bildet für den Schulunterricht eine gute Basis, da die kompletten Quellen der Java-Typen beiliegen und diese von Eclipse für die API-Dokumentation benutzt werden.

Ohne zusätzliche Installation einer Entwicklungsumgebung ermöglicht das JDK sofort auf einem Windows oder Unix-System die Java-Entwicklung. Sun liefert die Kommandozeilenprogramme javac zum Übersetzen der Quellen und java zum Ausführen der Programme mit aus. Eine grafische Entwicklungsumgebung enthält das JDK oder JRE zwar nicht, doch da bei einem zeitgemäßen Betriebssystem ein Editor zur Standardinstallation gehört, ließe sich dieser zum Schreiben des Java-Quellcodes verwenden. Der Quellcode kann mit dem Editor erstellt und gespeichert werden und eine Kombination von javac und java auf der Kommandozeile kann zum Übersetzen und Ausführen der Programme genutzt werden. Das hat zwei Schwächen, wobei sich die erste auf die Betriebssysteme und Editoren bezieht. Unter Windows gibt es für Textdateien standardmäßig nur Notepad, was den Quellcodeautor durch fehlende farbliche Hervorhebung der Schlüsselworte und Zeichenketten nicht großartig unterstützt. Immerhin produzierte der Markt in den letzten Jahren qualitative (auch freie) Alternativen, etwa Notepad++. Unter Linux leben dann gleich mehrere Editoren nebeneinander. Der zweite Makel ergibt sich durch den Kontextwechsel Texteditor und Kommandozeile. Der Wechsel kostet Zeit und bedarf bei – bedauerlicherweise nicht auszuschließenden – Compilerfehlern einer lästigen Synchronisation zwischen den von javac angezeigtem Fehlerzeilen und dem Editor mit anschließender Fehlerbehebung.

Integrierte Entwicklungsumgebungen versuchen das Manko zu beheben, in dem sie die Vorgänge Editieren, Refactoring, Übersetzen, Ausführen und Debuggen näher zusammenbringen, also in einer Software integrieren. Soll eine IDE das Team Kommandozeile und Standard-Editor absetzen, folgt die nächste Frage: Welches Produkt? Einige Kriterien allgemeiner Art, ohne auf eine Programmiersprache im Besonderen einzugehen, sind:

  • Entfallen auf die Software Kosten, ist das eine Bremse für die Verbreitung. Idealerweise sollte die Software kostenlos sein, damit Lizenzkosten nicht anfallen. Kommerzielle Produkte könnten Lernende dazu verführen, die Software illegal zu kopieren – Lehrende sind davon prinzipbedingt auch nicht ausgeschlossen. Diese Form der Urheberrechtsverletzungen darf in keinem Fall geduldet werden.
  • Die Anforderungen an Hardware und Software (Betriebssystem) stimmt mit der Situation in den Schulen überein. Nutzen Schulen nicht Windows, sondern Linux oder MacOS, so muss die IDE auf dem System ohne Problem laufen.
  • Im besten Fall gibt es eine gute Programmdokumentation, die aufzeigt, welche Voraussetzungen die Software hat und wie sie sich starten, bedienen und konfigurieren lässt.

Für Java gibt es nach 15 Jahren Präsenz eine große Anzahl Entwicklungsumgeben. Für Industrieprogrammierer eignen sich allerdings nur vier wirklich: Eclipse (Eclipse Foundation), NetBeans (Sun Microsystems), JDeveloper (Oracle Corporation), IntelliJ (JetBrains). In der Entwicklergemeinde hat Eclipse die meisten Anhänger und ist die beliebteste IDE überhaupt. Aber rechtfertigt dies, auch im Unterricht die Lernenden mit Eclipse zu beschäftigen? Sollten nicht vielleicht Alternative in Betracht gezogen werden? Als Argumente für und gegen Eclipse lassen sich anführen:

  • Eclipse ist frei und quelloffen. Es gibt viele Zusätze – Plugins genannt – etwa für UML-Diagramme. Da Eclipse eine Rahmenwerk ist, welches auch andere Programmiersprachen unterstützt, die im Unterricht eine weitere Rolle spielen können, wie PHP, muss sich der Lernende nur in eine Umgebung eingewöhnen.
  • Eclipse läuft unter diversen Betriebssystemen, wie Linux, Mac OS und Windows. Die Installation und der Betrieb läuft ohne Eingriff in System-Dateien und ist auch auf Rechnern mit eingeschränkten Benutzerrechten möglich.
  • Die Hardwareanforderungen sind nicht unerheblich. Die Rechner sollten 128 MB Hauptspeicher haben und schneller als 500 MHz sein. Der Festplattenbedarf für ein Java-Projekt liegt inklusive einiger Eclipse-Zusätze bei etwa 500 MB.
  • Es gibt internationalisierte Versionen, insbesondere eine deutschsprachige Oberfläche.
  • Der erste Blick auf die gestartete Oberfläche ist mit etwa 20 Symbolen, 10 Menüzeilen und insgesamt 100 Menüeinträgen in der Java-Perspektive beunruhigend.
  • Die Oberfläche von Eclipse lässt sich flexibel anpassen und „entschlacken“, sodass nur noch wenige Symbole Einsteiger verwirren können.
  • Eclipse hat gegenüber anderen Umgebungen den Vorteil, dass der Editor besonders Spracheinsteigern hilft, sich mit der Syntax von Java „anzufreunden“. Dazu unterschlängelt Eclipse ähnlich wie moderne Textverarbeitungssysteme fehlerhafte Stellen.
  • Vom im Editor geschriebenen zum ausgeführten Programm liegt eine transparente Übersetzung mittels des im Hintergrund ablaufenden Java-Compilers. Eclipse stößt beim Speichern automatisch den Compiler an, und aktualisiert in dem Zyklus die Fehlermeldungen. Da die Fehleranzeige unterschiedlich aktuell ist, müssen Lernende die Aktualisierung der Anzeige verstehen.
  • Die IDE bietet notwendigen Hilfen beim Entwickeln, wie etwa automatische Codevervollständigung.
  • Eclipse setzt auf dem Java SDK auf und unterstützt jede gewünschte Java-Versionen.
  • Lernende könnten mit ihrem Wissen im Umgang mit Eclipse vor den Lehrenden sein, was im Unterricht immer eine Herausforderung ist. Nicht jeder Lehrende fühlt sich wohl in der Haut, wenn Schüler im Stoff weiter sind und unangenehme Fragen stellen, und kann den Wissensvorsprung seiner Lernenden produktiv im Unterricht einbringen.
  • Mit F1 ist die API-Dokumentation der Java-Typen und Eigenschaften in einem eigenen Unterfenster (in Eclipse View genannt) sofort zu sehen.

Die Eclipse IDE ist sehr leistungsfähig und die Einarbeitung kostet Zeit. Bei jeder Software steht der Lehrende vor dem Problem, eine Produktschulung zu geben, die unterschiedlich lang dauern kann. Eclipse benötigt eine längere Einarbeitungszeit als ein vergleichsweise einfacher Notepad, und die Zeit geht von der wertvollen Unterrichtszeit ab, die zur Vermittlung wichtiger Konzepte benötigt wird. Nach eigener Erfahrung als Java-Dozent mit drei Jahren Eclipse-Erfahrung in der Lehre kann ich Entwarnung geben, denn die Grundlagen zu Eclipse sind in 45 Minuten vermittelt und das erste „Hallo Welt“-Java-Programm implementiert.

Von den Vorteilen von Eclipse möchte ich zwei näher ausführen:

  • Die syntaktische Prüfung parallel zum Schreiben ist eine unglaubliche Hilfe. Vielen Lehrenden ist es schmerzlich bewusst, dass die wertvolle Unterrichtszeit mit der Vermittlung und Korrektur von syntaktischen Dingen wie Semikolon am Ende, geschweiften Klammen und ähnlichen Dingen dahinschmilzt. Mit roten Schlangenlinien unter fehlerhaften Stellen und einer Glühbirne zur automatischen Korrektur können Lernende leichter die Fehler erkennen und korrigieren. (Die Glühbirne ist aber Fluch uns Segen zugleich: Ein unreflektierter Gebrauch führt dazu, dass Fehler durch Varianten korrigiert werden, die Lernende nicht kennen, und ohne nachzudenken, irgendeine Lösung akzeptieren, ohne den Fehler selbst erkannt zu haben.)
  • Eclipse eignet sich für den Schulunterricht genauso wie für ernsthafte Entwicklung. Das ermöglicht fortschrittliches Arbeiten der Lernenden zuhause.

Problemorientierter Unterricht soll nicht heißen, dass die Lernenden laufend Probleme mit der IDE haben. Dennoch lässt sich Eclipse so vorbereiten, dass Schwierigkeiten minimiert werden. Dazu einige Grundlagen. Nach dem Download von Eclipse unter http://www.eclipse.org/downloads/ ist für Windows ein Zip-Archiv auszupacken. Im ausgepackten Ordner befindet sich das Hauptprogramm eclipse.exe. Nach dem Start muss ein Pfad für den Workspace vergeben werden; das ist ein Verzeichnis, in dem Eclipse die Projekte ablegen kann (aber nicht muss) und Konfigurationsdateien speichert. Einem Workspace können beliebige viele Java-Projekte zugeordnet sein, aber eine Eclipse-Instanz kann nur einen Workspace zu einer Zeit geöffnet haben. Wurde Eclipse zum ersten Mal gestartet, folgt ein Willkommens-Bildschirm (der geschlossen werden kann) auf der Arbeitsoberfläche – die Workbench genant wird. Für die Java-Programmierung muss ein neues Java-Projekt angelegt werden. Dazu ist im Menü File > New > Project > Java Project zu wählen. Nach der Angabe eines Projektnamens im Dialog lässt sich eine Java-Klasse in das Java-Projekt setzen. Dazu wird File > New > Class verwendet. Nach Eingabe eines Klassen- und Paketnamens kann optional der Schalter für einen Rumpf der main()-Methode aktiviert werden. Nach dem Abschluss des Dialoges öffnet sich ein Editor mit der neuen Klasse. An der Stelle ist interessant, den Begriff View zu erklären. Eine View ist alles das, was im Fenster im abgerundeten Bereich zu sehen ist. Dazu gehören: der Package-Explorer, die Outline, die Console, der Editor selbst. Welche Views gerade sichtbar sind, bestimmt die Perspektive. Es kann immer nur eine Perspektive zu einer Zeit sichtbar sein und welche das ist, ist in der Symbolleiste oben rechts abzulesen. Im Editor können Lernende jetzt das obligatorische System.out.println("Hallo Welt"); schreiben und mit der automatischen Fehlerprüfung experimentieren. Nach dem Speichern und ohne rote Kringel ist das Programm korrekt. Um es auszuführen bietet Eclipse mehrere Möglichkeiten. Eine ist, im Editor das Kontextmenü aufzurufen und unter Run As > Java Application zu wählen. Die Ausgabe steht im Konsolenfenster.

clip_image002

Abbildung 1: Eclipse mit Fehleranzeige

Die genannten Schritte zum lauffähigen Beispiel sind zu vollziehen, wenn man mit einer neuen Installation von Eclipse beginnt. Für den Unterricht können diverse Dinge vorbereitet werden:

  • Eclipse ist schon ausgepackt und ein Symbol zum Starten von Eclipse ist im Menü des grafischen Betriebssystems eingetragen.
  • Eclipse wurde schon gestartet und ein Workspace zugewiesen. Der Schalter bei der Angabe des Workspace-Pfades wurde gesetzt, dass der Dialog nicht immer wieder kommt. Der Willkommens-Schirm ist schon geschlossen.
  • Im Workspace gibt es ein leeres Java-Projekt. Idealerweise trennen src/– und bin/-Order die Java-Quellcode-Dateien und die Java-.class-Dateien.
  • Das Java-Projekt enthält ein Beispielprogramm mit der Konsolenausgabe, was nur gestartet werden muss.

Haben Lehrende dies mit einer Eclipse-Installation schon gemacht, und etwa das Workspace-Verzeichnis unter das Eclipse-Installationsverzeichnis gelegt, kann dieses Eclipse-Verzeichnis einfach auf andere PCs übertragen werden. Die PCs „erben“ damit diese Standardeinstellungen.

Neben der Möglichkeit, ein erstes Projekt schon direkt eingestellt zu haben, ist weiterhin interessant, die Oberfläche zu vereinfachen. Dazu können einige Views wie Hierarchy oder JavaDoc geschlossen werden. Was genau in eine Perspektive für Views eingerichtet sind, und was zum Teil für Menüpunkte eingeblendet sind, bestimmen Einstellungen unter Window > Customize Perspective…

Eclipse bietet eine Reihe von Möglichkeiten, die jeden Informatikunterricht bereichern. Dazu zählen insbesondere die Refactoring-Möglichkeiten, um Bezeichner umzubenennen oder automatisch Schnittstellen aus Java-Klassen zu extrahieren. Die IDE ist sehr leistungsfähig, mächtiger als das, was im Unterricht je angesprochen werden könnte. Dennoch finden Nutzer immer wieder neue Kniffe, die sie gerne mit anderen teilen und die sie zum Erforschen anregen. Insbesondere die Glühbirne, die Fehler automatisch korrigiert, hat es Lernenden meiner Erfahrung nach angetan. Zu den Eclipse-Erweiterungen – den Plugins – gehören Produkte wie das Eclipse Web Tools Platform (WTP) das im Bündel Eclipse IDE for Java EE Developers eingebracht ist, die Eclipse erweiterte Funktionalität im Bereich XML, Datenbanken oder Web-Entwicklung geben. Mit dem WPT lassen sich etwa XML-Dokumente mit syntaktischer Hervorhebung und paralleler Fehlerprüfung schreiben, Datenbankverbindungen aufbauen und Tabellen visualisieren und einfache Web-Anwendungen mit JavaServer Pages schreiben. Das auch frei verfügbare Plugin Omondo (http://www.omondo.com/) gibt Einblicke in die UML-Entwicklung und kann Klassendiagramme mit Quellcode synchron halten.

DESIGN PATTERN kompakt

Grundlegende Pattern

Schnittstellen
Interfaces

Schnittstellen definieren die Operationen einer Klasse, und schreibt Verhalten vor, welches die Objekt dann zeigen.

Delegate
Delegate

Delegiere die Arbeit an ein anderes Objekt. Erweitere die Funktionalität einer Klasse nicht durch Vererbung, sondern durch Assoziation.

Markierungsschnittstelle
Marker-Interface

Durch die Implementierung einer Schnittstellen, die keine Operationen vorschreibt, wird das Objekt markiert und bekommt somit einen Typ.

Immutable
Abstract Factory

Die Zustände eines Objekts lassen sich nicht mehr verändern. Unveränderbare Objekte.

Typsicherer Aufzählungen
Lasse Konstanten typsicher sein, dass keine Verwechselungen der Reihenfolge, und der Inhalte auftreten.

Null-Objekt
Null object

Implementiere die Schnittstelle ohne Funktionalität.

Objekt-Pool
Object Pool

Halte teuer zu erzeugende Objekte in einem Objekt-Pool.

Erzeuger-Pattern (Creational Pattern)

Abstrakte Fabrik
Abstract Factory

Stelle eine Schnittstelle bereit, um eine Familie von verwandten Objekten zu erzeugen, ohne die konkreten Klassen zu offenbaren.

Erbauer
Builder

Trenne die Erzeugung und die Repräsentation eines komplexeren Objektes, so dass sich die Konstruktion auch mit anderen Repräsentationen durchführen lasst.

Fabrikmethode
Factory Method

Stelle für die Erzeugung von Objekten eine Schnittstelle bereit, lass aber eine Fabrik entscheiden, welche Unterklassen erzeugt wird.

Prototyp
Prototype

Lege durch eine Prototyp fest, welche Objekten zu erzeugen sind und erzeuge neue Objekte als Kopie dieses Prototyps.

Singleton
Stelle sicher, dass es nur ein Exemplar eine Klasse gibt. Dieses lässt sich durch eine statische Funktion erfragen.

Struktur-Muster (Structural Pattern)

Adapter
Passe die Schnittstelle eine Klasse auf die Schnittstelle einer anderen Klasse an. So können Klassen zusammenarbeiten, die sonst wegen inkompatibler Schnittstellen nicht miteinander zusammenarbeiten könnten.

Brücke
Bridge

Löse die Abstraktion von der Implementierung, so dass beide unabhängig verändert werden können.

Kompositum
Composite

Zusammengesetzte Objekte bilden über eine Teil/Ganzes-Beziehung eine Baumstruktur. Die Blätter und Knoten sollen gleich behandelt werden können.

Dekorator
Decorator

Ein Objekt kann (auch dynamisch) weitere Verantwortlichkeiten enthalten.

Fassade
Facade

Stelle eine einfache Schnittstelle für ein komplexes System bereit. Damit wird das tiefer liegende System einfacher zu nutzen.

Fliegengewicht
Flyweight

Behandle eine große Menge kleiner Objekte effizient, in dem unveränderliche Objekte gemeinsam genutzt werden.

Proxy
Stelle einen Stellvertreter für ein Objekt bereit.

Verhaltsmuster  (Behavioral Pattern)

Verantwortungskette
Chain Of Responsibility
Gehe eine Kette von Objekten durch und frage sie, ob sie eine Anfrage annehmen. Entkopple dadurch Sender und Empfänger.

Kommando
Command
Eine Operation wird als Objekt verpackt.

Interpreter
Definiere zu einer Sprache eine Grammatik und baue einen Interpreter, der Sätze der Sprache erkennt.

Iterator
Ermögliche ein sequenzielles Ablaufen einer Datensammlung ohne die interne Repräsentation der Daten freizugeben.

Vermittler
Mediator

Entkopple Objekte, damit sie sich nicht mehr direkt kommunizieren müssen.

Memento
Speichere ohne Freigabe der inneren Struktur den internen Zustand eines Objektes um ihn später wieder Herstellen zu können.

Beobachter
Observer
Definiere eine 1:n-Beziehung zwischen Objekten, so dass bei einer Zustandsänderung die Interessenten automatisch informiert werden.

Zustand
State
Erlaube bei einer Änderung des Zustandes eine Änderung des Verhaltens.

Strategie
Strategy
Definiere für jeden Algorithmus (Strategie) eine Klasse, die dann später vom Klienten genutzt werden kann.

Schablonenmethode
Template Methode

Definierte das Skelett einer Anwendung durch eine Reihe von Operationen und verschiebe die Implementierung der Operationen auf die Unterklassen.

Besucher
Visitor
Trenne Algorithmus und Datenstruktur voneinander, sodass sich insbesondere Verhalten anpassen lässt, ohne die Klasse für die Datenstruktur anfassen zu müssen.

Links

Was ist eigentlich kein Pattern?
Bjarne Stroustrup, OOPSLA 95

Der Report-Generator JasperReport

JasperReport ist eine weit verbreitete Open-Source-Software unter der LGPL-Lizenz zum Erstellen von Reports. Auf der Basis einer Vorlagendatei im XML-Format setzt JasperReports die Eingabe, die etwa aus einer relationalen Datenbank stammt, in eine Ausgabe um. Als Ausgabeformate unterstützt der Generator drei Typen von Ausgaben: Text-/Zeichenbasierte Dokumente HTML, RTF, CSV, TXT und XML, Dokumente in den Binärdateiformaten PDF, XLS und Export auf ein Grafik-Gerät (Graphics2D und Java Print Service). Der Unterschied ist, dass HTML, XLS und CSV nur eingeschränkte Positionierungen erlauben und nicht wie die anderen Formate pixelgenaue Boxen.

JasperReport greift auf weitere Open-Source-Bibliothek zurück, etwa auf iText (http://itextpdf.com/) für die PDF-Ausgabem, JFreeChart (http://www.jfree.org/jfreechart/) für diverse Diagramme oder Apache POI zu Generieren von Excel XLS Dokumenten (dann aber ohne Charts). Rund um JasperReport (http://community.jaspersoft.com/project/jasperreports-library) ist ein Ökosystem mit weiteren Tools entstanden. JasperReports wird zusammen mit anderen Tools von der JasperSoft Corporation (http://www.jaspersoft.com/) entwickelt. Das Unternehmen hat weiterhin die freie Swing-Anwendung iReport (http://community.jaspersoft.com/project/ireport-designer) zur Erstellung von JasperReport-Vorlagendateien im Portfolio. JasperAssistant (http://www.jasperassistant.com/) ist ein Beispiel für eine kommerzielle Software (129 US Dollar für eine Lizenz), um die Vorlagendateien grafisch in Eclipse zu erstellen.

Ein Report besteht bei JasperReport aus so genannten Bands, die auch Report-Sektionen genannt werden. Es lassen sich drei Typen von Sektionen ausmachen:

  • Sektionen, die nur genau einmal im Report auftauchen können, wie der Titel am Anfang, oder eine Zusammenfassung am Ende.
  • Sektionen, die auf jede Seite stehen können, wie die Seitenkopf- und Fußzeile.
  • Eine Detail-Sektion, die einer Tabelle mit Einträgen aus Datenquellen wie Datenbanken ähnelt.

Alle Bands haben eine feste Reihenfolge und eine benutzerdefinierte Höhe und Breite. Die Reihenfolge der Segmente ist: Hintergrund, Titel, Seitenkopf, Spaltenkopf, Gruppierungskopf, Detail, Gruppierungsende, Spaltenende, Seitenende, letztes Seitenende, Zusammenfassung.

Die Elemente dieser Bands nennen sich Report-Objekte. JasperReport definiert zwei Textelemente, vier grafische Objekte und einen Sub-Report – ein Sub-Report ist ein im Report eingebetteter Unter-Report. Dass es als Report-Objekte zweimal Textfelder gibt, liegt daran, dass JasperReport zwischen statischem Text, etwa für einen festen Spaltennamen, und einem dynamischen Text unterscheidet. Der dynamische Text kann sich während der Reporterstellung ändern, wenn etwa ein Feld aus einer Datenbank stammt oder die Seitenzahl angezeigt wird. Zu den grafischen Report-Objekten zählen Linie, Rechteck (auch abgerundet) und Ellipse. Alle weisen eine Vielzahl von Eigenschaften auf, wie Strichtypen, Dicken und Farben. Dazu kommen Bilder, die gewünscht skaliert und positioniert vom Datensystem, Datenbank oder über das Netzwerk kommen können.

Während bei statischen Textelemente sich der Werte nicht ändern, stellen dynamischen Textelemente so genannte Ausdrücke dar. Ein Ausdruck wird zur Laufzeit berechnet und als String in den Report gesetzt. Der Ausdruck kann auf verschiedene Datenspeicher zurückgreifen:

  • Parameter. Die Parameter sind Werte aus einer java.util.Map, die vor dem Aufbau eines Reports initialisiert wurde.
  • Felder. Sie ergeben sich aus Einträgen einer Datenquelle, wie der Datenbank oder einer Sammlung von Hibernate-Objekten.
  • Einträge aus einem Resource-Bundle.
  • Variablen. Während des Reports kann eine neu definierte Variable einen Wert zwischenspeichern. JasperReport bietet einige vorbelegte Variablen, zum Beispiel für die aktuelle Seite oder die aktuelle Spalte in der Verarbeitung.

Die Ausdrücke können mit den bekannten Operatoren aus Java zu komplexeren Ausdrücken zusammengesetzt werden, denn JasperReport erlaubt die gesamte Syntax der Sprache Java. Variablenzugriffe stehen in einer besonderen Notation: $P{name}, $F{name}, $K{name} und $V{name}.

Eine interessante Alternative ist Eclipse BIRT (http://www.eclipse.org/birt/phoenix/).

Fragen zur Java-Zertifizierung, Standardklassen

Frage

Was ist die Ausgabe des folgenden Programmstücks bei der Ausführung?

int i = 3;
int j = 0;
float k = 3.2F;
long m = -3;
if ( Math.ceil(i) < Math.floor(k) )
  if ( Math.abs(i) == m )     System.out.println( i );
  else     System.out.println( j );
else   System.out.println( Math.abs(m) + 1 );
  1. -3
  2. 0
  3. 3
  4. 4
  5. keine dieser

Die Ausgabe ist 4.

Frage

Welche der folgenden sind gültige Objektmethoden der Klasse String?

  1. equals( String )
  2. equals( Object )
  3. trim()
  4. round()
  5. toString()

equls(Object) und toString() sind zwei Objekte, die von Object kommen. Also sind B und E richtig. Eine equals() Funktion mit der Parameter String gibt es nicht und muss es auch nicht geben. Also ist A falsch. Denn da equals(Object) für alle Objekt gilt, gilt im besonderen auch equals(String). Da die String Klasse equals() überschreibt, wird schon darauf geachtet, dass keine beliebigen Objekte verglichen werden. trim() schneidet Leerzeichen vor und hinter der Zeichenkette ab, auch C ist richtig. Eine Rundungsfunktion ist bei String Objekten nicht nötig und möglich. round() ist eine Funktion der Klasse Math. Also ist D falsch.

Frage

Welche Anweisung ergibt den Wert -4.0?

  1. Math.floor( -4.7 )
  2. Math.round( -4.7 )
  3. Math.ceil( -4.7 )
  4. Math.min( -4.7 )

Antwort A und B erzeugen die Ausgabe -5 sind also falsch. Antwort C erzeugt die entsprechende Ausgabe. Lösung D kann nich kompiliert werden,da die min() Funktion zwei Parameter erwartet.

Frage

Was schreibt das Programm auf den Schirm?

String s = "Ulli"; 
int begin = 1;
char end = 3;
System.out.println( s.substring(begin, end) );
  1. Ull
  2. ll
  3. lli
  4. error: no method matching substring(int,char)

B) ll

Frage

Welche Operationen sind bei den gegeben Deklarationen erlaubt?

String s1 = new String("Hello")

String s2 = new String("there");

String s3 = new String();

  1. s3 = s1 + s2;
  2. s3 = s1-s2;
  3. s3 = s1 & s2
  4. s3 = s1 && s2

A s3 = s1 + s2;

Frage

Gegeben ist das folgende Programm:

import java.util.Date;

public class Datum {
  public static void main( String args[] ) {
    Date d1 = new Date( 99, 11, 31 );
    Date d2 = new Date( 99, 11, 31 );
    foo( d1, d2 );
    System.out.println( "d1 ist " + d1 + "\nd2 is " + d2 );
  }
  public static void foo( Date d1, Date d2 ) {
   d2.setYear( 100 );
   d1 = d2;
  }
}

Welche Aussage trifft auf das Programm zu?

  1. Das Programm kompiliert und die Ausgabe ist

    d1 ist Fri December 31 00:00:00 GMT 1999

    d2 ist Fri December 31 00:00:00 GMT 1999

  2. Das Programm kompiliert und die Ausgabe ist 
    d1 ist Fri December 31 00:00:00 GMT 1999

    d2 ist Sun December 31 00:00:00 GMT 2000

  3. Das Programm kompiliert und die Ausgabe ist 
    d1 ist Sun December 31 00:00:00 GMT 2000

    d2 ist Sun December 31 00:00:00 GMT 2000

  4. Die Anweisung ‚d1 = d2‘ wird vom Compiler zurückgewiesen, da Date den Gleich-Operator nicht überladen hat.
  5. Der Ausdruck (d1 is " + d1 + "\nd2 is " + d2) wird vom Compiler zurückgewiesen, da die Date Klasse den Plus-Operator nicht überladen kann.

Das Programm lässt sich korrekt kompilieren, da keine Zeile fehlerhaft ist. Daher sind Antwort D und E falsch. Mit dem Gleichheitszeichen werden lediglich die Referenzen unterschieden und das ist immer erlaubt. Auch der Plusoperator bereitet keine Probleme, da in im Zusammenbauen eines Ausgabestrings auf die Date-Objekte immer toString() aufgerufen wird. Fast alle Objekte nutzen diese Methode zur Identifikation ihrer inneren Werte. Nun bleiben zwei Dinge interessant. Zunächst einmal der Konstruktor mit den Werten und die Methode foo(). Der Konstruktor ist in Ordnung und in der Ausgabe sind Stunde, Minute und Sekunde auf Null, da sie von uns nicht gesetzt wurde. Werfen wir einen Blick auf foo(). Es ändert im ersten Objekt das Jahr auf 100. Aber da die Zuweisung nur lokal wirkt, ändert sich d2 nicht und es ist ein klassischer Programmierfehler. Lokale Variablen haben keine Außenwirkung. Nur im Block der Methode ist nun d1 mit d2 identisch, da d2 auf d1 zeigt. So sind auch die Jahreszahlen gleich. Aber nach dem Verlassen der Methode verschwindet auch die Zuweisung, nur die Änderung in d2 bleibt gültig. d1 behält das Jahr 99. Also ist C falsch. Es bleiben noch A und B als Lösungen. Nun muss man wissen, dass setYear(100) das Jahr tatsächlich auf  2000 setzt. Somit ergibt sich als richtige Lösung nur B, da d1 und d2 die Jahre 1999 und 2000 beschreiben.

Fragen zur Java-Zertifizierung, Threads

Frage

Welche Aussage ist wahr für die Funktion notify() in Verbindung mit wait()?

  1. Wenn mehr als ein Thread auf eine Bedingung wartet, wird nur der Thread benachrichtigt, der am längsten wartet.
  2. Wenn mehr als ein Thread auf eine Bedingung wartet, läßt sich nicht ausmachen, welcher Thread benachrichtigt wird.
  3. notify() ist in der Klasse Thread definiert.
  4. Es ist nicht unbedingt erforderlich, den Lock für das Objekt zu besitzen, dessen notify() Methode wir aufrufen.
  5. notify() sollte nur innerhalb einer while Schleife aufgerufen werden.

Frage

Gegeben sei die folgende Klasse

class Counter {
 public int start = 1;
 public int end = 100;
 public static void main( String args[] ) {
  new Counter().go();
 }
 void go() {
  // A
  Thread t = new Thread( a );
  t.start();
 }
}

Welcher Block kann in der Zeile A eingesetzt werden, so dass das Programm von start bis end zählt?

  1. Runnable a = new Runnable() {

    public void run() {for ( int i = start; i <= end; i++ )

    System.out.println( i );

    }

    }

  2. a implements Runnable {

    public void run() {for ( int i = start; i <= end; i++ )

    System.out.println( i );

    }

    }

  3. Thread a = new Thread() {

    public void run() {for ( int i = start; i <= end; i++ )

    System.out.println( i );

    }

    }

Frage

Analysiere das folgende Programm:

class WasPassiert implementes Runnable
{
 public static void main( String args[] )
 {
  Thread t = new Thread( this );
  t.start()
 }
 public void run()
 {
  System.out.println( "Schnickschnack" );
 }
}
  1. Das Programm lässt sich nicht kompilieren
  2. Das Programm lässt sich kompilieren, aber nichts erscheint auf der Standardausgabe
  3. Das Programm lässt sich kompilieren und das Wort „Schnickschnack“ erscheint einmal auf der Standardausgabe.
  4. Das Programm lässt sich kompilieren und das Wort „Schnickschnack“ erscheint ununterbrochen auf der Standardausgabe, bis der Benutzer mit Control-C das Programm beendet.

Frage

Was muss für die Klasse RunHandler gelten, so dass eine Instanz von RunHandler wie folgt benutzt werden kann?

class Test  
{
 public static void main( String args[] )
 {
  Thread t = new Thread( new RunHandler() );
  t.start();
 }
}
  1. RunHandler muss das Interface java.lang.Runnable implementieren.
  2. RunHandler muss die Klasse Thread erweitern.
  3. RunHandler muss eine Methode run() anbieten, die öffentlich ist und void zurückgibt.
  4. RunHandler muss eine init() Methode anbieten.

Fragen zur Java-Zertifizierung, Überladen, Überschreiben, Typen zur Laufzeit und Objektorientierung

Frage

Das Design einer Klasse erfordert, dass eine Instanzvariable von Unterklassen verändert werden kann. Klassen, die allerdings im gleichen Paket liegen, sollen keinen Zugriff haben. Wie muss der Modifier gewählt werden, so dass dies möglich wird?

  1. public
  2. private
  3. protected
  4. Kein besonderer Modifier.
  5. private und die es sollte Zugriffsmethoden geben.

Public würde die Variable für alle sichtbar machen, daher ist A falsch. Private würde die Variable auch für Unterklassen unsichtbar machen, daher ist auch B falsch. C ist korrekt, da die Variable von erbenden Klassen geändert werden kann. Gibt man keinen Modifier an, so können auch Klassen im gleichen Paket (also Verzeichnis) die Variable verändern. Dies ist aber in der Aufgabe verboten. So ist auch D falsch. Obwohl Zugriffsmethoden im Design einer Klasse zu einer schöne Modellierung führt, verbieten diese jedoch nicht, dass nur Methoden der Unterklassen auf die Variablen zugreifen können. Also ist auch E falsch.

Frage

Was passiert, wenn folgende Klasse compiliert und ausgeführt wird?

class Test
{
  static int myArg = 1;
  public static void main( String args[] )
  {
    int myArg;
    System.out.println( myArg );
  }
}
  1. Der Code lässt sich kompilieren und bei der Aufführung erscheint 0 auf dem Bildschirm.
  2. Der Code lässt sich kompilieren und bei der Aufführung erscheint 1 auf dem Bildschirm.
  3. Dieses Programmstück lässt sich nicht kompilieren, da eine lokale Variable nicht den gleichen Namen besitzen darf, wie eine statische Variable.

  4. Dieses Programmstück lässt sich nicht kompilieren, da die lokale Variable vor der Nutzung noch nicht initialisiert ist.

Der Programmcode wird nicht kompiliert, da myArg nicht initialisiert wurde, Antwort D ist korrekt. A ist falsch, da lokale Variablen nicht automatisch mit 0 initialisiert werden. B ist falsch, da der Zugriff erst auf innere Variablen erfolgt.

Frage

Welches der folgenden Code Fragmente ist geeignet, um die Anzahl der Argumente, die einer Java Applikation übergeben werden, in count zu speichern. Die Deklaration der main() Funktion ist wie folgt:

public static void main( String args[] )
{
  // Hier den Block einsetzen
}
  1. int count = args.length;
  2. int count = args.length – 1;
  3. int count = 0;

    while ( args[count] != null )

      count++;

  4. int count = 0;

    while ( !(args[count].equals("") ) )

      count++;

A ist korrekt. B ist falsch, da in Java der Name des Programmes nicht im ersten Argument steht und daher der Zähler nicht erniedrigt werden muss. Da alle Argumente gültige Strings sind, kann C nicht richtig sein. Leerstring können ebenso nicht in der Folge vorkommen, daher ist auch D falsch.

Frage

Analysiere die folgenden beiden Klassen:

final class First
{
  static int a = 3;
}
class Second extends First
{
  public void method() {     System.out.println( a );
  }
}
  1. Die Klasse First lässt sich kompilieren, aber nicht die Klasse Second.
  2. Die Klasse Second lässt sich kompilieren, aber nicht die Klasse First.
  3. Keine der beiden Klassen lässt sich kompilieren.
  4. Beide Klassen lassen sich kompilieren und wenn method() aufgerufen wird, schreibt es 3 auf die Standardausgabe.
  5. Beide Klassen lassen sich kompilieren, aber ein Aufruf von method() mündet in eine Exception.

Die Klasse First lässt sich problemlos kompilieren. Die Kompilation der Klasse Second scheitert jedoch daran, dass man final Klassen nicht erweitern kann. So ist A, C und D falsch und B die richtige Antwort. C ist falsch, da sich First kompilieren lässt.

Frage

Was passiert, wenn folgender Programmcode kompiliert und die main() Methode von B aufgerufen wird?

class A {
  int i;
  A( int i ) {
    this.i = i*2;
  }
}
class B extends A {
  public static void main( String args[] ) {
    B b = new B(2);
  }
  B( int i ) {     System.out.println( i );
  }
}
  1. Die Instanzvariable i bekommt den Wert 4.
  2. Die Instanzvariable i bekommt den Wert 2.
  3. Die Instanzvariable i bekommt den Wert 0.
  4. Der Code lässt sich nicht kompilieren.

Da sich der Programmcode nicht kompilieren lässt ist A, B und C falsch und D richtig. In der Vererbung ruft der Konstruktor der Klasse B den Standard-Konstruktor von A auf. Dieser existiert aber nicht. Um da Programm zum Laufen zu bringen, muss im Konstruktor B(int) mit super() der parametrisierte Konstruktor in A aufgerufen werden.

Frage

Was ist an folgendem Programmstück falsch?

final class First
{
  private int a = 1;
  int b = 2;
}
class Second extends First
{
  public void method() {     System.out.println( a + b );
  }
}
  1. Die println() Methode kann nicht ohne einen String Parameter benutzt werden.
  2. Da die Variable a privat ist, hat keine Klasse außer First Zugriff auf die Variable.
  3. Second kann First nicht erweitern.
  4. final ist kein gültiges Schlüsselwort für eine Klasse.

Eine Klasse kann eine final Klasse nicht erweitern. Daher ist Antwort C korrekt. println() ist auf verschiedenen Parametern definiert, auch mit int. Ließe man final weg, so käme dann der Fehler vom privaten Attribut a. final ist natürlich ein gültiges Schlüsselwort für Klassen.

Frage

Folgende Klassen sind auf einem Dateisystem, welches Groß/Kleinschreibung unterscheidet unter Fred.java gesichert. Welches sind korrekte Klassendeklarationen? (Zwei Antworten.)

  1. public class Fred {
      public int x = 0;
      public Fred( int x ) {
        this.x = x;
      }
    }
  2. public class fred {
      public int x = 0;
      public fred( int x ) {
        this.x = x;
      }
    }
  3. public class Fred extends MyBaseClass, MyOtherBaseClass {
      public int x = 0;
      public Fred( int xval ) {
        x = xval;
      }
    }
  4. protected class Fred {
      private int x = 0;
      private Fred( int xval ) {
        x = xval;
      }
    }
  5. import java.awt.*;
    public class Fred extends Object {
      int x;
      private Fred( int xval ) {
        x = xval;
      }
    }

Antwort A ist korrekt, da die Klasse die gleiche Groß/Kleinschreibung wie der Dateiname besitzt. B ist falsch, die die Schreibweise immer übereinstimmen muss. In Java gibt es keine Mehrfachvererbung, daher ist C falsch. Heißt die Klasse Fred.java, so muss sie auch von außen sichtbar sein. Daher muss sie public sein. Währe die Klasse zusammen mit einer anderen öffentlichen Klasse in einer Datei, so wäre dies korrekt. Auch E ist korrekt. Das Abstract Window Toolkit gehört zum Kern Paket von Java, ist also immer vorhanden. Obwohl jedes Objekt implizit Object erweitert, kann man dies noch einmal hinschreiben. Wenn der Konstruktor privat ist, lässt er sich nicht von außen aufrufen. So macht also diese Klasse wenig Sinn, da man sie nicht erzeugen kann. Man macht oft den Standard-Konstruktor private, wenn man den Benutzer zwingen will, einen parametrisieren zu verwenden.

Frage

Was passiert, wenn versucht wird, den folgenden Programmcode zu kompilieren und auszuführen?

class Mystery {
  String s;
  public static void main( Sting args[] ) {
    Mystery m = new Mystery();
    m.go();
  }
  void Mystery()
  {
    s = "palümpalüm";
  }
  void go()
  {
    System.out.println( s );
  }
}
  1. Dieser Code lässt sich nicht kompilieren.
  2. Dieser Code kompiliert, aber wirft eine Exception zur Laufzeit .
  3. Dieser Code läuft aber nichts erscheint in der Standard Ausgabe.
  4. Dieser Code läuft und schreibt "palümpalüm" in die Standard Ausgabe.
  5. Dieser Code läuft und schreibt "null" in die Standardausgabe.

Der Programmcode lässt sich nicht kompilieren, da die Methode Mystery() so heißt wie die Klasse. Da nur Konstruktoren so heißen dürfen wie die Klasse, Konstruktoren aber keine Rückgabewerte haben (sie geben explizit den this Zeiger des neuen Objektes zurück) muss man void von der Deklaration nehmen, so dass die Kompilation erfolgreich ist. So ist A korrekt. Wäre das void nicht vor dem Konstruktor würde das Programm palümpalüm ausgeben.

Frage

Welche der folgenden inneren Klassen sind korrekt für die Klasse A?

  1. class B { }
  2. class B extends A { }
  3. class B {
      B() {     System.out.println( "i = " + i );
      }
    }
  4. class B {
      class A { }
    }
  5. class A {
    }

Antwort A, B sind richtig. Antwort C ist falsch, da kein i deklariert wurde. D und E führen den Jikes Compiler an seine Grenzen.

Frage

Warum lassen sich die folgenden Klassen nicht kompilieren?

class A
{   private int x;
  public static void main( String args[] ) {
    new B();
  }
  class B {
    B() {       System.out.println( x );
    }   }
}
  1. Die Klasse B versucht, Zugriff zu der privaten Variablen zu bekommen, die in der äußeren Klasse definiert ist.
  2. Die Klasse A versucht, eine Instanz von B zu erzeugen, obwohl es keine gültige Instanz der Klasse A gibt.
  3. Der Konstruktor der Klasse B muss öffentlich sein.

Eine innere Klasse ist immer mit einer äußeren Klasse verbunden. Da aber eine static Method nicht an ein Objekt gebunden ist, wurde versucht ein B Objekt ohne Bezug zu einem A Objekt zu erzeugen. Dies ergibt die Fehlermeldung. So ist B korrekt. Folgende Zeilen würde die main() Funktion korrigieren.

public static void main( String args[] ) {

A a = new A();

A.B b = a.new B();

}

Frage

Was produziert das folgende Programm:

public class Test {
  static int total = 10;
  public static void main (String args []) {
    new Test();
  }
  public Test() {
    System.out.println( "In Test" );
    System.out.println( this );
    int temp = this.total;
    if ( temp > 5 )
      System.out.println( temp );
  }
}
  1. Die Klasse lässt sich nicht kompilieren.
  2. Der Compiler meldet einen Fehler in Zeile 2.
  3. Der Compiler meldet einen Fehler in Zeile 9.
  4. Der Wert 10 ist unter der Ausgabe.
  5. Die Klasse wird kompiliert, erzeugt jedoch einen Laufzeitfehler.

Die Klasse lässt sich kompilieren. Daher sind die Antworten A, B und C falsch. Die main() Methode erzeugt ein Test Objekt und dabei wird der Konstruktor aufgerufen. Dieser gibt erst "In Test" aus und anschließend den Hash Wert des Objektes. Da temp größer 5 ist, wird die Verzweigung genommen und 10 auf dem Schirm ausgegeben.

Frage

Was erfolgt, wenn man folgenden Progammcode kompiliert und ausführt.

abstract class Base {
  abstract void method(); 
  static int i;
}

public class Mine extends Base
{
  public static void main( String args[] ) {
  int[] a = new int[5];
  for( i = 0; i < a.length; i++ )
    System.out.println( a[i] );
  }
}
  1. Es werden  fünf Nullen ausgegeben.
  2. Das Feld a wird benutzt, bevor es initialisiert wurde.
  3. Kompilierfehler. Mine muss abstakt definiert werden.
  4. IndexOutOfBoundes Error i.

Abstrakte Klasse können Objekt- und Klassenvariablen besitzen sowie ausprogrammierte und wieder abstrakte Methoden. Erweitert eine Funktion eine abstrakte Klasse, so muss sie alle nicht ausprogrammierten Methoden implementieren, sonst ist diese Klasse wieder abstrakt. Mine erweitert zwar Base, überschreibt jedoch nicht die abstrakte Methode method(). Daher meldet der Compiler einen Fehler. So ist Antwort C korrekt und die anderen falsch.

Frage

Was passiert, wenn man folgendes Programm kompiliert und laufen lässt?

1: class Base {}
2: class Sub extends Base {}
3: class Sub2 extends Base {}
4:
5: public class Super {
6:   public static void main( String args[] ) {
7:     Base b = new Base();
8:     Sub s = (Sub) b;
9:   }
A: }
  1. Das Programm kompiliert und läuft ohne Fehler.
  2. Das Programm kompiliert nicht.
  3. Das Programm kompiliert aber liefert einen Fehler zur Laufzeit.

Die Definition aller vier Klassen ist korrekt und führt zu keinem Compilerfehler. Sub2 wird nicht benötigt und dient der Verwirrung. Die main() Methode legt eine lokale Variable b an. Diese Variable ist die Basisklasse von Sub. In Zeile 8 versuchen wir aber diese einfache Klasse über die explizite Typumwandlung zur Klasse Sub aufzubauen. Dies lässt der Compiler durch,  aber da Base einfach kein Sub werden kann, führt die Ausführung zu einem Laufzeitfehler. Ohne die Typumwandlung würde es schon zu einem Compilerfehler kommen. Daher ist Antwort C alleine korrekt.

Frage

Welche Modifier lassen sich im Programmcode für die innere Klasse korrekt einsetzen?

public class Clazz {
  public static void main( String args[] ) { }
  
  /* Modifier */ class MyInner { }
}
  1. public
  2. private
  3. static
  4. friend

public, private, static sind mögliche Modifier für innere Klassen. Also ist A, B und C korrekt. Es ist Eigenheit der Sprache C++ Freunde zu definieren, friend ist in Java kein eingetragenes Schlüsselwort.

Fragen zur Java-Zertifizierung, Flusskontrolle und Ausnahmebehandlung

Frage

Was wird ausgegeben, wenn die folgende Methode aufgerufen wird?

void test() {
one:
two:
  for ( int i = 0; i < 3; i++ ) {   three:
    for ( int j = 10; j < 30; j++ ) {      System.out.println( i + j );
      if ( i > 2 )
      continue one;
    }
  }
}
  1. 10 und 20
  2. 11 und 21
  3. 12 und 22
  4. 13 und 23
  5. 30, 31, 32, 33

Frage

Analysiere den folgenden Programmtext:

1: void schleife() {
2:   int x = 0;
3: one:
4:   while ( x < 100 ) {
5:   two:
6:     System.out.println( ++x );
7:     if ( x > 3 )
8:     break two;
9:   }
0: }
  1. Der Code lässt sich nicht kompilieren.
  2. Die Funktion schreibt die Zahl 0 in die Standardausgabe.
  3. Die Funktion schreibt die Zahl 1 und 2 in die Standardausgabe.
  4. Die Funktion schreibt die Zahl 3 in die Standardausgabe.
  5. Die Funktion schreibt die Zahl 4 in die Standardausgabe.
  6. Die Funktion schreibt die Zahlen 5 bis 9 in die Standardausgabe.
  7. Die Funktion schreibt die Zahl 10 in die Standardausgabe.

Der Programmcode kann nicht kompiliert werden, da Zeile 8 eine ungültige Sprungadresse enthält. Daher ist Antwort A korrekt. Die break Anweisung dient immer dazu, aus einer Schleife zu entkommen und nicht aus einer if Anweisung. Würde der Sprung nach two: gelingen, wäre man aber immer noch in der Schleife. Eine gültige Anweisung für Zeile 8 ist break one;

Frage

Betrachte den folgenden switch Block. (Zwei Antworten.)

char mychar = 'c';
switch ( mychar )
{   default:
  case 'a': System.out.println( "a" ); break;
  case 'b': System.out.println( "b" ); break;
}

Welche der folgenden Antworten ist richtig?

  1. Der switch Block ist illegal, weil nur Ganzzahlen in der switch Anweisung benutzt werden können.
  2. Mit der switch Anweisung ist alles in Ordnung.
  3. Der switch Block ist illegal, da die default Anweisung zum Schluss kommen muss.
  4. Wenn das Programmstück läuft, wird nichts auf die Standardausgabe geschrieben.
  5. Wenn das Programmstück ausgeführt wird, erscheint der Buchstabe "a" auf dem Bildschirm.

Der Ausdruck in einer switch Anweisung muss sich auf ein 32-Bit Wert konvertieren lassen. Also sind auch die kleineren Datentypen byte, char, short und int erlaubt. Im Beispiel ist ein Buchstabe ein char, der auf ein int konvertiert wird. Dies ist korrekt, also ist Antwort A falsch. Da die Kompilation somit in Ordnung ist, ist Antwort B korrekt. Die Reihenfolge, wann default auftaucht ist egal, somit ist C falsch. Daher folgt auch erst die richtige Ausgabe. Denn der Buchstaben ‚c‘ passt nicht auf ‚a‘ und ‚b‘, somit kümmerst sich der default Teil um die Eingabe. Da hinter default jedoch kein break steht läuft der Programmcode direkt in die Ausgabe "a" hinein. Also ist ebenfalls E korrekt und D falsch.

Frage

Welche der folgenden Aussagen über try, catch und finally sind wahr?

  1. Einem try Block muss immer ein catch Block folgen.
  2. Einem try Block kann entweder ein catch Block, ein finally Block oder beides folgen.
  3. Ein catch Block muss immer mit einem try Block verbunden sein.
  4. Ein finally kann nicht ohne ein try Block auftauchen.
  5. Keine dieser Aussagen sind wahr.

Einem try Block muss entweder ein finally ein catch oder beides folgen. Also ist A falsch und B richtig. Wenn man ein catch oder ein finally schreibt, so ist dies immer mit einem try verbunden, daher ist auch C und D richtig. Da mindestens eine Antwort richtig ist, ist E falsch.

Frage

1: public class Clazz {
2:   public static void main( String args[] ) {
3:     String s = null;
4:     if ( s != null & s.length() > 0 );
5:     if ( s != null && s.length() > 0 );
6:     if ( s == null | s.length() > 0 );
7:     if ( s == null || s.length() > 0 );
8:   }
9: }

Welche der Aussagen ist wahr?

  1. Zeile 4 erzeugt eine NullPointerException
  2. Zeile 5 erzeugt eine NullPointerException
  3. Zeile 6 erzeugt eine NullPointerException
  4. Zeile 7 erzeugt eine NullPointerException

Antwort A und C sind korrekt und Zeile 4 und 6 erzeugen eine NullPointerException. Man muss wissen, dass & und | beide Seiten auswerten. Ist also die Referenz null, so ist auch s.length() ungültig. In Zeile 4 und 6 kommen aber diese Auswertungen vor. && wertet den rechten Ausdruck nur dann aus, wenn der linke wahr ist. Also wird in Zeile 5 keine Länge abgefragt. || wertet nur dann die rechte Seite aus, wenn die linke Seite falsch ist. s == null ist aber wahr und die Länge muss nicht ausgewertet werden.

Frage

Welche Methoden kann man für das Kommentar //XX einsetzen?

class Base{
  public void method( int i ) { }
}
public class Extension extends Base {
  public static void main( String args[] ) { }
  //XX
}
  1. void method( int i ) throws Exception {}
  2. void method( long i ) throws Exception {}
  3. void method( long i ) {}
  4. public void method( int i ) throws Exception {}

Eine Methode kann in einer Unterklasse überschieben oder überladen werden. Überladene Funktionen haben den selben Namen wie eine andere Funktion in der Klasse oder der Basisklasse, jedoch verschiedene Parameter. Da in Antwort B und C die Methode mit einem long definiert ist, hat sie mit der in der Klasse Base definierten Methode außer den Namen nichts gemeinsam. So überlädt B und C method(int) aus Base und fügt sie der Klasse Extension zu. B und C sind korrekte Antworten und führen zu keinen Übersetzungsfehler. Anders dagegen A und D. Beide führen zu Compilerfehlern, da sie versuchen die Methode aus der Basisklasse zu überschreiben und eine Exception hinzuzufügen. Auch die Methode in Base müsste die Ausnahme schmeißen.

Fragen zur Java-Zertifizierung, Deklarationen und Zugriffskontrolle

Frage

Analysiere die Quellcodezeile

if ( 5 & 7 > 0 ) System.out.println( "Hullahupp-Reifen" );

  1. Diese Zeile lässt sich nicht kompilieren
  2. Diese Zeile lässt sich kompilieren, aber nichts erscheint auf der Standardausgabe
  3. Dieser Code lässt sich kompilieren und das Wort "Hullahupp-Reifen" erscheint in der Standard-Ausgabe.

Innerhalb vom Schleifen und Verzweigungen muss der Typ boolean sein. 5&7 ergeben zwar einen Wert, der verglichen mit 0 wahr oder falsch sein kann, jedoch ist die richtige Antwort A. Das Problem sind die unterschiedlichen Vorrangsregeln für Operationszeichen. Hier bindet 7>0 stärker als 5&7. So wird der Versuch unternommen 5 mit einem Wahrheits-Wert Und zu verknüpfen. Richtig wäre (5&7)>0.

Frage

Welche der Zeilen erzeugt ein Array mit fünf leeren Strings?

  1. String a [] = new String [5];
  2. for (int i = 0; i < 5; a[i++] = "") ;
  3. String a [] = {"", "", "", "", ""};
  4. String a [5];
  5. String [5] a;
  6. String [] a = new String [5];
    for ( int i = 0; i < 5; a[i++] = null ) ;

In Antwort A werden keine Strings erzeugt, sondern nur ein Feld mit 5 Referenzen. Somit ist A falsch. In B werden fünf leere Stings zugewiesen, ebenso in Antwort C, daher sind beide korrekt. Diese Strings sind natürlich nicht individuell, sondern die alle fünf Referenzen werden auf den gleichen Leerstring zeigen. Antwort D und E sind falsche Deklarationen für ein Feld. Antwort F ist falsch, da die Referenz auf Null zeigt, nicht aber auf einen leeren String "".

Frage

Welche Aussage beschreibt am besten die folgende Programmzeile?

String[][] s = new String[10][];

  1. Diese Zeile ist illegal.
  2. Es ist ein zweidimensionales Feld mit 10 Zeilen und 10 Spalten.
  3. Es ist ein Feld von 10 Feldern.
  4. Jedes Element in s ist mit "" initialisiert.
  5. Jedes Element in s ist nicht initialisiert und muss daher vor der Nutzung initialisiert werden.

Diese Zeile kann normal kompiliert werden, daher ist A falsch. C ist dagegen richtig, denn die Programmzeile schafft Platz für ein Array mit 10 Einträgen in einer Dimension. Wie groß die andere Dimension ist, bleibt offen. Daher ist B falsch. Da die andere Dimension in ihrer Größe noch nicht definiert ist, ist ein Zugriff auf ein Element noch ungültig. Daher kann jedes Element auch noch nicht mit einem Leerstring initialisiert sein. D ist also falsch und E richtig.

Frage

Welche Felddeklaration ist korrekt?

  1. String temp[] = new String { "u" "l" "l" "i" };
  2. String temp[] = { "u " "l" "l" "i" };
  3. String temp = { "a", "b", "c" };
  4. String temp[] = { "a", "b", "c" };

Antwort A, B und C erzeugen einen Übersetzungsfehler und D ist korrekt. Bei A und B fehlen für die Feldelemente das trennende Komma. Dann wäre B korrekt. Da A der Versuch einer Deklaration eines anonymen Feldes ist, muss noch hinter dem Typ String Klammern stehen. Korrigierte man A in String temp[] = new String[] { "u", "l", "l", "i" } ließe es sich kompilieren. Bei C fehlen ebenfalls die Klammern, da die geschweiften Klammern Elemente eines Feldes bezeichnen und nicht zu einem String zusammengesetzt werden können.

Frage

Was passiert, wenn folgendes Programm kompiliert wird?

1: public class Clazz {
2:  public static void main( String args[] ) {
3:    int feld[] = new int[]{1,2,3};
4:    System.out.println( feld[1] );
5:  }
6:}
  1. 1
  2. 2
  3. Übersetzungsfehler, da feld vor der Benutzung nicht definiert wurde.
  4. Übersetzungsfehler, da die Feldgröße festgelegt werden muss.

B ist die korrekte Antwort, da wir in Zeile 3 ein anonymes Feld mit den Elementen 1, 2 und 3 anlegen. An der Stelle 1 befindet sich die Zahl 2, da in Java genauso wie in C(++) der Feldindex bei Null beginnt. Da Felder in Java besondere Objekte sind (sogenannte Array-Objekte innerhalb der virtuellen Maschine) weist die Zuweisung in Zeile 2 dem feld Objekt eine gültige Referenz zu. Demnach ist C falsch. Die Feldgröße kann in Java nur durch die spezielle Form des new-Operator festgelegt werden, nicht durch reine Deklaration des Feldes. Also ist auch D falsch.

Frage

Was passiert, wenn folgendes Programm kompiliert wird?

public class Clazz {
  public static void main( String args[] ){
    int feld[] = new int[5];
    System.out.println( feld[0] );
  }
}
  1. Übersetzungsfehler, da feld vor der Benutzung nicht definiert wurde.
  2. null
  3. 0
  4. 5

Das Programm definiert eine Feld und weist der Referenz ein Feld der Größe von 5 int zu. Das Feld ist dabei mit 0 Werten initialisiert. Demnach ist der Zugriff auf feld[0] bis feld[4] erlaubt und jeder der Einträge ist 0. C ist die einzige richtige Antwort.

Fragen zur Java-Zertifizierung, Operatoren und Zuweisungen

Frage

Was ist die Ausgabe, wenn folgende Anweisungen ausgeführt werden?

Boolean b1 = new Boolean( true );
Boolean b2 = new Boolean( true );
Object obj1 = (Object)b1;
Object obj2 = (Object)b2;

if ( obj1 == obj2 )
  if ( obj1.equals(obj2) )     System.out.println( "a" );
  else
    System.out.println( "b" );
else
  if ( obj1.equals(obj2) )     System.out.println( "c" );
  else     System.out.println( "d" );
  1. a
  2. b
  3. c
  4. d

Die Ausgabe des Programms ist „c“, daher ist Antwort C korrekt. Da die Objektreferenz obj1 und obj2 nicht gleich sind, da sie auf verschiedene Boolean Objekte zeigen, kann die Ausgabe nicht „a“ oder „b“ sein. Da jedoch die equals() Methode den Wert (true) vergleicht, und beide Objekte den gleichen Wert haben, ist die Ausgabe „c“.

Frage

Analysiere die beiden Quellcodezeilen:

float f = 3.2;
int i = f;
  1. Dieses Programmstück wird nicht kompiliert
  2. Dieses Programmstück lässt sich kompiliert und i erhält der Wert 3
  3. Die zweite Zeile würde sich kompilieren lassen, wenn dort stände:

    int i = (byte) f;

  4. Die erste Zeile ließe sich kompilieren, wenn wir schreiben würden:

    float f = 3.2f;

Das Programm liefert beim Kompilieren eine Fehlermeldung, die besagt, dass der Datentyp float nicht mit double kompatibel ist. Der Datentyp double umfasst einen größeren Zahlenbereich und 3.2 ist als Literal automatisch ein double. Also ist A korrekt und B falsch. Der Fehler ist in der ersten Zeile ließe sich beheben, wenn man hinter dem 3.2 ein f hängt, wie es Lösung D vorschlägt. Somit ist D korrekt. Unabhängig von der erste Zeile ist auch mit der Typumwandlung auch die zweite Zeile richtig.

Frage

Welche der folgenden Anweisungen sind korrekt?

  1. 128 >> 1 ergibt 64
  2. 128 >>> 1 ergibt 64
  3. 128 >> 1 ergibt –64
  4. 128 >>> 1 ergibt –64

Antworten A und B sind korrekt.

Frage

Welche der folgenden Anweisungen ergeben den Rückgabewert true?

  1. „ulli“ == „ulli“
  2. „ulli“.equals(„ulli“)
  3. „ulli“ = „ulli“
  4. „ulli“.equals(new Button(„ulli“))

Da sich die virtuelle Maschine gleiche Strings in einem Konstanten-Pool hält, ist Antwort A korrekt. Bei B wird ein Zeichen-zu-Zeichen Vergleich unternommen. Da beide Strings identisch sind, ist auch hier das Ergebnis true, also ist auch Antwort B wahr. Man sollte sich nur bewusst sein, dass bei A kein inhaltlicher Vergleich stattfindet. Antwort C ergibt einen Compilerfehler, da ein einfaches Gleichheitszeichen eine Zuweisung definiert. Auf der linken Seite darf aber kein Literal stehen. Somit kann C nicht richtig sein. Die equals Methode, die auf der String Klasse definiert ist, überschreibt die equals() Methode der Klasse Object. Sie erlaubt aber als Parameter nur String Objekte. Es ist nicht so, dass bei nicht-Sting Objekten die toString() Methoden aufgerufen wird. Der Rückgabewert ist false. Also ist auch D falsch.

Frage

Welche der Anweisungen führt nicht zu einem Kompilierfehler?

  1. „ulli“ + “ plus “ + “ plus“
  2. „ulli“ + 29
  3. 345 + 45
  4. 5 + 5.5

Keine der Anweisungen führen zu einem Laufzeitfehler. Also sind Antwort A, B, C und D korrekt.

Frage

Welche der folgenden logischen Operatoren nennen sich „short circuit“?

  1. &
  2. ||
  3. &&
  4. |

B und C sind die kurzen Operatoren, die dann gelten, wenn der entsprechende Teil gilt.

Frage

Welchen Wert enthält die Variable temp nach der Ausführung der beiden Zeilen?

long temp = (int) 3.9;
temp %= 2;
  1. 0
  2. 1
  3. 2
  4. 3
  5. 4

Das Ergebnis ist 1, also die Antwort B wahr. Die Typumwandlung schneidet den Nachkommateil von 3.9 ab und das Ergebnis 3 Modulo 2 ist 1.

Frage

Welche Bildschirmausgabe erzeugt die folgende Anweisung?

System.out.println( 4 | 7 );

  1. 0
  2. 4
  3. 5
  4. 6
  5. 7

Die binäre Repräsentation der Zahl 4 ist 100 und von 7 ist 111. Beide mit Oder verknüpft gibt 111 und somit wiederum 7. Also ist Antwort E korrekt.

Fragen zur Java-Zertifizierung, Flusskontrolle und Ausnahmebehandlung

Frage

Was wird ausgegeben, wenn die folgende Methode aufgerufen wird?

void test() {
one:
two:
  for ( int i = 0; i < 3; i++ ) {   three:
    for ( int j = 10; j < 30; j++ ) {      System.out.println( i + j );
      if ( i > 2 )
      continue one;
    }
  }
}
  1. 10 und 20
  2. 11 und 21
  3. 12 und 22
  4. 13 und 23
  5. 30, 31, 32, 33

Frage

Analysiere den folgenden Programmtext:

1: void schleife() {
2:   int x = 0;
3: one:
4:   while ( x < 100 ) {
5:   two:
6:     System.out.println( ++x );
7:     if ( x > 3 )
8:     break two;
9:   }
0: }
  1. Der Code lässt sich nicht kompilieren.
  2. Die Funktion schreibt die Zahl 0 in die Standardausgabe.
  3. Die Funktion schreibt die Zahl 1 und 2 in die Standardausgabe.
  4. Die Funktion schreibt die Zahl 3 in die Standardausgabe.
  5. Die Funktion schreibt die Zahl 4 in die Standardausgabe.
  6. Die Funktion schreibt die Zahlen 5 bis 9 in die Standardausgabe.
  7. Die Funktion schreibt die Zahl 10 in die Standardausgabe.

Der Programmcode kann nicht kompiliert werden, da Zeile 8 eine ungültige Sprungadresse enthält. Daher ist Antwort A korrekt. Die break Anweisung dient immer dazu, aus einer Schleife zu entkommen und nicht aus einer if Anweisung. Würde der Sprung nach two: gelingen, wäre man aber immer noch in der Schleife. Eine gültige Anweisung für Zeile 8 ist break one;

Frage

Betrachte den folgenden switch Block. (Zwei Antworten.)

char mychar = 'c';
switch ( mychar )
{   default:
  case 'a': System.out.println( "a" ); break;
  case 'b': System.out.println( "b" ); break;
}

Welche der folgenden Antworten ist richtig?

  1. Der switch Block ist illegal, weil nur Ganzzahlen in der switch Anweisung benutzt werden können.
  2. Mit der switch Anweisung ist alles in Ordnung.
  3. Der switch Block ist illegal, da die default Anweisung zum Schluss kommen muss.
  4. Wenn das Programmstück läuft, wird nichts auf die Standardausgabe geschrieben.
  5. Wenn das Programmstück ausgeführt wird, erscheint der Buchstabe "a" auf dem Bildschirm.

Der Ausdruck in einer switch Anweisung muss sich auf ein 32-Bit Wert konvertieren lassen. Also sind auch die kleineren Datentypen byte, char, short und int erlaubt. Im Beispiel ist ein Buchstabe ein char, der auf ein int konvertiert wird. Dies ist korrekt, also ist Antwort A falsch. Da die Kompilation somit in Ordnung ist, ist Antwort B korrekt. Die Reihenfolge, wann default auftaucht ist egal, somit ist C falsch. Daher folgt auch erst die richtige Ausgabe. Denn der Buchstaben ‚c‘ passt nicht auf ‚a‘ und ‚b‘, somit kümmerst sich der default Teil um die Eingabe. Da hinter default jedoch kein break steht läuft der Programmcode direkt in die Ausgabe "a" hinein. Also ist ebenfalls E korrekt und D falsch.

Frage

Welche der folgenden Aussagen über try, catch und finally sind wahr?

  1. Einem try Block muss immer ein catch Block folgen.
  2. Einem try Block kann entweder ein catch Block, ein finally Block oder beides folgen.
  3. Ein catch Block muss immer mit einem try Block verbunden sein.
  4. Ein finally kann nicht ohne ein try Block auftauchen.
  5. Keine dieser Aussagen sind wahr.

Einem try Block muss entweder ein finally ein catch oder beides folgen. Also ist A falsch und B richtig. Wenn man ein catch oder ein finally schreibt, so ist dies immer mit einem try verbunden, daher ist auch C und D richtig. Da mindestes eine Antwort richtig ist, ist E falsch.

Frage

1: public class Clazz {
2:   public static void main( String args[] ) {
3:     String s = null;
4:     if ( s != null & s.length() > 0 );
5:     if ( s != null && s.length() > 0 );
6:     if ( s == null | s.length() > 0 );
7:     if ( s == null || s.length() > 0 );
8:   }
9: }

Welche der Aussagen ist wahr?

  1. Zeile 4 erzeugt eine NullPointerException
  2. Zeile 5 erzeugt eine NullPointerException
  3. Zeile 6 erzeugt eine NullPointerException
  4. Zeile 7 erzeugt eine NullPointerException

Antwort A und C sind korrekt und Zeile 4 und 6 erzeugen eine NullPointerException. Man muss wissen, dass & und | beide Seiten auswerten. Ist also die Referenz null, so ist auch s.length() ungültig. In Zeile 4 und 6 kommen aber diese Auswertungen vor. && wertet den rechten Ausdruck nur dann aus, wenn der linke wahr ist. Also wird in Zeile 5 keine Länge abgefragt. || wertet nur dann die rechte Seite aus, wenn die linke Seite falsch ist. s == null ist aber wahr und die Länge muss nicht ausgewertet werden.

Frage

Welche Methoden kann man für das Kommentar //XX einsetzen?

class Base{
  public void method( int i ) { }
}
public class Extension extends Base {
  public static void main( String args[] ) { }
  //XX
}
  1. void method( int i ) throws Exception {}
  2. void method( long i ) throws Exception {}
  3. void method( long i ) {}
  4. public void method( int i ) throws Exception {}

Eine Methode kann in einer Unterklasse überschieben oder überladen werden. Überladene Funktionen haben den selben Namen wie eine andere Funktion in der Klasse oder der Basisklasse, jedoch verschiedene Parameter. Da in Antwort B und C die Methode mit einem long definiert ist, hat sie mit der in der Klasse Base definierten Methode außer den Namen nichts gemeinsam. So überlädt B und C method(int) aus Base und fügt sie der Klasse Extension zu. B und C sind korrekte Antworten und führen zu keinen Übersetzungsfehler. Anders dagegen A und D. Beide führen zu Compilerfehlern, da sie versuchen die Methode aus der Basisklasse zu überschreiben und eine Exception hinzuzufügen. Auch die Methode in Base müsste die Ausnahme schmeißen.

Fragen zur Java-Zertifizierung, Ein- Ausgabe mit dem Paket java.io

Frage

Sie schreiben ein Programm, um den Text einer 8 Bit Datei, die den Zeichensatz nach ISO 8859-8 benutzt, in einem TextArea Objekt darzustellen. Die lokale Enkodierung ist auf 8859-8 gesetzt. Wie sieht der Programmcode aus, um die erste Zeile dieser Datei zu lesen?
Sie haben dabei Zugriff auf 3 Variablen. myfile ist der Name der Datei, aus der Sie lesen wollen, stream ist ein InputStream Objekt, der dem mit dieser Datei verknüpft ist und s ist ein String Objekt.

  1. InputStreamReader reader = new InputStreamReader( stream, „8859-8“ );
    BufferedReader buffer = new BufferedReader( reader );
    s = buffer.readLine();
  2. InputStreamReader reader = new InputStreamReader( stream );
    BufferedReader buffer = new BufferedReader( reader );
    s = buffer.readLine();
  3. InputStreamReader reader = new InputStreamReader( myfile, „8859-8“ );
    BufferedReader buffer = new BufferedReader( reader );
    s = buffer.readLine();
  4. FileReader reader = new FileReader( myfile );
    BufferedReader buffer = new BufferedReader( reader );
    s = buffer.readLine();

Frage

Was schreibt das folgende Programm auf die Konsole wenn die Datei „Hallo.txt“ im aktuellen Verzeichnis nicht existiert?

import java.io.*;
public class Leser {
  public static void main( String args[] ) {
    Leser l = new Leser();
    System.out.println( l.ließ() );
  }
  public int ließ() {
    try {
      FileInputStream dis = new FileInputStream("Hallo.txt");
    }
    catch ( FileNotFoundException fnfe ) {
      System.out.println( "Keine solche Datei " );
      return -1;
    }
    catch ( IOException ioe ) { }
    finally { System.out.println( "Schlusslicht " ); }
    return 0;
  }
}
  1. Keine solche Datei
  2. Keine solche Datei -1
  3. Keine solche Datei Schlusslicht -1
  4. 0

Ist die Datei nicht vorhanden, so wird eine FileNotFoundException (Spezialisierung von IOException) geschmissen. Da wir diese zuerst abfragen gibt das Programm die erste Meldung aus. Nun muss nicht noch weiter nach catch Klauseln gesucht werden und der finally Block wird ausgeführt. Dieser gibt noch die zweite Ausgabe aus. Vor dem Abarbeiten wird aber der Rückgabewert -1 gesichert und dann nach finallly in der main() Methode ausgegeben. Daher ist C die einzig richtige Antwort.

Fragen zur Java-Zertifizierung, Grundlegende Spracheigenschaften

Frage

In welcher Reihenfolge können die folgenden Klassen in der Quelldatei gesetzt werden, ohne dass Fehler oder Warnungen zur Übersetzungszeit auftreten.

// A
 import java.applet.*;
// B
 class Helper { }
// C
 package myclasses;
// D
 public class MyApplet extends java.applet.Applet { }
  1. A, B, C, D
  2. A, C, B, D
  3. C, A, B, D
  4. C, A, D, B
  5. C, B, A, D

Befindet sich eine Klasse in einem Paket, so muss in der ersten Zeile die package Deklaration stehen. So sind Antworten A, B falsch. Anschließend müssen die Import Anweisungen folgen. So ist auch E falsch. Antworten C und D sind korrekt. Bei D würde aber ein extends Applet reichen.

Frage

Was ist die korrekte Reihenfolge für Importanweisungen, Klassen und Pakete, wenn diese in einer einzigen Datei vorkommen?

  1. Paketanweisung, Importanweisung, Klasse
  2. Klasse, Importanweisung, Paketanweisung
  3. Importanweisung, Paketanweisung, Klassen
  4. Paketanweisung, Klassen, Importanweisung

Die korrekte Antwort ist A.

Frage

Was sind gültige Schlüsselwörter in Java?

  1. sizeof
  2. abstract
  3. native
  4. NULL
  5. BOOLEAN
  6. goto

sizeof ist ein Operator in C(++), der in Java keine Bedeutung hat. Er kann daher ein kein gültiges Schlüsselwort. A ist flasch. abstract und native sind reservierte Schlüsselwörter, daher sind Antwort B und C richtig. NULL ist ein Makro unter C(++), welches in Java nicht existiert. Nur null ist ein eingebautes Schlüsselwort. Auch BOOLEAN ist kein Schlüsselwort, da Java Groß/Kleinschreibung trennt. So sind D und E keine Schlüsselwörter. Obwohl es in Java kein goto gibt (jedoch Multibreak continue, was im Prinzip das gleiche ist), ist das Schlüsselwort reserviert und man kann es nicht als Bezeichner wählen. Auch F ist richtig.

Frage

Welche der folgenden Bezeichner ist illegal?

  1. #_pound
  2. _underscore
  3. 4Interface
  4. Interface5
  5. _5_

Bezeichner dürfen nur mit einem Unterstrich oder einem Buchstaben beginnen. Demnach sind A und C illegale Bezeichner. Die anderen sind gültige Namen, es sind B, D und E korrekt.

Frage

Welche der folgenden Deklarationen für die main() Methode in einer Konsole Applikation ist nicht korrekt?

  1. public static void main()
  2. public static void main( String[] string )
  3. public static void main( String args )
  4. public static void main( String[] args )
  5. static void main( String[] args )

Die korrekte Signatur ist public static void main( String [] ). Also B und D korrekt, da es auf die Namensgebung nicht ankommt. Da die main() Funktion immer als Argument ein String Feld hat, ist A und C falsch. E ist im Prinzip richtig, jedoch wird mit public die main() Funktion auch außerhalb des Paketes sichtbar. Also ist auch E falsch.

Frage

Wie lautet die Spezifikation für den Paramter der pulic static void main Methode?

  1. String args []
  2. String [] args
  3. Strings args []
  4. String args

Als Parameter wird ein Feld von Strings erwartet. Also ist D falsch. Da die Klammern bei der Deklaration eines Feldes vor oder hinter dem Bezeichner stehen können und der Name des Feldes unerheblich ist, sind A, B und C richtige Antworten.

Frage

Was enthält das erste Erste Element des Stringfeldes, das der public static void main Methode übergeben wird.

  1. Der Name des Programms.
  2. Die Anzahl der Argumente.
  3. Das erste Argument falls verfügbar.

In Java gibt es keine zusammengebunden Programme, sondern nur Klassen. Daher ist A falsch. Die Anzahl der Argumente ergibt sich dem Attribut length der Feldvariablen. Es bleibt C als richtige Antwort.

Frage

Was ist das Ergebnis des folgenden Programmes:

public class Alter {
 public static void main( String args [] ) {
  int age;
  age = age + 1;
  System.out.println( "Das Alter ist " + age);
  }
 }
  1. Kompiliert und läuft ohne Ausgabe.
  2. Kompiliert und die Ausgabe ist „Das Alter ist 1“.
  3. Kompiliert aber erzeugt einen Laufzeitfehler.
  4. Kompiliert nicht.

Das Programm kann nicht kompiliert werden, da auf die lokale Variable age vor dem Lesezugriff kein Schreibzugriff stattfand. Da lokale Variablen aber vor der Benutzung initialisiert werden müssen, meldet der Compiler einen Fehler.

Frage

Was ist das Ergebnis des folgenden Programmcodes?

public class Alter {
 static int age;
 public static void main( String args [] ) {
  age = age + 1;
  System.out.println( "Das Alter ist " + age );
 }
}
  1. Kompiliert und läuft ohne Ausgabe
  2. Kompiliert und liefert die Ausgabe „Das Alter ist 1 „
  3. Kompiliert aber erzeugt einen Laufzeitfehler.
  4. Kompiliert nicht.

Das Programm kompiliert und schreibt das Alter 1 auf den Schirm. Die int Objektvariablen wird mit Null initialisiert. Es ist egal, ob sie dabei static ist oder nicht. Nach dem einmaligen Erhöhen wird sie auf 1 gesetzt. Also ist B die richtige Antwort.

Frage

Welche der folgenden Anweisungen ist korrekt, um ein Zeichenliteral mit dem Wert a zu erzeugen.

  1. ‚a‘
  2. „a“
  3. new Character(a)
  4. \000a

Nur Antwort A ist korrekt. Ein Zeichenliteral muss in einfachen Hochkommas stehen. Nur Zeichenkette, also String-Objekte, stehen in doppelten Anführungszeichen. String Objekte sind aber Objekte und keine primitiven Datentypen wie ein char. Antwort C ist falsch, da dort versucht wird ein Character Objekt zu konstruieren. Der new Operator erzeugt allerdings ein Objekt und kein Literal. Außerdem muss Buchstabe a in Hochkomma stehen. Sonst würde die Zeile auf eine char Variable a zurückgreifen. Das Zeichen \0000a ist die Hexadezimalschreibweise und steht für die Zahl 10. Also ist auch D falsch.

Frage

Wie ist der Zahlenbereich eines byte?

  1. 0 bis 65.535
  2. –128 bis 127
  3. –32.768 bis 32.767
  4. –256 bis 255

Ein byte hat die Datengröße von 8 Bit. So sind Antwort A und C schon falsch. Mit 8 Bit kann man den Zahlenbereich von 0 bis 255 abdecken. Ein Vorzeichen kostet aber noch ein zusätzliches Bit. So ist auch D falsch, da wir hier 9 Bit bräuchten. Da alle primitiven Datentypen außer char mit einem vorzeichenbehaftet sind, bleibt als richtige Antwort B.

Frage

Welche der Anweisungen ist illegal?

  1. int i = 32;
  2. float f = 45.0;
  3. double d = 45.0;

Die erste Anweisung ist korrekt, da 32 im Bereich eines int liegt. Auch Anweisung C ist korrekt. Nur bei B ist das Literal ein double. Dies kann nicht ohne möglichen Verlust in ein float kopiert werden. Also ist Antwort B korrekt. Um die Zeile zu korrigieren schreiben wie float f = 4.0f.

Daten speichern in Google App Engines Datastore

Die Google App Engine (GAE) ist eine freie Hosting-Plattform auf der Java Web-Applikationen deployt werden können. Google bietet für die GAP eine Reihe von Diensten an, etwa zur Datenspeicherung, Mail-Versand/-Empfang, Messaging und mehr. Wer sich für die GAE entscheidet und Daten speichern möchte, dem steht quasi unendliche Speicherkapazität zur Verfügung. Google speichert die Daten in einer Cloud, der Google BigTable (http://en.wikipedia.org/wiki/BigTable). Sie ist im Wesentlichen ein riesiger verteilter Assoziativspeicher.

Der Datestore – so wird die Datenspeicher für die Google App Engine genannt – speichert Entities. Eine Entity hat einen Typ (Entity-Kind genannt) und eine Menge von Schlüssel/Werte-Paaren. Eine konkrete Entity für eine Person könnte etwa die Schlüssel-Wert-Menge { name = „Chris“, age = 36 } speichern. Die Entities sind nicht mit klassischen Tabellenzeilen von relationalen Datenbanken zu vergleichen, das sie schemalos sind: Es gibt also keine Spalten mit festen Datentypen. Es können also beliebig viele „Spalten“ für gewisse Entities hinzukommen. Eine Person in der Datastore kann wie im ersten Beispiel Name und Alter speichern, eine andere Person Name und Schuhgröße. Die Anzahl Schlüssel ist wirklich beliebig; eine Tatsache die vom relationalen Design totales Umdenken erfordert.

Zum Zugriff auf den Datastore steht auf unterster Schicht eine Low-Level API (https://developers.google.com/appengine/docs/java/javadoc/com/google/appengine/api/datastore/package-summary). Diese API bietet grundlegende CRUD-Operationen und ist mit 10 Interfaces und etwas mehr als 50 Klassen leicht verständlich und übersichtlich. Wenn eine Person gespeichert werden soll, sieht das so aus:

Entity chris = new Entity( "Person" );
chris.setProperty("name", "chris");
chris.setProperty("age", 20 );
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
datastore.put(chris);

Jede Entity ist durch einen Schlüssel gekennzeichnet. Nach dem Speichern einer Entity lässt sich dieser Schlüssel über getKey() erfragen. Ist der Schlüssel bekannt, kann eine Anfrage an den Datastore gemacht werden.

Key key = chris.getKey();
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Entity e = datastore.get( key );
Long age = (Long) alice.getProperty("age");

Die Anfragen werden über den Entity-Typ gestellt, der in Key steckt. Alle Entities haben einen Entity-Kind und die Suche auf dem Datastore ist immer mit diesem Entity-Kind verbunden.

Der Datastore bietet eine hohe Lese- und Anfrage-Geschwindigkeit ist aber nicht unbedingt komfortablen.

https://developers.google.com/appengine/docs/java/datastore/?hl=de gibt auch auf Deutsch ein paar Hinweise. Aufbauend auf dieser Low-Level API implementiert Google die Standards Java Persistence API (JPA) und Java Data Objects (JDO). Im Open-Source-Bereich gibt es noch https://code.google.com/p/objectify-appengine/.

Dynamisch. Praktisch. Gut Mit dem BeanUtils Properties einfach beherrschen

Die Java-Beans sind ein allgegenwärtiges Gut. Zwar sind die Anforderungen minimal, aber für viele Bereiche wie grafische Oberflächen, JSPs, OR-Mappern sind sie nicht wegzudenken. Damit eine Bean zur Bean wird, muss eine Klasse

  • öffentlich sein
  • einen öffentlichen Standard-Konstruktur besitzen und
  • öffentliche Zugriffsfunktionen anbieten.

Nach dieser Vorgabe ist Folgendes eine gültige Bean:

class Weihnachtsmann
{
  private String name;

  public String getName() {
    return name;
  }

  public void setName( String name ) {
    this.name = name;
  }
}

Die Zugriffsfunktionen kapseln die internen Eigenschaften und sind nach einem festen Muster aufgebaut. Für ein Attribut String name würden die beiden Methoden void setName(String name) und String getName() angelegt. Für boolean-Eigenschaften ist die Vorsilbe is- üblich. Wenn die Beans nicht nach dem Schema aufgebaut sind, kann eine zusätzliche BeanInfo-Beschreibung helfen.

Während auf der einen Seite die Bean als Datencontainer Einsatz findet, sind sie noch aus einen anderen Grund reizvoll: durch ihren festen Aufbau lässt sich leicht mit Reflection/Instrospection auf die Bean und ihre Eigenschaften zugreifen. Nicht umsonst beschreiben Bean grafische Komponenten, so dass ein GUI-Builder völlig automatisch die Eigenschaften auslesen kann und Manipulationen erlaubt. Ein Exemplar einer Bean lässt sich über ihren Namen einfach anlegen; mit einem bekannten Class-Objekt liefert newInstance() ein neues Exemplar.

Class beanClass = Class.forName( "Weihnachtsmann" );
Object santa = beanClass.newInstance(); 

Für das Auslesen der Methodennamen definiert Java Introspection und für den Zugriff Reflection. Vor dem Zugriff auf die Bean-Properties sind jedoch die Präfixe get- und set- zu setzen und Datentypkonvertierungen durchzuführen.

Um den Umgang mit Bean-Eigenschaften zu vereinfachen, hat die Apache-Gruppe in ihren Commons-Paket die BeanUtils aufgenommen. Die aktuelle Version der BeanUtils ist 1.6.1 und ist beim Indianer-Server http://jakarta.apache.org/commons/beanutils/ zuhause. BeanUtils setzt zwei weitere Pakete aus Commons voraus: Collections und Logging. Der prominenteste Nutzer der Bibliothek ist Struts.

Bean-Eigenschaften lesen und setzen

Eine Bean-Property kennzeichnet ähnlich wie ein Servlet/JSP-Attribut zwei Merkmale: Einen Namen (immer ein String) mit einer Belegung (beliebiger Objekttyp). Um bei einer Bean eine Property zu setzen, müssen ihr Name und die neue Belegung natürlich bekannt sein, beim Lesen nur der Name. Auslesen und Setzen von Eigenschaften erlauben zwei statische Funktionen der Utility-Klasse org.apache.commons.beanutils.PropertyUtils: PropertyUtils.getSimpleProperty(Object bean, String name) und PropertyUtils.setSimpleProperty( Object bean, String name, Object value). Um unserem Weihnachtsmann einen Namen zu geben — und diesen gleich wieder auszulesen — ist zu schreiben:

PropertyUtils.setSimpleProperty( santa, "name", "Santa Clause" );
String name = (String) PropertyUtils.getSimpleProperty( santa, "name" );

Die Methoden sind jedoch nicht ohne ausführliche Fehlerbehandlung auszuführen; auftreten kann eine IllegalAccessException, IllegalArgumentException (eine RuntimeException), InvocationTargetException und NoSuchMethodException.

Zu bedenken ist, dass eine Bean-Property ganz spezielle Methoden erzwingt. Das geht so weit, dass BeanUtils nur genau eine get-Methode erwartet. Gibt es neben der erforderlichen get-Methode eine weitere, so liefert BeanUtils beim Zugriff einen Fehler. Der Zugriff auf bounds einer java.awt.Component gelingt daher nicht, da neben der parameterlosen Variante von getBound() auch getBounds(Rectange) existiert — ein Aufruf ähnlich wie der Folgende funktioniert daher nicht und führt zu einer NoSuchMethodException: PropertyUtils.getSimpleProperty(new Label(), "bounds");

PropertyUtils, BeanUtils und Konvertierungen

Neben der Klasse PropertyUtils, die bisher Verwendung fand, gibt es noch zusätzlich die Klasse BeanUtils. Sie erinnert von der Methodenvielfalt an PropertyUtils, doch sie führt für Datentypen mögliche Konvertierungen durch. Das Beispiel zum Weihnachtsmann soll erweitert werden, so dass er ein Attribute int alter besitzt. Der Versuch, das Attribut mit einem Wert vom Datentyp String über PropertyUtils.setProperty() zu setzen scheitert mit einer IllegalArgumentException. Ein int muss mit einem Integer-Objekt belegt werden. Wird jedoch anstelle der Klasse PropertyUtils die Klasse BeanUtils gesetzt, so funktioniert alles, denn BeanUtils führt die notwendigen Konvertierungen von String in int selbst aus.

BeanUtils.setProperty( santa, "alter", "612" );

Ergibt sich bei der Konvertierung ein Fehler, so macht das nichts, denn dann wird die Zuweisung nicht durchgeführt. (Apropos Konvertierungen: Die Firma Lockheed, die die Raumsonde Mars Climate Orbiter in England konstruierte, nutze die englische Einheit Inch, die NASA in Amerika Zentimeter; beim Landeversuch crashte die Sonde 1999 auf den Mars.)

Die Konvertierung aus einem String für die unterschiedlichsten Datentypen nimmt BeanUtils automatisch vor. Im oberen Fall findet eine Konvertierung von String in ein Integer Objekt statt. Die Converter selbst sind Klassen, die die Schnittstelle org.apache.commons.beanutils.Convert implementieren und auf diese Weise von einem String in BigDecimal, BigInteger, boolean, Boolean, byte, Byte, char, Character, Class, Double, float, Float, int, Integer, long, Long, short, Short, String,

java.util.Date, java.sql.Date, java.sql.Time, java.sql.Timestamp und mehr konvertieren. Den passenden Konverter sucht ConvertUtils.lookup(). Falls also BeanUtils.setProperty() vor der Aufgabe steht, einen passenden Typ herauszufinden, so sucht es zuerst nach dem Typ (von alter wäre das int) und anschließend den passenden Konverter. Da ConvertUtils.lookup(int.class) den IntegerConverter liefert, darf er die Zeichenfolgen umwandeln. Diese Konvertierung ist die übliche Konvertierung mit Integer.parseInt().

Falls die Standard-Konverter einen Typ nicht abdeckt, kann mit register() ein neuer Konverter eingehängt werden. Nicht nötig ist das für sprachabhängige Konvertierungen, denn hier bietet das Paket schon Hilfe an. So gibt es den IntegerConverter in der lokalisierten Fassung auch als IntegerLocaleConverter. Zum Nachschlagen dient aber nicht mehr ConvertUtils, sondern LocaleConvertUtils. Die Basisklassen für lokalisierte eigene Konvertiert ist LocaleConverter. Es gibt für alle normalen Konverter auch lokalisierte Konverter. Das lokalisierte Parsen scheint allerdings noch nicht so ganz ausgereift.

Eine Liste aller Eigenschaften

Eine Belegung aller Eigenschaften liefert BeanUtils.describe() beziehungsweise PropertyUtils.describe().

Map description = BeanUtils.describe( new JButton() );

Der Assoziativspeicher unter dem Namen description enthält als Schlüssel alle Properties und als Werte die Belegungen der Eigenschaften.

verifyInputWhenFocusTarget=true
displayedMnemonicIndex=-1
width=0
...

Formular-Parameter übertragen

In JSPs erfreut eine Schreibweise den Entwickler, die alle Parameterwerte eines Requests in eine Bean überträgt. Das Tag <jsp:setProperty> wird dann mit dem Attribut property="*" belegt.


Diese Möglichkeit sieht zwar die JSP-Spezifikation vor, doch in Servlets fehlt diese Möglichkeit. Sie wird aber insbesondere für MVC-Frameworks interessant, in dem der Controller selbständig die Formulardaten in eine Bean überträgt. Das kann schnell nachimplementiert werden. Die Servlet-API bietet in ServletRequest die Funktion getParameterNames(), um mit einer Enumeration über die Parameter zu laufen. Für jeden der Parameter kann setProperty() auf einer passenden Bean aufgerufen werden.

for ( Enumeration params = request.getParameterNames(); params.hasMoreElements(); ) {
  String name = (String) params.next();
  if ( name != null )
    BeanUtils.setProperty( bean, name, request.getParameterValues(name) );
}

getParameterValues() liefert ein Feld, was von der Bean auch verarbeitet werden muss. Sonst tut’s bei eindeutigen Parametern auch getParameter().

Werte einer Map in eine Bean kopieren

Aufgabe: Eine Ini-Datei enthält Schlüssel/Werte-Paare und alle Schlüssel sollen auf Eigenschaften einer Bean übertragen werden. Für Schlüssel soll es also entsprechende set- und get-Methoden geben. Eine einfache Aufgabe mit den passenden Klassen. Zum Laden der Paar-Datei findet Properties und die Objektfunktion load() Einsatz. Ein Properties-Objekt ist eine spezialisierte Hashtable — eine dumme Designentscheidung Begründung ? —, und alle Schlüssel lassen sich aufzählen. An dieser Stelle kommen wieder die BeanUtils ins Spiel. Die statische Funktion setProperty() sieht jeden Schlüssel als Property und überträgt den Wert auf die Bean.

Unter der Annahme, dass alle Eigenschaften in einer Map vorliegen, bietet BeanUtils eine feine Funktion: static void populate(Object bean, Map properties). Die Map schreibt Schlüssel als String vor, die Werte sind beliebige Objekte. populate() iteriert über die Schlüssel von properties und initialisiert über viele setProperty()-Funktionen die Werte der Bean. Die Aufgabe löst sich somit in wenigen Zeilen Quellcode.

Die Funktion populate() ließe sich auch bei Formular-Parametern verwenden, wenn denn die Schlüssel/Werte-Paare in einer Map vorliegen würden. Doch das können sie so ohne weiteres nicht, denn ein Schlüssel darf durchaus mehrmals mit verschiedenen Werten vorkommen — eine Map kann das nicht unterstützen. Gilt, dass überladene Parameter nicht zu erwarten sind, können von Hand die Parameter in eine Map einsortiert werden, so dass doch wieder populate() nutzbar ist. Seit der Servlet 2.3 Spezifikation bietet HttpServletRequest die Methode getParameterMap(), was direkt eine Map liefert. Sie speichert die Werte jedoch immer als Felder; wenn der Schlüssel einmalig ist, dann ist das Feld nur ein Element groß.

Indexierte Properties

Da ein Weihnachtsmann immer Rentiere besitzt, soll ihn eine zusätzliche Klasse Rentier für die Zugtiere bei der Arbeit unterstützen. Er soll in einem internen Feld zugtier die Tiere speichern und dafür auch zwei neue Zugriffsfunktionen definieren.

class Weihnachtsmann
{
  private Rentier[] zugtier = new Rentier[8];
  public Rentier getZugtier( int index ) {
    return zugtier[index];
  }
  public void setZugtier( int index, Rentier zugtier ) {
    this.zugtier[index] = zugtier;
  }
}

Ist ein Weihnachtsmann aufgebaut und mit setZugtier() ganz klassisch ein Rentier zugewiesen kann im nächsten Schritt die Eigenschaft erfragt werden.

Weihnachtsmann w = new Weihnachtsmann();
w.setZugtier( 0, new Rentier() );
Rentier z = (Rentier) PropertyUtils.getIndexedProperty( w, "zugtier", 0 );

Diese indexierten Eigenschaften lassen sich mit getIndexedProperty() erfragen und mit setIndexedProperty() setzen; die Signaturen sind PropertyUtils.getIndexedProperty(Object bean, String name, int index) und PropertyUtils.setIndexedProperty(Object bean, String name, int index, Object value).

Rentier rudolph = new Rentier();
PropertyUtils.setIndexedProperty( w, "zugtier", 1, rudolph );

Eine Alternative zu den Funktionen, die den Index als Parameter erwarten ist eine Schreibweise, die den Index in den Eigenschaftennamen kodiert. Die Schreibweise ist bekannt von Arrays: name[index].

Rentier zt = (Rentier) PropertyUtils.getIndexedProperty( w, "zugtier[1]" )

Mapped Properties

Für indexierte Properties ist es üblich, dass der Index eine positiver ganzzahliger Wert ist. Eine Erweiterung der Java-Beans gehen die BeanUtils mit den Mapped Properties, was indexierte Beans mit Schlüsseln sind — das Konzept erinnert an einen Assoziativspeicher. Genauso wie indexierte Properties mit eckigen Klammern einen Index aufnehmen, gibt es eine Abkürzung auch bei den abgebildeten Eigenschaften. Dabei wird der Schlüssel in runden Klammern hinter die Eigenschaft gesetzt. Die Signaturen sind PropertyUtils.getMappedProperty(Object bean, String name) mit den alternativen Parametern (Object bean, String name, String key), (Object bean, String name, Object value) und (Object bean, String name, String key, Object value).

Ein Beispiel: System.getProperties() liefert ein Properties mit den Systemeigenschaften. Die Objektmethode getProperty() liefert zu einem gegebenen Schlüssel den assoziierten Wert. BeanUtils lässt mit getMappedProperty() die abgebildeten Eigenschaften erfragen.

Properties props = System.getProperties();
System.out.println( PropertyUtils.getMappedProperty( props, "property(user.name)" ) );

Wo ist der Vorteil gegenüber props.get("user.name") ;

Der Schlüssel muss nicht in einfachen oder doppelten Anführungszeichen eingeschlossen werden, wie es etwa die Expression Language unter JSP 2.0 oder Velocity vorschreibt.

Geschachtelte Properties

Um eine Liste aller Zeichensätze des Betriebssystems auszulesen, lässt sich über GraphicsEnvironment getLocalGraphicsEnvironment() aufrufen.

GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();

Ausdruck Der Name des ersten Fonts erfragt

String s = ge.getAllFonts()[0].getName();

Wie sieht das nun mit den BeanUtils aus? Sicherlich ist es denkbar, für den indexierten Property einmal getIndexedProperty() und für den Namen setSimpleProperty() aufzurufen, aber commons.beanutils kann es besser, und zwar mit getNestedProperty().

Object o = PropertyUtils.getNestedProperty( ge, "allFonts[0].name" );

Während mit getNestedProperty() eine Eigenschaft ausgelesen werden kann, lässt sie sich mit setNestedProperty() setzen. Die Signaturen für set- und get sind: PropertyUtils.getNestedProperty(Object bean, String name) und PropertyUtils.setNestedProperty(Object bean, String name, Object value).

Nun sind drei Möglichkeiten bekannt, auf Bean-Eigenschaften zuzugreifen: Einfache Properties (getSimpleProperty(), setSimpleProperty()), indexierte Properties (getIndexedProperty()/setIndexedProperty()), mapped Properites und geschachtelte Properites (getNestedProperty()/setNestedProperty()). Um den Umgang zu vereinfachen, hilft die allgemeine Funktion getProperty(), die sich den passenden Typ heraussucht. Damit lässt sich alles mit nur einer Art von Funktion beschreiben: PropertyUtils.getProperty(Object bean, String name) und PropertyUtils.setProperty(Object bean, String name, Object value).

Kopien von Bean-Eigenschaften

Stehen zwei Beans gegenüber, die sich Properties teilen, so kopiert BeanUtils. copyProperties() die Eigenschaften. Die Klasse Point definiert genauso wie Rectangle das Property x und y. Die Belegungen der Eigenschaften lassen sich übertragen, so dass das Rectangle alles annimmt, was in Point definiert wird:

Point p = new Point( 10, 20 );
Rectangle rec = new Rectangle(); 
BeanUtils.copyProperties( rec /* = */, p );
System.out.println( rec ); // java.awt.Rectangle[x=10,y=20,width=0,height=0]

Intern geht copyProperties() alle Eigenschaften vom Punkt durch, findet x, y und auch location. Für diese drei Eigenschaften werden die entsprechenden set-Methoden beim Rectangle-Objekt gesucht; sie werden nur für x und y gefunden, denn setLocation() ist überladen und so ungültig.

Ein Beispiel, unter dem Bean-Attribute zu kopiert sind, liefert Struts. Die Action Form Beans werden zwar allzu oft als Model eingesetzt und weiterverarbeitet, das ist aber falsch. Die Form Beans sind lediglich eine Hilfs-Struktur, die nur für die Formulardaten eine Berechtigung haben. Demnach sind sie auch nur in einem Struts-Durchlauf bekannt und werden nicht als Datencontainer weitergegeben. Trennt man allerdings Form-Bean von tatsächlicher Model-Bean (falls eine Model-Bean benötigt wird) sieht das Model sehr ähnlich der Form-Bean aus und entsteht leider doppelter Quellcode, insbesondere für die set- und get-Methoden. Weiterhin ist lästig, dass bei einer gültigen Formularbelegung alle Form-Eigenschaften auf die Model-Bean übertragen werden. Das artet aus zu einer Reihe von Aufrufen der Art

modelBean.setXXX( formBean.getXXX() );

An diese Stellt helfen die BeanUtils und die praktische Funktion copyProperties(). Sie überträgt automatisch die Eigenschaften der Form-Bean in die Model-Bean.

BeanUtils.copyProperties( modelBean, formBean );

Bean-Objekte klonen

Auf copyProperties() aufbauend lassen sich Kopien von Beans anlegen lassen. Da das Kopieren auf Grund der Eigenschaften und set/get-Methoden geschieht, ist eine Implementierung der Schnittstelle Clonable nicht nötig. Mit der clone()-Methode teilt cloneBean() jedoch mit, dass die Kopie wieder eine flache und keine tiefe ist: static Object cloneBean(Object bean).

Dynamische Beans

Bisher waren es existierende Beans, die über wirkliche set- und get-Methoden verfügten. Mit den BeanUtils lassen sich jedoch auch "virtuelle" Beans erzeugen, also Beans, die mit den BeanUtils verwaltet werden können, aber keine passenden set- und get-Funktionen besitzen. Diese Spezial-Beans implementieren die Schnittstelle DynaBean und verwalten über diese Schnittstelle Schlüssel/Werte-Paare. Genauso wie eine Klasse den Bauplan für Objekte beschreibt, beschreibt die Schnittstelle DynaClass den Bauplan für DynaBean-Ojekte. Exemplare der virtuellen Beans führen zuerst über die DynaClass. Die BeanUtils bringen eine Basisimplementierung in Form der Klasse BasicDynaClass mit; ihre Eigenschaften werden als Array von DynaProperty-Objekten übergeben.

DynaProperty[] dynaProp = new DynaProperty[] {
  new DynaProperty( "schenker", String[].class ),
  new DynaProperty( "bringer", Weihnachtsmann.class ),
  new DynaProperty( "volumen", int.class )
};

BasicDynaClass dynaClass = new BasicDynaClass( "geschenk", null, dynaProp ); 

Die BasicDynaClass beschreibt für ein Geschenk ein Feld von Schenkern (wie "Mama", "Papa"), den Weihnachtsmann und die Größe des Geschenks.

Nach der Beschreibung der Klasse bildet die Objektfunktion newInstance() ein Exemplar der dynamischen Bean:

DynaBean paket = dynaClass.newInstance();

Alternativ zu dieser Schreibweise lässt sich ein Exemplar BasicDynaBean bilden, der im Konstruktor die beschreibende Klasse annimmt: paket = new BasicDynaBean(dynaClass);

Diese DynaBean besitzt über die Schnittstelle Methoden zum Erfragen, Setzen oder Löschen von Attributbelegungen. Die Methode set() setzt zum Beispiel eine Eigenschaft.

paket.set( "schenker", new String[1] );
paket.set( "bringer", new Weihnachtsmann() );

Die set()-Funktion ist überladen, um auch indexierte und abgebildete Eigenschaften zu unterstützten. Das gleiche gilt für die get-Funktion. Während bei den herkömmlichen Beans der Zugriff get<Property>() steht, so heißt der nun get("<Property>").

System.out.println( paket.get("volumen") );

Da das BeanUtils-Paket bei den bekannten Funktionen wie PropertyUtils.getProperty() neben den "normalen" Beans auch die DynaBean unterstützen, müssen die über die Schnittstelle DynaBean vorgeschriebenen Funktionen nicht verwendet werden.

PropertyUtils.setSimpleProperty( paket, "volumen", new Integer(123) );
Integer volumen = (Integer) PropertyUtils.getSimpleProperty( paket, "volumen" );

Da im Beispiel vorher schon das Feld der Schenker mit einem String-Array der Länge eins initialisiert wurde, lässt sich ein konkreten Element einfach mit der allgemeinen Funktion setProperty() setzen.

PropertyUtils.setProperty( paket, "schenker[0]", "Ulli" );
System.out.println( PropertyUtils.getProperty(paket, "schenker[0]") ); // Ulli

Mantel um existierende Beans

Die DynaBeans bietet einen einheitlichen Standard für einfache, indexierte oder abgebildeten Eigenschaften über setXXX() und getXXX()-Funktionen. Um auch existierende Beans als DynaBeans zu verwaltet, sind die Klasse WrapDynaBean und WrapDynaClass definiert. Eine WrapDynaBean legt sich um eine bekannten Bean, so dass diese sich als DynaBean verwalten lässt.

Weihnachtsmann myWeih = new Weihnachtsmann();
DynaBean wrapper = new WrapDynaBean( myWeih );
wrapper.set( "name", "Schlittenheld" );
String nameW = (String) wrapper.get( "name" );
System.out.println( "Wrapper: " + nameW + ", Bean: " + myWeih.getName() );
wrapper.set( "alter", new Integer(891) );
int alterW = ((Integer) wrapper.get( "alter" )).intValue();
System.out.println( "Wrapper: " + alterW + ", Bean: " + myWeih.getAlter() );

DynaBeans in Struts *

In Struts führt die Implementierung der Form-Beans für die Abbildung der Formulardaten zu lästigen Aufwand. Die DynaBeans helfen, diesen Aufwand zu minimieren, da keine wirkliche Bean-Klasse mehr zu implementieren ist, sondern die "virtuelle" Bean Verwendung findet. In der struts-config.xml wird zunächst unter form-bean wie üblich die Bean eingetragen. Allerdings ist der Typ nun nicht der Form-Bean Typ, sondern DynaActionForm. In den folgenden Elementen der XML-Datei wird genau die schon bekannten DynaProperty für eine DynaClass eingetragen — nur nicht in Java, sondern in einer Konfigurationsdatei.

<form-bean name="dynaWeihnachtsmannForm"
           type="org.apache.struts.action.DynaActionForm">
  <form-property name="name" type="java.lang.String"/>
  <form-property name="alter" type="java.lang.Integer"/>
</form-bean>

Da Struts mit den HTML-Tags die DynaBeans berücksichtigt, muss an der View (JSP-Seite) keine Veränderung vorgenommen werden. Nur die Action muss eine Veränderung erfahren, da ja ohne DynaBeans das ActionForm aus der execute()-Funktion auf die konkrete Bean typangepasst würde, um die entsprechenden set- und get-Funktionen zu nutzen. Dieser Programmcode muss nun umgebaut werden. Statt dessen gibt Struts eine Klasse DynaActionForm vor, zu der eine Typanpassung vorgenommen wird. Über diese Klasse lässt sich mit get- und set-Funktionen die Bean nutzen.

public ActionForward execute(
 ActionMapping mapping, ActionForm form,
 HttpServletRequest request,
 HttpServletResponse response )
  throws ServletException, IOException
{
  DynaActionForm weihForm = (DynaActionForm) form;
  String name = weihForm.get( "name" );
  ...

Nicht umsonst sieht der Zugriff auf die Attribute über DynaActionForm so aus wie schon im vorangehenden Beispiel beschrieben: DynaActionForm implementiert die Schnittstelle org.apache.commons.beanutils.DynaBean.

Die DynaActionForm ersetzt vollständig die Form-Bean, wobei eine Sache auf der Streckte bleibt: die Validierung. Für die Überprüfung der Formularbelegungen wurde in der Unterklasse von ActionForm die Methode validate() überschrieben. Bei den DynaBean gibt es aber keine eigene Bean-Klasse! Eine Lösung ist eine Validierung über das Validator-Framework (auch eine Jakarta Commons), also über eine XML-Datei, eine andere ist doch wieder eine Unterklasse. Diese spezielle Bean-Klasse leitet von der schon bekannten DynaActionForm ab und implementiert wie bekannt die validate()-Funktion. In struts-config.xml muss natürlich DynaActionForm wieder verschwinden und der Name der Unterklasse eingesetzt werden.

DynaBeans für Datenbanken

Die DynaBeans finden auch an anderer Stelle Verwendung: bei Datenbanken. Ein DynaBeans-Objekt kann eine Zeile eines ResultSet repräsentieren. Um das zu erreichen, legt sich ein ResultSetDynaClass-Objekt um ein ergebnislieferndes ResultSet.

ResultSetDynaClass rsdc = new ResultSetDynaClass( rs, true );

ResultSetDynaClass implementiert die Schnittstelle DynaClass. Das ist wichtig, denn DynaClass verwaltet die Beschreibung der Eigenschaften über DynaProperty-Objekte. Bei der ResultSetDynaClass werden die Namen der Spalten zu den Namen der Eigenschaften. Der Konstruktor von ResultSetDynaClass ist überladen und verträgt ein zusätzliches boolean, damit die Namen der Eigenschaften nicht exakt in Groß- und Kleinbuchstaben übereinstimmen müssen. Ist die Belegung true, so reichen alle Namen in Kleinbuchstaben. Wichtiger ist aber die Funktion iterator() von ResultSetDynaClass, die der Reihe nach die Zeilen als DynaBean liefert; die Beschreibung der Klassen-Informationen war ja wichtiges Teil in den DynaProperty-Objekten.

for ( Iterator rows = rsdc.iterator(); rows.hasNext(); ) {
  DynaBean row = (DynaBean) rows.next();
  System.out.println( row.get("bla") );
}

Nach dem Durchlaufen kann das ResultSet geschlossen werden, das bedeutet, dass zur Zeit des Durchlaufens eine Verbindung zur Datenbank bestehen muss.

Die Frage ist nun, was ein ResultSetDynaClass bringt; zum Durchlaufen tut es auch das ResultSet. Ein Vorteil ist, dass die Daten, die über die DyaBean veröffentlicht werden, leicht auf andere Beans kopiert werden können; man erinnere sich an BeanUtils.copyProperties(ziel, quelle). Oftmals müssen die Datenbankinhalte auf das Model einer Applikation übertragen werden, so dass ein ResultSetDynaClass dazu gut geeignet ist.

Eine Eigenschaft der Klasse BasicDynaBean (und BasicDynaClass) ist ihre Serialisierungs-Möglichkeit, da die Klasse die Schnittstelle java.io.Serializable implementiert, und somit persistent gemacht werden kann. Eine normale DynaBean implementiert diese Schnittstelle nicht, so dass man an dieser Stelle die Serialisierung nicht erzwingt. Aber natürlich kann eine andere Klasse DynaBean und Serializable gleichzeitig implementieren und das wäre sogar praktisch, denn könnte die Daten gleich abgespeichert werden. Wurde zum Beispiel eine BasicDynaBean angelegt, können Daten auf diese Bean übertragen werden und sie lässt sich später abspeichern. Es wäre doch praktisch, wenn auch die Zeilen vom ResultSetDynaClass serialisierbar wären. Dann wäre das Problem gelöst, wie sich Datenbankinhalte einfach übertragen lassen — O.K., ein RowSet ist auch eine Lösung. Die Antwort aus den BeanUtils ist die Klasse RowSetDynaClass, die Serializable implementiert. RowSetDynaClass liest aus einem ResultSet alle Zeilen aus und speichert sie, so dass sie serialisierbar werden.

RowSetDynaClass rsdc = new RowSetDynaClass( rs );

Nach dieser Zeile kann das ResultSet rs schon geschlossen werden. Der Zugriff auf die Zeilen geschieht anschließend mit getRows(), was eine java.util.List mit den bekannten DynaBean-Objekten liefert. Mit dem serialisier-fähigen RowSetDynaClass lassen sich die Inhalte über RMI leicht transportieren, ohne dass viel Programmcode entwickelt werden muss.

Links

GUI-Tipp: Verschicke EMails mit der richtiger Kodierung

Also nicht so:

Hi Christian,<BR><BR>We have received a request to permanently delete your account. Your account has been deactivated from the site and will be permanently deleted within 14 days. <BR><BR>If you did not request to permanently delete your account, please login to Facebook to cancel this request:<BR><BR>https://www.facebook.com/login.php<BR><BR><BR>Thanks,<BR>The Facebook Team