Ursprungsmitteilung
Thema Java-Programme ohne main() - JavaOC 
Autor Aegidius Plüss 
Eingangsdatum 2006-02-11 17:38:12.0 
Mitteilung 1 Das entrümpelte Hello-World

Jede Lehrperson wird beim Vorbereiten eines Programmierkurses für Anfänger vor die heikle Frage gestellt, welches der methodisch beste Einstieg sei. Es ist hinreichend bekannt, dass die allererste Lektion bei den Kursteilnehmern bereits wichtige Weichen stellt und in der Regel Begeisterung oder Ablehnung auslöst, die entscheidend für den weiteren Lernprozess und damit für den Erfolg oder den Misserfolg des Kurses sein kann. Gemäß den Thesen meines Buches "Java - exemplarisch" ist es ein wichtiges didaktisches Prinzip, im Programmierunterricht Vorwärtsreferenzen möglichst weitgehend zu vermeiden, das heißt insbesondere, den Anfänger nicht durch unverständlichen oder schlecht erklärbaren Programmcode zu frustrieren.

Für die früher unterrichteten Programmiersprachen wie Basic stellt diese These keine besondere Hürde dar, denn das einfachste Programm besteht aus einer Aneinanderreihung von einzelnen Programmanweisungen, die beim Start des Programms sequentiell abgearbeitet werden. In Pascal, der ehemals beliebtesten Programmiersprache im Ausbildungsbereich, ist das Programm in einen begin-end-Block einzubetten, was von jedem Anfänger als völlig natürlich empfunden wird. In C/C++ sieht die Sache bereits etwas komplizierter aus, denn hier muss jedes Programm eine standardisierte main-Funktion enthalten, die als erste beim Programmstart ausgeführt wird. Für GUI-basierte Programme wird beim Start üblicherweise eine vom Betriebssystem standardmäßig geforderte Prozedur (Entry-Point) aufgerufen.

Für Java wird das Ganze nochmals komplizierter. Nicht nur, dass das einfachste Programm bereits aus einer Klassendeklaration gemäß den Vorgaben der objekt-orientierten Programmierung (OOP) besteht, noch schlimmer, das lauffähige Programm muss zwingend eine main-Methode mit den Schlüsselwörtern public, static, void und einem String-Array enthalten. Das besonderes Tückische daran ist, dass ein Programm gemäß den Grundprinzipien der OOP möglichst keine static-Methoden oder -Variablen aufweisen sollte, da diese gar nicht zu den Objekten, sondern zur ganzen Klasse gehören. Die Schizophrenie ist also total: man präsentiert in der ersten Stunde die OOP und die Wahl der Programmiersprache Java als Heilmittel gegen die veralteten prozeduralen Programmierprinzipien und wendet genau diese im ersten Programm an zentraler Stelle an. Das ist besonders dann krass, wenn man sich entscheidet, den ganzen Programmcode in der main-Methode abzuhandeln, wie dies in der üblichen Java-Version von Hello-World geschieht


class HelloWorld
{
  public static void main(String[] args)
  {
    System.out.print("Hello world!");
  }
}

Ein gewisser Ausweg aus diesem Dilemma bietet sich an, wenn man grundsätzlich von einer Applikationsklasse spricht und in der main-Methode lediglich eine Instanz dieser Klasse erzeugt.

class HelloWorld
{
  HelloWorld()
  {
    System.out.print("Hello world!");
  }

  public static void main(String[] args)
  {
    new HelloWorld();
  }
}

Natürlich muss nun erklärt werden, dass der Konstruktor eine Methode (mit dem Namen der Klasse) ist, welche bei der Erzeugung des Objekts automatisch ausgeführt wird. Dies ist zwar komplizierter als der Pascal-begin-end-Block, stellt aber keine besonders schwierige didaktische Hürde dar. Der Vorteil dieses Einstiegs ist, dass man gleich zu Beginn eine gesunde Vorstellung über Klassen und Objekterzeugung entwickelt.

Auch wenn man sich nun mit etwas fadenscheinigen Begründungen durch die Schlüsselwörter public, static und void geschummelt hat, wird es noch einmal heikel: Wie ist es zu verstehen, dass die Applikationsklasse eine Methode enthält, in der eine Instanz "von sich selbst" erzeugt wird? Ist das nicht etwa eine rekursive Programmstruktur? Hat dabei wirklich auch jeder fortgeschrittene Java-Programmierer einen klaren Durchblick?

