Zum Einstieg

Logen Sie unter Linux ein, öffnen Sie ein Terminal und starten Sie den Editor Ihrer Wahl, oder eine IDE wie eclipse. Legen Sie eine Klasse Hello mit einer main Methode an, die eine kurze Begrüßung ausgibt:

In die Datei schreiben Sie den Programmcode, z.B. das folgende Hello-World-Programm:

public class Hello {
  public static void main(String[] args) {
    System.out.println("Hello, Java World!");
  }
}

Falls Sie Ihr Sourcefile mit einem Text-Editor (vi, emacs, jed, pico) erstellt haben, dann können Sie Ihr Programm mit

javac Hello.java

compilieren und mit

java Hello

starten. Es sollte auf den standard output (d.h. in diesem Fall in Ihrem Login-Fenster) den Hello-Text ausgeben.

Zur Übung: erweitern Sie dieses Programm, sodaß es die Begrüßung 10 mal ausgibt. Schlechte Lösung: die println-Zeile kopieren...

Building Blocks - Divide and Conquer

Gehen Sie bei der Entwicklung einer Problemlösung wenn möglich in kleinen Schritten vor. Teilen Sie das Problem in kleinere Aufgaben. Das hat mehrere Vorteile:

Argumente auf der Kommandozeile

Die meisten Programme erwarten irgendwelche Eingaben. Diese können z.B. als Argumente auf der Kommandozeile mitgegeben werden.

Schreiben Sie ein Programm, das als einziges Argument eine natürliche Zahl n erwartet, und dann alle natürlichen Zahlen kleiner gleich n ausgibt, die Primzahlen sind. Eine Primzahl ist nur durch sich selbst und Eins teilbar.

Performance und Just In Time Compiler

Das sehr rechenaufwendige naive Primzahlenprogramm vom vorigen Beispiel wurde auf genau gleiche Art in Java und in C programmiert (primes.c); hier sind die Laufzeiten in Sekunden:

nJavaC
100 0.599 0.011
200 0.631 0.005
300 0.534 0.006
1000 0.613 0.031
2000 0.814 0.183
3000 1.105 0.257
10000 3.251 2.596
20000 11.069 10.341
30000 23.977 23.243

Es zeigt sich, daß durch den JIT (Just In Time) Compiler der Java Runtime Environment eine Performance erreicht wird, die jener von in Maschinencode kompilierten Sprachen wie C nahekommt; durch den Startup-Overhead der JRE von ca. 0.6 Sekunden allerdings erst ab einer bestimmten Problemgröße.

Eingabedaten über Standard Input

Der standard output dient zur Ausgabe von Texten; siehe voriges Beispiel. Der standard input ist eine einfache Möglichkeit, Eingabedaten an Programme zu übergeben. Die Syntax ist:

myprog < input.file

Schreiben Sie ein Java-Programm, das sowohl ein Argument s als auch den Standard Input f als Eingabe bekommt und jene Zeilen von f ausgibt, in denen der Text s vorkommt.

Unix Shell Scripts als Wrapper

Der Aufruf von Java-Programmen mit java ClassName ist etwas mühsam; Sie können aber ein kleines Shell-Skript schreiben, das Sie dann wie einen Unix-Befehl verwenden. Mit dem Editor Ihrer Wahl erstellen Sie z.B. eine Datei mygrep und schreiben dort hinein:

#!/bin/sh
java Grep $*

Speichern Sie ab, verlassen Sie den Editor und geben Sie dann ein:

chmod 755 mygrep

Nun können Sie Ihr mygrep wie das Unix-Grep verwenden:

mygrep print < Grep.java

In der ersten Zeile wird definiert, welcher Befehlsinterpreter verwendet wird, hier die Standard Shell sh. In der zweiten Zeile steht der Aufruf für Ihr Java-Programm, und $* bedeutet 'alle Argumente'.

HTTP

Mit den in java.net.* vordefinierten Klassen kann relativ einfach ein Programm geschrieben werden, daß einen auf der Kommandozeile mitgegebenen URL vom entsprechenden Webserver holt und den Inhalt auf den standard output ausgibt. Allerdings kann dabei einiges schiefgehen; deshalb müssen Sie mit Hilfe von Exceptions auf mögliche Fehler Rücksicht nehmen.

javadoc

Ein wesentlicher Teil der Systementwicklung ist die Dokumentation. Schlecht dokumentierte Systeme verursachen große Schwierigkeiten und damit Kosten bei der Wartung, der Erweiterung und der Anpassung an geänderte Anforderungen. Ihr Java-Code muß sorgfältig dokumentiert werden, sodaß andere ihre Arbeitsweise nachvollziehen können.

Mit dem Kommando javadoc werden HTML-Seiten erstellt, die aus dem Java-Code erzeugt werden. Alle Kommentare der Form /** ... */ werden verwendet. Daher sollte zumindest jede Klasse, jeder Konstruktor und jede Methode mit einem solchen Kommentar versehen werden. javadoc unterstützt auch weitere Optionen, siehe man javadoc.

Die HTML-Seiten werden standardmäßig im selben Verzeichnis wie die Quelldateien erstellt; das kann bald unübersichtlich werden. Mit der Option -d werden die HTML-Seiten in ein besonderes Verzeichnis gestellt:

