Javaschubla.de - Java als erste Programmiersprache

Variablen in Java

In dieser Lektion geht es um (primitive, lokale) Variablen.

Variablen definieren

Vielleicht hast du in einer anderen Programmiersprache (z.B. JavaScript) schon einmal so eine Variablendefinition gesehen:

var i;

In Java braucht man kein Schlüsselwort wie var. Stattdessen gibt man den Typ der Variablen an. Für den Anfang benutzen wir int. int steht für integer, das sind in Java positive und negative ganze Zahlen, die in 4 Byte passen, also von -231 bis +231-1. Man muss der Variablen nicht sofort einen Wert zuweisen, aber man muss das tun, bevor man sie benutzt. (Ausnahmen, bei denen Variablen automatisch initialisiert werden, werden später erklärt.)

Beispiel:

int i;

deklariert eine Variable namens i vom Typ int.

Erlaubte Variablennamen:

Es ist strikte Konvention, Variablennamen klein zu schreiben, innere Worte groß zu beginnen und keine _ zu verwenden, z.B. dasIstEinVariablenName. Diese Schreibweise nennt man Camel Case.

Zurück zum Beispiel

int i;

Für diese Variable sind jetzt schon 4 Byte Platz im Arbeitsspeicher reserviert. Aber sie ist noch nicht initialisiert, d.h. ihr wurde noch kein Wert zugewiesen. Man kann sie deshalb noch nicht benutzen. Z.B.

System.out.println(i);

würde beim Kompilieren eine Fehlermeldung "i might not have been initialized" verursachen.

