Multiplizieren von long-Ganzzahlen

Es gibt keinen primitiven Datentyp der mehr als 64 Bit (8 Byte) hat, sodass das Ergebnis von long * long mit seinen 128 Bit nur in ein BigInteger komplett passt. Allerdings erlaubt eine neue Methode aus Java 9, die oberen 64 Bit einer long-Multiplikation getrennt zu erfragen, und zwar mit der Methode multiplyHigh(long x, long y) in Math und StrictMath – wobei StrictMath nur auf Math leitet.

BigInteger v = BigInteger.valueOf( Long.MAX_VALUE )

                         .multiply( BigInteger.valueOf( Long.MAX_VALUE ) );

System.out.println( v );  // 85070591730234615847396907784232501249




long lowLong = Long.MAX_VALUE * Long.MAX_VALUE;

long highLong = Math.multiplyHigh( Long.MAX_VALUE, Long.MAX_VALUE );

BigInteger w = BigInteger.valueOf( highLong )

                         .shiftLeft( 64 )

                         .add( BigInteger.valueOf( lowLong ) );

System.out.println( w );  // 85070591730234615847396907784232501249

Multiplizieren von int-Ganzzahlen

Der *-Operator führt bei int keine Anpassung an den Datentypen durch, sodass die Multiplikation von zwei ints wiederum int liefert. Doch das Produkt kann schnell aus dem Wertebereich laufen, sodass es zum Überlauf kommt. Selbst wenn das Produkt in eine long-Variable geschrieben wird, erfolgt die Konvertierung von int in long erst nach der Multiplikation:

int  i = Integer.MAX_VALUE * Integer.MAX_VALUE;

long l = Integer.MAX_VALUE * Integer.MAX_VALUE;

System.out.println( i );     // 1

System.out.println( l );     // 1

Sollen zwei ints ohne Überlauf multipliziert werden, ist einer der beiden Faktoren auf long anzupassen, damit es zum korrekten Ergebnis 4611686014132420609 führt.

System.out.println( Integer.MAX_VALUE * (long) Integer.MAX_VALUE );

System.out.println( (long) Integer.MAX_VALUE * Integer.MAX_VALUE );

Da diese Typanpassung schnell vergessen werden kann und nicht besonders explizit ist, bieten die Klassen Math und StrictMath die statische Methode long multiplyFull(int x, int y), die für uns über (long)x * (long)y die Typumwandlung vornehmen.

Multiply-Accumulate, fma(…)

Die mathematische Operation a × b + c  kommt in Berechnungen oft vor, sodass Prozessoren heute diese Berechung optimiert – das heißt schneller und mit besserer Genauigkeit – durchführen können. In Java 9 gibt es bei Math und StrictMath die neue Methode fma(…) für die “fused multiply-accumulate“-Funktion aus dem IEEE 754 Standard.

class java.lang.Math
class java.lang.StrictMath

  • static double fma(double a, double b, double c)
  • static float fma(float a, float b, float c)
    Führt a × b + c mit dem HALF_EVEN durch. Gegenüber einem einfachen a * b + c berücksichtigt fma(…) die Besonderheiten Unendlich und NaN.

Rundungsmodus BigDecimal

Eine Besonderheit stellt die Methode divide(…) dar, die zusätzlich einen Rundungsmodus und optional auch eine Anzahl gültiger Nachkommastellen bekommen kann. Zunächst ohne Rundungsmodus:

BigDecimal a = new BigDecimal( "10" );
BigDecimal b = new BigDecimal( "2" );
System.out.println( a.divide(b) );   // 5

Es ist kein Problem, wenn das Ergebnis eine Ganzzahl oder das Ergebnis exakt ist:

System.out.println( BigDecimal.ONE.divide(b) );  // 0.5

Wenn das Ergebnis aber nicht exakt ist, lässt sich divide(…) nicht einsetzen. Die Anweisung new BigDecimal(1).divide( new BigDecimal(3) ) ergibt den Laufzeitfehler: „java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.“

An dieser Stelle kommen diverse Rundungsmodi ins Spiel, die bestimmen, wie die letzte Ziffer einer Rundung bestimmt werden soll. Sie lassen sich über eine Aufzählung java.math.RoundingMode übermitteln. Folgende Konstanten gibt es:

Konstante in RoundingMode Bedeutung
DOWN Runden nach 0
UP Runden weg von 0
FLOOR Runden nach negativ unendlich
CEILING Runden nach positiv unendlich
HALF_DOWN Runden zum nächsten Nachbarn und weg von der 0, wenn beide Nachbarn gleich weit weg sind
HALF_UP Runden zum nächsten Nachbarn und hin zur 0, wenn beide Nachbarn gleich weit weg sind
HALF_EVEN Runden zum nächsten Nachbarn und zum geraden Nachbarn, wenn beide Nachbarn gleich weit weg sind
UNNECESSARY Kein Runden, Operation muss exakt sein

 

ROUND_UNNECESSARY darf nur dann verwendet werden, wenn die Division exakt ist, sonst gibt es eine ArithmeticException.

BigDecimal one   = BigDecimal.ONE;

BigDecimal three = new BigDecimal( "3" );

System.out.println( one.divide( three, RoundingMode.UP ) );      // 1

System.out.println( one.divide( three, RoundingMode.DOWN ) );    // 0

Jetzt kann noch die Anzahl der Nachkommastellen bestimmt werden:

System.out.println( one.divide( three, 6, RoundingMode.UP ) );   // 0.333334

System.out.println( one.divide( three, 6, RoundingMode.DOWN ) ); // 0.333333

Beispiel

BigDecimal bietet die praktische Methode setScale(…) an, mit der sich die Anzahl der Nachkommastellen setzen lässt. Das ist zum Runden sehr gut. In unserem Beispiel sollen 45 Liter Benzin zu 1,399 bezahlt werden:

BigDecimal petrol = new BigDecimal( "1.399" ).multiply( new BigDecimal(45) );
 System.out.println( petrol.setScale( 3, BigDecimal.ROUND_HALF_UP ) );
 System.out.println( petrol.setScale( 2, BigDecimal.ROUND_HALF_UP ) );

Die Ausgaben sind 62955 und 6296.

class java.math.BigDecimal
extends Number
implements Comparable<BigDecimal>

  • BigDecimal divide(BigDecimal divisor,RoundingMode roundingMode)
  • BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
  • BigDecimal setScale(int newScale,RoundingMode roundingMode)