javadoc -d /home/userid/www/projectid *.java

Document Similarity

Im Information Retrieval sind verschiedene Indikatoren für die automatisierte Bestimmung der Ähnlichkeit von Texten bekannt; einer davon ist der Jacquard-Koeffizient j(a,b). Die Texte a und b werden als Mengen von Wörtern betrachtet, d.h. keine mehrfachen Vorkommen. Die Schnittmenge von a und b ist s(a,b), das sind jene Elemente, die sowohl in a als auch b vorkommen; die Vereinigungsmenge ist v(a,b), das sind alle Elemente von a und b zusammen; dann ist j(a,b) = s(a,b) / v(a,b). Dieser Wert muß offensichtlich zwischen 0 und 1 liegen. Je mehr Wörter in beiden Texten vorkommen, desto ähnlicher sind die beiden Texte.

Verschlüsselung mit Block Cipher

Ein einfaches Verschlüsselungsverfahren ist die Block Cipher. Dabei wird der Klartext in Blöcke gleicher Größe geteilt und jeder Block unabhängig vom Rest verschlüsselt. Der Schlüssel ist eine beliebige Textfolge. Die Blockgröße entspricht der Schlüssellänge. Der Block Klartext m sowie der Schlüssel k werden in Bitfolgen übersetzt, die dann mit der XOR-Funktion in die verschlüsselte Form c gebracht werden. Die XOR-Funktion hat die Eigenschaft, daß der Klartext mit dem Schlüssel wieder zurückgewonnen werden kann: xor(m,k) = c und xor(c,k) = m.

Schreiben Sie ein solches Programm zur Verschlüsselung. Dabei sollen

  1. Texte zeilenweise abgearbeitet werden, d.h. jede Zeile wird wie eine eigene zu verschlüsselnde Nachricht betrachtet. Das Programm soll verschlüsselten Text liefern, der nur aus druckbaren ASCII Zeichen besteht. Sie können auf ein bestimmtes Alphabet einschränken, z.B. nur Kleinbuchstaben und Leerzeichen. Oder:
  2. es soll Byte-weise verschlüsselt werden, ohne Rücksicht auf plain text output.

Die Schlüssellänge soll beliebig sein. Ihr Programm soll vom standard input lesen und auf den standard output schreiben.

Die Kontrolle Ihres Programms ist wegen der genannten Eigenschaft der XOR Funktion einfach:

java BlockCipher beamMeUpScotty < input.txt 

liefert die verschlüsselte Version; nochmaliges Verschlüsseln muß wieder das Original liefern:

java BlockCipher beamMeUpScotty < input.txt | java BlockCipher beamMeUpScotty

Siehe auch: java.sun.com

Turingmaschine

Bei der Systementwicklung kommt es nicht nur auf die Beherrschung einer Programmiersprache an, sondern auch auf korrekte Problemdefinition und Analyse. Deshalb nun eine etwas schwierigere Aufgabe, die programmiertechnisch nicht viel Neues bringt, aber inhaltlich komplexer ist.

Eine Turingmaschine ist ein Gedankenmodell eines Rechners. Sie ist durch folgendes definiert:

Die Maschine hat ein Band, auf dem zu Beginn der Verarbeitung eine Folge von Eingabesymbolen steht. Der Kopf steht anfangs über dem ersten Symbol am linken Rand der Folge. Die Folge ist konventionsgemäß links und rechts von unbegrenzt vielen Leersymbolen e umgeben. Als Eingabesymbole finden oft 0 und 1 Verwendung.

Die Maschine hält, wenn sie in einen der Endzustände kommt (in diesem Fall sagt man: sie akzeptiert die Eingabe), oder wenn kein Übergang definiert ist (in diesem Fall sagt man: sie akzeptiert die Eingabe nicht).

Programmieren Sie einen Simulator für eine solche Turingmaschine in Java! Die notwendigen Eingaben erfolgen auf der Kommandozeile, für jeden Verarbeitungsschritt soll der Folgezustand und das aktuelle Band auf den Standardoutput ausgegeben werden.

Beispiel für Turingmaschine

Die Maschine hat die Zustände A und E; der Anfangszustand ist A und der Endzustand ist E. Die Menge der Bandsymbole ist {0,1,e}. Es sind folgende Übergänge definiert:

A,0 --> A,1,R
A,1 --> A,0,R
A,e --> E,e,L

Simulieren wir die Arbeitsweise der Maschine für die Eingabe e0101e (den Schreib/Lese-Kopf symbolisieren wir durch eckige Klammern). Er steht konventionsgemäß zu Beginn der Verarbeitung am linken Rand der Eingabe. Nun suchen wir für die Kombination aus aktuellem Zustand und Bandsymbol nach einem Übergang; dann schreiben wir den neuen Zustand und das neue Band in die nächste Zeile, usw:

A  e[0]1 0 1 e
A  e 1[1]0 1 e
A  e 1 0[0]1 e
A  e 1 0 1[1]e
A  e 1 0 1 0[e]
E  e 1 0 1[0]e

Wir halten im Endzustand. Die Turingmaschine ersetzt offensichtlich alle 1 durch 0 und alle 0 durch 1.

Links

Offenlegung gem. §25 MedienG