In Java (und verwandten Sprachen wie C, C++, C# und JavaScript) weist man Variablen mit dem Gleichheitszeichen einen Wert zu.

i = 3;

Das ist nicht als "i ist gleich 3" zu lesen. Es ist kein Vergleich! Mögliche Leseweisen sind "setze i auf 3", "schreibe 3 in i", "i ergibt sich zu 3", "i wird 3".

Ein kompilierbares Beispielprogramm:

class Variablen
{
  public static void main(String[] args)
  {
    int i;
    i = 3;
    System.out.println(i);
  }
}

Es gibt, wie man sich leicht denken kann, 3 im DOS-Fenster bzw. auf der Konsole aus.

Natürlich kann man den Wert der Variablen auch ändern:

class Variablen
{
  public static void main(String[] args)
  {
    int i;
    i = 3;
    System.out.println(i);
    i = 5;
    System.out.println(i);
  }
}

Gibt 3 und eine Zeile tiefer 5 aus.

Die Definition der Variablen und die Initialisierung (erste Zuweisung eines Wertes) kann in einer Zeile erfolgen:

int i = 3;

Beispielprogramm:

class Variablen
{
  public static void main(String[] args)
  {
    int i = 3;
    System.out.println(i);
    i = 5;
    System.out.println(i);
  }
}

Aber Achtung, nicht beim zweiten Mal int i = 5; schreiben. Damit würde man eine zweite Variable namens i definieren. Das ist nicht möglich. Der Compiler würde eine Fehlermeldung ausgeben, die besagt, dass die Variable i schon existiert.

class Variablen
{
  public static void main(String[] args)
  {
    int i = 3;
    System.out.println(i);
    int i = 5; // Fehler, doppelter Variablenname
    System.out.println(i);
  }
}

Mit // beginnt übrigens ein Kommentar, der bis zum Zeilenende geht. Mit /* ... */ umschließt man einen Kommentar, der auch über mehrere Zeilen gehen kann.

Natürlich kann man die üblichen Berechnungen ausführen.

class Variablen
{
  public static void main(String[] args)
  {
    int a = 17;
    int b = 3;
    int summe = a + b;
    System.out.print("a+b=");
    System.out.println(summe);
    int differenz = a - b;
    System.out.print("a-b=");
    System.out.println(differenz);
    int produkt = a * b;
    System.out.print("a*b=");
    System.out.println(produkt);
    int quotient = a / b;
    int rest = 17 % 3;     
    System.out.print("a:b=");
    System.out.print(quotient);
    System.out.print(" Rest ");
    System.out.println(rest);
  }
}

Gibt aus:

a+b=20
a-b=14
a*b=51
a:b=5 Rest 2

An diesem Beispiel sieht man:

Anmerkungen:

Man muss keine Leerzeichen vor und nach dem Variablennamen setzen. Da =, +, - etc. keine erlaubten Symbole in Variablennamen sind, weiß der Compiler, dass dort der Variablenname zu Ende ist. Z.B. int summe=a+b; ist genau dasselbe wie int summe = a + b;

Wie wir bereits im Kapitel Konkatenation gesehen haben, muss man die Ausgaben nicht in zwei Anweisungen unterteilen, z.B.

    System.out.print("a+b=");
    System.out.println(summe);

sondern man kann sie mit + aneinanderhängen:

    System.out.println("a+b=" + summe);

Auch hier kommt es wie bereits erwähnt nicht auf die Leerzeichen an, dasselbe wäre System.out.println("a+b="+summe);

Sämtliche Zeilenumbrüche kann man auch weglassen. Wenn man will, kann man also alle Anweisungen hintereinander in eine Zeile schreiben. Durch die Semikolons weiß der Compiler ja, wo die nächste beginnt. Das ist aber ziemlich unübersichtlich, deshalb schreibt man wenn überhaupt höchstens mal zwei kurze Anweisungen in eine Zeile.

Weitere Datentypen

Kommen wir zu ein paar anderen Datentypen außer int. Die primitiven Datentypen in Java sind:

ganze Zahlen
byte 1 Byte ganze Zahlen -27 bis 27-1 (-128 ... 127)
short 2 Byte ganze Zahlen -215 bis 215-1 (-32768 .. 32767)
int 4 Byte ganze Zahlen -231 bis 231-1 (ca. 2 Mrd.)
long 8 Byte ganze Zahlen -263 bis 263-1
Fließkommazahlen (Dezimalzahlen, gebrochene Zahlen)
float 4 Byte Fließkommazahlen mit einfacher Genauigkeit
double 8 Byte Fließkommazahlen mit doppelter Genauigkeit
Wahrheitswerte
boolean 1 Byte Wahrheitswerte true oder false
Zeichen
char 2 Byte Zeichen Unicode

char steht für character, was im Englischen "Zeichen" (im Sinne von Buchstabe, Zahl, Sonderzeichen) bedeutet (aber auch Charakter, (Roman-/Film-/Comic-)Figur).

Es lohnt sich normalerwiese nicht, um Speicherplatz zu sparen, byte, short oder float zu verwenden. Man benutzt eigentlich immer int, long und double, weil das schneller ist. Ein 32-Bit-Computer kann nun mal auf 32 Bit (4 Byte) am schnellsten zugreifen, nur Teile davon zu verwenden, verlängert die Zugriffszeit.

Beispiele:

    byte b = 5;
    short s = 400;
    int i = -17;
    long l = 100000000000L;
    float f = 0.123f;
    double d = 0.123;
    boolean w = true;
    boolean nw = false;
    char c = 'x';

Es ist zu beachten,

Casten (Typumwandlung)

Man kann ohne Probleme eine Zuweisung an einen "höheren"/"weiteren" Typ machen:

    short s = 300;
    int i = s;

Aber umgekehrt geht das nicht, selbst wenn die Zahl theoretisch hineinpassen würde:

    int i = 500;
    short s = i; // verursacht einen Fehler

Aber das kann man erzwingen, indem man explizit umwandelt. Dieses Umwandeln nennt man casten.

    int i = 500;
    short s = (short) i;

Man wandelt etwas also in einen anderen Typ um, indem man den Typ davor in runden Klammern schreibt.

Ein anderes Beispiel:

class IntToByte
{
  public static void main(String[] args)
  {
    int i = 500;
    byte b = (byte) i;
    System.out.println(b);
  }
}

Das funktioniert auch, aber das Ergebnis ist nicht richtig. byte geht nur bis 127, also fließt die 500 über (Overflow), es gibt einen Genauigkeitsverlust (loss of precision). 500-127=373 sind immer noch zu viel, die fangen jetzt wieder von unten, also von -128 an zu zählen. Das ist auch noch zu viel, 255 weitere Zählschritte später ist Schluss, es sind noch 373-255=116 übrig. Die beginnen wieder von unten von -128 an zu zählen, -128+116=-12, also wird in b -12 gespeichert. Probier es aus! Es wird -12 ausgegeben.

Es ist nicht möglich, booleans (Wahrheitswerte) in andere Typen oder andere Typen in booleans umzuwandeln.

Umwandlung von Zeichen in Zahlen und umgekehrt

Man kann auch zwischen char und int durch Casten umwandeln.

    char c = (char) 97;
    System.out.println(c);

gibt ein a aus, denn 97 ist die ASCII- und Unicode-Nummer des kleinen a. Die Umwandlung von char zu int funktioniert analog:

    int i = (int) 'A';
    System.out.println(i);

gibt 97 aus.

Division

Wir hatten oben schon, dass 17/3 5 ergibt. Es wird nicht einmal gerundet, sondern einfach abgeschnitten.

Nehmen wir mal als Beispiel 7/2, denn Periode 3 wäre etwas aufwändig zu schreiben. 7/2 ergibt 3. Wie können wir erzwingen, dass 3.5 herauskommt? Vielleicht hilft es ja, wenn wir das Ergebnis in einer double-Variablen speichern:

    double d = 7/2;
    System.out.println(d);

Nein, hilft leider nicht. 7/2 wird ausgerechnet, es kommt 3 heraus, und erst anschließend wird es in der double-Variablen d gespeichert. Es wird also 3.0 statt 3.5 ausgegeben.

Wenn also bei der Division von zwei int-Zahlen immer eine int-Zahl herauskommt, müssen wir einfach mindestens eine der beiden Zahlen zu double konvertieren:

    double d = 7.0/2; oder
    double d = 7/2.0; oder
    double d = 7.0/2.0;

führt alles zum gewünschten Erfolg :-) .

