Von C nach Java, Teil 1: Der schnelle Umstieg von der Kommandozeile aus

News  –  Kommentare

Der Beitrag ist Auftakt einer mehrteiligen Artikelreihe für Entwickler, die bereits viele und umfangreiche Programme in C entwickelt und immer schon neidisch, vielleicht sogar argwöhnisch auf die Kollegen geschielt haben, die scheinbar mühelos in Java die schönsten Oberflächen und Dialoge auf den Bildschirm zauberten.

Mit reinem C-Code und unter Verwendung von Bordmitteln eine Dialog-Anwendung auf den Schirm zu bringen ist mühsam. Unmöglich ist das nicht, schließlich enthalten die Bibliotheken der Betriebssysteme Windows und Unix reinen C-Code und die Schnittstellen zu deren Funktionen und zum System werden in reinem C am direktesten und unverfänglichsten bedient. Aber diese Bibliotheken sind nicht dafür ausgelegt, dass Entwickler sie für große Applikationen direkt ansprechen. Zu rudimentär und systemnah sind die Aufrufe.

Von C nach Java – Chronik der Artikel

  • Teil 1: Der schnelle Umstieg von der Kommandozeile aus
  • Teil 2: Files, I/O und eine Entwicklungsumgebung
  • Teil 3: HTML-Dokumente aus dem Internet laden
  • Teil 4: Datenkompression und Verschlüsselung
  • Teil 5: Wie eine grafische Oberfläche entsteht

Es mag die eine oder andere Graphics Library für C geben, die den Einstieg in die Programmierung einer GUI augenscheinlich zu vereinfachen vermag, jedoch dürfte die anfängliche Begeisterung schnell einer Ernüchterung weichen. Spätestens wenn es darum geht, die neuen Funktionen in eigenen Programmcode zu verpacken und das Ganze zu einem lauffähigen Programm zu machen, ist angesichts der Unmengen einzelner Dateien, eben solcher Unmengen an kryptischen Fehlermeldungen, die der Compiler bei etwaigen Übersetzungsversuchen auswirft, und völlig überfrachteten Programm-Codes die Vorfreude auf die schnelle Umsetzung dahin. Es schließen sich meistens Stunden um Stunden an Debugging-Sessions, Suchen von irgendwelchen Dokus im Netz und verzweifeltes Starren auf unverständlichen Sourcecode an, der bösartig vom Bildschirm den geröteten Augen des Entwicklers entgegenblinkt ... An ein lauffähiges Programm, das vielleicht sogar noch etwas sinnvolles tut, ist in diesen Augenblicken überhaupt nicht mehr zu denken.

Das hier – zugegebenermaßen etwas überspitzt – beschriebene Szenario verdeutlicht, dass C eben eine rudimentäre Programmiersprache ist, die in ihrem strukturellen Aufbau und in der Historie näher an Assembler als an einer modernen objektorientierten Programmiersprache angelehnt ist. Diese beliebige Offenheit und die "niedrige" Eintrittsschwelle bei erweiternden Bibliotheken erfordern einen extrem hohen Integrieraufwand und jede Menge Code für die Umsetzung vergleichbar kleiner Aufgabenstellungen. Doch zurück zu den "Bordmitteln".

Als Beispiel sei die Funktion CreateWindow() unter Windows genannt. Nahezu alle auf dem Bildschirm darstellbaren Objekte sind "Windows". Sei es der anklickbare Button, sei es die Liste, die beispielsweise aktuelle Börsenkurse zum Besten gibt, oder gar das Icon, das innerhalb eines Kontrollelements das Navigieren vereinfacht. Alle diese Objekte sind "Windows" und werden eben über die Funktion CreateWindow() erzeugt. Die Parameter für die Funktion, abhängig vom jeweiligen Typ des Windows, unterscheiden sich erheblich voneinander. Schnell wird das reine C-Programm, das einen Dialog mit einigen der Kontrollelemente erzeugt, sehr groß. Und hier war bislang nur von "erzeugen" die Rede, nicht etwa auch von "bedienen", denn diese Kontrollelemente wollen ja nicht nur gezeigt, sondern auch sinnvoll bedient werden. Spätestens jetzt erreicht die Größe des Quelltexts Rekordlängen, und die Wartbarkeit des Codes wird ein schier unerreichbares Unterfangen – und all das nur wegen eines kleinen Dialogs.

Das Zauberwort "Objekt" kam bereits jetzt einige Male vor. Im Grunde werden insbesondere bei grafisch orientierten Anwendungen Objekte beschrieben, angezeigt und bedient. Dialog sowie Kontroll- und Anzeigeelement sind Objekte. Was liegt demnach näher, als auch die Entwicklung von Programmen nahe an den Objekten zu betreiben. Genau das tun die längst etablierten, eben deswegen objektorientiert genannten Programmiersprachen C++ und Java.

