Guava 16.0 is now available on Maven Central! You can install it using Mave… [link]“ http://feedly.com/e/cCsIP0HO
Allgemein
Link: sun.misc.Unsafe and off heap memory
Für Microsoft ist Java performanter als C# …
… oder wie soll ich die Grafik von MS deuten?
Quelle: http://joeduffyblog.com/2013/12/27/csharp-for-systems-programming/
Example for JavaFX CubicCurve with animation
import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.application.Application; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.paint.Color; import javafx.scene.shape.CubicCurve; import javafx.scene.shape.Line; import javafx.stage.Stage; import javafx.util.Duration; public class CubicCurveDemo extends Application { @Override public void start( Stage stage ) { double startX = 200, startY = 200; DoubleProperty controlX1 = new SimpleDoubleProperty( 20 ); DoubleProperty controlY1 = new SimpleDoubleProperty( 20 ); DoubleProperty controlX2 = new SimpleDoubleProperty( 400 ); DoubleProperty controlY2 = new SimpleDoubleProperty( 20 ); double endX = 300, endY = 200; // Linie von [controlX1, controlY1] nach [startX, startY] Line line1 = new Line( 0, 0, startX, startY ); line1.startXProperty().bind( controlX1 ); line1.startYProperty().bind( controlY1 ); line1.setStrokeWidth( 2 ); // Linie von [controlX2, controlY2] nach [endX, endY] Line line2 = new Line( 0, 0, endX, endY ); line2.startXProperty().bind( controlX2 ); line2.startYProperty().bind( controlY2 ); line2.setStrokeWidth( 2 ); // Animierte Kontrollpunkte Timeline timeline = new Timeline( new KeyFrame( Duration.millis( 1000 ), new KeyValue( controlX1, 300 ), new KeyValue( controlY2, 300 ) ) ); timeline.setCycleCount( Timeline.INDEFINITE ); timeline.setAutoReverse( true ); timeline.play(); CubicCurve curve = new CubicCurve( startX, startY, 0, 0, 0, 0, endX, endY ); curve.controlX1Property().bind( controlX1 ); curve.controlY1Property().bind( controlY1 ); curve.controlX2Property().bind( controlX2 ); curve.controlY2Property().bind( controlY2 ); curve.setFill( null ); curve.setStroke( Color.BLUEVIOLET ); curve.setStrokeWidth( 3 ); stage.setScene( new Scene( new Group( line1, line2, curve ), 450, 300 ) ); stage.show(); } public static void main( String[] args ) { launch( args ); } }
Inselraus: Swing/AWT-Bilder im Speicher erzeugen
Nicht immer kommen die Bilder vom Datensystem oder aus dem Internet. Mit der Java-Bibliothek lassen sich einfach auch eigene (Buffered)Image-Objekte anlegen. Dazu bieten sich – wieder historisch bedingt – verschiedene Varianten an:
- Jede AWT-Komponente, wie Frame oder Panel, bietet die Methode createImage(…). Die Anweisung Image image = panel.createImage(800, 600); erzeugt ein Image-Objekt mit 800 Pixeln in der Breite und 600 in der Höhe, das mit getGraphics() Zugriff auf den Grafikkontext bietet. Wenn die AWT-Komponente noch nicht angezeigt wurde, liefert createImage(…) die Rückgabe null, sodass hier leicht eine NullPointerException entstehen kann. Auch unterstützen die Bilder keine Transparenz.
- Java 1.2 führte die Klasse BufferedImage ein, die eine Erweiterung der Image-Klasse ist. Image ist eine abstrakte Klasse und BufferedImage eine konkrete nicht abstrakte Unterklasse. Ein zentraler Unterschied ist, dass der Zugriff auf die Pixel von BufferedImage-Objekten einfach ist, weil sie auf der Java-Seite in Byte-Arrays gespeichert sind, dass aber der Zugriff auf die Pixel von Image-Objekten schwierig ist, da Image-Objekte vom Betriebssystem kommen. Bei BufferedImage ist die Manipulation der Pixel einfach. Die Klasse bietet drei Konstruktoren. Beim Erzeugen ist immer ein Bildtyp anzugeben, der über die physikalische Speicherung bestimmt.[1]
- createCompatibleImage(…) über GraphicsConfiguration erzeugt ein BufferedImage und benötigt keinen Bildtyp.
BufferedImage erzeugen lassen
Ein Bild über createCompatibleImage(…) zu erzeugen, hat den großen Vorteil, dass das Daten- und Farbmodell optimal gewählt ist. Der einzige Nachteil dieser Methode ist die große Menge an benötigten Hilfsobjekten – was zusätzliche Schreibarbeit bedeutet:
GraphicsConfiguration gfxConf = GraphicsEnvironment.getLocalGraphicsEnvironment().
getDefaultScreenDevice().getDefaultConfiguration();
int width = 600, height = 400;
BufferedImage image = gfxConf.createCompatibleImage( width, height );
Neben createCompatibleImage(int, int) gibt es auch eine Variante, die die Angabe einer Transparenz ermöglicht.
abstract class java.awt.GraphicsConfiguration
- abstract BufferedImage createCompatibleImage(int width, int height)
Erzeugt ein BufferedImage. - BufferedImage createCompatibleImage(int width, int height, int transparency)
Erzeugt ein BufferedImage mit optionaler Transparenz. Das Argument für transparency kann sein: Transparency.OPAQUE (keine Transparenz, der Alpha-Wert ist 1,0), Transparency.BITMASK (Bilddaten sind komplett sichtbar, also opak mit Alpha-Wert 1, oder transparent, also Alpha-Wert 0), Transparency.TRANSLUCENT (Grafik erlaubt das Durchscheinen mit Alpha-Werten von 0,0 bis 1,0).
Das Bild bemalen
Image-Objekte (BufferedImage ist eine Unterklasse) geben über getGraphics() das Graphics-Objekt zurück, mit dem sich das Bild bemalen lässt. Im Fall eines speziellen BufferedImage-Objekts ist es jedoch üblich, die Methode createGraphics() einzusetzen, da sie ein Graphics2D-Objekt – eine Unterklasse von Graphics – liefert, mit dem weitere Zeichenoperationen möglich sind. Außerdem ruft getGraphics() sowieso createGraphics() auf …
Beispiel: Initialisiere ein Bild img mit weiß.
Graphics2D g = img.createGraphics();
g.setColor( Color.WHITE );
g.fillRect( 0, 0, b – 1, h – 1 );
Alternativ kann zum Löschen des Hintergrunds auch g.setBackground(Color.WHITE); g.clearRect(…); verwendet werden.
BufferedImage von Hand erzeugen
Der Konstruktor der Klasse BufferedImage wird mit den Maßen parametrisiert und zusätzlich mit einem Speichermodell für die Bildinformationen. Das ermöglicht die Verwendung von beliebigen Farb- und Speichermodellen:
int h = 400,
b = 600;
BufferedImage img = new BufferedImage( b, h, BufferedImage.TYPE_INT_RGB );
Das notwendige dritte Argument kennzeichnet den Speichertyp; hier sind die Farben durch je 8 Bit Rot, Grün und Blau abgebildet. Um weitere 2 der über 10 Bildtypen zu nennen: TYPE_USHORT_GRAY (Graubilder) oder TYPE_INT_ARGB (RGB mit jeweils 8 Bit sowie Alpha).
class java.awt.image.BufferedImage
extends Image
implements RenderedImage, Transparency, WritableRenderedImage
- BufferedImage(int width, int height, int imageType)
Liefert ein neues Hintergrundbild mit den gegebenen Maßen.
[1] Details finden Sie unter http://weblogs.java.net/blog/chet/archive/2004/08/toolkitbuffered.html.
Inselraus: Neue TrueType-Fonts in AWT/Swing nutzen
Die auf allen Systemen vordefinierten Standardzeichensätze sind etwas dürftig, obwohl die Font-Klasse selbst jeden installierten Zeichensatz einlesen kann. Da ein Java-Programm aber nicht von der Existenz eines bestimmten Zeichensatzes ausgehen kann, ist es praktisch, einen Zeichensatz mit der Installation auszuliefern und dann diesen zu laden; das kann die Font-Klasse mit der statischen Methode createFont(…) sein. Aus einem Eingabestrom liest die Methode den TrueType-Zeichensatz und erstellt das entsprechende Font-Objekt, Bsp.:
Font font = Font.createFont( Font.TRUETYPE_FONT,
getClass().getResourceAsStream( „/NASALIZA.TTF“) );
Das erste Argument ist immer Font.TRUETYPE_FONT. Das zweite Argument bestimmt den Eingabestrom zur Binärdatei mit den Zeichensatzinformationen. Die Daten werden ausgelesen und zu einem Font-Objekt verarbeitet.
Waren die Beschreibungsinformationen in der Datei ungültig, so erzeugt die Font-Klasse eine FontFormatException(„Unable to create font – bad font data“). Dateifehler fallen nicht darunter und werden extra über eine IOException angezeigt. Der Datenstrom wird anschließend nicht wieder geschlossen.
An dieser Stelle verwundert es vielleicht, dass die Arbeitsweise der statischen Methode createFont(…) der des Konstruktors ähnlich sein müsste, aber der Parameterliste die Attribute fehlen. Das liegt daran, dass die Methode automatisch einen Zeichensatz der Größe 1 im Stil Font.PLAIN erzeugt. Um einen größeren Zeichensatz zu erzeugen, müssen wir ein zweites Font-Objekt anlegen, was am einfachsten mit der Methode deriveFont(…) geschieht.
class java.awt.Font
implements Serializable
- static Font createFont(int fontFormat, InputStream fontStream)
throws FontFormatException, IOException
Liefert ein neues Zeichensatzobjekt in der Größe von einem Punkt und mit keinem besonderen Stil.
Soll nicht direkt der Font verwendet werden, sondern soll der Zeichensatz unter seinem Namen in den Namensraum gelegt werden, sodass er später auch über den Font-Konstruktor gefunden werden kann, lässt er sich mit registerFont(Font) anmelden. Das sieht etwa so aus:
GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont( font );
Node.js bei PayPal, Java geht
Inselraus: Punkt in einer Form, Schnitt von Linien, Abstand Punkt/Linie
Die unterschiedlichen Klassen für die geometrischen Formen aus dem Java 2D-Paket besitzen Methoden, um zum Beispiel festzustellen, ob ein Punkt in einer Form liegt.
interface java.awt.Shape
- boolean contains(int x, int y )
- boolean contains(Point2D p)
Liefert true, wenn der Punkt in der Form liegt. - boolean contains(int x, int y, int w, int h)
- boolean contains(Rectangle2D r)
Liefert true, wenn das beschriebene Rechteck komplett in der Form liegt.
Besonders praktisch ist die Methode contains(…) für Polygone.[1] Sie arbeitet aber nur korrekt für Punkte innerhalb der eingeschlossenen Fläche. Bei Abfrage von Punkten, die den Eckpunkten entsprechen, kommen immer sehr willkürliche Werte heraus – und genauso bei der Abfrage, ob die Punkte auf der Linie zum Innenraum gehören oder nicht.
Die Klasse Point2D berechnet den Abstand zweier Punkte mit den Methoden:
- double distance(double PX, double PY)
- static double distance(double X1, double Y1, double X2, double Y2)
- double distance(Point2D pt)
- double distanceSq(double PX, double PY)
- static double distanceSq(double X1, double Y1, double X2, double Y2)
- double distanceSq(Point2D pt)
Verwandte Methoden zur Berechnung des Abstands eines Punktes zur Line bietet auch Line2D:
- double ptLineDist(double PX, double PY)
- static double ptLineDist(double X1, double Y1, double X2, double Y2, double PX, double PY)
- double ptLineDist(Point2D pt)
- double ptLineDistSq(double PX, double PY)
- static double ptLineDistSq(double X1, double Y1, double X2, double Y2, double PX, double PY)
- double ptLineDistSq(Point2D pt)
- double ptSegDist(double PX, double PY)
- static double ptSegDist(double X1, double Y1, double X2, double Y2, double PX, double PY)
- double ptSegDist(Point2D pt)
- double ptSegDistSq(double PX, double PY)
- static double ptSegDistSq(double X1, double Y1, double X2, double Y2, double PX, double PY)
- double ptSegDistSq(Point2D pt)
Die relativeCCW(…)-Methoden von Line2D können herausfinden, ob der Punkt rechts oder links einer Linie liegt. Ob sich zwei Linien schneiden, ermitteln zwei überladene Line2D-Methoden intersectsLine(…). Neben der Objektmethode testet die mit acht Parametern gesegnete statische Methode linesIntersect(…), ob zwei Liniensegmente sich schneiden. Zwei allgemeine intersects(…)-Methoden deklariert die Schnittstelle Shape, doch bei diesen Methoden aus Line2D geht es darum, ob eine Form ein Rechteck schneidet. intersectsLine(…) bietet auch Rectangle2D und meldet damit, ob ein Rechteck eine Linie schneidet.
Genau das Gegenteil vom Schnitt ist die Vereinigung. So legt die Methode union(Rectangle2D src1, Rectangle2D src2, Rectangle2D dest) von Rectangle2D zwei Rechtecke zusammen, wobei ein neues Rechteck entsteht, das die äußersten Koordinaten der beiden Ursprungsrechtecke besitzt. Die Methode outcode(double, double) ist ebenfalls interessant, da sie über eine Bit-Maske in der Rückgabe angibt, wo ein außerhalb des Rechtecks befindlicher Punkt steht, also etwa OUT_BOTTOM, OUT_LEFT, OUT_RIGHT, OUT_TOP.
[1] Ob ein Punkt im Polygon ist, entscheidet der Gerade/Ungerade-Test (http://en.wikipedia.org/wiki/Point_in_polygon).
Der Array-Index beginnt bei 0. Schon immer so?
Eine schöne Geschichte dazu bereitet ein Blog-Post http://exple.tive.org/blarg/2013/10/22/citation-needed/ auf.
Offtopic: Unterstützt Ärzte ohne Grenzen …
JavaScript und Ruby lernen über interaktive Tutorials
Für Einsteiger und für Fortgeschrittene gibt es interaktive Kurse, etwa die Einführung in JS unter http://www.codecademy.com/de:
Optisch einfach perfekt gemacht ist auch der Kurs für Ruby, http://tryruby.org/levels/1/challenges/0:
Kein Wunder, dass mit solchem Lehrmaterial Bücher einfach uncool sind.
IdentityHashMap
Es gibt eine besondere Datenstruktur mit dem Namen IdentityHashMap, die statt der internen equals(…)-Vergleiche einen Identitätsvergleich mit == durchführt. Die Implementierung ist selten im Einsatz, kann aber im Bereich der Performance-Optimierung eine interessante Rolle übernehmen und auch das Problem lösen, wenn in der Map denn absichtlich Objekte enthalten sein sollen, die equals-gleich, aber nicht identisch sind. Es lässt sich auch so sehen: IdentityHashMap ist attraktiv, wenn als Schlüssel Objekte zum Einsatz kommen, bei denen Gleichheit und Identität dasselbe bedeuten.
Hinweis: An Integer-Objekten in einer IdentityHashMap zeigt sich genau der Unterschied zur klassischen Map, wie einer HashMap. Nehmen wir
Integer key1 = 1;
Integer key2 = 1;
Integer key3 = 1000;
Integer key4 = 1000;
dann sind wegen dem Autoboxing und wegen dem internen Cache key1 == key2, aber key3 != key4 (die Integer-Klasse cached standardmäßig Ganzzahlen im Wertebereich eines byte). Anfragen mit equals-gleichen Integer-Objekten sind in der HashMap üblich, laufen aber bei IdentityHashMap ins Leere, da es unmöglich ist, zum Beispiel später über Integer.value(1000) ein genau identisches Integer-Objekte aufzubauen, sodass es als Schlüssel im IdentityHashMap „passt“ und der Identitätsvergleich wahr wird.
Algorithmen für Rastergrafik, 2. Einfache Füllalgorithmen
In der Computergrafik sind Objekte, die durch Linien und Punkte abgegrenzt sind selten. Vielmehr bekommen die Flächen eine Farbe oder ein Überzug von einer Textur. Ein ausgefülltes Objekt kann durch zwei Techniken entstehen. Zunächst einmal kann es sofort so konstruiert werden, dass es gefüllt erscheint, beispielsweise ergeben viele gleichförmige Linien ein Rechteck. Aber Objekte können nachträglich gefüllt werden. Dazu sind Begrenzungen nötig. Ein einfacher Algorithmus, der sich auf die Informationen des Bildschirmspeichers verlässt ist Seed-Fill. Die Idee von Seed-Fill ist einfach: Wir starten bei einem bekannten Punkt — dem Seed (zu deutsch Korn) — und geben dem Nachbarpixel die gleiche Füllfarbe, wenn dieser nicht die Begrenzungsfarbe besitzt. Der Algorithmus ist schnell rekursiv definiert. Wenn wir auf den Bildschirmspeicher zugreifen, so umschreiben wir dies mit einem Zugriff auf das Feld pixelArray. Und boundaryValue bezeichnet die begrenzende Farbe und fillValue die zu füllende Farbe:
void seedFill( int x, int y )
{
if ( ( pixelArray[x,y] != boundaryValue ) && ( pixelArray[x,y] != fillValue ) ) {
setPixel( x, y, fillValue );
seedFill( x+1, y );
seedFill( x-1, y);
seedFill( x+1, y+1 );
seedFill( x-1, y-1 );
}
}
Die rekursive Implementierung krankt an dem Problem, dass der interne Stack sehr schnell ansteigt. Auch besitzt diese Implementierung den Nachteil, dass für jeden zu setzenden Punkt immer der Bildschirmspeicher ausgelesen wird und mehrere gleiche Punkte auf den Stack kommen und zurückgewiesen werden.
Eine mögliche Verbesserung erhalten wir dadurch, dass wir die direkten Rekursion eliminieren. Dadurch wird dieser Algorithmus aber nicht schneller, da das setzen eines Pixels vier Stack-Operationen nach sich zieht. Außerdem ist eine Implementierung wie die folgende schon deswegen nicht schneller, da bei der Stack-Implementierung der Objekt-Overhead mit sich gezogen wird. Daher dient folgender Algorithmus lediglich zur Anschauung, wie Flood-Fill mit einem Stack umgesetzt werden kann.
void seedFill( int xSeed, int ySeed )
{
Stack s = new Stack();
floodValue = pixelArray[xSeed, ySeed];
s.push( xSeed );
s.push( ySeed );
while ( s.notEmpty() )
{
y = s.pop();
x = s.pop();
setPixel( x, y );
if ( pixelArray[x+1, y] == floodValue ) {
s.push( x+1 ); s.push( y );
}
if ( pixelArray[x-1, y] == floodValue ) {
s.push( x-1 ); s.push( y );
}
if ( pixelArray[x, y+1] == floodValue ) {
s.push( x ); s.push( y+1 );
}
if ( pixelArray[x, y-1] == floodValue ) {
s.push( x ); s.push( y-1 );
}
}
Überlegen wir, wodurch diese Implementierung ineffizient wird. Ein Grund haben wir schon erschlossen: jeder gesetzte Punkt wird mehrmals überprüft. Nehmen wir einen Punkt (x,y) heraus. Dann wird (x+1,y) auf den Stapel gesetzt aber mit diesen Koordinaten wird wiederum (x+1-1,y) überprüft. Eine verbesserte Variante vom Seed-Fill müsste sich daher einfach die Richtung merken und daraufhin die Rückrichtung nicht mehr gehen. Anstelle der Methode seedFill(…) treten nun vier Implementierungen an, für jede Richtung eine Funktion. Zur Demonstration sei seedFillLeft(…) aufgeführt.
seedFillLeft( int x, int y )
{
if ( pixelArray[x, y] != boundaryValue ) {
setPixel( x, y, fillValue );
seedFillLeft( x+1, y );
seedFillUp( x-1, y);
seedFillDown( x+1, y+1 );
}
Wollen wir noch schnellere Füllalgorithmen benutzen, so kommen wir mit diesem Ansatz nicht weiter. Mit einer modifizierten Version des Scan-Line-Algorithmus ist dies aber zu schaffen.
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.
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
- Patterns for Java and Distributed Computing
- Design Patterns (Vince Huston)
- Cetus Links Patterns
- Croft’s Design Patterns In Java
- Industrial Logic Patterns Page
- Pattern Depot
- Portland Pattern Repository
- Wiki Wiki Web
- Design Patterns Home Page
- James Cooper’s The Design Patterns Java Companion
- Design Patterns in Java AWT
- A Catalogue of General-Purpose Design Patterns
Was ist eigentlich kein Pattern?
Bjarne Stroustrup, OOPSLA 95
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 );
- -3
- 0
- 3
- 4
- keine dieser
Die Ausgabe ist 4.
Frage
Welche der folgenden sind gültige Objektmethoden der Klasse String?
- equals( String )
- equals( Object )
- trim()
- round()
- 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?
- Math.floor( -4.7 )
- Math.round( -4.7 )
- Math.ceil( -4.7 )
- 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) );
- Ull
- ll
- lli
- 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();
- s3 = s1 + s2;
- s3 = s1-s2;
- s3 = s1 & s2
- 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?
- Das Programm kompiliert und die Ausgabe ist
d1 ist Fri December 31 00:00:00 GMT 1999d2 ist Fri December 31 00:00:00 GMT 1999
- Das Programm kompiliert und die Ausgabe ist
d1 ist Fri December 31 00:00:00 GMT 1999d2 ist Sun December 31 00:00:00 GMT 2000
- Das Programm kompiliert und die Ausgabe ist
d1 ist Sun December 31 00:00:00 GMT 2000d2 ist Sun December 31 00:00:00 GMT 2000
- Die Anweisung ‚d1 = d2‘ wird vom Compiler zurückgewiesen, da Date den Gleich-Operator nicht überladen hat.
- 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()?
- Wenn mehr als ein Thread auf eine Bedingung wartet, wird nur der Thread benachrichtigt, der am längsten wartet.
- Wenn mehr als ein Thread auf eine Bedingung wartet, läßt sich nicht ausmachen, welcher Thread benachrichtigt wird.
- notify() ist in der Klasse Thread definiert.
- Es ist nicht unbedingt erforderlich, den Lock für das Objekt zu besitzen, dessen notify() Methode wir aufrufen.
- 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?
- Runnable a = new Runnable() {
public void run() {for ( int i = start; i <= end; i++ )
System.out.println( i );
}
}
- a implements Runnable {
public void run() {for ( int i = start; i <= end; i++ )
System.out.println( i );
}
}
- 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" );
}
}
- Das Programm lässt sich nicht kompilieren
- Das Programm lässt sich kompilieren, aber nichts erscheint auf der Standardausgabe
- Das Programm lässt sich kompilieren und das Wort „Schnickschnack“ erscheint einmal auf der Standardausgabe.
- 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(); } }
- RunHandler muss das Interface java.lang.Runnable implementieren.
- RunHandler muss die Klasse Thread erweitern.
- RunHandler muss eine Methode run() anbieten, die öffentlich ist und void zurückgibt.
- 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?
- public
- private
- protected
- Kein besonderer Modifier.
- 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 ); } }
- Der Code lässt sich kompilieren und bei der Aufführung erscheint 0 auf dem Bildschirm.
- Der Code lässt sich kompilieren und bei der Aufführung erscheint 1 auf dem Bildschirm.
-
Dieses Programmstück lässt sich nicht kompilieren, da eine lokale Variable nicht den gleichen Namen besitzen darf, wie eine statische Variable.
- 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 }
- int count = args.length;
- int count = args.length – 1;
- int count = 0;
while ( args[count] != null )count++;
- 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 ); } }
- Die Klasse First lässt sich kompilieren, aber nicht die Klasse Second.
- Die Klasse Second lässt sich kompilieren, aber nicht die Klasse First.
- Keine der beiden Klassen lässt sich kompilieren.
- Beide Klassen lassen sich kompilieren und wenn method() aufgerufen wird, schreibt es 3 auf die Standardausgabe.
- 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 ); } }
- Die Instanzvariable i bekommt den Wert 4.
- Die Instanzvariable i bekommt den Wert 2.
- Die Instanzvariable i bekommt den Wert 0.
- 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 ); } }
- Die println() Methode kann nicht ohne einen String Parameter benutzt werden.
- Da die Variable a privat ist, hat keine Klasse außer First Zugriff auf die Variable.
- Second kann First nicht erweitern.
- 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.)
-
public class Fred { public int x = 0; public Fred( int x ) { this.x = x; } }
-
public class fred { public int x = 0; public fred( int x ) { this.x = x; } }
-
public class Fred extends MyBaseClass, MyOtherBaseClass { public int x = 0; public Fred( int xval ) { x = xval; } }
-
protected class Fred { private int x = 0; private Fred( int xval ) { x = xval; } }
-
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 ); } }
- Dieser Code lässt sich nicht kompilieren.
- Dieser Code kompiliert, aber wirft eine Exception zur Laufzeit .
- Dieser Code läuft aber nichts erscheint in der Standard Ausgabe.
- Dieser Code läuft und schreibt "palümpalüm" in die Standard Ausgabe.
- 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?
-
class B { }
-
class B extends A { }
-
class B { B() { System.out.println( "i = " + i ); } }
-
class B { class A { } }
-
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 ); } } }
- Die Klasse B versucht, Zugriff zu der privaten Variablen zu bekommen, die in der äußeren Klasse definiert ist.
- Die Klasse A versucht, eine Instanz von B zu erzeugen, obwohl es keine gültige Instanz der Klasse A gibt.
- 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 ); } }
- Die Klasse lässt sich nicht kompilieren.
- Der Compiler meldet einen Fehler in Zeile 2.
- Der Compiler meldet einen Fehler in Zeile 9.
- Der Wert 10 ist unter der Ausgabe.
- 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] ); } }
- Es werden fünf Nullen ausgegeben.
- Das Feld a wird benutzt, bevor es initialisiert wurde.
- Kompilierfehler. Mine muss abstakt definiert werden.
- 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: }
- Das Programm kompiliert und läuft ohne Fehler.
- Das Programm kompiliert nicht.
- 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 { } }
- public
- private
- static
- 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; } } }
- 10 und 20
- 11 und 21
- 12 und 22
- 13 und 23
- 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: }
- Der Code lässt sich nicht kompilieren.
- Die Funktion schreibt die Zahl 0 in die Standardausgabe.
- Die Funktion schreibt die Zahl 1 und 2 in die Standardausgabe.
- Die Funktion schreibt die Zahl 3 in die Standardausgabe.
- Die Funktion schreibt die Zahl 4 in die Standardausgabe.
- Die Funktion schreibt die Zahlen 5 bis 9 in die Standardausgabe.
- 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?
- Der switch Block ist illegal, weil nur Ganzzahlen in der switch Anweisung benutzt werden können.
- Mit der switch Anweisung ist alles in Ordnung.
- Der switch Block ist illegal, da die default Anweisung zum Schluss kommen muss.
- Wenn das Programmstück läuft, wird nichts auf die Standardausgabe geschrieben.
- 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?
- Einem try Block muss immer ein catch Block folgen.
- Einem try Block kann entweder ein catch Block, ein finally Block oder beides folgen.
- Ein catch Block muss immer mit einem try Block verbunden sein.
- Ein finally kann nicht ohne ein try Block auftauchen.
- 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?
- Zeile 4 erzeugt eine NullPointerException
- Zeile 5 erzeugt eine NullPointerException
- Zeile 6 erzeugt eine NullPointerException
- 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 }
- void method( int i ) throws Exception {}
- void method( long i ) throws Exception {}
- void method( long i ) {}
- 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.