Hier die etwas vereinfachte Erklärung, die für Anfänger völlig ungeeignet ist: Der Quellcode unseres Programms wird beim Compilieren mit dem Kommandozeilen-Tool javac (vielleicht verdeckt in einer Entwicklungsumgebung, IDE) in eine Java-Klasse umgewandelt, deren Bytecode sich in der Datei HelloWorld.class befindet. Beim Ausführen unseres Programms mit dem Kommandozeilen-Tool java oder javaw (ebenfalls vielleicht verdeckt in einer IDE) wird zuerst eine Java-Umgebung, eine so genannte JVM (Java Virtual Machine), erzeugt und der Bytecode in diese Umgebung geladen. Jetzt können (mit sog. Introspection/Reflection) alle Methoden der Klasse untersucht und, wenn eine main-Methode gefunden wird, diese aufrufen werden. Da es sich um eine statische Methode handelt, wird dabei nicht etwa ein Objekt der Klasse HelloWorld erzeugt. Erst der von uns eingefügte Code new HelloWorld() erzeugt eine Instanz und bewirkt dadurch die Ausführung des Konstruktors.

Hat man diesen Prozess einmal verstanden, so fragt man sich sehr schnell, ob denn all das so sein muss. Ein vereinfachtes Verfahren liegt auf der Hand, das in keiner Art und Weise die Java-Syntaxregeln verletzt. Unser Programm kommt auch ohne main-Methode aus, wenn ein Kommandzeilen-Tool javaoc (Java Object Creator) zur Verfügung steht, dass nach der Introspection selbst ein Objekt erzeugt und dabei den Konstruktor zur Ausführung bringt. Es ergibt sich dann lediglich ein Problem, wenn die Klasse mehrere (überladene) Konstruktoren aufweist. Auf Grund der Kommandozeilen-Argumente wird dann entschieden, welcher der Konstruktoren verwendet wird. Absolute Eindeutigkeit ergibt sich dabei allerdings nicht, da die Argumente nur Basistypen und Strings sein können. Das Hello-World kommt jetzt aber ohne jedes main aus.

class HelloWorld
{
  HelloWorld()
  {
    System.out.print("Hello world!");
  }
}

Wie wir sehen, ist dieses Programm nun viel besser für die erste Java-Lektion geeignet, und wir Lehrer werden vom Alptraum befreit, bereits in der ersten Stunde den Zugriffsbezeichner public, das Schlüsselwort static, den leeren (?) Rückgabetyp void und String-Arrays erklären zu müssen. Zusätzlich fördern wir gleich zu Beginn auf natürliche Weise einen guten Programmierstil, da mit javaoc immer ein Objekt erzeugt wird und der Anfänger wegen des fehlenden main gar nicht erst in Versuchung kommt, mit static Variablen und static Methoden dem Gedankengut der OOP aus dem Weg zu gehen.

2 Einstieg mit Console, GPanel und Turtle

Es ist eher schwierig, in Zeiten von feudalen grafikorientierten Benützeroberflächen Programmieranfänger von der Notwendigkeit des Programmierens und der Schönheit einer Programmiersprache zu überzeugen, wenn in einer zeilenorientierten Betriebssystemumgebung editiert, compiliert und Kommandozeilen-Programme, wie Hello-World diskutiert und ausführt werden. Viel motivierender ist es, bereits zu Beginn einfache Hilfsklassen einzusetzen, damit die Java-Programme automatisch mit einem GUI-Fenster ausgestattet werden. Wie im Buch "Java -exemplarisch" dargelegt, gibt es, je nach Vorlieben des Lehrers, verschiedene Möglichkeiten.

Konzept                                 Klassen/Package
arithmetisch/klassisch                  ch.aplu.util.console
grafisch                                ch.aplu.util.gpanel
spielerisch                             ch.aplu.turtle

Für jedes der drei Konzepte soll im Folgenden ein Beispiel gezeigt werden, dass sich an Stelle des Hello-World unter Verwendung von JavaOC als Einstieg eignet. Zuerst erinnern wir uns nostalgisch an die Begeisterung in den achtziger Jahren, mit einem Basic- oder Turbo-Pascal-Programm eine elementare Rechnung auszuführen. Ähnliches versuchen wir in Java.

// MyFirstC.java

