Schriftzug: Fachbereich Mathematik 
  UHH > Fakultäten > MIN-Fakultät > Mathematik > Java-Kurs   STiNE |  KUS-Portal |  Sitemap Suchen Hilfe there is no english version of this page  
 Index   Buch   DOC   API  Kompaktkurs JAVA   
 <<    <     >    >>  Bock / Löbnitz / Werner

6. Einfache Dateneingabe,
formatierte Datenausgabe,
Fehlerbehandlung

Eingaben von der Befehlszeile, über die Parameterliste oder über ein Fenster; Ausgabeformatierungen mit printf; Ausnahmesituationen abfangen: throws, try, catch, finally

Damit Programme möglichst flexibel arbeiten, soll die Dateneingabe häufig interaktiv gestaltet werden. Dazu kann ein Teil der zu verarbeitenden Daten bei Programmstart eingelesen und/oder während der Laufzeit vom Benutzer abgefragt werden. Das ist - auch wenn heutzutage interaktives Arbeiten an Computern selbstverständlich ist - keine triviale Aufgabe und erfordert gut durchdachte Programme mit relativ vielen Systemressourcen im Hintergrund - oder zumindest eine objektorientierte Programmiersprache, die - wie Java - auch Ausnahmen behandeln kann. Wir wollen hier vier unterschiedlich komplexe Möglichkeiten vorstellen, die vom Programmierer leicht einzubinden sind.

Seit JDK 5 bietet Java auch eine einfache Möglichkeit, Datenausgaben zu formatieren. Während in den alten Java-Versionen float und double Variablen immer mit der gegebenen Genauigkeit Werte mit allen Kommastellen ausdruckte kann der Programmierer nun mit Hilfe einfacher Anweisungen die Anzahl der Stellen und Darstellung der Werte beeinflussen und so auch für eine passende Rundung der Stellen sorgen.


6.1 Datenübergabe bei Programmstart

Wir gehen von einer einfachen Java-Klasse aus:

class Bsp_6_1 {
   public static void main( String[] args ) { 
      //das Programm 
   }
}

Die auf der Befehlszeile unserer Applikation eingegebenen Informationen (java Bsp_6_1 2007) werden in Java an ein String Array übergeben. Sie haben das schon gesehen und damit gearbeitet, allerdings noch keine Informationen an Ihre Applikation übertragen.

Warum übergibt man Daten an ein Zeichenketten-Array? Zum Einen, weil für Applikationen der Aufbau der Main-Methode vorgeschrieben ist. Man kann eine Klasse mit folgender main-Zeile zwar übersetzen ( public static void main( int[] args ) ), aber zur Laufzeit wird main dann nicht mehr als gültige Main-Methode einer Applikation erkannt ("java.lang.NoSuchMethodError: main"). Zum Anderen können über Zeichenketten beliebige Informationen transportiert und im Programm entsprechend umgeformt werden. Mit anderen Datenformaten wäre das nicht möglich, zumindest schwieriger.

Strings werden also als Folge beliebiger Zeichen gespeichert. Bis hierhin ist unsere Datenübergabe bei Programmstart ein unkritischer Vorgang. Nun zeigt sich gleich die erste Schwierigkeit: wie stellen wir sicher, dass die übergebenen Daten das gewünschte Format haben bzw. vom gewünschten Typ sind? Hier ist nun Programmiertechnik gefragt, abhängig von dem, was das Anwendungsprogramm bewerkstelligen soll und für wen es gedacht ist. Denkbar ist, dass z.B. eine unvollständige Eingabe bei Programmstart durch einen sofortigen Programmabbruch beantwortet wird. Oder aber, dass in der Folge vordefinierte Standardwerte die fehlende Eingabe ersetzen. Auch könnte man nach einfacher Überprüfung der Daten eine Eingabe erneut anfordern.

Zunächst sollte man feststellen, ob die Anzahl bzw. der Umfang der Daten unserer Erwartung entspricht (im Beispiel erwarten wir nur einen Wert):