Aber was tun, wenn die Zahlen in Variablen gespeichert sind?

    int zahl1 = 7;
    int zahl2 = 2;
    double d = zahl1/zahl2;

Wir benutzen einfach wieder das Casten!

    double d = (double) zahl1 / zahl2; oder
    double d = zahl1 / (double) zahl2; oder
    double d = (double) zahl1 / (double) zahl2;

Damit erhalten wir auch hier eine 3.5. :-)

Eine Alternative ist das Multiplizieren mit 1.0. Dadurch wird das Ergebnis automatisch double, weil eine double-Zahl in der Multiplikation vorkommt. Auch, wenn das Ergebnis nur 0 als Nachkommastelle hat.

     double d = 1.0 * zahl1 / zahl2; oder
     double d = zahl1 * 1.0 / zahl2;

Nicht helfen würde

    double d = zahl1 / zahl 2 * 1.0;

denn dann würde 7/2 ausgerechnet werden, es kommt 3 heraus, anschließend wird 3 * 1.0 gerechnet und es kommt 3.0 heraus. Falsch wäre

    double d = zahl1 / 1.0 * zahl2;

denn dann würde zahl1 (also 7) durch 1.0 geteilt werden, wobei 7.0 herauskommt, und das würde dann mit zahl2 multipliziert werden statt dadurch zu teilen, und man erhielte 14. Das könnte man durch Klammern wie in der Mathematik verhindern:

    double d = zahl1 / (1.0 * zahl2);

Abkürzungen für Operatoren

Für bestimmte Operationen gibt es abkürzende Notationen. Z.B. will man eine Zahl häufig um eine andere erhöhen, vermindern, damit multiplizieren oder dadurch dividieren. Also z.B. statt "summe = a + b;" "a = a + b;". Denk daran, dass = kein Vergleich ist. Natürlich ist a nicht gleich a + b (wenn b nicht gerade 0 ist), sondern zum alten Wert von a wird b addiert und das Ergebnis in a gespeichert. Z.B.:

    int a = 5;
    int b = 7;
    a = a + b;

