Kurs:Java – ein schneller Einstieg/Rechnen mit eingegebenen Zahlen
Rechnen mit eingegeben Zahlen
BearbeitenFolgende scheinbar einfache Aufgabe ist zu lösen:
- Aufgabe:
- Einem Kunden soll auf seinen Rechnungsbetrag ein Rabatt gewährt werden.
- Der Rechnungsbetrag steht in einem Anzeigefeld (muss aber zu Testzwecken manuell eingegeben werden).
- Darunter der prozentuale Rabatt, ebenfalls in einem Anzeigefeld.
- Ein weiteres Anzeigefeld soll den endgültigen Betrag enthalten.
- Die gesamte Anwendung ist mit einer grafischen Benutzeroberfläche ausgestattet.
Der Lösungsansatz
BearbeitenZugegeben, die Aufgabe ist etwas komplexerer Natur, die Vorgehensweise noch nicht klar und die folgende Beschreibung ist als abschreckendes Beispiel gedacht. Sie enthält alle relevanten Begriffe, die in modernen Werkzeugen (Eclipse, NetBeans usw.) verwendet werden. Um diese Werkzeuge sinnvoll einsetzen zu können, müssen die Begriffe auch verstanden werden. Deshalb dient folgende "Beschreibung" erst einmal als "Sammlung von Begriffen" und keinesfalls als "Pflichtenheft":
- Das JRootPane[1] eines JFrames[2] wird mit einem JPanel[3] belegt. Letzteres erhält einen LayoutManager[4] der Klasse BorderLayout[5] zur Steuerung der Constraints der JComponents[6]. Dem JFrame wird ein interner, argumentativer Listener[7] in Form eines Adapters hinzugefügt und sofort mit dem WindowClosingEvent-Handler[8] belegt. Der erforderliche FocusListener[9] der JComponents wird als separate Klasse realisiert und ist auch für das ExceptionHandling[10] der Zahleneingabe verantwortlich. FocusRequest[11] liegt selbstverständlich auf dem JTextField[12] des Rechnungsbetrags.
Scheinbar hat diese Aufgabe mehr mit der amerikanischen Verballhornung der englischen Sprache gemein, als mit der Berechnung von Rabatten. Über die Berechnung wurde kein Wort verloren und wo kommen die Daten her? Irgendwo muss die Eingabe erfolgen, da liegt es nahe, ein eigenes Fenster mit den entsprechenden Möglichkeiten auszustatten. Nichts anderes wurde beschrieben. Außerdem dient dieser Ausflug in die GUI-Erstellung (GUI=GraphicalUserInterface) dem Kennenlernen der vorhandenen Dokumentation. Jedenfalls wird von der GUI-Programmierung noch die Rede sein.
Um die letzten eventuell vorhandenen Illusionen zu rauben: Java umfasst in der einfachsten Installation über 25000 Befehle. Ein Wortschatz also, den noch nicht einmal alle Menschen in ihrer Muttersprache besitzen. Es ist also unmöglich, den gesamten Umfang von Java jemals zu beherrschen. Um trotzdem Programme erstellen zu können, muss die Online-Dokumentation der APIs (API=ApplicationProgrammingInterface) bei jeder Programmierung vorhanden sein. Dort sind alle Klassen, Methoden, Variablen ... aufgeführt und kurz beschrieben. Die Navigation durch die API-Doc ist, mit einiger Übung, recht einfach.
Objekte mit Eigenschaften
BearbeitenIm "Lösungsansatz" sind alle Java-relevanten Begriffe hervorgehoben. Diese sind so auch in der API-Dokumentation (Application Programming Interface) vorhanden. Allerdings ist das Entscheidende dort nicht zu finden, nämlich wie die Daten in das Programm kommen. Von "Anzeigefeldern" war ursprünglich die Rede. Dabei handelt es sich um JTextFields[12], die für die Interaktionen natürlich eigene Methoden besitzen. Wie, warum, wieso, weshalb spielt für die gestellte Aufgabe eigentlich keine Rolle, denn zunächst muss die Berechnung selbst erfolgen. Berechnungen benötigen Werte. Werte basieren auf Daten. Daten müssen irgenwo herkommen. Aus den JTextFields. In diese gibt der Anwender seine Zeichenfolgen ein und genau diese gilt es entgegenzunehmen. Der eingegebene Text ist eine Eigenschaft (property) des JTextFields. Die Klasse JTextField stellt die erforderlichen Methoden bereit, also stehen sie in der API-Dokumentation. Hier ein erster "Merksatz" für die Arbeit mit Java und der API-Doc:
|
Für die Objekte der Klasse JTextField gilt zunächst einmal, dass es sich bei dem angezeigten Text um eine Eigenschaft handelt und deshalb sehr wahrscheinlich auch mit einer get-Methode ausgelesen werden kann. Tatsächlich bestätigt ein tiefergehender Blick in die API-Dokumentation diese Vermutung, denn es findet sich getText()[13] als eine von JTextComponent[14] geerbte Methode.
Es ist aber noch ein weiter Weg, bis diese Methode relevant wird. Wichtig ist zu diesem Zeitpunkt nur, in welcher Form die Daten vorliegen. Es handelt sich um Zeichenfolgen (getText() übergibt ein String[15]-Objekt).
Von der Zeichenfolge zum Wert
BearbeitenDie Ursprungsdaten sind also Zeichenfolgen. Leider können sie nicht, wie im vorangegangenen Abschnitt dem Compiler überantwortet werden, denn sie fallen ja während des laufenden Programms an. Zeichenfolgen sind Strings[15]. Es ist also sinnvoll, Zeichenfolgen als "Textwerte" anzusehen und sie über Referenzen wie Werte zu behandeln. Die erste Zeichenfolge soll die Bezeichnung „betragText“ erhalten und die zweite die Bezeichnung „rabattText“. Die entsprechende Sequenz lautet:
String betragText = "1234.56"; String rabattText = "7";
Die Typenbezeichnung String ist nicht so einfach wie die bereits bekannten (int, float ...). Zunächst fällt auf, dass der erste Buchstabe großgeschrieben ist. Das hat seinen Grund! Bei String handelt es sich bereits um eine Klassenbezeichnung, wodurch die beiden Referenzen „betragText“ und „rabattText“ Objekte referenzieren. Die Klasse String wird sehr häufig eingesetzt, weshalb ein Blick in die API-Dokumentation[15] durchaus lohnend ist.
Wie soll nun ohne Hilfe des Compilers aus einem String (ab jetzt ein Synonym für Text) eine Zahl werden? Die Klasse String besitzt scheinbar keine hilfreich erscheinenden Methoden. Überlegungen zum Typ des gewünschten Wertes führen weiter. Beim Betrag können Nachkommastellen auftreten, weshalb der Typ des Wertes mindestens „float“ sein muss. Ein Blick in die API-Dokumentation bringt scheinbar nichts, denn diese kleingeschriebenen Typenvereinbarungen stehen nicht drin. Aber da steht etwas über „Float“[16]. Wenn es eine Klasse „Float“ gibt, was soll dann dieses kleine „float“?
Unterschiede zwischen groß- und kleingeschriebenen „Typen“
BearbeitenEine sehr wichtige Regel, die hilft, "Anfängerfehler" zu vermeiden und viel Arbeit erspart:
|
In der Java-Notation sieht diese Regel ungefähr so aus:
float einWert = 123.45; Float eineZahl = new Float( "124.45");
Für das Beispiel werden zwei Zahlen benötigt, die beide vom Typ Float (NEIN!!! Objekte der Klasse Float) sind. Objekte brauchen ebenfalls eine Referenz die bisherige Sequenz kann erweitert werden.
String betragText = "1234.56"; String rabattText = "7"; Float betragZahl = new Float( betragText); Float rabattZahl = new Float( rabattText);
Nun ist es eigentlich nicht einzusehen, wieso mit diesen Zahlen nicht gerechnet werden könne. Zahlen sind mathematische Objekte (es sind immer nur Elemente von bestimmten Zahlenmengen). Rechnen ist ein Vorgang, der sich nur innerhalb bestimmter Zahlenmengen betreiben lässt. Zur Erinnerung: Ganze Zahlen sind nur bedingt teilbar (2/3=0). Rechnen ist nur mit Werten möglich. Es gilt also jetzt, den Wert der Zahlen (Float-Objekte) zu ermitteln. Werte sind Eigenschaften von Zahlen, also sollte es eine „get“-Methode geben, die den Wert dieses Objektes übergibt. Das englische Wort für Wert lautet „value“ und eine Suche in der API-Dokumentation unter der Klasse „Float“ nach „getValue“ ergibt nichts. Offensichtlich taugen die Merksätze auch nichts.
Mit etwas mehr Geduld und viel mehr Nachdenken zeigt ein Blick in die bisher aufgebaute Sequenz aber einen Hinweis. Aus dem "rabattText" wird ein Float-Objekt, obwohl kein Dezimalpunkt vorhanden ist. Das legt den Schluss nahe, dass Float-Objekte auch mit ganzzahligen Werte (int) klarkommen. Eine Folgerung daraus wäre, dieses Zahl-Objekt kann auch ganzzahlige Werte liefern. Also nach einer Methode namens „getFloatValue“ suchen. Bringt auch nichts, denn hier ist man von der schönen „get“-„set“-Regel abgewichen (warum weiß wohl Keiner mehr). Der Name der Methode lautet „floatValue“[17]. Übrigens gibt es die anderen Wertetypen ebenfalls, sie lauten dann entsprechend „intValue“, „doubleValue“ usw. Für jeden Werte-Typ existiert in Java das entsprechende Zahlen-Objekt. Jedes Zahlen-Objekt kann seinerseits jeden Werte-Typ liefern.
Die Sequenz kann um die Wertetypen erweitert werden.
String betragText = "1234.56"; String rabattText = "7"; Float betragZahl = new Float( betragText); Float rabattZahl = new Float( rabattText); float betragWert = betragZahl.floatValue(); float rabattWert = rabattZahl.floatValue();
Weil es einfacher ist, mit Faktoren statt Ausdrücken zu rechnen, soll der „rabattWert“ gleich in einen „rabattFaktor“ und das Produkt aus „rabattFaktor“ und „betragWert“ in „betragDifferenz“ abgespeichert werden. Der Endbetrag kann ebenfalls eine Wertdefinition mit der Bezeichnung „endBetrag“ erhalten Damit erweitert sich die Sequenz erneut.
String betragText = "1234.56"; String rabattText = "7"; Float betragZahl = new Float( betragText); Float rabattZahl = new Float( rabattText); float betragWert = betragZahl.floatValue(); float rabattWert = rabattZahl.floatValue(); float rabattFaktor = rabattWert / 100; float betragDifferenz = betragWert * rabattFaktor; float endBetrag = betragWert - betragDifferenz;
Pflichtenheft
BearbeitenEs ist hohe Zeit, die erarbeitete Sequenz in einem Programm auszuprobieren. Zunächst wird also ein „Pflichtenheft“ erstellt, in dem die wesentlichen Merkmale des Programms enthalten sind. Im vorliegenden Fall wird wohl eine kleine Auflistung genügen.
- Aufgabe:
- Es soll eine Applikation (Anwendung) erstellt werden.
- Der Name soll „Rabatt“ lauten.
- Die Ergebnisse werden auf dem Standard-Systemausgabestrom[18] ausgegeben.
Aufbau einer eigenen Klasse
BearbeitenEgal was in Java gemacht wird - eine Klasse wird benötigt. Klassen haben einen Namen. Der zweite Punkt im "Pflichtenheft" kann durch
class Rabatt
bereits als erledigt betrachtet werden. Klassen haben einen Anfang und ein Ende, was dem Compiler unbedingt durch entsprechende Klammern mitzuteilen ist.
class Rabatt { }
Java-Anwendungen haben eine koordinierende Methode, die tatsächlich alles erst "in Gang bringt". Normalerweise ist diese Methode sehr kurz, weil sie nur die wesentlichen Objekte (JFrame[2]) instanziiert und anzeigt. Für das aktuelle Beispiel wird diese Methode also ungewöhnlich lang ausfallen. Methoden haben auch einen Namen, in diesem Fall ist der Name jedoch vorgegeben. Der Name der koordinierenden Methode lautet bei allen Java-Applications: „main“. Wegen der immer noch vorhandenen Kommandozeile (bei Windows: DOS-Ebene), hat diese Methode noch ein zusätzliches Argument, um etwaige Angaben vor dem Start aus der Kommandozeile zu übernehmen. Die Methode erweitert sich also um dieses Argument zu „main( String[] args)“. Auch Methoden müssen, wie Klassen, mit diesen Anfangs- und Endeklammern ausgestattet werden. Weil diese Methode zur Klasse „Rabatt“ gehört, muss sie unbedingt zwischen den Anfangs- und Endeklammern der Klassendefinition stehen. Also einfügen, womit sich etwas wie
class Rabatt { main( String[] args) { } }
ergibt. Unter dem Dateinamen "Rabatt.java" (Java unterscheidet zwischen Groß- und Kleinschreibung) gespeichert, liegt der erste lauffähige Java-Quelltext bereit. Die Übersetzung dieser Datei würde tatsächlich eine lauffähige aber völlig aktionslose Klassendatei erzeugen. In jedem Fall ist dieser Aufbau für alle Java-Anwendungen gleich und sie unterscheiden sich nur durch den Klassennamen. Nunmehr kann in diesen Rahmen die gesamte, bisher erstellte Sequenz eingefügt werden. Der Ort ist natürlich die Methode „main“.
class Rabatt {
public static void main( String[] args) {
String betragText = "1234.56";
String rabattText = "7";
Float betragZahl = new Float( betragText);
Float rabattZahl = new Float( rabattText);
float betragWert = betragZahl.floatValue();
float rabattWert = rabattZahl.floatValue();
float rabattFaktor = rabattWert / 100;
float betragDifferenz = betragWert * rabattFaktor;
float endBetrag = betragWert - betragDifferenz;
}
}
- Aufgabe:
Starten des Editors, kopieren der obigen Sequenz, speichern in einer Datei namens Rabatt.java
und compilieren mit
javac Rabatt.java
Vorgang wiederholen bis keine Fehler mehr vorhanden sind.
Dies ist eine reine Übungsaufgabe, scheinbar ohne Nutzen, aber mit Übungseffekt. Ein Start des Programms führte zu nichts und wäre reine Zeitverschwendung. Es geht hier nur um die Behandlung von Tippfehlern. Sollten keine Fehlermeldungen aufgetreten sein, sollten diese gezielt provoziert werden. So führt "Sting" statt "String" oder ein "vergessenes" Semikolon sicher zu Meldungen des Compilers.
Ausgabe über System-Streams
BearbeitenEs fehlt nur noch ein Punkt im „Pflichtenheft“: Die Ausgabe auf dem "SystemStream". Wo ist das? Besser: Was ist das? Jedes System hat sog. standardisierte Ein-Ausgabedatenströme. Insgesamt sind es drei.
standard_in: Eingabedatenstrom = Tastatur standard_out: Ausgabedatenstrom = Konsole = Bildschirm standard_err: Fehlermeldungsdatenstrom = Konsole = Bildschirm
Diese System-Dateien sind in Java unter dem Begriff System[19] vorhanden (was es mit System genau auf sich hat, folgt sehr viel später) Ein „Stream“ ist etwas, worin Daten von einem Ort zum anderen „fließen“. Der „Ausgabestrom“ des Systems wird die Daten logischerweise zu seiner Ausgabe fließen lassen. Er ist erreichbar unter der Bezeichnung „out“[18]. Auf das System greift Java über die Klasse „System“ zu, womit der Instanzenweg „System.out“ bereits feststeht. „Streams“, in diesem Fall also das Objekt „out“, haben auch ihre Methoden. Eine Methode gibt eine Zeile aus und lautet (wohl aus historischen Gründen) „println“[20]. Diese Methode erwartet eine Zeichenkette in der Argumentliste und gibt dies aus. So würde System.out.println( betragText);
den String[15] „betragText“ auf dem Bildschirm anzeigen.
Schon ganz gut, aber es sollen ja Werte ausgegeben werden. Werte sind eben keine Strings, wie verlangt. Also alle Werte wieder in Strings wandeln? Nein, es geht zum Glück einfacher. Der Compiler von Java hilft. Eine Anweisung wie System.out.println( betragWert) ergibt eine Fehlermeldung beim Übersetzen. Der Compiler findet keine Methode, die es gestattet, Werte des Typs float auszugeben. Eine kleine Änderung überzeugt den Compiler aber davon, dass der Wert als Bestandteil eines Strings anzusehen ist - System.out.println( "Betrag "+betragWert);
. Der Compiler erkennt in der Argumentliste zunächst einen String und ist erst einmal zufrieden. Nun folgt ein Pluszeichen, was vom Compiler dahingehend interpretiert wird, dass an den bereits erkannten String noch etwas angehängt (concatenated) werden soll. Nun findet der Compiler einen float-Wert und ist so tolerant, diesen in einen String umzuwandeln und an den vorhandenen anzuhängen. Dem Compiler kommt es übrigens nur auf das Vorhandensein eines Strings in der Argumentliste an, was bedeutet, dass diese nicht unbedingt mit einem String beginnen muss.
Ausgabe innerhalb der eigenen Anwendung
BearbeitenDie Ausgabe für das Beispiel soll ungefähr folgendes Aussehen haben:
7% von 1234.56 sind: ... Der Endbetrag ist: ...
Erst werden die feststehenden Strings aufgebaut und danach die Variablen eingesetzt. Angeführt wird jede Ausgabeanweisung von
System.out.println( ?1+"% von "+?2+" sind: "+?3); System.out.println( "Der Endbetrag ist: "+?4);
- Aufgabe:
- Erweitern der Datei
Rabatt.java
um sinnvolle Ausgabebefehle (die Fragezeichen). Danach compilieren und mit java Rabatt
ausführen.
Sieht gut aus, wären da nicht die Fragezeichen. Diese werden natürlich ersetzt durch die Referenzen (Variablen) der entsprechenden Werte. Der Ersatz von "?1" ist der Wert der Prozentangabe – „rabattWert
“. Der Ersatz von "?2" ist der Wert des Ausgangsbetrags – „betragWert
“. Bei "?3" geht es bereits an die selbst errechneten Wert, und der Ersatz ist – „betragDifferenz
“. Soweit die Ersetzungen der ersten Ausgabezeile.
System.out.println( rabattWert+"% von "+betragWert+" sind: "+betragDifferenz);
In der zweiten Zeile ist nur "?4" mit dem endgültigen Ergebnis – „endBetrag
“ zu substituieren, woraus sich ergibt:
System.out.println( "Der Endbetrag ist: "+endBetrag);
Sollte es wahr sein? Ja! Das erste Java-Programm ist fertiggestellt (natürlich erst, wenn die beiden Ausgabezeilen in die bestehende Sequenz eingefügt wurden. So sieht es also aus.
class Rabatt { public static void main( String[] args) { String betragText = "1234.56"; String rabattText = "7"; Float betragZahl = new Float( betragText); Float rabattZahl = new Float( rabattText); float betragWert = betragZahl.floatValue(); float rabattWert = rabattZahl.floatValue(); float rabattFaktor = rabattWert / 100; float betragDifferenz = betragWert * rabattFaktor; float endBetrag = betragWert - betragDifferenz; System.out.println( rabattWert+"% von "+betragWert+" sind: "+betragDifferenz); System.out.println( "Der Endbetrag ist: "+endBetrag); } }
Das Ergebnis aller bisherigen Bemühungen ist mit
7.0% von 1234.56 sind: 86.419205 Der Endbetrag ist: 1148.1409
zwar korrekt, aber irgendwie "mickrig"? Es fehlt die grafische Umgebung und Möglichkeiten der Eingabe und Gestaltung und überhaupt ...
Die Aufgabe war ja auch etwas anderer Natur. Da war von JFrame[2] und JComponents[6] und weiteren völlig unbekannten Begriffen die Rede. Genau diese Dinge sind es aber, die Java für den Anwender ausmachen. Hier wird gestaltet und entworfen. Hier sind die kreativen Fähigkeiten gefragt. Leider erschließt sich dieses Gebiet auch erst mit viel Mühe, weshalb es bereits zu diesem frühen Zeitpunkt angegangen wird.
Referenzen
Bearbeiten- ↑ JRootPane in der Java-API
- ↑ 2,0 2,1 2,2 JFrame in der Java-API
- ↑ JPanel in der Java-API
- ↑ LayoutManager in der Java-API
- ↑ BorderLayout in der Java-API
- ↑ 6,0 6,1 JComponent in der Java-API
- ↑ EventListener in der Java-API
- ↑ WindowEvent in der Java-API
- ↑ WindowFocusListener in der Java-API
- ↑ Exception in der Java-API
- ↑ FocusEvent in der Java-API
- ↑ 12,0 12,1 JTextField in der Java-API
- ↑ JTextComponent.getText() in der Java-API
- ↑ JTextComponent in der Java-API
- ↑ 15,0 15,1 15,2 15,3 String in der Java-API
- ↑ Float in der Java-API
- ↑ Float.floatValue in der Java-API
- ↑ 18,0 18,1 System.out in der Java-API
- ↑ System in der Java-API
- ↑ PrintStream.println in der Java-API
<< Klassen und Objekte | Rechnen mit eingegebenen Zahlen | Eine grafische Benutzeroberfläche >>