Jahr: 2014
Endlich transferTo(…) im InputStream
http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/003295073abf zeigt uns in Java 9 endlich eine lang erwartete Methode:
+ /** + * Reads all bytes from this input stream and writes the bytes to the + * given output stream in the order that they are read. On return, this + * input stream will be at end of stream. This method does not close either + * stream. + * <p> + * This method may block indefinitely reading from the input stream, or + * writing to the output stream. The behavior for the case where the input + * and/or output stream is <i>asynchronously closed</i>, or the thread + * interrupted during the transfer, is highly input and output stream + * specific, and therefore not specified. + * <p> + * If an I/O error occurs reading from the input stream or writing to the + * output stream, then it may do so after some bytes have been read or + * written. Consequently the input stream may not be at end of stream and + * one, or both, streams may be in an inconsistent state. It is strongly + * recommended that both streams be promptly closed if an I/O error occurs. + * + * @param out the output stream, non-null + * @return the number of bytes transferred + * @throws IOException if an I/O error occurs when reading or writing + * @throws NullPointerException if {@code out} is {@code null} + * + * @since 1.9 + */ + public long transferTo(OutputStream out) throws IOException { + Objects.requireNonNull(out, "out"); + long transferred = 0; + byte[] buffer = new byte[TRANSFER_BUFFER_SIZE]; + int read; + while ((read = this.read(buffer, 0, TRANSFER_BUFFER_SIZE)) >= 0) { + out.write(buffer, 0, read); + transferred += read; + } + return transferred; + }
Java 9, Stand 2014
In diesem Jahr hat sich viel geklärt, was wir in Java 9 erwarten können.
Die bisher geplanten JEPs (JDK Enhancements) sind:
- JEP 102: Process API Updates (Improve the API for controlling and managing operating-system processes.
- JEP 143: Improve Contended Locking (Improve the performance of contended Java object monitors.
- JEP 158: Unified JVM Logging. Introduce a common logging system for all components of the JVM.
- JEP 165: Compiler Control. Improve the control of the JVM compilers. It will allow the user to apply sets of options depending on which method is being compiled. It also adds the possibility of changing the option sets during run time.
- JEP 197: Segmented Code Cache. Divide the code cache into distinct segments, each of which contains compiled code of a particular type, in order to improve performance and enable future extensions.
- JEP 198: Light-Weight JSON API. Provide a light-weight API for consuming and generating JSON documents and data streams.
- JEP 199: Smart Java Compilation, Phase Two.. Improve the sjavac tool so that it can be used by default in the JDK build, and generalize it so that it can be used to build large projects other than the JDK.
- JEP 201: Modular Source Code. Reorganize the JDK source code into modules, enhance the build system to compile modules, and enforce module boundaries at build time.
- JEP 211: Elide Deprecation Warnings on Import Statements. As of Java SE 8, java compilers are required by reasonable interpretations of the Java Language Specification to issue deprecation warnings when a deprecated type is imported by name or when a deprecated member (method, field, nested type) is imported statically. These warnings are uninformative and should not be required. Deprecation warnings at actual uses of deprecated members should remain.
- JEP 212: Resolve Lint and Doclint Warnings. The JDK code base contains numerous lint and doclint errors as reported by javac. These warnings should be resolved, at least for the fundamental parts of the platform.
- JEP 213: Milling Project Coin. Allow @SafeVargs on private instance methods.Allow effectively-final variables to be used as resources in the try-with-resources statement. Allow diamond with inner classes if the argument type of the inferred type is denotable. Complete the removal, begun in Java SE 8, of underscore from the set of legal identifier names.
- JEP 214: Remove GC Combinations Deprecated in JDK 8. Remove the GC combinations that were previously deprecated in JDK 8 via JEP 173.
- JEP 216: Process Import Statements Correctly. Fix javac to properly accept and reject programs regardless of the order of import statements and extends and implements clauses.
- JEP 219: Datagram Transport Layer Security (DTLS). Define an API for Datagram Transport Layer Security (DTLS) version 1.0 (RFC 4347) and 1.2 (RFC 6347).
- JEP 220: Modular Run-Time Images. Restructure the JDK and JRE run-time images to accommodate modules and to improve performance, security, and maintainability. Define a new URI scheme for naming the modules, classes, and resources stored in a run-time image without revealing the internal structure or format of the image. Revise existing specifications as required to accommodate these changes.
- JEP 224: HTML5 Javadoc. Enhance the javadoc tool to generate HTML5 markup.
- 228: Add More Diagnostic Commands. Define additional diagnostic commands, in order to improve the diagnosability of Hotspot and the JDK.
- JEP 229: Create PKCS12 Keystores by Default. Transition the default keystore type from JKS to PKCS12.
- JEP 231: Remove Launch-Time JRE Version Selection. Remove the ability to request, at JRE launch time, a version of the JRE that is not the JRE being launched.
Wie beim Java 8 bildet das OpenJDK die Referenzimplementierung, auf deren Basis das Oracle JDK stehen wird. Das Mercurial Repository ist offen. Change-Sets der Builds dokumentieren die Änderungen.
- http://download.java.net/jdk9/changes/
- http://hg.openjdk.java.net/jdk9/jdk9
- http://openjdk.java.net/projects/jdk9/
Die Mailing-Listen dokumentieren weiterhin Updates:
Der Java-Compiler wird keinen Byte-Code mehr erzeugen von Versionen für Java 5 und Java 6. Siehe dazu auch http://www.infoq.com/news/2013/06/java-jep182.
Aufgehoben/Aufgeschoben für Java 10
Abgeblasen wurden:
- JEP 110: HTTP 2 Client. Ein Ersatz für die
HttpURLConnection
für moderne HTTP 2.0-Anwendungen. Vielleicht abgeblasen, weil es in der Java EE schon eine REST-Client-API definiert wurde (sieheDokumentation von Jersey), und genügend Open-Source-Bibliotheken die Lücke füllen. - JEP 198: Light-Weight JSON API; für die Java EE gibt es mit der JSR 353: Java API for JSON Processingschon eine API, im Java SE wollte man nicht noch eine weitere API aufnehmen. Zudem gibt es diverse JSON-Bibliotheken.
- JEP 222: Java Read-Eval-Print Loop (REPL). Eine Art Skript-Umgebung zum interaktiven Auswerten von Anweisungen und Ausdrücken.
Denkbar für Java 10 ist eine Spracherweiterung genannt Value Objects (eine Art Strukturtyp), diskutiert unter http://openjdk.java.net/jeps/169.
In der Zukunft könne es besondere Optimierungen der JVM in der Cloud bzw. in/auf einem Hypervisor geben.
Angedacht ist auch eine GPU-Beschleunigung von Java-Anwendungen. Mithttp://openjdk.java.net/projects/sumatra/ gibt es erste Lösungen.
Immer aktuell unter http://www.tutego.de/java/java-9-opendjk-9-java-se-9.html.
Java Open-Source Libs Update Dezember
- Dropwizard (Apache). Auf der Basis von Jetty integriert das Framework weitere Projekte, um einfach REST-Anwendungen in Java entwickeln zu können. Dazu kommen Jersey, Jackson, Metrics, Guava, Logback, Hibernate Validator zusammengebunden in einem fetten Jar.
- JDBI (Apache). Annotiert mit SQL-Kommandos DAOs und baut mit einer fluid-API JDBC-Anweisungen zusammen. Gehostet bei GitHub.
- Glazed Lists (LGPL und MPL). Beobachtbare Listen, auch filter-/transformierbar, ähnlich wie es Java FX bietet. Unterstützt Swing und SWT.
- Gson (Apache). Java-Objekt nach JSON-Serialisierer
- ICU4J (ICU Licence). Immer mit dem aktuellsten Unicode-Standard und allen Ländern der Welt Zeichen/-folgen parsen, formatieren, Zeitberechnungen durchführen. Aktueller als das JDK.
Weitere quelloffene Java-Software unter http://www.tutego.de/java/java-open-source.htm.
Tellerrandblick: C# 6.0
http://blogs.msdn.com/b/csharpfaq/archive/2014/11/20/new-features-in-c-6.aspx. Zumindest das statische Import kennen Java-Programmierer, sonst ist C# meilenweit voraus.
Eclipse Project 4.5 (Mars) M4
Je länger ich Eclipse benutze, desto seltener beschäftige ich mich mit Release-Dates oder den Features — es sei denn, ich warte auf die Unterstützung von neuen Sprache-Features.
So gingen auch die bisherigen Milestones von 4.5 an mir spurlos vorbei, auch der letzte Milestone M4 vom 12.12. Zusammenfassend Neuerungen, die (für mich) interessanter sind:
https://www.eclipse.org/eclipse/news/4.5/M4/
Assigning stdin to a file
Stdin can now be assigned to a file in the „Common“ tab of launch configuration dialogs.
Automatic scroll lock in Console view
Scrolling up in the Console view using keys, mouse wheel, or scroll bar now automatically enables the Scroll Lock mode.
When you scroll down to the end of the console, the scroll lock is automatically released again.
Improved flow analysis for loops
Flow analysis has been improved to more precisely capture the flow of null values in loops. This mainly achieves a reduction of false positive reports from null analysis.
Previously, example method „test1“ would raise a potential null pointer warning at point (3). To correct this issue the merging of information leading towards point (3) has been improved to correctly see that the null value from point (1) can never reach point (3).
In example method „test2“ JDT previously reported a redundant null check at (3), because analysis didn’t see that the assignment directly above could indeed assign a non-null value.
In example method „test3“ it was reported that „o can only be null“ at (3), because the information from the two null-assignments wrongly overruled the one assignment from non-null. With improved analysis this is now softened to saying „o may be null“.
The graph on the right hand side illustrates the new composition of flow information: for each relevant point (3) inside a loop, the analysis first merges the flows that lead into (1). This result is concatenated with the partial flow (b.c), which leads from the loop start to point (3). Improved precision has thus been achieved within the design limits of a single AST traversal in order to minimize impact on compiler performance.
https://www.eclipse.org/eclipse/news/4.5/M3:
‚Terminate/Disconnect All‘ in Console view
You can invoke the Terminate/Disconnect All action from the Console view’s context menu:
Add inferred lambda parameter types
You can explicitly add the inferred types of the parameters in a lambda expression by invoking the Quick Assist (Ctrl+1) – Add inferred lambda parameter types:
Convert method reference to lambda and back
New Quick Assists (Ctrl+1) have been added to convert…
- from method reference to lambda expression:
- from lambda expression to method reference:
https://www.eclipse.org/eclipse/news/4.5/M2/
Compiler schneller.
https://www.eclipse.org/eclipse/news/4.5/M1/
Word wrap in the Console
A new formatting option has been contributed to the Console view for all I/O consoles: Word Wrap.
The new option is available on the Console view toolbar and in the content popup menu within the Console view.
Der http://www.eclipse.org/projects/project-plan.php?planurl=/eclipse/development/plans/eclipse_project_plan_4_5.xml gibt uns noch einen Milestone vor, dann beginnt die Feinarbeit:
- M5 2015-01-30 4.5M5
- 2015-02-13 CQ Submission Deadline
- M6 2015-03-20 .5M6 (API Freeze)
- M7 2015-05-01 4.5M7 (Feature Freeze)
Interessanter Sicherheits-Bug in 7u51 gefixed
Details unter http://weblog.ikvm.net/2014/01/16/PubliclyReportedOpenJDKVulnerabilityFixedIn7u51.aspx:
import java.lang.invoke.*;
class test extends java.io.FileOutputStream {
static test t;
test() throws Exception {
super(„“);
}
protected void finalize() {
t = this;
}
public static void main(String[] args) throws Throwable {
MethodHandle mh = MethodHandles.lookup().findVirtual(test.class, „open“,
MethodType.methodType(void.class, String.class, boolean.class));
System.out.println(mh);
try { new test(); } catch (Exception _) { }
System.gc();
System.runFinalization();
mh.invokeExact(t, „oops.txt“, false);
}
}
Joda-Time auf Java 8 java.time konvertieren
Wie man von Joda-Time zur Java 8 Date&Time-API konvertiert diskutiert der Beitrag http://blog.joda.org/2014/11/converting-from-joda-time-to-javatime.html von Stephen Colebourne.
Der Autor von Joda-Time selbst empfiehlt bei neuen Projekten die Date-Time-API von Java:
If you are writing code in Java SE 8, its time to migrate to
java.time
, perhaps using ThreeTen-Extra to fill any gaps.
Android Studio basiert auf IntelliJ, Eclipse ist raus
Googles Android-IDE war lange Zeit ein ADT-Plugin für Eclipse. Das ist nun vorbei, das Plugin wird nicht mehr (von Google offiziell) weiterentwickelt. Eine Version auf der Basis der quelloffenen Community Edition von IntelliJ war seit 2 Jahren in Entwicklung, und ist nun die offizielle IDE für Android-Anwendungen. Mit IntelliJ hat man sich sicherlich eine gute Basis ausgesucht, nur wird es viele Eclipse-IDE-Nutzer verprellen, die bei den Shortcuts und der Projektorganisation dazulernen müssen.
Links:
Tuning an der JDK JavaScript Engine Nashorn
In der kommenden Version JDK 8u40 gibt es einige Änderungen, die die Performance von JS-Anwendungen auf der JVM massiv verbessern können. Details dazu in dem Blog-Post https://blogs.oracle.com/nashorn/entry/nashorn_performance_work_in_the.
JUnit 4.12 freigegeben
Es gibt Software, die ist so stabil, dass Änderungen sehr selten sind. Dazu zählt JUnit, was nun in der Version 4.12 erschienen ist. Die Änderungen listet die Github-Seite (ja, auch JUnit ist bei Git gelandet) auf: https://github.com/junit-team/junit/blob/master/doc/ReleaseNotes4.12.md. Das allermeiste ist sehr speziell.
Vorschau auf die Dialoge/Alerts in JavaFX 8u40…
…gibt der Artikel http://code.makery.ch/blog/javafx-dialogs-official/ mit vielen Screenshots.
Offtopic: Flow, statische Typprüfung für JavaScript
Wer viel mit JavaScript arbeitet, wird sich über eine Lösung von FaceBook freuen, die JS-Code statisch prüft, um Typfehler zu finden. Mehr unter ttps://code.prod.facebook.com/posts/1505962329687926/flow-a-new-static-type-checker-for-javascript/ und auf der Hauptseite http://flowtype.org/.
Ein Beispiel:
/* @flow */ function foo(x) { return x * 10; } foo('Hello, world!');
Führt zu nach dem Aufruf vom Kommandozeilenprogramm flow zum Fehler:
01_HelloWorld/hello.js:7:5,17: string This type is incompatible with 01_HelloWorld/hello.js:4:10,13: number
GWT 2.7 final
Aus den Release-Notes: http://www.gwtproject.org/release-notes.html#Release_Notes_2_7_0 (nur Fixes, Neuerungen in den RC erklärt):
Highlights
Super Dev Mode is now the default. DevMode automatically starts Super Dev Mode and reloading a web page automatically runs the compiler when necessary. (The -noSuperDevMode flag may be used to revert to the old behavior.)
Compiling in Super Dev Mode is much faster after the first compile.
Experimental support for GSS, also known as Closure Stylesheets. (See below.)
Known Issues
- gwttar files are incorrect. (Fixed in the next version.)
Deprecations
GWT Designer doesn’t work with 2.7 and is no longer supported. (Source code is available if someone wishes to revive this project.)
IFrameLinker and XSLinker are deprecated because they don’t work in Super Dev Mode. However, we don’t have suitable replacements for all use cases yet. For updates and possible workarounds, see issue 8997.
Compiler changes
In draft mode and Super Dev Mode, all compiler optimizations are turned off for better debugging. For example, null pointers are detected sooner.
JSNI references no longer require fully qualified class names when this wouldn’t be necessary in Java. (For example, imports work.)
We’ve started implementing JS Interop annotations, which will make it much easier to use GWT with JavaScript libraries. The specification is not final and there are bugs, so production GWT apps and libraries should continue to use JSNI for now. If you wish to experiment, you can enable JS Interop using the
-XjsInteropMode
flag, which is available for the compiler and Super Dev Mode. (It doesn’t work with old DevMode.)The experimental
-XmethodNameDisplayMode
flag adds adisplayName
property to each JavaScript function containing the name of the Java method. This makes Java method names available in browser debuggers at the expense of code size. (Also available in Super Dev Mode.)Boxed JavaScript strings (created in JavaScript using
new String(...)
) are no longer considered equivalent to Java strings. They should be unboxed before being passed to Java.Many bugfixes.
Library Changes
JDK emulation
Significant performance improvements in
String
,ArrayList
,HashMap
, andException
.New emulated classes:
Locale
,NavigableSet
, andNavigableMap
.New emulated methods in
Class
,String
,Exception
,RuntimeException
,Logger
,Arrays
,Collections
, andMap.Entry
.
LinkedList
extendsDeque
and handles incorrect usage better.Logging and Stack Traces
Better wrapping of exceptions thrown from JavaScript.
GWT apps that inherit the
com.google.gwt.logging.Logging
module have different default behavior for messages logged using thejava.util.logging
package. The new default is to log messages at levelSEVERE
and above to the browser’s console.PopupLogHandler
andSystemHandler
are no longer enabled by default.
FirebugLogHandler
andNullLoggingPopup
have been removed. ()Experimental GSS support
The
CssResource.enableGss
configuration property turns on GSS support.
When enabled, resource files with a ‚gss‘ extension are parsed as a Closure Stylesheet.
When enabled, GSS can be used in a UiBinder file by setting
gss=true
on aui:style
tag.If the
CssResource.legacy
configuration property is set, .css resources andui:style
tags withoutgss=true
will first be converted to GSS and then parsed as GSS.UiBinder
- The
ui:data
tag has new attributes:mimeType
anddoNotEmbed
.GWT-RPC
The
rpc.XserializeFinalFields
configuration property turns on experimental support for serializing final fields.
LinkedHashSet
may be serialized without a serialization policy.deRPC is removed.
RequestFactory
Support overridden methods and generics better.
Fix support for
@SkipInterfaceValidation
onRequestContext
methods.Internationalization
- Upgraded to CLDR 25.
Browser API changes
Updated support for typed arrays.
Added
History.replaceItem()
.Fixed an issue with
Window.addWindowScrollHandler
on Chrome.Widgets
The deprecated
com.google.gwt.widgets
package is removed.Various bugfixes and minor improvements.
Developer Tool Changes
Dev Mode
The
-noSuperDevMode
flag may be used to turn off Super Dev Mode and revert to old Dev Mode. (However, most current browsers no longer support Dev Mode plugins.)The
-modulePathPrefix
flag may be used to move DevMode’s output to a subdirectory of the war directory.Super Dev Mode
Compiling is much faster after the first compile. (Compiling is skipped altogether if no files have changed.)
The first compile no longer happens at startup.
Chrome reloads the page faster while debugging. (Sourcemap file size is reduced.)
The
-launcherDir
flag may be used to avoid running the GWT compiler before starting Super Dev Mode. When enabled, Super Dev Mode writes stub .nocache.js files that automatically recompile the GWT app before loading it. Therefore the bookmarklets aren’t needed. (This feature is automatically enabled when launched via DevMode.)The
-logLevel
flag may be used to adjust how compile errors are reported.The
Dev Mode On
bookmarklet dialog shows whether Super Dev Mode is turned on for each module on the page.Messages logged using
java.util.logging
at levelSEVERE
and above are written to the browser console by default.Fixed a startup failure caused by locked directories on Windows.
Testing
Better error reporting for compile errors while running tests.
Messages logged using
java.util.logging
at levelSEVERE
and above are written to the browser console and test output by default.
-Dgwt.htmlunit.debug
may be used to open a JavaScript debugger window when running a test using HtmlUnit.Removed
RunStyleRemoteWeb
and theBrowserManager
tool.Removed flags:
-standardsMode
,-nostandardsMode
,-quirksMode
. (GWTTestCase tests are always run in an HTML page in standards mode.)For even more detail, see the Issue Tracker.
Endlich passt auch das Google Eclipse Plugin direkt zur neuen Version, bei meinem GWT-Projekt gab es keine Fehler, ich konnte direkt die neue Version und das Plugin nutzen.
Noch etwas:
This year’s sessions will include coverage of new functionality in upcoming versions of GWT, including Java 8 support and better interoperability with Javascript and Web Components. We will also talk about how Inbox by Gmail was built, using GWT and j2objc together to run the same code on the web and mobile devices.
JDK 9 Images sind nun modular
Das schreibt Mark Reinhold auf der Mailing-Liste: http://mail.openjdk.java.net/pipermail/jdk9-dev/2014-December/001663.html. Daraus ergeben sich eine Reihe von fundamentalen Änderungen bei den Verzeichnissen und Dateien einer JDK/JRE-Installation:
FYI, the initial changesets for JEP 220: Modular Run-Time Images [1] were pushed just a few minutes ago. If you build JDK 9 yourself, or if you download the next early-access build [2] (which should be available tomorrow), you'll see all of the changes documented in JEP 220. To summarize (please see the JEP for details): - The "jre" subdirectory is no longer present in JDK images. - The user-editable configuration files in the "lib" subdirectory have been moved to the new "conf" directory. - The endorsed-standards override mechanism has been removed. - The extension mechanism has been removed. - rt.jar, tools.jar, and dt.jar have been removed. - A new URI scheme for naming stored modules, classes, and resources has been defined. - For tools that previously accessed rt.jar directly, a built-in NIO file-system provider has been defined to provide access to the class and resource files within a run-time image. We have a few open issues to finish up, so further changes will follow for this JEP, but none will be as disruptive as today's merge. - Mark [1] http://openjdk.java.net/jeps/220 [2] https://jdk9.java.net/download/
IntelliJ IDEA 14, tolle neue Features
Alle Details unter https://www.jetbrains.com/idea/whatsnew/. Auf die Schnelle:
- built-in decompiler
- Show Referring Objects im Debugger
- evaluate to operator expressions im Debugger
- infers the@NotNull, @Nullable and @Contract
- Scala plugin comes with theChange Signature refactoring, reworked SBT integration, faster performance, brand new project configuration model, and many more
- enhancements and new features introduced in Android Studio Beta, including support for Google Wear andTV.
- Advanced coding assistance for Thymeleaf
- visual diagrams forSpring Integration
- standard Test Runner für Gradle task
- The Scene Builder is now built into the Editor.
- support for GlassFish 4.1, TomEE 1.7.1, WildFly 9.0.0 and tcServer 3.0.1.
- better log viewer for Git and Mercurial
- tools for SQL developers have been improved
Turtle-Grafik
Die Turtle-Grafik (engl. turtle, zu Deutsch Schildkröte) ist eine Idee von Seynour Papert, der damit ein Lehrmodell für den Unterricht schaffen wollte. Populär wurde der Turtle sicherlich durch die „Kindersprache“ LOGO. Aber auch anderswo wurde mit dem Turtle viel gearbeitet. Und so erhielt er viele Namen. Eingeführt in der Oberstufe durch einen Roboter (Robi oder so) bzw. den Hamster Nikki.
Das Turtle-Grundprogramm
Um den (das?) Turtle zu steuern, brauchen wir lediglich ein paar Unterprogramme:
- void turnright(int winkel)
- void turnleft(int winkel)
- void forwd(float step)
- void back(float step)
Die Implementierung dieser grundlegenden Befehle ist sehr einfach. Wir werden dazu den Winkel und die Position als lokale Variablen einführen und darauf zurückgreifen. Die Unterprogramme werden die Variablen angle, x, y‚ oldx, oldY nutzen:
import java.awt.AWTEvent; import java.awt.Frame; import java.awt.Graphics; import java.awt.event.WindowEvent; @SuppressWarnings( "serial" ) public class Turtle extends Frame { private double angle = 0, x = 300, y = 300, oldX = 300, oldY = 300; private Graphics graphics; // Initialisiert Turtle-Grafik und bringt Frame auf den Schirm public Turtle() { setTitle( "Turtle-Grafi" ); setSize( 600, 600 ); enableEvents( AWTEvent.WINDOW_EVENT_MASK ); setVisible( true ); } public void setGraphics( Graphics graphics ) { this.graphics = graphics; } // Methoden, die den Stift bewegen public void turnright( double degree ) { if ( (angle += degree) > 360 ) angle %= 360; } public void turnleft( double degree ) { if ( (angle -= degree) < 0 ) angle %= 360; } public void forwd( double step ) { back( -step ); } public void back( double step ) { oldX = x; oldY = y; x -= step * Math.sin( Math.toRadians( angle ) ); y += step * Math.cos( Math.toRadians( angle ) ); graphics.drawLine( (int) x, (int) y, (int) oldX, (int) oldY ); } // Events vom Schließen des Fensters abfangen @Override protected void processWindowEvent( WindowEvent e ) { if ( e.getID() == WindowEvent.WINDOW_CLOSING ) System.exit( 0 ); super.processWindowEvent( e ); } }
Mit dieser kleinen Basisklasse ist schon eine Menge machbar, wie es uns der folgende Abschnitt uns zeigen wird.
Dreiecke und Kurven
Zum Einstieg in die Turtle-Grafik eignen sich rekursive Grundstrukturen. Dazu bieten sich Dreiecke und Kurven besonders an.
Dreiecke
Das erste darzustellende Objekt wird ein Dreieck sein. Im Dreieck werden weitere Dreiecke erscheinen, was durch Rekursion ein leicht lösbares Problem ist. Die Dreiecke bestehen dabei aus wiederum drei Dreiecken mit der halben Seitenlänge, die in den Spitzen sitzen. Schauen wir uns einmal das Listing an:
import java.awt.Graphics; final class Dreiecke extends Turtle { public static void main( String args[] ) { new Dreiecke(); } void tri( float len, int deep ) { if ( deep != 0 ) { for ( int i = 1; i < 4; i++ ) { forwd( len ); turnright( 120 ); // (a) tri( len / 2, deep - 1 ); // (b) } } } public void paint( Graphics g ) { setGraphics( g ); tri( 100, 2 ); } }
Werden die die Zeilen (a) und (b) vertauscht, so erscheinen die kleineren Dreiecke in die größeren geklappt.
Mit
tri(len/2, deep-1); back(len); turnright(120);
in der for-Schleife erhalten wir einen weiteren Effekt, nach dem Zeichnen einer Seite wird der Turtle rückwärts gesetzt. Somit ist nicht das innere des Dreieckes gefüllt, sondern die Seiten wandern nach außen.
Peano-Kurve
Bisher wurden von uns nur gleichseitige Dreiecke untersucht. Die Peano-Kurve ist ebenfalls aus einem Dreieck zusammengesetzt, jedoch ist hier die Grundfigur gleichseitig und rechtwinklig. In einem Dreieck passen zwei weitere. Auffallend ist, dass sich Peano-Kurven immer schneiden, andere Kurven machen dies — wie wir sehen werden — nicht immer. Die Peano-Kurve ist das beste Beispiel einer flächendeckenden Kurve.
import java.awt.*; final class PeanoKurve extends Turtle { final static double SQRT2 = Math.sqrt( 2 ); public static void main( String args[] ) { new PeanoKurve(); } void peano( double len, int deep ) { if ( deep != 0 ) { forwd( len ); turnleft( 135 ); peano( len / SQRT2, deep - 1 ); forwd( len / SQRT2 ); turnleft( 90 ); peano( len / SQRT2, deep - 1 ); forwd( len / SQRT2 ); turnleft( 135 ); } } public void paint( Graphics g ) { setGraphics( g ); peano( 200, 5 ); } }
Koch-Kurve
Das Idee einer Koch-Kurve ist einfach: Ersetzte jede gezeichnete Strecke durch eine Grundfigur. Diese Grundfigur setzt sich aus einer kleineren Grundfigur zusammen, usw. Im Gegensatz zu den anderen Programmen, wird nicht auf jeder Schachtlungstiefe gezeichnet, sondern nur auf der Untersten. Das bedeutet, nur die kleinen Strukturen kommen auf den Schirm. Zur Verdeutlichung das folgende Programm:
import java.awt.*; final class KochKurve extends Turtle { public static void main( String args[] ) { new KochKurve(); } void koch( double len, int deep ) { if ( deep != 0 ) { koch( len / 3, deep - 1 ); turnleft( 60 ); koch( len / 3, deep - 1 ); turnright( 120 ); koch( len / 3, deep - 1 ); turnleft( 60 ); koch( len / 3, deep - 1 ); } else // deep == 0 forwd( len ); } public void paint( Graphics g ) { setGraphics( g ); koch( 200, 3 ); } }
Die immer gern gesehene Schneeflocke erhalten wir durch zusammenfügen der drei Seiten, die durch die Funktion koch() einzeln gezeichnet wurden. Die paint(…)–Methode erweitern wir durch eine kleine Schleife.
public void paint( Graphics g ) { setGraphics(g); for (int i=0; i < 3; i++) { koch(100, 2); turnright(120); } }
Hilbert-Kurven
Mit der Hilbert-Kurve wollen wir das Kapitel der Kurven beenden. Das Grundprinzip wurde deutlich und die wichtigsten Namen genannt — Koch, Peano und Hilbert. In der Hilbert—Kurve wird nun in den Ecken die Funktion wieder rekursiv aufgerufen, gezeichnet wird in jeder Schachtelungstiefe. Hier gilt es sich zu merken: Die Kurve hat weder Berührungspunkte noch Schnittpunkte.
import java.awt.*; final class HilbertKurve extends Turtle { public static void main( String args[] ) { new HilbertKurve(); } void hilbert( double len, int deep, int direction ) { if ( deep > 0 ) { turnleft( direction * 90 ); hilbert( len, deep - 1, -direction ); forwd( len ); turnright( direction * 90 ); hilbert( len, deep - 1, direction ); forwd( len ); hilbert( len, deep - 1, direction ); turnright( direction * 90 ); forwd( len ); hilbert( len, deep - 1, -direction ); turnleft( direction * 90 ); } } public void paint( Graphics g ) { setGraphics( g ); hilbert( 50, 4, 1 ); } }
Ist die Variable direction gerade, so beginnt die Hilbert-Kurve mit einer Links-‚ andernfalls mit einer Rechtsdrehung. Das Programm enthält noch einen Fehler, welchen?
Die ersten Bäume
In diesem Abschnitt wollen wir uns mit Bäumen beschäftigen. Sie sind ein ebenso wie die Kurven ein gutes Beispiel der Turtle-Grafik.
Ein simpler Baum
Beginnen wir mit einem einfachen Baum, wo noch nicht Viel nötig ist. Hier sind einfach ein paar Turtle-Befehle hintereinander gereiht worden.
public void tree( float len ) { turnleft( 45 ); forwd( len ); back( len ); turnright( 90 ); forwd( len ); back( len ); turnleft( 45 ); }
Rekursion bringt’s
Nun kann es mit der Baumgenerierung so richtig losgehen. Ein Baum ist ein einfaches Geflecht aus Ästen. Das Gute daran: jeder Ast hat weitere Äste. Das hört sich natürlich schon nach Rekursion an. Wir deklarieren ein Unterprogramm tree()‚ dass eine V-förmige Grundstruktur zeichnet. Beim Zeichnen jedes weiteren Astes wird wieder tree() aufgerufen, bis die Länge eines Astes auf unter 10 schrumpft.
import java.awt.*; final class Tree extends Turtle { public static void main( String args[] ) { new Tree(); } void tree( double len ) { if ( len > 10 ) { turnleft( 45 ); forwd( len ); tree( len / 2 ); back( len ); turnright( 90 ); forwd( len ); tree( len / 2 ); back( len ); turnleft( 45 ); } } public void paint( Graphics g ) { setGraphics( g ); tree( 150 ); } }
Anstatt mit einer Zweiglänge abzubrechen, kann auch nach Erreichen einer bestimmten Schachtelungstiefe — also die Anzahl rekursiver Aufrufe — abgebrochen werden. Dies wollen wir jetzt einmal versuchen. Zu der Länge eines Astes soll die Schachtelungstiefe und der Zeichenwinkel hinzukommen.
Um etwas Spannung in den Algorithmus zu bekommen, lassen wir den Winkel immer etwas größer werden. Das Ganze sieht dann so aus:
void tree2( float len, int deep, int angle ) { if ( deep != 0 ) { turnleft( angle ); forwd( len ); tree2( len / 2, deep - 1, angle + 3 ); back( len ); turnright( 2 * angle ); forwd( len ); tree2( len / 2, deep - 1, angle + 3 ); back( len ); turnleft( angle ); } }
Und wir erhalten mit dem Unterprogramm und der Zeile tree2( 100, 6, 20 ) einen kleinen Baum — doch irgendetwas fehlt! Es ist der Stamm. Um ihn anzufügen bedarf es lediglich kleiner Änderungen.
void tree3( double len, int deep ) { if ( deep != 0 ) { forwd( len ); turnleft( 45 ); tree3( len / 1.5, deep - 1 ); turnright( 90 ); tree3( len / 1.5, deep - 1 ); turnleft( 45 ); back( len ); } }
Doch auch mit Stamm wirkt der Baum zu künstlich. Um dem etwas entgegenzuwirken, wollen wir den Abknickwinkel und ein Verkürzungsverhältnis mit einbauen. Zudem soll der rechte und linke Winkel ein anderer sein. Sie sollen dem Unterprogramm übergeben werden. Hier das Ergebnis:
void tree4( double len, int deep, int angleL, int angleR, double lenL, double lenR ) { if ( deep != 0 ) { forwd( len ); turnleft( angleL ); tree4( len * lenL, deep - 1, angleL, angleR, lenL, lenR ); turnright( angleL + angleR ); tree4( len * lenR, deep - 1, angleL, angleR, lenL, lenR ); turnleft( angleR ); back( len ); } }
Hier können wir viele Parameter variieren und bekommen eine Vielzahl von Bäumen. Mit tree(100, 6, 30, -5, 0.5, 0.75) generieren wir einen Baum im Wind.
Mehr Formenvielfalt
Leider lässt auch dieser Baum seine Herkunft nicht verleugnen. Die Äste sind noch zu gleichmäßig. Um dem ein Ende zu setzen, werden Zufallswerte eingesetzt, damit ein Baum öfters einmal anders aussieht. Um Zufallszahlen mit Hilfe von Funktionen der Java-Bibliothek nutzen zu können, ist das Mathe-Paket einzubinden. Die Funktion rand() wird dann Zufallszahlen zwischen Null und Eins liefern. Sie können als Multiplikatoren zu der Länge oder dem Winkel genommen werden. Die Bilder bekommen dadurch eine ganz andere Wirkung.
Eine ganz andere Möglichkeit ist das Abweichen vom V-förmigen Grundmuster. Es ist aus verschiedenen Gründen sinnvoll, von dieser Form abzuweichen, und eine asymmetrische Figur zu wählen. Die Symmetrie wird insofern gebrochen, dass nicht auf jedes V ein weites folgt, die linke Seite bleibt etwas „unterentwickelt“. Ziel ist ein harmonischeres Bild, und eine natürlichere Form.
void tree5( double len, int deep, int angle, double factor ) { if ( deep != 0 ) { turnleft( angle ); forwd( len ); tree5( len * factor, deep - 1, angle, factor ); back( len ); turnright( angle * 2 ); forwd( len ); turnright( angle ); forwd( len ); tree5( len * factor, deep - 1, angle, factor ); back( len ); turnleft( angle * 2 ); forwd( len ); turnleft( angle ); forwd( len ); tree5( len * factor, deep - 1, angle, factor ); back( len ); turnright( angle * 2 ); forwd( len ); tree5( len * factor, deep - 1, angle, factor ); back( len ); turnleft( angle ); back( len ); turnright( angle ); back( len ); turnleft( angle ); } }
Ein Beispielaufruf: tree5( 50, 4, 30, 0.6 ),
L-Systeme
Bisher war die Umsetzung von Wachstumsprozessen ein aufwändiges Unterfangen. Anstatt Zwei rechts, zwei links zu diktieren, ist es wünschenswert eine Sprache zur Formulierung von Wachstumprozessen einzuführen. Immer ein Programm vor Augen zu haben welches mit Schlüsselwörtern wie forwd(), left()ist auch vom mathematischen Gesichtspunkt her nicht sinnvoll. Im Jahre 1968 führte der Biologe Aristid Lindenmayer die sogenannten L-Svsteme (Lindenmayer-Systeme) zur Beschreibung von Wachstumsprozessen ein. Dieses System entpuppt sich bei näherem Hinschauen als eine kontextfreie Grammatik (CFG). Wie bei der Grammatik so üblich besteht das L-System aus einem Tupel, bestehend aus einem Alphabet V = {a1, …, an}, Produktionsabbildungen P: V → V* wobei V* die Menge aller bildbaren Zeichenketten beschreibt. Mit einer Zeichenkette werden nun die Wachstumsprozesse beschrieben. Dazu werden Turtle-Kürzel eingeführt, die in folgender Tabelle aufgelistet sind.
Zeichen: Reaktion
F: um Schrittweite l nach vorne
f: hebe Schwanz und zeichne nicht
+: Winkel delta gegen den Uhrzeigersinn
-: Winkel delta im Uhrzeigersinn
Tabelle: Befehle des Automaten
Die Konstanten l und d sind Länge und Winkel.Die Zustände können als Tupel verwaltet werden. Die Einträge: x bzw. y ist die Koordinate, alpha der Winkel. Der Start ist mit (0,0,0) festgesetzt. Nach Rechts wird also losgelaufen. Die oben aufgelisteten Operationen angewandt, verändern das Tupel wie in der folgenden Tabelle angegeben.
Befehl: Zustand geht über in
F: (x + l * cos(alpha) , y + l * sin(alpha), alpha)
f: (x + l * cos(alpha), y + l * sin(alpha), alpha)
+: (x, y. alpha + delta)
-: (x, y. alpha – delta)
Tabelle: Änderung des Zustandes
Das L-System wird auch unter dem Namen Graftal geführt. Gelegentlich tauchen auch die Ziffern 0 und 1 auf, um keinen bzw. einen Schritt in die vorgegebene Richtung zu gehen. Eckige Klammern werden ebenso verwendet wie kennengelernt. Beispiel: 11[11]1. Gehe zwei Schritte nach vorne, zeichne dann einen Ast (die Verzweigung) von 2 Einheiten Länge, kehre zum Verzweigungspunkt zurück und ergänze den Stamm um eine Einheit. Einige bekannte Kurven sollen nun in L-System-Notation verdeutlicht werden.
Koch
Wir erinnern uns da sicherlich noch an die Koch-Kurve, bestehend aus 4 Strecken. Der Winkel der Strecken betrug immer 60 Grad, sodass wir in unserem System delta = 60° setzen können. Der Erzeugungs-String ist dann F+F–F+F. Die Abarbeitung folgt von links nach rechts, wie ein normaler mathematischer Ausdruck. Die Interpretation dieses Strings: Der Turtle geht einen Schritt der Länge l vor, dreht sich dann um 60 Grad nach links, geht wieder einen Schritt voraus, dreht sich zweimal um 60 Grad (also dann um 120 Grad) nach links, geht anschließend einen Schritt vor, um dann nach einer erneuten Drehung und Schritt voran zum Ende zu kommen. In der oben geschriebenen Schreibweise hätten wir also eine Produktionsregel, die in der Informatik die Schreibweise F → F+F–F+F bekäme.
Sierpinski-Pfeilspitze
Die gesamte Information über den Aufbau eines Objektes fasst man nun in einem Axiom zusammen. Somit wird die Beschreibung eines Objektes sehr kurz und kann in einer Tabelle leicht beschrieben werden.
Axiom: L
Produktionsregeln: L → +R-L-R+
R → -L+R+L
delta: 60°
Man sieht an diesem Beispiel, wie günstig es ist, mehrere Variablen einzuführen, um das Bild etwas übersichtlicher zu gestalten.
Achtung! Obwohl es nach einer Endlos-Verschachtelung aussieht — L ruft R auf und wieder umgekehrt — läuft das Programm trotzdem zu Ende. Es ist vielmehr dem Programmierer überlassen eine Abbruchbedingung zu implementieren. So beispielsweise das Abbrechen bei einer bestimmten Schachtlungstiefe oder Stammlänge.
Drachen-Kurve
Axiom: D
Produktionsregeln: D → -D++E
E → D–E+
delta: 60°
Bäume und Büsche
Nachdem wir das Grundsystem kennengelernt haben, dürfte es nicht schwerfallen, fraktale Gewächse zu entwickeln. Die Frage ist hier nur: Wie kann man eine Struktur, die sich verzweigt in einer Zeichenkette darstellen, die das L—System letztendlich verwendet? Notwendig dazu ist die Einführung zweier Symbole: [ und ]. Gelangt der Turtle bei der Interpretation der Zeichenkette an eine eckige Klammer, so muss er die Position und die Richtung des Turtles sichern und später wieder restaurieren. Nun hier ein Beispiel für eine krautartige Pflanze:
Axiom: F
Produktionsregeln: F —> F[+F]F[—F]F
delta: 25,7°
und noch ein Kraut:
Axiom: B
Produktionsregeln: F → FB
B → F[+B]F[—B]+B
delta: 25,7°
Bisher waren die vorgestellten Systeme immer deterministisch, das heißt es gab ein absehbares Ende und eine voraussehbare Form. Wenn der Zufall in ein L-System einzieht, so nennt man dies nicht-deterministisch Systeme stochastisch. Doch wenn der Zufall einfließt ist eine Aussage über das Wachstum schwierig. Dennoch ist es wichtig zu bestimmen wie groß die Wahrscheinlichkeit sein soll, dass ein Ereignis eintritt. Wenn der Baum z.B. nach rechts driften soll, so kann man Umgangssprachlich sagen: In 2 von drei Fällen gehe nach rechts. Um dies auszudrücken erweitern wir die Schreibweise ein wenig, und fügen die Wahrscheinlichkeit mit an. Hier das Beispiel von Kraut Nr. 3. (Wahrscheinlichkeit wurde
mit R abgekürzt.)
Axiom: F
Produktionsregeln: F → F[+F]F[—F]FR 1/3
F → F[+F]FR 1/3
F → F[—F]FR 1/3
delta: 25,7°
Die Anzahl der Produktionsregeln bestimmt also immer den Nenner des Bruches.
Etwas entfernt von der Pflanzen soll abschließend das L-System einer zufälligen Koch-Kurve aufgezeigt werden:
Axiom: F
Produktionsregeln: F → F-F++F—FR 0.5
F → F+F–F+FR 0.5
delta: 60°
Inselupdate: Wie wo was dynamisch binden
Es gibt bei Methoden von konkreten Klasse, abstrakte Klassen und Schnittstellen Unterschiede, wo der Aufruf letztendlich landet. Nehmen wir folgende Methode an:
void f( T t ) {
t.m();
}
Fordert die Methode ein Argument vom Typ T und ruft auf dem Parameter t die Methode m() auf, so können wir folgendes festhalten:
· Ist T eine finale Klasse, so wird immer die Methode m() von T aufgerufen, da es keine Unterklassen geben kann, die m() überschreiben.
· Ist T eine nicht-finale Klasse und m() eine finale Methode, wird genau m() aufgerufen, weil kein Unterklasse m() überschreiben kann.
· Ist T eine nicht-finale Klasse und m() keine finale Methode, so könnten Unterklassen von T m() überschreiben und t.m() würde dann dynamisch die überschriebene Methode aufrufen.
· Ist T eine abstrakte Klasse und m() eine abstrakte Methode, so wird in jedem Fall eine Realisierung von m() in einer Unterklasse aufgerufen.
· Ist T eine Schnittstelle, und m() keine Default-Implementierung, so wird in jedem Fall eine Implementierung m() einer implementierenden Klasse aufgerufen.
· Ist T eine Schnittstelle, und m() eine Default-Implementierung, so kann t.m() bei der Default-Implementierung landen, oder bei einer überschriebenen Version einer implementierenden Klasse.
GWT 2.7 RC 1
Oracle Java Mission Control Tutorial von Marcus Hirt online
http://hirt.se/blog/?p=611. 45 Seiten Text und umfangreiche Workspaces zum Nacharbeiten vom JMC. Prima, davon müsste es mehr geben.