Die Änderungen sind im Wiki dokumentiert: und referenziert https://code.google.com/p/guava-libraries/wiki/Release16.
- guava-16.0-rc1.jar
- guava-gwt-16.0-rc1.jar (for GWT users)
- guava-16.0-rc1-javadoc.jar (Javadoc)
- guava-16.0-rc1-sources.jar (Source)
Die Änderungen sind im Wiki dokumentiert: und referenziert https://code.google.com/p/guava-libraries/wiki/Release16.
Neu in Java 8 ist die Methode find(…) in Files um Dateien nach gewissen Kriterien zu finden.
final class java.nio.file.Files
Beispiel und Hinweis: Finde alle Ordner unter dem Standard-Windows Bilder-Verzeichnis und gib sie aus:
Files.find( Paths.get( System.getProperty( „user.home“ ) )
.resolve( „Pictures“ ),
Integer.MAX_VALUE,
(p,attr) -> Files.isReadable( p ) && attr.isDirectory()
).forEach( System.out::println );
Intern greift find(…) auf den gleichen Mechanismus wie walk(…) zurück, doch ist eine Eigenimplementierung mit Hilfe von walk(…) mitunter besser, da wir beim visitFileFailed(…) Fehler ignorieren können – bei find(…) führen Fehler direkt zum Abbruch. Bei Windows führt eine rekursive Suche schnell zu einem java.nio.file.AccessDeniedException durch einen Ordner, bei dem Java nicht dran darf und dann ist mit find(…) sofort Schluss.
Die Utility-Klasse Files bietet vier statische Methoden (zwei mehr in Java 8), die, bei einem Startordner beginnend, die Verzeichnisse rekursiv ablaufen.
final class java.nio.file.Files
Bei allen Varianten bestimmt der erste Parameter den Startordner. Während walk(…)einen java.util.stream.Stream liefert (die Methoden sind neu in Java 8), erwarten die anderen beiden walkFileTree(…)-Methoden ein Objekt mit Callback-Methoden, die walkFileTree(…) beim Ablaufen des Verzeichnisbaums aufruft
In der Klasse Files finden sich vier Methoden (eine mehr unter Java 8), um zu einem gegebenen Verzeichnis alle Dateien und Unterverzeichnisse aufzulisten:
final class java.nio.file.Files
Die Rückgabe DirectoryStream<T> ist ein Closeable (und somit AutoCloseable) sowie Iterable<T>, und so unterscheidet sich die Möglichkeit zur Anfrage der Dateien im Ordner grundsätzlich von der Methode list(…) in der Klasse File, die immer alle Dateien in einem Feld auf einmal zurückliefert. Bei einem DirectoryStream wird Element für Element über den Iterator geholt; trotz des Namensanhangs „Stream“ ist der DirectoryStream kein Strom im Sinne von java.util.stream. Ein Stream<String> hingegen liefert die kompakte Methode list(Path), sie nutzt intern einen DirectoryStream.
try ( DirectoryStream<Path> files =
Files.newDirectoryStream( Paths.get( „c:/“ ) ) ) {
for ( Path path : files )
System.out.println( path.getFileName() );
}
Aus der Tatsache, dass die Dateien und Unterverzeichnisse nicht in einem Rutsch geholt werden, leitet sich die Konsequenz ab, dass der DirectoryStream/Stream<String> geschlossen werden muss, da nicht klar ist, ob der Benutzer wirklich alle Dateien abholt oder nach den ersten 10 Einträgen aufhört. Die Schnittstelle DirectoryStream erweitert die Schnittstelle Closeable (und die ist AutoCloseable, weshalb unser Beispiel ein try-mit-Ressourcen nutzt) und Stream implementiert AutoCloseable, daher ist es guter Stil, den DirectoryStream/Stream am Ende zu schließen, um blockierte Ressourcen freizugeben. try-mit-Ressourcen gibt immer etwaige Ressourcen frei, auch wenn es beim Ablaufen des Verzeichnisses zu einer Ausnahme kam.
Da die Klasse Path nur Pfade, aber keine Dateiinformationen wie die Länge oder Änderungszeit repräsentiert und Path auch keine Möglichkeit bietet, Dateien anzulegen und zu löschen, übernimmt die Klasse Files diese Aufgaben.
Mit den Methoden readAllBytes(…), readAllLines(…), lines(…) bzw. write(…) kann Files einfach ein Dateiinhalt einlesen oder Strings bzw. ein Byte-Feld schreiben.
URI uri = ListAllLines.class.getResource( „/lyrics.txt“ ).toURI();
Path path = Paths.get( uri );
System.out.printf( „Datei ‚%s‘ mit Länge %d Byte(s) hat folgende Zeilen:%n“, path.getFileName(), Files.size( path ) );
int lineCnt = 1;
for ( String line : Files.readAllLines( path /*, StandardCharsets.UTF_8 vor Java 8 */) )
System.out.println( lineCnt++ + „: “ + line );
final class java.nio.file.Files
Die Aufzählung OpenOption ist ein Vararg, und daher sind Argumente nicht zwingend nötig. StandardOpenOption ist eine Aufzählung vom Typ OpenOption mit Konstanten wie APPEND, CREATE, …
Hinweis: Auch wenn es naheliegt, die Files-Methode zum Einlesen mit einem Path-Objekt zu füttern, das ein HTTP-URI repräsentiert, funktioniert dies nicht. So liefert schon die erste Zeile des Programms eine Ausnahme des Typs »java.nio.file.FileSystemNotFoundException: Provider „http“ not installed«.
Path path = Paths.get( new URI „http://tutego.de/javabuch/aufgaben/bond.txt“ ) );
List<String> content = Files.readAllBytes( path );
System.out.println( content );
Vielleicht kommt in der Zukunft ein Standard-Provider von Oracle, doch es ist davon auszugehen, dass quelloffene Lösungen diese Lücke schließen werden. Schwer zu programmieren sind Dateisystem-Provider nämlich nicht.
Sollen die Daten nicht direkt aus einer Datei in eine byte-Feld/String-Liste gehen bzw. aus einer byte-Feld/String-Sammlung in eine Datei, sondern von einer Datei in einen Datenstrom, so bieten sich zwei copy(…)-Methoden an:
final class java.nio.file.Files
Neben den statischen Files-Methoden newOutputStream(…) und newInputStream(…) gibt es zwei Methoden, die zeichenorientierte Ströme liefern, also Writer/Reader.
final abstract java.nio.file.Files
BufferedReader und BufferedWriter sind Unterklassen von Reader/Writer die zum Zwecke der Optimierung Dateien im internen Puffer zwischenspeichern.
Die MAC-Adresse (von Media-Access-Control) ist eine (im Idealfall) eindeutige Adresse eines Netzwerkgeräts. MAC-Adressen sind für Ethernet-Verbindungen essenziell, da auf der physikalischen Übertragungsebene Signale zu einer gewünschten Netzwerkkarte aufgebaut werden. Wegen der Eindeutigkeit eignen sie sich gut als Schlüssel, und es ist interessant, auch in Java diese Adresse auszulesen. Das geht mit der Klasse NetworkInterface recht unkompliziert. Alle lokale Netzwerkschnittstellen liefert NetworkInterface.getNetworkInterfaces(), ist die IP-Adresse bekannt, können wir NetworkInterface.getByInetAddress(InetAddress) nutzen.
for ( NetworkInterface ni : Collections.list( NetworkInterface.getNetworkInterfaces() ) ) { byte[] adr = ni.getHardwareAddress(); if ( adr == null || adr.length != 6 ) continue; String mac = String.format( "%02X:%02X:%02X:%02X:%02X:%02X", adr[0], adr[1], adr[2], adr[3], adr[4], adr[5] ); System.out.println( mac ); }
Auf der Windows Kommandozeile liefert ipconfig /all alle MAC-Adressen, die dort „physikalische Adresse” heißen.
Details siehe http://wiki.apidesign.org/wiki/IOS.
Laut Media Control ist mein Java-Buch das bestverkaufte Computing-Buch des Jahres 2013. Ausgewertet wurden alle Titel zu Computing-Themen (Programmierung, Netze, Server, Web ) aller Verlage.
import java.util.concurrent.atomic.AtomicInteger; import javafx.application.Application; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.event.EventHandler; import javafx.scene.Cursor; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.shape.Polygon; import javafx.stage.Stage; class DrawingPane extends Pane { public DrawingPane( Image image, Polygon poly ) { poly.setFill( Color.web( "ANTIQUEWHITE", 0.8 ) ); poly.setStroke( Color.web( "ANTIQUEWHITE" ) ); poly.setStrokeWidth( 2 ); getChildren().addAll( new ImageView( image ), poly ); for ( int i = 0; i < poly.getPoints().size(); i += 2 ) { Circle circle = new Circle( poly.getPoints().get( i ), poly.getPoints().get( i + 1 ), 5 ); circle.setFill( Color.web( "PERU", 0.8 ) ); circle.setStroke( Color.PERU ); circle.setStrokeWidth( 2 ); AtomicInteger polyCoordinateIndex = new AtomicInteger( i ); circle.centerXProperty().addListener( new ChangeListener<Number>() { @Override public void changed( ObservableValue<? extends Number> observable, Number oldValue, Number newValue ) { poly.getPoints().set( polyCoordinateIndex.get(), newValue.doubleValue() ); } } ); circle.centerYProperty().addListener( new ChangeListener<Number>() { @Override public void changed( ObservableValue<? extends Number> observable, Number oldValue, Number newValue ) { poly.getPoints().set( polyCoordinateIndex.get() + 1, (Double) newValue ); } } ); setDragHandler( circle ); getChildren().add( circle ); } } private double dragDeltaX, dragDeltaY; private void setDragHandler( Circle circle ) { circle.setOnMousePressed( new EventHandler<MouseEvent>() { @Override public void handle( MouseEvent mouseEvent ) { dragDeltaX = circle.getCenterX() - mouseEvent.getSceneX(); dragDeltaY = circle.getCenterY() - mouseEvent.getSceneY(); } } ); circle.setOnMouseDragged( new EventHandler<MouseEvent>() { @Override public void handle( MouseEvent mouseEvent ) { circle.setCenterX( mouseEvent.getSceneX() + dragDeltaX ); circle.setCenterY( mouseEvent.getSceneY() + dragDeltaY ); circle.setCursor( Cursor.MOVE ); } } ); circle.setOnMouseEntered( new EventHandler<MouseEvent>() { @Override public void handle( MouseEvent mouseEvent ) { circle.setCursor( Cursor.HAND ); } } ); circle.setOnMouseReleased( new EventHandler<MouseEvent>() { @Override public void handle( MouseEvent mouseEvent ) { circle.setCursor( Cursor.HAND ); } } ); } } public class JavafxDemo extends Application { @Override public void start( Stage stage ) { Image image = new Image( "http://tours-tv.com/uploads/maps/map-Medizinische-Hochschule-Hannover-karta.jpg" ); Polygon poly = new Polygon( 10, 10, 100, 10, 200, 100, 50, 200 ); stage.setScene( new Scene( new DrawingPane( image, poly ), 450, 300 ) ); stage.show(); } public static void main( String[] args ) { launch( args ); } }
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 ); } }
Siehe http://dooapp.github.io/FXForm2/, Änderungen der Updates bei http://blog.dooapp.com/2013/11/fxform-two-releases-and-lot-of-new.html.
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:
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
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.
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
[1] Details finden Sie unter http://weblogs.java.net/blog/chet/archive/2004/08/toolkitbuffered.html.
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
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 );
Die Klasse Class überschreibt die Methode toString() und greift dabei auf getName() zurück. toString() fügt zusätzlich Informationen über die Art des repräsentierten Typs (normale Klasse, Schnittstelle oder primitiver Datentyp) ein. Neu in Java 8 ist toGenericString(), was auch noch in spitzen Klammern die Typvariablen anzeigt. (Natürlich nicht den Typparameter, da der zur Laufzeit wegen der Typlöschung nicht bekannt ist.) Um die Typvariable zu erfragen wird getTypeName() verwendet; die Methode ist eine Implementierung aus der Schnittstelle Type, die Class implementiert.
Beispiel: Teste für ein Class-Objekt die drei String-Repräsentationen:
Class<?> c = HashMap.class;
System.out.println( c.getName() ); // java.util.HashMap
System.out.println( c.toString() ); // class java.util.HashMap
System.out.println( c.toGenericString() ); // public class java.util.HashMap<K,V>
Bei inneren Typen trennt ein $ bei der String-Repräsentation den äußeren und inneren Typ. Anders verhält sich getCanonicalName(), wie das Beispiel zeigt:
Anweisung |
Rückgabe |
Map.Entry.class.getName() |
java.util.Map$Entry |
Map.Entry.class.getCanonicalName() |
java.util.Map.Entry |
Map.Entry.class.toString() |
interface java.util.Map$Entry |
Map.Entry.class.toGenericString() |
public abstract static interface java.util.Map$Entry<K,V> |
String-Repräsentation bei inneren Typen
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
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:
Verwandte Methoden zur Berechnung des Abstands eines Punktes zur Line bietet auch Line2D:
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).