import ch.aplu.util.*;

class MyFirstC extends Console
{
  MyFirstC()
  {
    print("Number of items: ");
    int items = readInt();
    print("Unit price: ");
    double price = readDouble();
    double toPay = items * price;

    print("Amount to pay: " + toPay);
  }
}

Hier wird zwar bereits eine Klassenableitung benötigt, dafür kann man aber darauf verzichten, eine Instanz der Console-Klasse zu erzeugen, wodurch sich die Syntax wesentlich vereinfacht, da wir auf den Punktoperator bei Methoden-Aufrufen verzichten können. Didaktisch ist dies sicher verantwortbar, man spricht davon, dass "ein Objekt (eine Instanz) der Klasse MyFirstC durch den Zusatz extends Console auch gleichzeitig ein Objekt der Klasse Console ist (is-a) und damit alle Fähigkeiten (Methoden) dieser Klasse besitzt(erbt) ".

Die Ausführung erfolgt erwartungsgemäß in einem ausgewachsenen GUI-Fenster.



Für viele Menschen sagt ein Bild bekanntlich mehr als tausend Worte. Daher eignen sich elementare Grafikprogramme sehr gut für den Einstieg in das Programmieren. Die Klasse GPanel stellt dazu ein einfach anzuwendendes Hilfsmittel zur Verfügung, das den aufwändigen Javacode für animierte Grafik und Doppelbufferung vollständig verdeckt. Auch in nächsten Beispiel wird die eigene Klasse MyFirstG von GPanel abgeleitet, damit man auf die mühsame Punktnotation verzichten kann.

// MyFirstG.java

import static java.lang.Math.*;
import ch.aplu.util.*;

class MyFirstG extends GPanel
{
  MyFirstG()
  {
    for (int i = 0; i < 200; i++)
    {
      move(random()random());
      fillCircle(0.07*random());
    }
  }
}

Bekanntlich machen Zufallsgrafiken nicht nur den Anfängern immer viel Spaß. Noch schöner ist es, den Kreisen eine zufällige Farbe zu geben. In einer völlig anderen Bedeutung als bei Methoden und Variabeln wird im import das dem Anfänger unbekannte Schlüsselwort static verwendet. Man kann dies so begründen, dass uns dadurch alle Fähigkeiten (Methoden) der Klasse Math zur Verfügung stehen, ohne dass wir dies explizite beim Aufruf "sagen" müssen (funktioniert erst mit Java 5). Lehrpersonen und Programmieranfänger haben gleichermaßen an solchen Programmierübungen viel Spaß und Freude, weil der Experimentierfreudigkeit kaum Grenzen gesetzt sind.



Langjährige Erfahrungen von Lehrkräften und Lehrerbildnern zeigen, dass sich unter Verwendung der Turtlegrafik ein ganzes Ausbildungskonzept mit Schwergewicht OOP realisieren lässt. Vorbildhaft zeigt uns dies Jarka Arnold von der Pädagogischen Hochschule Bern mit ihrem Lernprogramm zur Turtlegrafik (http://clab.phbern.ch). Dazu ist es allerdings nötig, dass die Turtle mehr als nur ein Zeichenstift für Richtungsgrafik ist. Vielmehr muss sie sich wie ein Objekt im Sinn der OOP verhalten. Insbesondere ist es nötig, dass für mehrere Turtles im gleichen Fenster eine sinnvolle Art der Überdeckung geregelt ist. Bereits für Anfänger wird klar, dass eine Vererbungshierarchie mit Software-Turtles analog wie eine Tierklassenhierarchie aufgefasst werden kann, wobei die neuen Tierklassen alle Fähigkeiten der hierarchisch höheren Klassen besitzen und überdies noch neue Fähigkeiten "dazu gelernt" haben. Im folgeden Beispielprogramm lernt eine JagTurtle, eine Spitze (jag) zu zeichnen. Damit kann sie dann beispielsweise leicht einen Stern malen.

// JagTurtle.java

import ch.aplu.turtle.*;

class JagTurtle extends Turtle
{
  JagTurtle()
  {
    for (int i = 0; i < 4; i++)
    {
      jag();
      right(60);
    }
    fill(-1010);
  }

  void jag()
  {
    forward(100);
    left(150);
    forward(100);
  }
}




Weitere Informationen und Download von JavaOC 
 
      
Antworten
Kein Eintrag