Erweiterung von @Deprecated

In Java 9 wurde der Annotationstyp @Deprecated um zwei Eigenschaften erweitert:

  • String since() default „“. Dokumentiert die Version, seit der das Element veraltet ist
  • boolean forRemoval() default false. Zeigt an, dass das Element in Zukunft gelöscht werden soll.

Beispiel

Ein Beispiel aus der Thread-Klasse:

@Deprecated(since="1.2", forRemoval=true)

public final synchronized void stop(Throwable obj) {

  throw new UnsupportedOperationException();

}

Klassenladername und getPlatformClassLoader

Da es mehrere Klassenlader gibt und diese für das Debuggen leicht zu unterscheiden sein sollen, können Sie Namen tragen. Sie lassen sich im Konstruktor vom URLClassLoader setzen. Die Basisklasse ClassLoader bietet eine Methode getName() zum Erfragen des Namens.

ClassLoader bootstrapLoader = ClassLoader.getSystemClassLoader().getParent();

System.out.println( bootstrapLoader.getName() );                     // platform

System.out.println( ClassLoader.getPlatformClassLoader().getName() );// platform

System.out.println( T.class.getClassLoader().getName() );            // app

Die Methode getName() ist neu in Java 9 sowie auch die ClassLoader-Methode getPlatformClassLoader().

Der Paketname mit Class#getPackageName()

Seit Java 9 gibt es die Methode getPackageName(), die ausschließlich den Paketnamen liefert. Das funktioniert auch auf Class-Objekten von Arrays und primitiven Datentypen.

Beispiel: Alle folgenden Ausgaben sind java.lang.

System.out.println( System.class.getPackageName() );       // java.lang

System.out.println( Thread.State.class.getPackageName() ); // java.lang

System.out.println( byte.class.getPackageName() );         // java.lang

System.out.println( byte[].class.getPackageName() );       // java.lang

 

Beispiel: Finde heraus, ob zwei Klassen im gleichen Paket liegen:

static boolean isSameClassPackage( Class<?> c1, Class<?> c2 ) {

  return   ( c1.getClassLoader() == c2.getClassLoader() )

        && ( c1.getPackageName().equals( c2.getPackageName() ) );

}

Private Attribute auslesen/ändern und der Typ AccessibleObject

Wenn es der Sicherheitsmanager zulässt, kann ein Programm auch private- oder protected-Attribute ändern und Methoden/Konstruktoren eingeschränkter Sichtbarkeit aufrufen. Die Schlüsselfigur in diesem Spiel ist die Oberklasse java.lang.reflect.AccessibleObject, die den Klassen Field und Excecutable (und damit Constructor und Method) die Methode setAccessible(boolean) vererbt. Ist das Argument true und lässt der Sicherheitsmanager die Operation zu, lässt sich auf jedes Element (also Konstruktor, Attribut oder Methode) ungleich der Sichtbarkeitseinstellungen zugreifen:

public class ReadPrivate {
 
   @SuppressWarnings( "unused" )
   private String privateKey = "Schnuppelhase";
 
   public static void main( String[] args ) throws Exception {
     ReadPrivate key = new ReadPrivate();
     Class<?> c = key.getClass();
     java.lang.reflect.Field field = c.getDeclaredField( "privateKey" );
     field.setAccessible( true );
     System.out.println( field.get(key) ); // Schnuppelhase
     field.set( key, "Schnuckibutzihasidrachelchen");
     System.out.println( field.get(key) ); // Schnuckibutzihasidrachelchen
   }
 }

class java.lang.reflect.AccessibleObject
implements AnnotatedElement

  • void setAccessible(boolean flag)
    Nachfolgede Abfragen sollten die Java-Sichtbarkeiten ignorieren. Falls das nicht erlaubt ist, gibt es eine InaccessibleObjectException.
  • final boolean trySetAccessible()
    Wie setAccessible(true), nur löst die Methode beim nicht gewährten Zugriff keine InaccessibleObjectException aus, sondern liefert false. Neu in Java 9.

Warnung: Mit dieser Technik lässt sich viel Unsinn anrichten. Es gibt Dinge, die in der Laufzeitumgebung einfach fest sein müssen. Dazu zählen einmal angelegte Strings oder Wrapper-Objekte. Strings sind immutable, weil sie intern in einem privaten char-Feld gehalten werden und es keine Modifikationsmöglichkeiten gibt. Auch Wrapper-Objekte sind, wenn sie einmal mit einem Konstruktor angelegt wurden, nicht über öffentliche Methoden veränderbar. Sie anschließend per Reflection zu modifizieren, bringt große Unordnung, insbesondere bei den gecachten Integer/Long-Wrapper-Objekten, die die statischen valueOf(…)-Methoden liefern.

Schwache Referenzen und Cleaner

Die Referenzen auf Objekte, die wir im Alltag um uns herum haben, heißen starke Referenzen, weil die automatische Speicherbereinigung niemals ein benutztes Objekt freigeben würde. Neben den starken Referenzen gibt es jedoch auch schwache Referenzen, die es dem GC erlaubt, die Objekte zu entfernen. Was erst einmal verrückt klingt wird dann interessant, wenn es um die Implementierung von Caching-Datenstrukturen geht; ist das Objekt im Cache, ist das schön und der Zugriff schnell – ist das Objekt nicht im Cache, dann ist das auch in Ordnung, und der Zugriff dauert etwas länger. Wir können schwache Referenzen also gut verwenden, um im Cache liegende Objekte aufzubauen, die die automatische Speicherbereinigung wegräumen darf, wenn es im Speicher knapp wird.

Schwache Referenzen interagieren also in einer einfachen Weise mit der automatischen Speicherbereinigung und dafür gibt es im java.base-Modul im Paket java.lang.ref ein paar Typen. Am Wichtigsten sind die Behälter (engl. reference object gennant), die wie ein Optional ein Objekt referenzieren, das aber plötzlich verschwunden sein kann:

  • SoftReference<T>. Ein Behälter für softly reachable Objekte. Die Objekte werden vom GC spät freigegeben, wenn es kurz vor einem OutOutMemoryError
  • WeakReference<T>. Ein Behälter für weakly reachable Objekte. Die Objekte werden vom GC schon relativ früh beim ersten GC freigegeben.
  • PhantomReference<T>. Ein Behälter, der immer leer ist, aber dazu dient mitzubekommen, wenn der GC sich von einem Objekt trennt.
  • Reference<T>. Abstakte Basisklasse von PhantomReference, SoftReference, WeakReference.