Nun ist a 12. Hierfür gibt es die Kurznotation +=.

    a += b;

heißt genau dasselbe wie a = a + b;

Ebenso gibt es -=, *=, /= und %=.

Inkrement und Dekrement

Besonders häufig ist das Erhöhen oder Vermindern um 1, dafür gibt es extra Notationen.

Statt a = a + 1; kann man nicht nur a += 1; sondern auch a++ oder ++a schreiben. Das heißt Post- bzw. Präinkrement (inkrementieren = um 1 erhöhen).

Statt a = a - 1; kann man nicht nur a-= 1; sondern auch a-- oder --a schreiben. Das heißt Post- bzw. Prädekrement (dekrementieren = um 1 vermindern).

Wenn a++ oder ++a allein in einer Anweisung steht, macht es keinen Unterschied, ob das ++ vorn oder hinten steht:

    int a = 3;
    a++;
    // jetzt ist a 4
    ++a;
    // jetzt ist a 5

(Zur Erinnerung: mit // beginnt ein Kommentar bis zum Zeilenende.)

Aber wenn man das Ergebnis sofort verwendet, macht es einen Unterschied.

    int a = 3;
    int b = 0;
    b = a++;
    // a ist 4, aber b ist 3. Das Inkrement wurde hinterher ausgeführt.
    b = ++a;
    // jetzt ist a 5 und b ist auch 5. Das Inkrement wurde vorher ausgeführt.

Dasselbe gilt für --.

Konstanten

Eine Konstante definiert man, indem man vor den Typ final schreibt. Konstantennamen schreibt man in ALL_CAPS, also komplett groß mit Unterstrichen zur Unterteilung.

class Konstantin
{
  public static void main(String[] args)
  {
    final int MAXIMUM_SCHUELER_PRO_KLASSE = 30;
    System.out.println("In jeder Klasse dürfen maximal " + MAXIMUM_SCHUELER_PRO_KLASSE + " Schüler sein.");
    // MAXIMUM_SCHUELER_PRO_KLASSE = 33; würde eine Fehlermeldung verursachen
  }
}

Man sollte alle Konstanten, egal ob für Zahlen oder für Strings, immer durch final Variablen ersetzen. Besonders wichtig ist das, wenn sie an mehreren Stellen im Quelltext vorkommen, also wenn z.B. öfter im Programm geprüft wird, ob auch wirklich noch höchstens 30 Schüler in eine Klasse eingeteilt wurden. Vielleicht erhöht sich die Zahl mal auf 31, dann müsste man es überall ändern und übersieht vielleicht eine Stelle. Bei Strings ist es wichtig, sie in final Variablen zu speichern, weil die meisten Programme irgendwann einmal in eine andere Sprache übersetzt werden. So findet man alle zu übersetzenden Texte schnell.

Mehrere Variablen desselben Typs

Mehrere Variablen desselben Typs kann man auf einmal deklarieren:

    int i, j, k;
    i = 14;
    j = -1;
    k = 5;

int i, j, k; bewirkt dasselbe wie int i; int j; int k;

Man kann sogar die Initialisierungen auf einmal durchführen:

    int i=14, j=-1, k=5;

Übung 1

Probier die Operatoren aus, besonders die Division.

Vermutlich würdest du gern den Benutzer zwei Zahlen eingeben lassen und dann Summe, Differenz usw. ausgeben. Leider ist das Einlesen von der Tastatur etwas schwieriger als in anderen Sprachen, deshalb kommt es erst später dran. Leg so lange die Zahlen im Quellcode selbst fest und denk dir, dass sie später vom Benutzer eingegeben werden.

Übung 2

Probier die verschiedenen Datentypen und die Umwandlung zwischen ihnen aus. Wann gibt es eine Fehlermeldung und wie lautet sie? Wann nicht?


In der nächsten Lektion werden Vergleiche und Bedingungen vorgestellt.