if ( args.length != 1 ) { //Fehlermeldung } else { //normaler Ablauf }

Gehen wir einmal von einer Anwendung aus, die einen Integer verarbeiten möchte, z.B. das Alter, Geburtsjahr oder die Matrikelnummer. Die uns übergebene Eingabe im Array args liegt in der 1. Speicherzelle des Felds: args[0]. Java kann geeignete Strings in Integer, Long, Double, Float wandeln indem es entsprechende Parser (Computersprache: Syntaxanalysierer) verwendet: Integer.parseInt(String), Double.parseDouble(String), Float.parseFloat(String), Long.parseLong(String). Doch was passiert, wenn der String unpassend ist? Derartige sogenannte Ausnahmen (Exceptions) werden in Java systematisch in bestimmten Objekten abgewickelt. Im Fehlerfall werfen die Parser ein Ausnahmesignal aus (throws IOException), das entweder vom Benutzer oder dem System abgefangen wird, im Zweifelsfall zu einem Programmabbruch führt.

Nun wollen wir feststellen, ob der übernommene Wert vom richtigen Typ ist. Wir machen uns die Befehle zur Behandlung von Ausnahmesituationen zu Nutze: try - catch - finally. Im try-Block werden die Befehle ausgeführt, die zu der Ausnahmesituation führen können. Im Fall der Ausnahmesituation, ausgelöst durch ein programm- oder benutzergesteuertes throws, wird der catch-Block ausgeführt. Der finally-Block ist optional. Falls vorhanden wird der dort enthaltene Programmcode in jedem Fall ausgeführt. Hier wird zunächst auf finally verzichtet.

int n, z=0;
try {
   n = Integer.parseInt( args[0] );    // wandle den String in Integer
   System.out.println("Integer ="+n);  // und falls das nicht geht,
} catch (NumberFormatException e) {    // melde eine Ausnahme
   System.out.println("Bitte genau eine ganze Zahl >= 0 angeben!");
   n = 0;
} //optional finally { ... }


6.2 Datenübergabe im laufenden Programm von der Befehlszeile

6.2.1 Verwendung von System.in, BufferedReader und readline()

Nun soll unser Programm Eingaben von der Befehlszeile abfragen. Es ist für Java kein triviales Unterfangen, Daten zeichenweise von der Tastatur zu lesen. Zwar steht ein vordefinierter Eingabe-Stream System.in zur Verfügung. Er ist aber nicht in der Lage, die eingelesenen Zeichen in primitive Datentypen zu konvertieren. Statt dessen muss zunächst eine Instanz der Klasse InputStreamReader und daraus ein BufferedReader erzeugt werden. Dieser kann dann dazu verwendet werden, die Eingabe zeilenweise zu lesen und das Ergebnis in einen primitiven Typ umzuwandeln.

Vergessen wir zunächst die zugrunde liegende komplizierte Technik und wenden uns einem einachen Kochrezept zu. Zunächst müssen wir die entsprechende Klassenbibliothek laden: java.io.* mit allem, was darin steckt. Dann müssen wir die Definition der main-Methode um die Behandlung von Ausnahmesituationen erweitern. Wie oben erwähnt muss der eingegebene Zeichenstrom in einem BufferedReader-Objekt gepuffert werden. Nach diesen Vorarbeiten können wir nun Methoden anwenden, die mit diesem Objekt verknüpft sind. Wir erkennen hier schon die wesentlichen Konzepte der Objektorientierung: Abstraktion, Kapselung, Wiederverwendung von Programmcode.

import java.io.*;
public class einfacheEingaben {
public static void main(String[] args)
   throws IOException {

   BufferedReader ein = new BufferedReader(
      new InputStreamReader(System.in));

      System.out.println("Eingabe: ");
      String a = ein.readLine();
      System.out.println(a);
   }
}


6.2.2 Verwendung von System.in und Scanner

Eine weitere einfach zu realisierende Möglichkeit, Daten von der Kommandozeile zu lesen, bietet seit Java 5 das Dienstprogramm Scanner. Dieses Klasse eignet sich besonders gut zum Einlesen primitiver Datentypen und von Strings. Für Programmierer interessant ist hierbeit die Möglichkeit, sogenannte Regular Expressions anzuwenden, um z.B. regelhaft eingerichtete Dateien gezielt auszulesen. Ebenso interessant ist die Möglichkeit, Zahlen im Format der eingestellten locale einzulesen.

Zunächst importieren wir java.util.Scanner und java.io.IOException. Wie Sie sehen, können wir auch gezielt einzelne Klassen laden, anstatt wie vorhin ganze Pakete. Scanner bietet Methoden zur direkten Umwandlung in die von uns gewünschten Datentypen. Hier verwenden wir netxInt(). In der API-Dokumentation finden Sie Hinweise auf andere: nextDouble(), nextFloat(), nextLine(), hasNext() u.v.a.m.

import java.util.Scanner;
import java.io.IOException;

public class einfacheEingaben2 {
public static void main(String[] args)
   throws IOException {

   int a;
      
   Scanner scanner = new Scanner(System.in);
   
   System.out.println("Bitte a eingeben: ");
   a = scanner.nextInt();
   System.out.println("a="+a);
   }
}


6.3 Fenster für Ein- und Ausgaben nutzen

Dieses letzte Beispiel für Benutzereingaben nutzt grafische Elemente und stellt im Hntergrund die größten Systemressourcen bereit. Auf wenn Java in der Zwischenzeit durch diverse Optimierungen, begünstigt durch immer schnellere Prozessoren und großzügige Speicher, sehr schnell ausgeführt wird, haftet dieser Programmiersprache immer noch der Ruf der Langsamkeit an. Dies nicht zuletzt wegen der im Hintergrund bereitgestellten Systemresourcen für grafische Anwendungen, insbesondere Swing. In der Zwischenzeit erhält man jedoch mit den aktuellen Java-Versionen ein ausgezeichnet optimiertes Produktpaket, die auf heute üblicher Hardware anderen Compilern und Programmiersprachen nicht nachsteht.

Swing versteckt vor uns in eindrucksvoller Weise Systemkomplexität und erlaubt mit einfachen Mechanismen die Nutzung von Ein- und Ausgabefenstern. Auch diese Technik wollen wir ausprobieren.

In diesem Beispiel müssen wir zunächst die Swing-Pakete importieren. Die seit JDK 1.2 verfügbaren Standarderweiterungen zu Java tragen Paketnamen, die mit javax. beginnen

import javax.swing.*;
public class dialog_ein_ausgabe {
   public static String InputWithDialog ( String hinweis ) {
      String eingabe;
      eingabe = JOptionPane.showInputDialog( hinweis );
      return (eingabe);
   }

   public static void OutputDialog ( String title, String ausgabe ) {
      JOptionPane.showMessageDialog(null, ausgabe, title, 
			   JOptionPane.INFORMATION_MESSAGE);
   }
   
   public static void main ( String[] args ) {
      String text; int i; boolean istEineGanzeZahl=false;
      text = InputWithDialog("Geben Sie bitte etwas ein:");
      try {
         i=Integer.parseInt(text); 
         text="Ergebnis: "+i+" * "+i+" = "+i*i;
         OutputDialog ("Ausgabe:", text);
         istEineGanzeZahl=true;
      } catch (NumberFormatException e){
         System.out.println("Es wurde keine ganze Zahl eingegeben!");
         text+="\n...ist keine ganze Zahl!";
      } finally {
         if (istEineGanzeZahl) OutputDialog ("Quadrieren", text);
         else OutputDialog ("Fehler", text);
      }
   }
}


6.4 printf-Formatierungen

Seit JDK 5.0 erlaubt Java die formatierte Ausgabe von Variableninhalten. Orientiert hat man sich dabei am printf der Programmiersprache C. Nicht nur der Name wurde übernommen sondern auch die wesentlichen Formatierungsoptionen.

Im nachfolgenden Beispiel wird nur ein kleiner Teil der mit printf möglichen Ausgabeformatierungen gezeigt. Details finden Sie (in englischer Sprache) im Java API unter java.util.Formatter

public class Bsp_6_4 {
   public static void main( String[] args ) { 
      int i, ii=0;
      try {
         if (args.length==0) ii=16;
         else ii=Integer.parseInt(args[0]);
      } 
      catch (NumberFormatException e) {
         System.out.println("keine gültige Datenübergabe");
         ii=16;
      } 
      finally {
         System.out.printf ("dezimal,  oktal, hexa.%n");
         for(i=0;i<ii;i++) {
            if (i%10==0) System.out.println("-------  ------  ----");
            System.out.printf ("%1$7d  %1$6o  %1$4x%n", i);
         }
      }
   }
}


Weiter mit Klassen und Objekte (Teil 1).


 Index   Buch   DOC   API  Kompaktkurs JAVA   
 <<    <     >    >>  Bock / Löbnitz / Werner

  Seitenanfang  Impressum 2009-03-14, wwwmath (WL)