Die Behälter selbst werden vom GC nicht entfernt, sodass eine ReferenceQueue<T> ein Abfragen erlaubt, um festzustellen, welche Reference-Behälter leer sind und z. B. aus einer Datenstruktur entfernt werden können – leere Behälter sind nutzlos und können nicht wieder recycelt werden.

Ein neuer Typ ab Java 9 im Paket ist Cleaner, der eine Alternative zur Finalizierung ist. Beim Cleaner lässt sich eine Operation (vom Typ Cleaner.Cleanable) anmelden, die immer dann aufgerufen wird, wenn die automatische Speicherbereinigung zuschlägt und das Objekt nicht mehr erreichbar ist. Intern greift die Klasse auf PhantomReference zurück.

Beispiel: Lege einen Punkt an, registriere einen Cleaner und rege danach den GC an. Eine Konsolenausgabe „Punkt ist weg!“ ist wahrscheinlich:

Point p = new Point( 1, 2 );

Cleaner.create().register( p, () -> System.out.println( "Punkt ist weg!" ) );

p = null;

byte[] bytes = new byte[ (int) Runtime.getRuntime().freeMemory() ];

Auf keinen Fall darf die Aufräumoperation p wieder referenzieren.

 

Deserialisierung absichern mit einem ObjectInputFilter

Alles über readObject() einzulesen was dem ObjectInputStream gegeben wird ist ein Sicherheitsrisiko. Ab Java 8 Update 121[1] – und folglich auch Java 9 – lässt sich ein Filter bei der Deserialisierung setzen, der mit Informationen über die

  • deserialisierte Klasse,
  • Anzahl Objektreferenzen,
  • Array-Längen,
  • aktuelle Tiefe im Objektgraph und
  • Größe des serialisierten Objekts in Bytes

versorgt wird, und sein OK geben muss wenn es zu keiner InvalidClassException während readObject() kommen soll.

Ein Filter ist vom Typ ObjectInputFilter und wird auf einem ObjectInputStream über setObjectInputFilter(ObjectInputFilter filter) gesetzt; den aktuell gesetzten Filter liefert getObjectInputFilter(). Die Deklaration der funktionalen Schnittstelle ObjectInputFilter enthält eine Methode checkInput(…), zwei Aufzählungstypen und eine innere Klasse.

@FunctionalInterface

interface ObjectInputFilter {




  Status checkInput( FilterInfo filterInfo );




  interface FilterInfo {

    Class<?> serialClass();

    long arrayLength();

    long depth();

    long references();

    long streamBytes();

  }




  enum Status {

    UNDECIDED, ALLOWED, REJECTED;

  }




  final class Config {

    public static ObjectInputFilter getSerialFilter() {…}

    public static void setSerialFilter(ObjectInputFilter filter) {…}

    public static ObjectInputFilter createFilter(String pattern) {…}

  }

}

Programmieren wir ein kleines Beispiel mit dieser API. Beginnen wir damit, einen java.awt.Point, ein byte-Array, eine Swing-Komponente und ein leeres org.w3c.dom.Document in ein ByteArrayOutputStream zu serialisieren.

ByteArrayOutputStream bos = new ByteArrayOutputStream();

try ( ObjectOutputStream oos = new ObjectOutputStream( bos ) ) {

  oos.writeObject( new Point(10, 20) );

  oos.writeObject( new byte[1000] );

  oos.writeObject( new JLabel() );

  oos.writeObject( DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() );

}

Im nächsten Schritt können wir einen ObjectInputStream auf das erzeugte Byte-Array anwenden und Deserialisieren. Zusätzlich setzen wir einen Filter, dessen Implementierung aus drei Teilen besteht:

  1. Testen, ob ein prozessweiter Filter existiert, der eine Entscheidung schon gefallen hat,
  2. Loggen aller Informationen aus dem FilterInfo-Objekt,
  3. Alle serialisierten XML-Dokumente blockieren.

Zum Code:

ObjectInputStream ois = new ObjectInputStream(

                          new ByteArrayInputStream( bos.toByteArray() ) );

ois.setObjectInputFilter( filterInfo -> {




  ObjectInputFilter serialFilter = ObjectInputFilter.Config.getSerialFilter();

  if ( serialFilter != null ) {

    ObjectInputFilter.Status status = serialFilter.checkInput( filterInfo );

    if ( status != ObjectInputFilter.Status.UNDECIDED )

      return status;

  }




  System.out.printf( "depth=%s, references=%s, serialClass=%s, arrayLength=%s, streamBytes=%s%n",

                     filterInfo.depth(), filterInfo.references(), 

                     filterInfo.serialClass(), filterInfo.arrayLength(), filterInfo.streamBytes() );




  if ( Document.class.isAssignableFrom( filterInfo.serialClass() ) )

    return Status.REJECTED;




  return Status.ALLOWED;

} );

Als letzte versuchen wir die vier geschriebenen Objekte einzulesen.

for ( int i = 0; i < 4; i++ )

  System.out.printf( "%d. gelesenes Objekt: %s%n%n", i + 1, ois.readObject() );

Beim letzten Versuch kommt es zu einem Fehler, wie die Ausgabe zeigt:

depth=1, references=1, serialClass=class java.awt.Point, arrayLength=-1, streamBytes=43
  1. gelesenes Objekt: java.awt.Point[x=10,y=20]
