Und aha, IS24 nutzt Corba?
Snippet: Kalender ausdrucken, Teil 2: von-bis
Die erste Version meines Kalenderprogramms druckte ein Kalender für ein Jahr. In Anwendungen dürfte häufiger vorkommen, dass es ein Start- und Enddatum gibt, das auch über Jahresgrenzen liegt. Das macht dieses Programm:
public static class CalLine
{
public int year;
public int weekOfYear;
public int month = -1; // 0 <= month <= 11
public int[] day = { -1, -1, -1, -1, -1, -1, -1 };
}
public static List<CalLine> calenderOfTheYear( Date start, Date end ) { Calendar startCal = new GregorianCalendar(); startCal.setTime( start ); Calendar endCal = new GregorianCalendar(); endCal.setTime( end ); return calenderOfTheYear( startCal, endCal ); } public static List<CalLine> calenderOfTheYear( Calendar start, Calendar end ) { List<CalLine> lines = new ArrayList<>(); // Calender instances are mutable, so copy them Calendar startCal = (Calendar) start.clone(); Calendar endCal = (Calendar) end.clone(); // For start date: first go backwards to the beginning of the month // then find monday of this week while ( startCal.get( Calendar.DAY_OF_MONTH ) != 1 ) startCal.add( Calendar.DAY_OF_YEAR, -1 ); while ( startCal.get( Calendar.DAY_OF_WEEK ) != Calendar.MONDAY ) startCal.add( Calendar.DAY_OF_YEAR, -1 ); // For end date: go forwards and find end of month // then find sunday of this week while ( endCal.get( Calendar.DAY_OF_MONTH ) != startCal.getActualMaximum( Calendar.DAY_OF_MONTH ) ) endCal.add( Calendar.DAY_OF_YEAR, 1 ); while ( endCal.get( Calendar.DAY_OF_WEEK ) != Calendar.SUNDAY ) endCal.add( Calendar.DAY_OF_YEAR, 1 ); endCal.add( Calendar.DAY_OF_YEAR, 1 ); // add 1 to test with < not <= CalLine line = new CalLine(); while ( startCal.before( endCal ) ) { if ( line.year == 0 ) line.year = startCal.get( Calendar.YEAR ); if ( line.weekOfYear == 0 ) line.weekOfYear = startCal.get( Calendar.WEEK_OF_YEAR ); int dayOfMonth = startCal.get( Calendar.DAY_OF_MONTH ); int dayOfWeek = startCal.get( Calendar.DAY_OF_WEEK ); if ( dayOfMonth == 1 ) line.month = startCal.get( Calendar.MONTH ); line.day[dayOfWeek - 1] = dayOfMonth; if ( dayOfWeek == Calendar.SUNDAY ) { // Days are Sun, Mon, ..., Sat. Rearange to Mon, ..., Sun int first = line.day[ 0 ]; // This is faster then System.arraycopy() line.day[ 0 ] = line.day[ 1 ]; line.day[ 1 ] = line.day[ 2 ]; line.day[ 2 ] = line.day[ 3 ]; line.day[ 3 ] = line.day[ 4 ]; line.day[ 4 ] = line.day[ 5 ]; line.day[ 5 ] = line.day[ 6 ]; line.day[ 6 ] = first; lines.add( line ); line = new CalLine(); // it ends always with SUN, last line is not added } startCal.add( Calendar.DAY_OF_YEAR, 1 ); } return lines; }
Beispielaufruf:
List<CalLine> lines = DateUtils.calenderOfTheYear( new GregorianCalendar( 2011, Calendar.NOVEMBER, 12 ), new GregorianCalendar( 2012, Calendar.JANUARY, 22 ) ); String[] monthNames = { "Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez" }; System.out.println( "KW Mo Do Mi Do Fr Sa So" ); // to lazy for DateFormatSymbols here... for ( CalLine l : lines ) { String monthStr = (l.month == -1) ? " " : monthNames[ l.month ]; String s = String.format( "%2d %s %(2d %(2d %(2d %(2d %(2d %(2d %(2d", l.weekOfYear, monthStr, l.day[0], l.day[1], l.day[2], l.day[3], l.day[4], l.day[5], l.day[6] ).replace( "(1)", " " ); System.out.println( s ); }
Das führt zu
KW Mo Do Mi Do Fr Sa So 44 Nov 31 1 2 3 4 5 6 45 7 8 9 10 11 12 13 46 14 15 16 17 18 19 20 47 21 22 23 24 25 26 27 48 Dez 28 29 30 1 2 3 4 49 5 6 7 8 9 10 11 50 12 13 14 15 16 17 18 51 19 20 21 22 23 24 25 52 Jan 26 27 28 29 30 31 1 1 2 3 4 5 6 7 8 2 9 10 11 12 13 14 15 3 16 17 18 19 20 21 22 4 23 24 25 26 27 28 29 5 Feb 30 31 1 2 3 4 5
Thema der Woche: Externe Programme starten, URL Protokoll unter Windows registrieren
Schreibe ein Java-Programm, welches per Kommandozeile eine Bug-ID annimmt, und dann die entsprechende Seite unter http://bugs.sun.com/ aufruft. Beispiel: Zur Bug-ID 6787890 soll der Browser http://bugs.sun.com/view_bug.do?bug_id=6787890 ansteuern.
Lies http://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx
Kopiere folgendes in eine temporäre Datei, ersetzte dabei YourApp mit etwas Eigenem und passe den Pfad korrekt auf unser Bug-ID-Öffnen-Programm an. Das Protokoll kann zum Beispiel “sunbugid” sein.
REGEDIT4
[HKEY_CLASSES_ROOT\YourApp]
@=“URL:YourApp Protocol“
„URL Protocol“=““
[HKEY_CLASSES_ROOT\YourApp\DefaultIcon]
@=“\“C:\\Program Files\\YourApp\\YourApp.exe\““
[HKEY_CLASSES_ROOT\YourApp\shell]
[HKEY_CLASSES_ROOT\YourApp\shell\open]
[HKEY_CLASSES_ROOT\YourApp\shell\open\command]
@=“\“C:\\Program Files\\YourApp\\YourApp.exe\“ \“%1\“ \“%2\“ \“%3\“ \“%4\“ \“%5\“ \“%6\“ \“%7\“ \“%8\“ \“%9\““
(Quelle http://stackoverflow.com/questions/389204/how-do-i-create-my-own-url-protocol-e-g-so)
Führe die Datei mit regedit aus.
Steht dann auf einer Webseite die URL sunbugid:6787890 sollte bei der Aktivierung des Links das eigene Java-Programm ein neues Browserfenster mit der Bug-Beschreibung aufkommen.
Über eine Reihe von Werten laufen, Ist-Element-von-Test
Rechts vom Doppelpunkt lässt sich auf die Schnelle ein Feld aufbauen, über welches das erweiterte for dann laufen kann.
for ( int prime : new int[]{ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 } )
System.out.println( prime );
Das ist praktisch, um über eine feste Menge von Werten zu laufen. Das funktioniert auch für Objekte, etwa Strings:
for ( String name : new String[]{ "Krissy", "Morris", "Dan" } )
System.out.println( name );
Einige Programmierer verstecken die Objekterzeugung auch in einen Methodenaufruf:
for ( String name : Arrays.asList( "Krissy", "Morris", "Dan" ) )
System.out.println( name );
Arrays.asList(…) erzeugt kein Array als Rückgabe, sondern baut aus der variablen Argumentliste eine Sammlung auf, die von einem speziellen Typ Iterable ist – das kann auch die erweiterte for-Schleife ablaufen.
Unabhängig vom erweiterten for hat die Nutzung von Arrays.asList(…)noch einen anderen Vorteil, etwa bei ist-Element-von-Anfragen, etwa so:
if ( Arrays.asList( 1, 2, 3, 4, 5, 6, 7, 8, 10 ).contains( number ) )
…
java.net.ConnectException? Was tun?
Falls der Computer keinen direkten Internetzugriff hat, kommt es zu einem Timeout. Der übliche Fehler ist eine „java.net.ConnectException: Operation timed out“. Hier gilt zu prüfen, woher der Fehler kommt. Eine Fehlerquelle sind Proxies, die zwischen dem eigenen Rechner und dem Internet hängen. Proxy-Einstellungen können in Java gesetzt werden, wie im im Kapitel über Proxies in der Insel beschrieben. Eine gute Idee ist, mithilfe des Kommandozeilenprogramms telnet[1] die Erreichbarkeit eines Servers zu überprüfen. Es kann auch sein, dass die Verbindung grundsätzlich besteht, sie jedoch langsam ist, und Java aus Ungeduld aufgibt. Die Lösung ist dann, den Timeout hochzusetzen, etwa bei dem URLConnection-Objekt über setConnectTimeout(millis).
[1] Muss in einem aktuellen Windows erst aktiviert werden.
Snippet: Kalender des Jahres ausgeben
Angeregt durch den Compact Calendar (http://davidseah.com/compact-calendar/) wollte ich etwas ähnliches in meine Webanwendung einbauen, sodass das Ergebnis tabellenartig wie https://docs.google.com/spreadsheet/ccc?key=0AkyxK00VLnSidEl1SS1sZjZiVlpuRnJIY1JmUW9IRHc#gid=0 formatiert wird.
import java.text.DateFormatSymbols; import java.util.*; public class PrintCalender { public static class CalLine { int weekOfYear; int month = -1; // 0 <= month <= 11 int[] day = { -1, -1, -1, -1, -1, -1, -1 }; } public static List<CalLine> calenderOfTheYear( int year ) { Calendar cal = new GregorianCalendar( year, 1, 1 ); List<CalLine> lines = new ArrayList<>(); CalLine line = new CalLine(); for ( int dayOfYear = 1; dayOfYear <= cal.getActualMaximum( Calendar.DAY_OF_YEAR ); dayOfYear++ ) { cal.set( Calendar.DAY_OF_YEAR, dayOfYear ); line.weekOfYear = cal.get( Calendar.WEEK_OF_YEAR ); // Set several times, thats ok int dayOfMonth = cal.get( Calendar.DAY_OF_MONTH ); int dayOfWeek = cal.get( Calendar.DAY_OF_WEEK ); if ( dayOfMonth == 1 ) line.month = cal.get( Calendar.MONTH ); line.day[dayOfWeek - 1] = dayOfMonth; if ( dayOfWeek == Calendar.SUNDAY ) { lines.add( line ); line = new CalLine(); } } lines.add( line ); return lines; } public static void main( String[] args ) { List<CalLine> lines = calenderOfTheYear( 2012 ); String[] monthNames = new DateFormatSymbols( Locale.GERMANY ).getMonths(); System.out.println( "KW Mo Do Mi Do Fr Sa So" ); // to lazy for DateFormatSymbols here... for ( CalLine l : lines ) { String monthStr = (l.month == -1) ? " " : monthNames[ l.month ].substring( 0, 3 ); // Days are Sun, Mon, ..., Sat. Rearange to Mon, ..., Sun String s = String.format( "%2d %s %(2d %(2d %(2d %(2d %(2d %(2d %(2d", l.weekOfYear, monthStr, l.day[1], l.day[2], l.day[3], l.day[4], l.day[5], l.day[6], l.day[0] ).replace( "(1)", " " ); System.out.println( s ); } } }
Zur Demo gibt eine Textausgabe (mit einem Hack). Das Ergebnis für dieses Jahr:
kw mo do mi do fr sa so 52 jan 1 1 2 3 4 5 6 7 8 2 9 10 11 12 13 14 15 3 16 17 18 19 20 21 22 4 23 24 25 26 27 28 29 5 feb 30 31 1 2 3 4 5 6 6 7 8 9 10 11 12 7 13 14 15 16 17 18 19 8 20 21 22 23 24 25 26 9 mär 27 28 29 1 2 3 4 10 5 6 7 8 9 10 11 11 12 13 14 15 16 17 18 12 19 20 21 22 23 24 25 13 apr 26 27 28 29 30 31 1 14 2 3 4 5 6 7 8 15 9 10 11 12 13 14 15 16 16 17 18 19 20 21 22 17 23 24 25 26 27 28 29 18 mai 30 1 2 3 4 5 6 19 7 8 9 10 11 12 13 20 14 15 16 17 18 19 20 21 21 22 23 24 25 26 27 22 jun 28 29 30 31 1 2 3 23 4 5 6 7 8 9 10 24 11 12 13 14 15 16 17 25 18 19 20 21 22 23 24 26 jul 25 26 27 28 29 30 1 27 2 3 4 5 6 7 8 28 9 10 11 12 13 14 15 29 16 17 18 19 20 21 22 30 23 24 25 26 27 28 29 31 aug 30 31 1 2 3 4 5 32 6 7 8 9 10 11 12 33 13 14 15 16 17 18 19 34 20 21 22 23 24 25 26 35 sep 27 28 29 30 31 1 2 36 3 4 5 6 7 8 9 37 10 11 12 13 14 15 16 38 17 18 19 20 21 22 23 39 24 25 26 27 28 29 30 40 okt 1 2 3 4 5 6 7 41 8 9 10 11 12 13 14 42 15 16 17 18 19 20 21 43 22 23 24 25 26 27 28 44 nov 29 30 31 1 2 3 4 45 5 6 7 8 9 10 11 46 12 13 14 15 16 17 18 47 19 20 21 22 23 24 25 48 dez 26 27 28 29 30 1 2 49 3 4 5 6 7 8 9 50 10 11 12 13 14 15 16 51 17 18 19 20 21 22 23 52 24 25 26 27 28 29 30 1 31
Thema der Woche: Excel API
- Sieh das Video unter http://de.engadget.com/2012/08/13/stop-motion-musikvideo-aus-excel-tabellen/.
- Entscheide dich für eine Java Excel API
- Baue ein Excel-Sheet und setzte die Breite der Spalten so, dass die der Höhe entsprechen, 64 x 64 Zellen sind interessant.
- Zeichne eine vertikale Linie in hellblau von 1/1 nach 64/64.
- Setzte die Farbpalette so, dass sie Graustufen entsprechen.
- Lade ein beliebiges Bild, skaliere es auf 64 x 64 Pixel. Laufe das Bild Zeile für Zeile ab, konvertiere die Farben in Graustufen. Nutzte dazu toGray(Color) von http://introcs.cs.princeton.edu/java/31datatype/Luminance.java.html. Setze entsprechend die Zelle in der Excel-Tabelle.
Abbruch-Signale
Das Betriebssystem sendet beim Abbruch eines Programms Signale. Für Windows/Unix-Systeme sind das zum Beispiel INT (Abbruch z. B. mit Strg-C), TERM (Aufforderung zur Terminierung), BREAK (Break-Anforderung vom Termin), usw.
Java kann diese unterschiedlichen Feinheiten des Programmabbruchs nicht auswerten, jedenfalls nicht mit einer erlaubten API. Es gibt aber zwei Klassen sun.misc.Signal und sun.misc.SignalHandler, die verwendet werden können, wenn Entwickler sich der Konsequenzen bewusst sind, dass diese API in Zukunft verschwinden könnte. Die Nutzung sieht so aus:
class MySignalHandler implements SignalHandler {
public void handle( Signal signal ) { … }
}
String signalName = "TERM";
handler = Signal.handle( new Signal( signalName ), new MySignalHandler() );
Weitere Informationen auch zu den unterschiedlichen Signaltypen liefern http://stackoverflow.com/questions/5023520/sending-signals-to-a-running-jvm und die referenzierten Unterseiten.
Thema der Woche: JPA
Arbeite JPA Queries and Tips (ohne 10) durch. Beantworte:
- Welche Strategien gibt es bei @GeneratedValue und wie muss die Datenbank helfen?
- Erkläre @SqlResultSetMappings
- Welche Hints erlauben Hibernate und EclipseLink?
- Was ist ein Metamodel im Zusammenhang mit der Criteria-API?
Neue Version von Google Guava, nun Version 13
Änderungen unter http://code.google.com/p/guava-libraries/wiki/Release13.
Gruppen in regulären Ausdrücken
In regulären Ausdrücken lassen sich Teilausdrücke in runde Klammen setzen und diese bilden dann Gruppen; im Englischen heißen sie capturing groups. Gruppen haben zwei Vorteile:
· An die Gruppe gesetzte Operationen wirken für die Gruppe. Beispiel: Der Ausdruck (\d\d)+ steht für gerade Anzahl von Ziffern.
· Auf eine Gruppe lässt sich gezielt zurückgreifen.
Beispiel 1
Ein String enthält zwei Zahlen, die mit einem Doppelpunkt getrennt sind. Wie interessieren uns für die erste und zweite Zahl:
Matcher matcher = Pattern.compile( "(\\d+):(\\d+)").matcher( "1:123" );
if ( matcher.find() )
System.out.println( matcher.group(1) + "," + matcher.group(2) ); // 1,123
Der Trick ist also, bei group(int) einen Index anzugeben; der beginnt bei 1.
Beispiel 2
Ein String aus einer Datei enthält einen Block in geschweiften Klammen. Wir interessieren uns für den Block.
String input = "line1\n{line2\nline3}\nline4";
Matcher matcher = Pattern.compile( "\\{(.*)\\}", Pattern.MULTILINE | Pattern.DOTALL ).matcher( input );
if ( matcher.find() )
System.out.println( matcher.group( 1 ) ); // line1, dann Umbruch, line2
Bei der Angabe ohne Index, also group(), ist die Rückgabe alles was find() gefunden hat. Die Fundstellen sind natürlich Teilstrings der gesamten Gruppe.
Welche Jars braucht man mindestens für ein JPA-Hibernate-Beispiel?
- javax.persistence-2.0.0.jar
- hibernate-commons-annotations-4.0.1.Final.jar
- hibernate-core-4.1.4.Final.jar
- hibernate-entitymanager-4.1.4.Final.jar
- hibernate-validator-4.3.0.Final.jar
- javassist-3.7.ga.jar
- jboss-logging-3.1.1.GA.jar
- jta-1.1.jar
- antlr-2.7.5H3.jar
- dom4j-1.6.1.jar
Zu beziehen in einem Maven-Repository des Vertrauens oder gleich über Maven auflösen lassen.
Inselupdate: Property-Dateien mit java.util.Properties lesen und schreiben
Dateien, die Schlüssel-Werte-Paare als String repräsentieren und die Schlüssel und Wert durch ein Gleichheitszeichen trennen, nennen sich Property-Dateien. Sie kommen zur Programmkonfiguration häufig vor, und Java bietet mit der Klasse Properties die Möglichkeit, die Property-Dateien einzulesen und zu schreiben.
store() und load()-Methoden vom Properties Objekt
Die Methode store(…) dient zum Speichern der Zustände und load(…) zum Initialisieren eines Properties-Objekts aus einem Datenstrom. Die Schlüssel und Werte trennt ein Gleichheitszeichen. Die Lade-/Speicher-Methoden sind:
class java.util.Properties
extends Hashtable<Object,Object>
§ void store(OutputStream out, String comments)
§ void store(Writer writer, String comments)
Schreibt die Properties-Liste in des Ausgabestroms. Am Kopf der Datei wird eine Kennung geschrieben, die im zweiten Argument steht. Die Kennung darf null sein.
§ void load(InputStream inStream)
§ void load(Reader reader) throws IOException
Liest eine Properties-Liste aus einem Eingabestrom.
Ist der Typ ein Binärstrom also OutputStream/InputStream, so behandeln die Methoden die Zeichen in der ISO 8859-1 Kodierung. Reader/Writer erlauben eine freie Kodierung. Eine ähnliche Methode list(…) ist nur für Testausgaben gedacht ist, sie sollte nicht mit store(…) verwechselt werden.
Das folgende Beispiel initialisiert ein Properties-Objekt mit den Systemeigenschaften und fügt dann einen Wert hinzu. Anschließend macht store(…) die Daten persistent, load(…) liest sie wieder, und list(…) gibt die Eigenschaften auf dem Bildschirm aus:
Path path = Paths.get( "properties.txt" );
try ( Writer writer = Files.newBufferedWriter( path, StandardCharsets.UTF_8 ) ) {
Properties prop1 = new Properties( System.getProperties() );
prop1.setProperty( "MeinNameIst", "Forrest Gump" );
prop1.store( writer, "Eine Insel mit zwei Bergen" );
try ( Reader reader = Files.newBufferedReader( path, StandardCharsets.UTF_8 ) ) {
Properties prop2 = new Properties();
prop2.load( reader );
prop2.list( System.out );
}
}
catch ( IOException e ) {
e.printStackTrace();
}
Besonderheiten des Formats
Beginnt eine Zeile mit einem „#“ oder „!“ gilt sie als Kommentar und wird überlesen. Da der Schlüssel selbst aus einem Gleichheitszeichen bestehen kann, steht in dem Fall ein „\“ voran, folglich liefert Properties p = new Properties(); p.setProperty("=", "="); p.store(System.out, null); neben dem Kommentar die Zeile „\==\=“. Beim Einlesen berücksichtigen die Lesemethoden auch Zeilenumbrüche: eine Zeile darf mit \ enden und dann führt die folgende Zeile die vorangehende fort. Die Property „cars“ ist also “Honda, Mazda, BMW”, wenn steht:
cars = \
Honda, Mazda, \
BMW
Mit der internen Compiler-API auf den AST einer Klasse zugreifen
Der Zugriff zum Java-Compiler ist über die Java-Compiler-API standardisiert, jedoch sind alle Interna, wie die tatsächliche Repräsentation des Programmcodes verborgen. Die Compiler-API abstrahiert alles über Schnittstellen, und so kommen Entwickler nur mit JavaCompiler, StandardJavaFileManager und CompilationTask in Kontakt – alles Schnittstellen aus dem Paket javax.tools. Um etwas tiefer einzusteigen, lässt sich zum einem Trick greifen: Klassen implementieren Schnittstellen und wenn ein Programm den Schnittstellentyp auf den konkreten Klassentyp anpasst, dann stehen in der Regel mehr Methoden zur Verfügung. So lässt sich der CompilationTask auf eine com.sun.tools.javac.api.JavacTaskImpl casten und dann steht eine parse()-Methode für Verfügung. Die parse()-Methode liefert als Rückgabe eine Aufzählung von CompilationUnitTree. Um diesen Baum nun abzulaufen, lässt sich das Besuchermuster einsetzen. CompilationUnitTree bietet eine accept(…)-Methode; der übergeben wir einen TreeScanner. Die accept(…)-Methode ruft dann beim Ablaufen jedes Knotens unseren Besucher auf.
package com.tutego.tools.javac; import java.io.*; import java.net.*; import javax.tools.*; import javax.tools.JavaCompiler.CompilationTask; import com.sun.source.tree.*; import com.sun.source.util.TreeScanner; import com.sun.tools.javac.api.JavacTaskImpl; public class PrintAllMethodNames { final static TreeScanner<?, ?> methodPrintingTreeVisitor = new TreeScanner<Void, Void>() { @Override public Void visitCompilationUnit( CompilationUnitTree unit, Void arg ) { System.out.println( "Paket: " + unit.getPackageName() ); return super.visitCompilationUnit( unit, arg ); }; @Override public Void visitClass( ClassTree classTree, Void arg ) { System.out.println( "Klasse: " + classTree.getSimpleName() ); return super.visitClass( classTree, arg ); } @Override public Void visitMethod( MethodTree methodTree, Void arg ) { System.out.println( "Methode: " + methodTree.getName() ); return super.visitMethod( methodTree, arg ); } }; public static void main( String[] args ) throws IOException, URISyntaxException { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null ); URI filename = PrintAllMethodNames.class.getResource( "PrintAllMethodNames.java" ).toURI(); Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects( new File( filename ) ); CompilationTask task = compiler.getTask( null, null, null, null, null, fileObjects ); JavacTaskImpl javacTask = (JavacTaskImpl) task; for ( CompilationUnitTree tree : javacTask.parse() ) tree.accept( methodPrintingTreeVisitor, null ); } }
Ein TreeScanner hat viele Methoden, wir interessieren uns nur für den Start einer Compilationseinheit für den Paketnamen, für alle Klassen und Methoden. Wir könnten uns aber auch über alle Annotationen oder do-while-Schleifen informieren lassen. Die Ausgabe ist:
Paket: com.tutego.tools.javac
Klasse: PrintAllMethodNames
Klasse:
Methode: visitCompilationUnit
Methode: visitClass
Methode: visitMethod
Methode: main
Die zweite Angabe für den Klassennamen ist leer, da die anonyme Klasse eben keinen Namen hat.
Snippet: How to test if a list of dates is sorted
List<Date> dates = Arrays.asList( new Date( 2L ), new Date( 3L ) );
boolean isSorted = new ArrayList<>( new TreeSet<>( dates ) ).equals( dates );
Inselupdate: Konstruktoren der Formatter Klasse
Die String.format(…)-Methode und prinf(…)-Methoden der Ein-/Ausgabeklassen übernehmen die Aufbereitung nicht selbst, sondern delegieren sie an die Klasse java.util.Formatter. Das ist auch der Grund, warum die Dokumentation für die Formatspezifizierer nicht etwa an String.format(…) hängt, sondern an Formatter.
Konstruktor vom Formatter
Die Klasse Formatter hat eine beeindruckende Anzahl von Konstruktoren:
· Formatter()
· Formatter(Appendable a)
· Formatter(Appendable a, Locale l)
· Formatter(File file)
· Formatter(File file, String csn)
· Formatter(File file, String csn, Locale l)
· Formatter(Locale l)
· Formatter(OutputStream os)
· Formatter(OutputStream os, String csn)
· Formatter(OutputStream os, String csn, Locale l)
· Formatter(PrintStream ps)
· Formatter(String fileName)
· Formatter(String fileName, String csn)
· Formatter(String fileName, String csn, Locale l)
Wird nicht der Standardkonstruktur eingesetzt, schreibt der Formatter in die angegebene Quelle. Daher ist die Klasse schön für das Schreiben von Texten in Dateien geeignet. Formatter implementiert Closeable, ist also auch AutoCloseable. Ein Beispiel zum Schreiben in Dateien:
try ( Formatter out = new Formatter( "ausgabe.txt", StandardCharsets.ISO_8859_1.name() ) ) {
for ( int i = 0; i < 10; i++ )
out.format( "%02d%n", i );
}
catch ( FileNotFoundException | UnsupportedEncodingException e ) {
e.printStackTrace();
}
Inselupdate: Lokale und innere anonyme Klasse für einen Timer nutzen
Lokale Klasse für einen Timer nutzen
Damit die Beispiele etwas praxisnäher werden, wollen wir uns anschauen, wie ein Timer wiederholende Aufgaben ausführen kann. Die Java-Bibliothek bringt hier schon alles mit: Es gilt ein Exemplar von java.util.Timer() zu bilden und der Objektmethode scheduleAtFixedRate(…) ein Exemplar vom Typ TimerTask zu übergeben. Die Klasse TimerTask schreibt eine abstrakte Methode run() vor, in die der parallel und regelmäßig abzuarbeitende Programmcode gesetzt wird.
Nutzen wir das für ein Programm, welches uns sofort und regelmäßig daran erinnert, wie wichtig doch Sport ist:
public class SportReminder {
public static void main( String[] args ) {
class SportReminderTask extends TimerTask {
@Override public void run() {
System.out.println( "Los, beweg dich du faule Wurst!" );
}
}
new Timer().scheduleAtFixedRate( new SportReminderTask(), 0 /* ms delay */, 1000 /* ms period */ );
}
}
Unser Klasse SportReminderTask, die TimerTask erweitert, ist direkt in main(…) deklariert. Das erzeugte Exemplar kommt später in scheduleAtFixedRate(…) und los rennt der Timer, um uns jede Sekunde an die Wichtigkeit von Bewegung zu erinnern.
Nutzung einer anonymen innerer Klassen für den Timer
Eben gerade haben wir für den Timer extra eine neue lokale Klasse deklariert, aber genau genommen haben wir diese nur einmal nutzen müssen, nämlich um ein Exemplar bilden, und scheduleAtFixedRate(…) übergeben zu können. Das ist ein perfektes Szenario für anonyme innere Klassen. Aus
class SportReminderTask extends TimerTask {
@Override public void run() { … }
}
new Timer().scheduleAtFixedRate( new SportReminderTask(), … );
wird
new Timer().scheduleAtFixedRate( new TimerTask() {
@Override public void run() {
System.out.println( "Los, …" );
}
},
0 /* ms delay */,
1000 /* ms period */);
Im Kern ist es also eine Umwandlung von new SportReminderTask() in new TimerTask() { … }. Von dem Klassennamen SportReminderTask ist nichts mehr zu sehen, das Objekt ist anonym.
Inselupdate: Methode mit variabler Argumentanzahl (Vararg)
Bei vielen Methoden ist es klar, wie viele Argumente sie haben; eine Sinus-Methode bekommt nur ein Argument, equals(Object) ein Objekt, println(…) nichts oder genau ein Argument, usw. Es gibt jedoch Methoden, bei denen die Zahl mehr oder weniger frei ist. Ein paar Beispiele:
· Wenn der Aufruf System.out.printf(formatierungsstring, arg1, args2, arg3, …) etwas auf dem Bildschirm ausgibt, ist erst einmal nicht bekannt, wie viele Argumente die Methode besitzt, denn sie sind abhängig vom Formatierungsstring.
· Fügt Collections.addAll(sammlung, elem1, elem2, elem3, …) etwas einer Sammlung hin, ist frei, wie viele Elemente es sind.
· Wird ein Pfad für das Dateisystem zusammengebaut, ist vorher unbekannt, wie viele Segmente Paths.get( anfang, segment1, segment2, … ) besitzt.
· Startet new ProcessBuilder("kommando", "arg1", "arg1", …).start() ein Hintergrundprogramm, ist der Methode unbekannt, wie viele Argumente dem externen Programm übergeben werden.
Um die Anzahl der Parameter beliebig zu gestalten, sieht Java Methoden mit variabler Argumentanzahl, auch Varargs genannt, vor.
Eine Methode mit variabler Argumentanzahl nutzt die Ellipse (»…«) zur Verdeutlichung, dass eine beliebige Anzahl Argumente angegeben werden dürfen, dazu zählt auch die Angabe keines Elements. Der Typ fällt dabei aber nicht unter den Tisch; er wird ebenfalls angegeben.
System.out.printf() nimmt eine beliebige Anzahl von Argumenten an
Eine Methode mit Varargs haben wir schon einige Male verwendet: printf(…). Die Deklaration ist wie folgt:
class java.io.PrintStream extends FilterOutputStream implements Appendable, Closeable
PrintStream printf(String format, Object… args)
Nimmt eine beliebige Liste von Argumenten an und formatiert sie nach dem gegebenen Formatierungsstring format.
Gültige Aufrufe von printf(…) sind demnach:
Aufruf |
Variable Argumentliste |
System.out.printf("%n") |
ist leer |
System.out.printf("%s", "Eins") |
besteht aus nur einem Element: "Eins" |
System.out.printf("%s,%s,%s", "1", "2", "3") |
besteht aus drei Elementen: "1", "2", "3" |
Maximum eines Feldes finden
Die Klasse java.lang.Math sieht eine statische max(…)-Methode mit zwei Argumenten vor, doch grundsätzlich könnte die Methode auch beliebig viele Argumente entgegennehmen und von diesen Elementen das Maximum bilden. Die Methode könnte so aussehen:
static int max( int… array ) {
}
max(…) behandelt den Parameter array wie ein Feld. Da wir Argumente vom Typ int fordern, ist array vom Typ int[] und kann so zum Beispiel mit dem erweiterten for durchlaufen werden:
for ( int e : array )
…
Werden variable Argumentlisten in der Signatur definiert, so dürfen sie nur den letzten Parameter bilden; andernfalls könnte der Compiler bei den Parametern nicht unbedingt zuordnen, was nun ein Vararg und was schon der nächste gefüllte Parameter ist:
public class MaxVarArgs {
static int max( int… array ) {
if ( array == null || array.length == 0 )
throw new IllegalArgumentException( "Array null oder leer" );
int currentMax = Integer.MIN_VALUE;
for ( int e : array )
if ( e > currentMax )
currentMax = e;
return currentMax;
}
public static void main( String[] args ) {
System.out.println( max(1, 2, 9, 3) ); // 9
}
}
Tipp: Vararg-Design. Muss eine Mindestanzahl von Argumenten garantiert werden – bei max(…) sollten das mindestens zwei sein – ist es besser, eine Deklaration wie folgt zu nutzen: max(int first, int second, int… rest).
Compiler baut Array auf
Der Nutzer kann jetzt die Methode aufrufen, ohne ein Feld für die Argumente explizit zu definieren. Er bekommt auch gar nicht mit, dass der Compiler im Hintergrund ein Feld mit vier Elementen angelegt hat. So übergibt der Compiler:
System.out.println( max( new int[] { 1, 2, 9, 3 } ) );
An der Schreibweise lässt sich gut ablesen, dass wir ein Feld auch von Hand übergeben können:
int[] ints = { 1, 2, 9, 3 };
System.out.println( max( ints ) );
Hinweis. Da Varargs als Felder umgesetzt werden, sind überladene Varianten wie max(int… array) und max(int[] array), also einmal mit einem Vararg und einmal mit einem Feld, nicht möglich. Besser ist es hier, immer eine Variante mit Varargs zu nehmen, da diese mächtiger ist. Einige Autoren schreiben auch die Einstiegsmethode main(String[] args) mit variablen Argumenten, also main(String… args). Das ist gültig, denn im Bytecode steht ja ein Array.
Arrays.asList() Beispiel und Hinweis
Beispiel: Gib das größte Element eines Feldes aus.
Integer[] ints = { 3, 9, -1, 0 };
System.out.println( Collections.max( Arrays.asList( ints ) ) );
Zum Ermitteln des Maximums bietet die Utility-Klasse Arrays keine Methode, daher bietet sich die max(…)-Methode von Collections an. Auch etwa zum Ersetzen von Feldelementen bietet Arrays nichts, aber Collections. Sortieren und Füllen kann Arrays aber schon, hier muss asList() nicht einspringen.
Hinweis: Wegen der Generics ist der Parameter-Typ von asList() ein Objekt-Feld, aber niemals ein primitives Feld. In unserem Beispiel von eben würde so etwas wie
int[] ints = { 3, 9, -1, 0 };
Arrays.asList( ints );
zwar kompilieren, aber die Rückgabe von Arrays.asList(ints) ist vom Typ List<int[]>, was bedeutet, die gesamte Liste besteht aus genau einem Element und dieses Element ist das primitive Feld. Zum Glück führt Collections.max(Arrays.asList(ints)) zu einem Compilerfehler, denn von einer List<int[]>, also eine Liste von Feldern, kann max(Collection<? extends T>) kein Maximum ermitteln.
How to put files in a ZIP file with NIO.2
URI p = Paths.get( "c:/Users/Christian/Dropbox/jokes.zip" ).toUri(); URI uri = URI.create( "jar:" + p ); Map<String, String> env = new HashMap<>(); env.put( "create", "true" ); try ( FileSystem zipfs = FileSystems.newFileSystem( uri, env ) ) { Files.write( zipfs.getPath( "/j1.txt" ), "The truth is out there. Anybody got the URL?".getBytes() ); Files.write( zipfs.getPath( "/j2.txt" ), "The more I C, the less I see.".getBytes() ); }