Diese Artikelreihe soll den Übergang von C nach Java, nicht etwa nach C++ begleiten. Während C++ die prozedural orientierte Sprache C um die Fähigkeit erweitert, objektorientiert zu entwickeln, ist Java "von Natur aus" objektorientiert. In C++ sind nahezu noch alle C-Sünden möglich, weshalb damit eben auch ein nicht stringentes objektorientiertes Entwickeln, inklusive des Vermischens beider Ansätze, denkbar ist. Das geht in Java nicht (abgesehen vielleicht vom ausschließlichen Verwenden statischer Methoden, auf das die Reihe noch detaillierter eingeht).

Los geht es mit einem Beispiel aus der Praxis, das die ersten Schritte mit der Java-Umgebung aufzeigt. Um den Aufwand und zu Beginn aufkeimende Verwirrungen in Grenzen zu halten, ist eine sogenannte Kommandozeilen-Anwendung vorgesehen. Das Programm wird demnach von einer Shell aus gestartet, also innerhalb von Unix von einer bash und in Windows von einer Eingabeaufforderung aus.

Für die Beispiele in C ist ein GNU-C/C++-Compiler erforderlich, der sowohl unter Unix als auch für Windows (dort etwa innerhalb des Pakets mingw) frei verfügbar ist. Bei Java genügt für die ersten Schritte Oracles JDK (Java Developer Kit), das ebenfalls kostenlos erhältlich ist. Im weiteren Verlauf, wenn die Beispiele umfangreicher werden, kommt für die Java-Welt eine Entwicklungsumgebung zum Zuge, wobei der Autor sich für Eclipse entschieden hat, das intuitiv und einfach in der Bedienung ist.

Den Quelltext (C und Java) bearbeitet der Entwickler "klassisch" im Editor, von der Kommandozeile aus wird der Quelltext kompiliert und anschließend ausgeführt. Daran ist zu erkennen, dass bei Java das gleiche Prinzip vorherrscht wie in C: Quelltext, Compiler, Ausführung. Während aber der C-Compiler mehr oder weniger direkt ausführbaren Code erzeugt, bildet Java einen maschinenunabhängigen Zwischencode, den dann die virtuelle Maschine (VM) ausführt.

Zunächst zur Aufgabe. Es soll ein Kommandozeilen-Tool entwickelt werden, das analog dem type-Kommando unter Unix den genauen Pfad eines Kommandos beziehungsweise einer beliebigen Datei anzeigt, auf die ein sogenannter "Pfad" liegt. Das folgende Beispiel zeigt einen typischen Anwendungsfall:

$ type ls
ls is /usr/bin/ls
$

Das Programm entnimmt die zu suchenden Dateien oder Kommandos aus der Kommandozeile und durchsucht im Anschluss alle Verzeichnisse der "PATH"-Umgebungsvariablen. Es soll so geschrieben sein, dass es seinen Dienst unter jedem Unix- und jedem Windows-Betriebssystem verrichtet.

Gegenüber der existierenden POSIX-konformen Umsetzung im Unix soll das vorgestellte Programm noch einige Dinge mehr beherrschen, die diverse optionale Switches, die die Kommandozeile mit übergibt, bewerkstelligen sollen.

Es werden in diesem Artikel zwei Varianten vorgestellt: cType.c (C-Entwicklung) und jType.java (Java-Programmierung) – beide finden sich hier (nieden_insel.zip). Laufen sollen beide Varianten sowohl unter Windows als auch unter Unix. Für die C-Variante ist der Quelltext jeweils für die Zielplattform zu kompilieren, bei Java ist das kompilierte class-File auf beiden Betriebssystemen lauffähig. Bewusst seien also erfahrene C-Entwickler von irgendwelchen "Hello World"-Progrämmchen verschont.

Der Einstieg in das Programm ist in C die main-Funktion, die als Parameter die Anzahl der Argumente und einen Zeiger auf die Strings der Argumente mitbekommt:

int main(int argc, char *argv[]) {
...
}

In Java beginnt die Ausführung ebenfalls mit einer main-Funktion, allerdings sind bereits hier die Unterschiede deutlich. Ein Java-Programm ist immer eine Anwendungsklasse. Innerhalb ihr sucht die VM die statische Methode main(String[] args), die sie dann ausführt. Demnach sieht der Einsprung in das Programm so aus:

public class jType {
public static void main(String[] args) {
}
}

Die Anwendungsklasse jType muss also das gesamte Programm umschließen. Innerhalb der Klasse gibt es nun die "statisch" deklarierte Methode main(String[] args), die die VM aufruft.