depth=1, references=2, serialClass=class [B, arrayLength=-1, streamBytes=70

depth=1, references=2, serialClass=class [B, arrayLength=1000, streamBytes=74
  1. gelesenes Objekt: [B@45dd4eda
depth=1, references=3, serialClass=null, arrayLength=-1, streamBytes=1311

Exception in thread "main" java.io.InvalidClassException: filter status: REJECTED

       at java.base/java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:1276)

…

       ... 10 more

Die API-Dokumentation der Typen erklärten weitere paar Details, z. B. was die Bedeutung von Status.UNDECIDED ist, und wie die System-Property jdk.serialFilter mit einer textuellen Filter-Beschreibung belegt werden kann.

[1]       http://www.oracle.com/technetwork/java/javase/8u121-relnotes-3315208.html

Die Klassen OutputStream und InputStream

Die abstrakte Basisklasse OutputStream

Wenn wir uns den OutputStream anschauen, dann sehen wir auf den ersten Blick, dass hier alle wesentlichen Operationen rund um das Schreiben versammelt sind. Der Clou bei allen Datenströmen ist, dass spezielle Unterklassen wissen, wie sie genau die vorgeschriebene Funktionalität implementieren. Das heißt, dass ein konkreter Datenstrom, der in Dateien oder in eine Netzwerkverbindung schreibt, weiß, wie Bytes in Dateien oder ins Netzwerk kommen. Und an der Stelle ist Java mit seiner Plattformunabhängigkeit am Ende, denn auf einer so tiefen Ebene können nur native Methoden die Bytes schreiben.

abstract class java.io.OutputStream
implements Closeable, Flushable

  • abstractvoidwrite(intb)throwsIOException
    Schreibt ein einzelnes Byte in den Datenstrom.
  • voidwrite(byte[]b)throwsIOException
    Schreibt die Bytes aus dem Array in den Strom.
  • voidwrite(byte[]b,intoff,intlen)throwsIOException
    Schreibt Teile des Byte-Feldes, nämlich len Byte ab der Position off, in den Ausgabestrom.
  • voidclose()throwsIOException
    Schließt den Datenstrom. Einzige Methode aus Closeable.
  • voidflush()throwsIOException
    Schreibt noch im Puffer gehaltene Daten. Einzige Methode aus der Schnittstelle Flushable.

Die IOException ist keine RuntimeException und muss behandelt werden.

Beispiel: Die Klasse ByteArrayOutputStream ist eine Unterklasse von OutputStream und speichert intern alle Daten in einem byte-Array. Schreiben wir ein paar Daten mit den drei gegeben Methoden hinein:

byte[] bytes = { 'O', 'N', 'A', 'L', 'D' };

//                0    1    2    3    4

ByteArrayOutputStream out = new ByteArrayOutputStream();

try {

  out.write( 'D' );          // schreibe D

  out.write( bytes );        // schreibe ONALD

  out.write( bytes, 1, 2 );  // schreibe NA

  System.out.println( out.toString( StandardCharsets.ISO_8859_1.name() )  );

}

catch ( IOException e ) {

  e.printStackTrace();

}

Über konkrete und abstrakte Methoden *

Zwei Eigenschaften lassen sich an den Methoden vom OutputStream ablesen: zum einen, dass nur Bytes geschrieben werden, und zum anderen, dass nicht wirklich alle Methoden abstract sind und von Unterklassen für konkrete Ausgabeströme überschrieben werden müssen. Nur write(int) ist abstrakt und elementar. Das ist trickreich, denn tatsächlich lassen sich die Methoden, die ein Byte-Array schreiben, auf die Methode abbilden, die ein einzelnes Byte schreibt. Wir werfen einen Blick in den Quellcode der Bibliothek:

java/lang/OutputStream,java, Ausschnitt

public abstract void write(int b) throws IOException;
 
 public void write(byte[] b) throws IOException {
   write(b, 0, b.length);
 }
 
 public void write(byte b[], int off, int len) throws IOException {
   if (b == null) {
     throw new NullPointerException();
   } else if ((off < 0) || (off > b.length) || (len < 0) ||
              ((off + len) > b.length) || ((off + len) < 0)) {
     throw new IndexOutOfBoundsException();
   } else if (len == 0) {
     return;
   }
   for (int i = 0 ; i < len ; i++) {
     write(b[off + i]);
   }
 }

An beiden Implementierungen ist zu erkennen, dass sie die Arbeit sehr bequem an andere Methoden verschieben. Doch diese Implementierung ist nicht optimal! Stellen wir uns vor, ein Dateiausgabestrom überschreibt nur die eine abstrakte Methode, die nötig ist. Und nehmen wir weiterhin an, dass unser Programm nun immer ganze Byte-Felder schreibt, etwa eine 5-MiB-Datei, die im Speicher steht. Dann werden für jedes Byte im Byte-Array in einer Schleife alle Bytes der Reihe nach an eine vermutlich native Methode übergeben. Wenn es so implementiert wäre, könnten wir die Geschwindigkeit des Mediums überhaupt nicht nutzen, zumal jedes Dateisystem Funktionen bereitstellt, mit denen sich ganze Blöcke übertragen lassen. Glück-licherweise sieht die Implementierung nicht so aus, da wir in dem Modell vergessen haben, dass die Unterklasse zwar die abstrakte Methode implementieren muss, aber immer noch andere Methoden überschreiben kann. Ein Blick auf die Unterklasse FileOutputStream bestätigt dies.

Hinweis: Ruft eine Oberklasse eine abstrakte Methode auf, die spätger die Unterklasse implementiert, ist das ein Entwurfsmuster mit dem Namen Schablonenmuster oder auf Englisch template pattern.

Gleichzeitig stellt sich die Frage, wie ein OutputStream, der die Eigenschaften für alle erdenklichen Ausgabeströme vorschreibt, wissen kann, wie ein spezieller Ausgabestrom etwa mit close() geschlossen wird oder seine gepufferten Bytes mit flush() schreibt – die Methoden müssten doch auch abstrakt sein! Das weiß OutputStream natürlich nicht, aber die Entwickler haben sich dazu entschlossen, eine leere Implementierung anzugeben. Der Vorteil besteht darin, dass Unterklassen nicht verpflichtet werden, immer die Methoden zu überschreiben, auch wenn sie sie gar nicht nutzen wollen.

Ein Datenschlucker *

Damit wir sehen können, wie alle Unterklassen prinzipiell mit OutputStream umgehen, wollen wir eine Klasse entwerfen, die alle ihre gesendeten Daten verwirft. Die Klasse ist mit dem Unix-Device /dev/null vergleichbar. Die Implementierung muss lediglich die abstrakte Methode write(int) überschreiben.

 public final class NullOutputStream extends java.io.OutputStream {

  @Override public void write( int b ) { /* Empty */ }
 }

Da close() und flush() ohnehin schon mit einem leeren Block implementiert sind, brauchen wir sie nicht noch einmal zu überschreiben.

Die abstrakte Basisklasse InputStream

Das Gegenstück zu OutputStream ist InputStream; jeder binäre Eingabestrom wird durch die abstrakte Klasse InputStream repräsentiert. Die Konsoleneingabe System.in ist vom Typ InputStream. Die Klasse bietet mehrere readXXX(…)-Methoden und ist auch ein wenig komplexer als OutputStream.

abstract class java.io.InputStream
implements Closeable

  • intavailable()throwsIOException
    Gibt die Anzahl der verfügbaren Zeichen im Datenstrom zurück, die sofort ohne Blockierung gelesen werden können.
  • abstractintread()throwsIOException
    Liest ein Byte aus dem Datenstrom und liefert ihn zurück. Die Rückgabe ist -1, wenn der Datenstrom keine Daten mehr liefern kann. Der Rückgabetyp ist int, weil -1 (0xFFFFFFFF) das Ende des Datenstroms anzeigt, und ein -1 als byte (das wäre 0xFF) nicht von einem normalen Datum unterschieden werden könnte.
  • intread(byte[]b)throwsIOException
    Liest bis zu length Bytes aus dem Datenstrom und setzt sie in das Array b. Die tatsächliche Länge der gelesenen Bytes wird zurückgegeben und muss nicht b.length sein, es können auch weniger Bytes gelesen werden. In der OutputStream einfach als return read(b, 0, b.length); implementiert.
  • intread(byte[]b,intoff,intlen)throwsIOException
    Liest den Datenstrom aus und setzt die Daten in das Byte-Array b, an der Stelle off Zudem begrenzt len die maximale Anzahl der zu lesenden Bytes. Intern ruft die Methode zunächst read() auf und wenn es zu einer Ausnahme kommt, endet auch damit unsere Methode mit einer Ausnahme. Es folgenen wiederholten Aufrufe von read(), die dann enden, wenn read() die Rückgabe -1 liefert. Falls es zu einer Ausnahme kommt, wird diese aufgefangen und nicht gemeldet.
  • intreadNBytes(byte[]b,intoff,intlen)throwsIOException
    Versucht len viele Bytes aus dem Datenstrom zu lesen und in das Byte-Array zu setzen. Im Gegensatz zu read(byte[], int, int) übernimmt readNBytes(…) mehrere Anläufe len viele Daten zu beziehen. Dabei greift es auf read(byte[], int, int) zurück. Neue Methode in Java 9.
  • byte[] readAllBytes() throws IOException
    Liest alle verbleibenden Daten aus den Datenstrom und liefert ein Array mit diesen Bytes aus Rückgabe. Neue Methode in Java 9.
  • long transferTo(OutputStream out) throws IOException
    Liest alle Bytes aus dem Datenstrom aus und schreibt sie in out. Wenn es zu einer Ausnahme kommt, wird empfohlen, die Datenströme beide zu schließen. Neue Methode in Java 9.
  • longskip(longn)throwsIOException
    Überspringt eine Anzahl von Zeichen. Die Rückgabe gibt die tatsächlich gesprungenen Bytes zurück, was nicht mit n identisch sein muss.
  • booleanmarkSupported()
    Gibt einen Wahrheitswert zurück, der besagt, ob der Datenstrom das Merken und Zurücksetzen von Positionen gestattet. Diese Markierung ist ein Zeiger, der auf bestimmte Stellen in der Eingabedatei zeigen kann.
  • voidmark(intreadlimit)
    Merkt sich eine Position im Datenstrom.
  • voidreset()throwsIOException
    Springt wieder zu der Position zurück, die mit mark() gesetzt wurde.
  • voidclose()throwsIOException
    Schließt den Datenstrom. Operation aus der Schnittstelle Closeable.

Auffällig ist, dass bis auf mark(int) und markSupported() alle Methoden im Fehlerfall eine IOException auslösen.

Hinweis: available() liefert die Anzahl der Bytes, die ohne Blockierung gelesen werden können. („Blockieren“ bedeutet, dass die Methode nicht sofort zurückkehrt, sondern erst wartet, bis neue Daten vorhanden sind.) Die Rückgabe von available() sagt nichts darüber aus, wie viele Zeichen der InputStream insgesamt hergibt. Während aber bei FileInputStream die Methode available() üblicherweise doch die Dateilänge liefert, ist dies bei den Netzwerk-Streams im Allgemeinen nicht der Fall.

Google Guava steigt (endlich) auf Java 8 um

Die News unter https://github.com/google/guava/wiki/Release21:

Significant API additions and changes

common.base

  • Function, Predicate and Supplier: changed to extend the new java.util.functioninterfaces with the same names.
  • Optional: added toJavaUtil and fromJavaUtil methods for easy conversion between Guava’s Optional and java.util.Optional.
  • Objects: removed deprecated firstNonNull and toStringHelper methods (both found on MoreObjects since Guava 18.0).

common.cache

New default methods on ConcurrentMap that were added in Java 8 are now implemented and safe to use for Cache.asMap() views.

common.collect

Many APIs in collect now have better implementations of many of the default methods added to Collection and Map types in Java 8.

New classes

  • Comparators: With the addition of many useful methods to the Comparator type in Java 8, Ordering now provides little benefit. Comparators is a new location for methods on Orderingthat still don’t have a good equivalent in the JDK.
  • Streams: Utility class for working with java.util.stream.Stream. Includes methods for creating streams (such as stream(Iterable), stream(Optional) and concat(Stream...)) and methods that do things with streams (such as findLast(Stream)).
  • MoreCollectors: Factories for java.util.stream.Collector objects; note that Collectors for Guava’s collection types are generally found on those types themselves rather than here.
  • Interners.InternerBuilder: Builder for Interner instances, with options similar to those found on MapMaker. Created with Interners.newBuilder().

Removed classes

  • MapConstraint and MapConstraints: deprecated since 19.0.

Changes

  • FluentIterable: added stream() method.
  • ForwardingBlockingDeque: deprecated; moved to util.concurrent.
  • Immutable* types: added methods to all named toImmutable[Type]() (e.g. ImmutableList.toImmutableList()) which return a Collector for collecting a Stream into an immutable collection/map object. As with most methods that create Collectors, these are generally intended to be used as static imports.
  • Multimap: added forEach(BiConsumer) method.
  • Multimaps: added toMultimap and flatteningToMultimap methods returning Collectorobjects that collect to a Multimap.
  • Multiset: added forEachEntry(ObjIntConsumer) method.
  • Maps: added toImmutableEnumMap methods returning Collector objects that collect to an ImmutableMap with enum keys.
  • Sets: added toImmutableEnumSet method returning a Collector that collects to an ImmutableSet of enums.
  • Tables: added toTable methods returning Collector objects that collect to a Table.
  • RangeSet: added default addAll(Iterable<Range>), removeAll(Iterable<Range>) and enclosesAll(Iterable<Range>) methods.
  • ImmutableRangeSet: added copyOf(Iterable<Range>), unionOf(Iterable<Range>), union(RangeSet), intersection(RangeSet) and difference(RangeSet) methods.
  • TreeRangeSet: added create(Iterable<Range>) method.
  • A number of rarely-used methods on concrete implementations of Guava collection types that aren’t present on the interface they implement have been deprecated. These include: ArrayListMultimap.trimToSize(), TreeMultimap.keyComparator(), and TreeBasedTable.row/columnComparator().

common.io

  • MoreFiles: New class which contains methods similar to those in Files, but for use with java.nio.file.Path objects.
  • This includes deleteRecursively and deleteDirectoryContents methods which are secure against race conditions that Java previously had no way of dealing with provided that the FileSystem supports SecureDirectoryStream (modern Linux versions do; Windows [NTFS at least] does not). For security, these will throw an exception if SecureDirectoryStream is not supported unless RecursiveDeleteOption.ALLOW_INSECURE is passed when calling the method.

common.primitives

  • Most classes: added constrainToRange([type] value, [type] min, [type] max) methods which constrain the given value to the closed range defined by the min and max values. They return the value itself if it’s within the range, the min if it’s below the range and the max if it’s above the range.

common.util.concurrent

  • ForwardingBlockingDeque: added; moved from common.collect because BlockingDeque is a concurrent type rather than a standard collection (it’s defined in java.util.concurrent).
  • AtomicLongMap: added a number of methods such as accumulateAndGet(K, LongBinaryOperator) that take advantage of new Java functional types.
  • Monitor: added newGuard(BooleanSupplier).
  • MoreExecutors: removed sameThreadExecutor(); deprecated since 18.0 in favor of directExecutor() (preferable) or newDirectExecutorService().

JDK 9 ist Feature Complete

So schreibt Mark Reinhold unter http://mail.openjdk.java.net/pipermail/jdk9-dev/2017-January/005505.html:

We achieved the Feature Extension Complete milestone [1] in late
December.  All JEPs and small enhancements granted extensions [2] have
been integrated into the JDK 9 master forest.  Thanks to everyone for
all your hard work leading up to this milestone!

We're now in the first phase of the rampdown process, in which we aim to
fix the bugs that need to be fixed and understand why we're not going to
fix some bugs that perhaps ought to be fixed.  We'll use the process that
I previously proposed [3], which is now also documented under the JDK 9
Project page [4][5].

The overall feature set is, at this point, frozen.  It's highly unlikely
that any further JEPs will be targeted to the release.

Small enhancements to new features will be considered, but the bar is
now much higher.  Please request approval for such enhancements via the
existing FC-extension process [2].  Low-risk enhancements that add small
bits of missing functionality or improve usability may be approved,
especially when justified by developer feedback.  Enhancements that add
significant new functionality will require very strong justification.
Enhancements to tests or documentation do not require advance approval.

In eigener Sache: BINARIUM eröffnet

Neben meiner Tätigkeit als Java-Autor und Trainer beschäftige ich mich seit fünf Jahren mit dem Aufbau eines Museums für Heimcomputer und Videospielkonsolen. Angefangen habe ich im April 2011 mit dem Aufbau der Sammlung. Heute zählt die Sammlung etwa 800 relevante Objekte.

Nachdem die Sammlung eine hinreichende Größe erreichte, erwarb ich im Juli 2014 eine 2400 qm-Immobilie der ehemaligen Ruhrkohle AG und baute es zu einen Museum, Café/Bistro und Schulungsräumen um. tutego führt die öffentlichen Java-Trainings schon seit über einem Jahr durch, das Gastro NETZWERK ist seit Oktober geöffnet. Mit etwa einem Jahr Verspätung kann es nun auch mit dem Museum im EG losgehen.

Am 8.12.2016 eröffnet das BINARIUM, das Deutsche Museum der digitalen Kultur, in Dortmund Huckarde das Erdgeschoss. Die umfassende Sammlung bietet einen Überblick über die Videospielkonsolen und Geschichte der letzten 40 Jahre. Mehr als 200 Exponate und 20 Spielstationen erwarten euch.

2017 wird die Ausstellung mit persönlichen Computern/Heimcomputern im OG eröffnet.

PlantUML, Styling von Diagrammen

PlantUML ist ein Open Source Projekt, zum Erstellen von UML-Diagrammen in einer textuellen Sprache. Zum Beispiel:

Bob->Alice : hello

Die Heimat des Projektes ist http://plantuml.com/.

Man beschreibt sein grafisches Projekt in dieser Sprache und die Software erzeugt dann das Diagramm. Allerdings ist der Standardstyle weit vom „quasi“-Standard entfernt. Um aber auch User in Genuss dieser Software kommen zu lassen, die grafisch weniger bunte Diagramme vorziehen, bedarf es einiger Anpassungen.

Im Folgenden ein paar Anpassungen, die mit PlantUML ein Diagramm erzeugen, das mehr dem „quasi“ Standard entspricht:

skinparam monochrome true

Entfernt die Farben aus dem Diagramm

skinparam shadowing false

Entfernt den Schlagschatten

skinparam classAttributeIconSize 0

Entfernt die Custom-Icons vor Klassenattributen und Methoden (Nur Klassendiagramm)

skinparam DefaultFontName arial

Legt die Standard-Schriftart fest (Der Name ist hier Systemabhängig)

skinparam DefaultFontSize 12

Legt die Standard-Schriftgröße in Pixeln fest.

skinparam ClassFontStyle bold

Ändert die Schrift im Klassenkopf auf Fettdruck (Nur Klassendiagramm)

hide empty fields

Versteckt leere Felder zB.wenn eine Klasse keine Attribute hat.

hide circle

Versteckt das Icon vor den Klassennamen
Der Effekt:

PlantUML Standard
PlantUML Standard

Anzeige parametrisiert:

PlantUML mit Parametern
PlantUML mit Parametern

Zur Verwendung genannter Befehle

Um die Parameter einzusetzen, genügt es sie in dem Körper des UML-Diagrammtextes, als erstes aufzuführen z. B.:

@startuml
skinparam monochrome true
skinparam shadowing false
skinparam classAttributeIconSize 0
skinparam DefaultFontName arial
skinparam DefaultFontSize 12
skinparam ClassFontStyle bold
hide empty fields
hide circle
...
@enduml

Des Weiteren ist es möglich, wenn man viele Diagramme hat, die man alle gleich gestalten möchte, die Parameter einmalig in einer separaten Datei anzulegen, z. B. style.iuml und diese dann per !include einzubinden. In dieser Datei wird auf @startuml und @enduml verzichtet. Zur Verdeutlichung:

Inhalt der style.iuml:

skinparam monochrome true
skinparam shadowing false
skinparam classAttributeIconSize 0
skinparam DefaultFontName arial
skinparam DefaultFontSize 12
skinparam ClassFontStyle bold
hide empty fields
hide circle

Inhalt einer möglichen UML-Datei

@startuml
!include style.iuml
...
@enduml

Autor: André Fischer

HTML-Datei in eine PDF-Datei mit java-wkhtmltopdf-wrapper

Lösungen, um aus Java heraus eine HTML-Datei eine PDF-Datei zu generieren gibt es einige, allerdings ist die Qualität nicht immer gut. Unter Zuhilfenahme der freien Software wkhtmltopdf auf der Basis der WebKit Rendering Engine gibt es mitunter bessere Ergebnisse. Für wkhtmltopdf gibt es mit java-wkhtmltopdf-wrapper von Jhonny Mertz einen passendem quelloffenen Java-Wrapper.

wkhtmltopdf

Wkhtmltopdf ist eine Open Source Applikation die Headless (ohne jegliche grafische Oberfläche) eine HTML-Datei rendert und als PDF-Datei speichert. Gesteuert wird das Programm über die Kommandozeile im folgenden Format:

$ wkhtmltopdf <<Quell-HTML>> <<Ziel-PDF>>

ein Beispiel zum besseren Verständnis:

$ wkhtmltopdf www.google.com google.pdf

Um erfolgreich aufgerufen zu werden, muss entweder die Kommandozeile im Installationsverzeichnis von wkhtmltopdf befinden oder wkhtmltopdf in den Umgebungsvariablen (PATH) hinzugefügt werden, andernfalls kann die Anwendung nicht gefunden werden.

Des weiteren kann der Applikation eine Vielzahl von Parametern übergeben werden, um das Ergebnis den eigenen Bedürfnissen anzupassen, z. B. mit dem Parameter -s kann man die Größe der PDF-Datei festlegen. Der Standard liegt hier bei DIN A4.

Ein Beispiel für DIN A3 Format:

$ wkhtmltopdf www.google.com google.pdf -s “A3“

Dies ist natürlich nur ein Bruchteil der möglichen Parameter, eine vollständige Liste in Englisch finden Sie hier:

Global Options:

    --collate                       Collate when printing multiple copies
                                      (default)
      --no-collate                    Do not collate when printing multiple
                                      copies
      --cookie-jar <path>             Read and write cookies from and to the
                                      supplied cookie jar file
      --copies <number>               Number of copies to print into the pdf
                                      file (default 1)
  -d, --dpi <dpi>                     Change the dpi explicitly (this has no
                                      effect on X11 based systems)
  -H, --extended-help                 Display more extensive help, detailing
                                      less common command switches
  -g, --grayscale                     PDF will be generated in grayscale
  -h, --help                          Display help
      --htmldoc                       Output program html help
      --image-dpi <integer>           When embedding images scale them down to
                                      this dpi (default 600)
      --image-quality <integer>       When jpeg compressing images use this
                                      quality (default 94)
      --license                       Output license information and exit
  -l, --lowquality                    Generates lower quality pdf/ps. Useful to
                                      shrink the result document space
      --manpage                       Output program man page
  -B, --margin-bottom <unitreal>      Set the page bottom margin
  -L, --margin-left <unitreal>        Set the page left margin (default 10mm)
  -R, --margin-right <unitreal>       Set the page right margin (default 10mm)
  -T, --margin-top <unitreal>         Set the page top margin
  -O, --orientation <orientation>     Set orientation to Landscape or Portrait
                                      (default Portrait)
      --page-height <unitreal>        Page height
  -s, --page-size <Size>              Set paper size to: A4, Letter, etc.
                                      (default A4)
      --page-width <unitreal>         Page width
      --no-pdf-compression            Do not use lossless compression on pdf
                                      objects
  -q, --quiet                         Be less verbose
      --read-args-from-stdin          Read command line arguments from stdin
      --readme                        Output program readme
      --title <text>                  The title of the generated pdf file (The
                                      title of the first document is used if not
                                      specified)
  -V, --version                       Output version information and exit

Outline Options:
      --dump-default-toc-xsl          Dump the default TOC xsl style sheet to
                                      stdout
      --dump-outline <file>           Dump the outline to a file
      --outline                       Put an outline into the pdf (default)
      --no-outline                    Do not put an outline into the pdf
      --outline-depth <level>         Set the depth of the outline (default 4)

Page Options:
      --allow <path>                  Allow the file or files from the specified
                                      folder to be loaded (repeatable)
      --background                    Do print background (default)
      --no-background                 Do not print background
      --cache-dir <path>              Web cache directory
      --checkbox-checked-svg <path>   Use this SVG file when rendering checked
                                      checkboxes
      --checkbox-svg <path>           Use this SVG file when rendering unchecked
                                      checkboxes
      --cookie <name> <value>         Set an additional cookie (repeatable),
                                      value should be url encoded.
      --custom-header <name> <value>  Set an additional HTTP header (repeatable)
      --custom-header-propagation     Add HTTP headers specified by
                                      --custom-header for each resource request.
      --no-custom-header-propagation  Do not add HTTP headers specified by
                                      --custom-header for each resource request.
      --debug-javascript              Show javascript debugging output
      --no-debug-javascript           Do not show javascript debugging output
                                      (default)
      --default-header                Add a default header, with the name of the
                                      page to the left, and the page number to
                                      the right, this is short for:
                                      --header-left='[webpage]'
                                      --header-right='[page]/[toPage]' --top 2cm
                                      --header-line
      --encoding <encoding>           Set the default text encoding, for input
      --disable-external-links        Do not make links to remote web pages
      --enable-external-links         Make links to remote web pages (default)
      --disable-forms                 Do not turn HTML form fields into pdf form
                                      fields (default)
      --enable-forms                  Turn HTML form fields into pdf form fields
      --images                        Do load or print images (default)
      --no-images                     Do not load or print images
      --disable-internal-links        Do not make local links
      --enable-internal-links         Make local links (default)
  -n, --disable-javascript            Do not allow web pages to run javascript
      --enable-javascript             Do allow web pages to run javascript
                                      (default)
      --javascript-delay <msec>       Wait some milliseconds for javascript
                                      finish (default 200)
      --load-error-handling <handler> Specify how to handle pages that fail to
                                      load: abort, ignore or skip (default
                                      abort)
      --load-media-error-handling <handler> Specify how to handle media files
                                      that fail to load: abort, ignore or skip
                                      (default ignore)
      --disable-local-file-access     Do not allowed conversion of a local file
                                      to read in other local files, unless
                                      explicitly allowed with --allow
      --enable-local-file-access      Allowed conversion of a local file to read
                                      in other local files. (default)
      --minimum-font-size <int>       Minimum font size
      --exclude-from-outline          Do not include the page in the table of
                                      contents and outlines
      --include-in-outline            Include the page in the table of contents
                                      and outlines (default)
      --page-offset <offset>          Set the starting page number (default 0)
      --password <password>           HTTP Authentication password
      --disable-plugins               Disable installed plugins (default)
      --enable-plugins                Enable installed plugins (plugins will
                                      likely not work)
      --post <name> <value>           Add an additional post field (repeatable)
      --post-file <name> <path>       Post an additional file (repeatable)
      --print-media-type              Use print media-type instead of screen
      --no-print-media-type           Do not use print media-type instead of
                                      screen (default)
  -p, --proxy <proxy>                 Use a proxy
      --radiobutton-checked-svg <path> Use this SVG file when rendering checked
                                      radiobuttons
      --radiobutton-svg <path>        Use this SVG file when rendering unchecked
                                      radiobuttons
      --run-script <js>               Run this additional javascript after the
                                      page is done loading (repeatable)
      --disable-smart-shrinking       Disable the intelligent shrinking strategy
                                      used by WebKit that makes the pixel/dpi
                                      ratio none constant
      --enable-smart-shrinking        Enable the intelligent shrinking strategy
                                      used by WebKit that makes the pixel/dpi
                                      ratio none constant (default)
      --stop-slow-scripts             Stop slow running javascripts (default)
      --no-stop-slow-scripts          Do not Stop slow running javascripts
      --disable-toc-back-links        Do not link from section header to toc
                                      (default)
      --enable-toc-back-links         Link from section header to toc
      --user-style-sheet <url>        Specify a user style sheet, to load with
                                      every page
      --username <username>           HTTP Authentication username
      --viewport-size <>              Set viewport size if you have custom
                                      scrollbars or css attribute overflow to
                                      emulate window size
      --window-status <windowStatus>  Wait until window.status is equal to this
                                      string before rendering page
      --zoom <float>                  Use this zoom factor (default 1)

Headers And Footer Options:
      --footer-center <text>          Centered footer text
      --footer-font-name <name>       Set footer font name (default Arial)
      --footer-font-size <size>       Set footer font size (default 12)
      --footer-html <url>             Adds a html footer
      --footer-left <text>            Left aligned footer text
      --footer-line                   Display line above the footer
      --no-footer-line                Do not display line above the footer
                                      (default)
      --footer-right <text>           Right aligned footer text
      --footer-spacing <real>         Spacing between footer and content in mm
                                      (default 0)
      --header-center <text>          Centered header text
      --header-font-name <name>       Set header font name (default Arial)
      --header-font-size <size>       Set header font size (default 12)
      --header-html <url>             Adds a html header
      --header-left <text>            Left aligned header text
      --header-line                   Display line below the header
      --no-header-line                Do not display line below the header
                                      (default)
      --header-right <text>           Right aligned header text
      --header-spacing <real>         Spacing between header and content in mm
                                      (default 0)
      --replace <name> <value>        Replace [name] with value in header and
                                      footer (repeatable)

TOC Options:
      --disable-dotted-lines          Do not use dotted lines in the toc
      --toc-header-text <text>        The header text of the toc (default Table
                                      of Contents)
      --toc-level-indentation <width> For each level of headings in the toc
                                      indent by this length (default 1em)
      --disable-toc-links             Do not link from toc to sections
      --toc-text-size-shrink <real>   For each level of headings in the toc the
                                      font is scaled by this factor (default
                                      0.8)
      --xsl-style-sheet <file>        Use the supplied xsl style sheet for
                                      printing the table of content

Quelle: http://wkhtmltopdf.org/usage/wkhtmltopdf.txt

java-wkhtmltopdf-wrapper

Dank der Zuhilfenahme des wkhtmltopdf-wrappers, welchen ich in mein Projekt eingebunden habe, durch die Einbindung der Maven-Dependency, ist es nun recht mühelos mit wkhtmltopdf aus Java heraus zu arbeiten. Am einfachsten ist es, denke ich, an einem Codebeispiel mit Kommentaren zu erkennen:

import java.io.IOException;
import com.github.jhonnymertz.wkhtmltopdf.wrapper.Pdf;
import com.github.jhonnymertz.wkhtmltopdf.wrapper.configurations.WrapperConfig;
import com.github.jhonnymertz.wkhtmltopdf.wrapper.page.PageType;
import com.github.jhonnymertz.wkhtmltopdf.wrapper.params.Param;

public class WkhtmltopdfTest {
 public static void main( String[] args ) { 
  // Erzeugen eines neuen Pdf Objekts als Parameter wir eine WrapperConfig übergeben, die den Pfad der wkhtmltopdf.exe definiert.
  Pdf pdf = new Pdf(new WrapperConfig("C:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltopdf.exe"));
  //Hinzufügen einer test HTML-Datei und Festlegung auf den Typ File aus dem Enum PageType
  pdf.addPage( "C:\\test\\test.html", PageType.file );
  //Hinzufügen diverser Parameter wie Format, Orientierung und Randgröße
  pdf.addParam( new Param( "-s", "A6" ), new Param( "-O", "landscape" ),
                new Param( "-B", "0" ), new Param( "-R", "0" ),
                new Param( "-T", "0" ), new Param( "-L", "0" ) );
  try {
   //Speichern der PDF-Datei unter dem angebenen Pfad
   pdf.saveAs( "C:\\test\\test.pdf" );
  }
  catch ( IOException | InterruptedException e ) {
   e.printStackTrace();
  }
 }
}

Dieses Programm erzeugt aus einer HTML-Datei mit dem Namen “test.html“, welche im Ordner “C:\test\“ liegt, eine “test.pdf“ im selben Verzeichnis.

Autor: André Fischer