Primitive und Referenzvariablen
Variablen in Java werden in primitive und Referenzvariablen unterteilt. Aus Sicht der Programmierenden wird die Information einer primitiven Variablen als Wert dieser Variablen gespeichert, während eine Referenzvariable einen Verweis auf Informationen enthält, die mit dieser Variablen in Zusammenhang stehen. Referenzvariablen sind in Java praktisch immer Objekte. Schauen wir uns beide Typen anhand von zwei Beispielen an.
int value = 10;
System.out.println(value);
10
public class Name {
private String name;
public Name(String name) {
this.name = name;
}
}
Name luke = new Name("Luke");
System.out.println(luke);
Name@4aa298b7
Im ersten Beispiel erstellen wir eine primitive int
-Variable, und die Zahl 10 wird als deren Wert gespeichert. Wenn wir die Variable an die Methode System.out.println
übergeben, wird die Zahl 10
ausgegeben. Im zweiten Beispiel erstellen wir eine Referenzvariable namens luke
. Der Konstruktor der Klasse Name
gibt beim Aufruf eine Referenz auf ein Objekt zurück, und diese Referenz wird als Wert der Variablen gespeichert. Wenn wir die Variable ausgeben, erhalten wir Name@4aa298b7
als Ausgabe. Was verursacht das?
Der Methodenaufruf System.out.println
gibt den Wert der Variablen aus. Der Wert einer primitiven Variablen ist konkret, während der Wert einer Referenzvariablen eine Referenz ist. Wenn wir versuchen, den Wert einer Referenzvariablen auszugeben, enthält die Ausgabe den Typ der Variablen und eine von Java erstellte Kennung: Die Zeichenfolge Name@4aa298b7
zeigt uns, dass die gegebene Variable vom Typ Name
ist und ihre Kennung 4aa298b7
lautet.
Das vorherige Beispiel gilt immer dann, wenn die Programmierenden das Standardausgabeformat eines Objekts nicht verändert haben. Sie können die Standardausgabe ändern, indem Sie die Methode toString
innerhalb der Klasse des betreffenden Objekts definieren, in der Sie angeben, wie die Ausgabe des Objekts aussehen soll. Im folgenden Beispiel haben wir die Methode public String toString()
innerhalb der Klasse Name
definiert, die die Instanzvariable name
zurückgibt. Wenn wir nun ein Objekt der Klasse Name
mit dem Befehl System.out.println
ausgeben, wird die Zeichenfolge, die von der Methode toString
zurückgegeben wird, ausgegeben.
public class Name {
private String name;
public Name(String name) {
this.name = name;
}
public String toString() {
return this.name;
}
}
Name luke = new Name("Luke");
System.out.println(luke); // gleichbedeutend mit System.out.println(luke.toString());
Luke
Primitive Variablen
boolean
(ein Wahrheitswert: entweder true
oder false
), byte
(ein Byte mit 8 Bit, Wertebereich -128
bis 127
), char
(ein 16-Bit-Wert, der ein einzelnes Zeichen darstellt), short
(ein 16-Bit-Wert, der eine kleine Ganzzahl darstellt, Wertebereich -32768
bis 32767
), int
(ein 32-Bit-Wert, der eine mittelgroße Ganzzahl darstellt, Wertebereich -231
bis 231-1
), long
(ein 64-Bit-Wert, der eine große Ganzzahl darstellt, Wertebereich -263
bis 263-1
), float
(eine Gleitkommazahl, die 32 Bit verwendet), und double
(eine Gleitkommazahl, die 64 Bit verwendet).Von all diesen haben wir hauptsächlich die Wahrheitswertvariable (boolean
), Ganzzahlvariable (int
) und Gleitkommazahlvariable (double
) verwendet.
boolean truthValue = false;
int integer = 42;
double floatingPointNumber = 4.2;
System.out.println(truthValue);
System.out.println(integer);
System.out.println(floatingPointNumber);
false 42 4.2
Die Deklaration einer primitiven Variablen führt dazu, dass der Computer etwas Speicher reserviert, in dem der der Variablen zugewiesene Wert gespeichert werden kann. Die Größe des reservierten Speicherbereichs hängt vom Typ der primitiven Variablen ab. Im folgenden Beispiel erstellen wir drei Variablen. Jede hat ihren eigenen Speicherort, an dem der zugewiesene Wert gespeichert wird.
int first = 10;
int second = first;
int third = second;
System.out.println(first + " " + second + " " + third);
second = 5;
System.out.println(first + " " + second + " " + third);
10 10 10
10 5 10
Der Name der Variablen gibt den Speicherort an, an dem ihr Wert gespeichert ist. Wenn Sie einer primitiven Variablen mit einem Gleichheitszeichen einen Wert zuweisen, wird der Wert auf der rechten Seite in den durch den Namen der Variablen angegebenen Speicherort kopiert. Zum Beispiel reserviert die Anweisung int first = 10
einen Speicherort namens first
für die Variable und kopiert den Wert 10
in diesen Speicherort.
Ebenso reserviert die Anweisung int second = first;
in diesem Fall einen Speicherort namens second
für die erstellte Variable und kopiert den in der Speicheradresse von first
gespeicherten Wert hinein.
Die Werte von Variablen werden auch dann kopiert, wenn sie in Methodenaufrufen verwendet werden. Dies bedeutet praktisch, dass der Wert einer Variablen, die als Parameter in einem Methodenaufruf übergeben wird, in der aufrufenden Methode nicht durch die aufgerufene Methode geändert wird. Im folgenden Beispiel deklarieren wir eine Variable number
in der Hauptmethode, deren Wert als Parameter des Methodenaufrufs kopiert wird. In der aufgerufenen Methode wird der Wert, der über den Parameter kommt, ausgegeben, dann wird sein Wert um eins erhöht. Der Wert der Variablen wird dann noch einmal ausgegeben, und die Programmausführung kehrt schließlich zur Hauptmethode zurück. Der Wert der Variablen number
in der Hauptmethode bleibt unverändert, da er nichts mit der Variablen number
zu tun hat, die als Parameter der aufgerufenen Methode definiert ist.
Referenzvariablen
Alle von Java bereitgestellten Variablen (mit Ausnahme der oben erwähnten acht primitiven Variablen) sind Referenztypen. Programmierende können auch eigene Variablentypen erstellen, indem sie neue Klassen definieren. Praktisch jedes Objekt, das aus einer Klasse instanziiert wird, ist eine Referenzvariable.
Betrachten wir das Beispiel vom Anfang des Kapitels, in dem wir eine Variable namens leevi
vom Typ Name
erstellt haben.
Name leevi = new Name("Leevi");
Der Aufruf hat folgende Bestandteile:
- Wann immer eine neue Variable deklariert wird, muss zuerst ihr Typ angegeben werden. Unten deklarieren wir eine Variable vom Typ
Name
. Damit das Programm erfolgreich ausgeführt werden kann, muss eine KlasseName
vorhanden sein, die vom Programm verwendet werden kann.
Name ...
- Wir geben den Namen der Variablen an, wenn sie deklariert wird. Sie können später auf den Wert der Variablen über ihren Namen zugreifen. Unten wird die Variable
leevi
genannt.
Name leevi...
- Variablen können Werte zugewiesen werden. Objekte werden aus Klassen durch Aufruf des Klassenkonstruktors erstellt. Dieser Konstruktor definiert die Werte, die den Instanzvariablen des erstellten Objekts zugewiesen werden. Im folgenden Beispiel nehmen wir an, dass die Klasse
Name
einen Konstruktor hat, der eine Zeichenkette als Parameter entgegennimmt.
... new Name("Leevi");
- Der Konstruktorruf gibt einen Wert zurück, der eine Referenz auf das neu erstellte Objekt ist. Das Gleichheitszeichen sagt dem Programm, dass der Wert des Ausdrucks auf der rechten Seite als Wert der Variablen auf der linken Seite kopiert werden soll. Die Referenz auf das neu erstellte Objekt, die vom Konstruktorruf zurückgegeben wird, wird als Wert der Variablen
leevi
kopiert.
Name leevi = new Name("Leevi");
Der wichtigste Unterschied zwischen primitiven und Referenzvariablen besteht darin, dass primitive Variablen (in der Regel Zahlen) unveränderlich sind. Der interne Zustand von Referenzvariablen kann hingegen typischerweise verändert werden. Dies liegt daran, dass der Wert einer primitiven Variablen direkt in der Variablen gespeichert wird, während der Wert einer Referenzvariablen eine Referenz auf die Daten der Variablen, also deren internen Zustand, darstellt.
Arithmetische Operationen wie Addition, Subtraktion und Multiplikation können mit primitiven Variablen verwendet werden – diese Operationen ändern die ursprünglichen Werte der Variablen nicht. Arithmetische Operationen erzeugen neue Werte, die bei Bedarf in Variablen gespeichert werden können. Im Gegensatz dazu können die Werte von Referenzvariablen durch diese arithmetischen Ausdrücke nicht geändert werden.
Der Wert einer Referenzvariablen – also die Referenz – zeigt auf einen Speicherort, der Informationen über die gegebene Variable enthält. Nehmen wir an, dass uns eine Klasse Person
zur Verfügung steht, die eine Instanzvariable age
enthält. Wenn wir
ein Personenobjekt aus der Klasse instanziiert haben, können wir auf die Variable age
zugreifen, indem wir der Referenz des Objekts folgen. Der Wert dieser Variablen kann dann nach Bedarf geändert werden.
Primitive und Referenzvariablen als Methodenparameter
Wir haben bereits erwähnt, dass der Wert einer primitiven Variablen direkt in der Variablen gespeichert wird, während der Wert einer Referenzvariablen eine Referenz auf ein Objekt enthält. Wir haben auch erwähnt, dass das Zuweisen eines Wertes mit dem Gleichheitszeichen den Wert auf der rechten Seite (möglicherweise einer anderen Variablen) kopiert und ihn als Wert der Variablen auf der linken Seite speichert.
Eine ähnliche Art von Kopie erfolgt während eines Methodenaufrufs. Unabhängig davon, ob es sich um eine primitive oder eine Referenzvariable handelt, wird der Wert, der als Argument an die Methode übergeben wird, für die aufgerufene Methode kopiert. Bei primitiven Variablen wird der Wert der Variablen an die Methode übergeben. Bei Referenzvariablen ist es eine Referenz.
Schauen wir uns das in der Praxis an und nehmen wir an, dass uns die folgende Klasse Person
zur Verfügung steht.
public class Person {
private String name;
private int birthYear;
public Person(String name) {
this.name = name;
this.birthYear = 1970;
}
public int getBirthYear() {
return this.birthYear;
}
public void setBirthYear(int birthYear) {
this.birthYear = birthYear;
}
public String toString() {
return this.name + " (" + this.birthYear + ")";
}
}
Wir werden die Programmausführung Schritt für Schritt untersuchen.
public class Example {
public static void main(String[] args) {
Person first = new Person("First");
System.out.println(first);
youthen(first);
System.out.println(first);
Person second = first;
youthen(second);
System.out.println(first);
}
public static void youthen(Person person) {
person.setBirthYear(person.getBirthYear() + 1);
}
}
First (1970) First (1971) First (1972)
Die Ausführung des Programms beginnt mit der ersten Zeile der main
-Methode. In der ersten Zeile der main
-Methode wird eine Variable vom Typ Person
deklariert, und der vom Konstruktor der Klasse Person
zurückgegebene Wert wird als ihr Wert kopiert. Der Konstruktor erstellt ein Objekt, dessen Geburtsjahr auf 1970 gesetzt wird und dessen Name auf den als Parameter empfangenen Wert gesetzt wird. Der Konstruktor gibt eine Referenz zurück. Nachdem die Zeile ausgeführt wurde, sieht der Zustand des Programms wie folgt aus – ein Person
-Objekt wurde im Speicher erstellt, und die in der main
-Methode definierte Variable first
enthält eine Referenz auf dieses Objekt.
Im folgenden verwenden wir pythontutor.com zur Visualiserung des Ablaufes, insbesondere Sehen Sie die Veränderungen des Aufrufstapel und des Hauptspeichers auf der rechten Seite.
In der dritten Zeile der main
-Methode (Zeile 5 in der Visualisierung) geben wir den Wert der Variablen first
aus. Der Methodenaufruf System.out.println
sucht nach der toString
-Methode der Referenzvariablen, die ihm als Parameter übergeben wurde. Die Klasse Person
hat die Methode toString
, sodass diese Methode auf dem Objekt aufgerufen wird, auf das die Variable first
verweist. Der Wert der Variablen name
in diesem Objekt ist "First", und der Wert der Variablen birthYear
ist 1970. Die Ausgabe lautet "First (1970)".
Danach ruft das Programm die Methode youthen
auf, der die Variable first
als Argument übergeben wird. Wenn die Methode youthen
aufgerufen wird, wird der Wert der Parametervariablen für die Verwendung durch die Methode youthen
kopiert. Die Ausführung der youthen
-Methode erzeugt einen neuen frame im Aufrufstapel. Da die Variable first
ein Referenztyp ist, wird die zuvor erstellte Referenz für die Verwendung durch die Methode kopiert. Am Ende der Methodenausführung ist das Geburtsjahr des Objekts auf 1971 erhöht, und das frame wird vom Aufrufstapel genommen.
Nachdem wir vom Methodenaufruf zurückgekehrt sind, geben wir erneut den Wert der Variablen first
aus. Das Objekt, auf das die Variable first
verweist, wurde während des Aufrufs der Methode youthen
verändert: Die Variable birthYear
des Objekts wurde um eins erhöht. Der endgültige Wert, der ausgegeben wird, ist "First (1971)".
Dann wird im Programm eine neue Variable vom Typ Person
namens second
deklariert. Der Wert der Variablen first
wird in die Variable second
kopiert, d.h. der Wert der Variablen second
wird zu einer Referenz auf das bereits vorhandene Person
-Objekt.
Beachten Sie das first
und second
auf das gleiche Objekt im SPeicher verweisen.
Danach wird die Methode youthen
aufgerufen, der die Variable second
als Parameter übergeben wird. Der Wert der während des Methodenaufrufs übergebenen Variablen wird als Wert für die Methode kopiert, d.h. die Methode erhält die in der Variablen second
enthaltene Referenz zur Verwendung. Nach der Ausführung der Methode ist das Geburtsjahr des Objekts, auf das die Methode verweist, um eins auf 1972 erhöht.
Schließlich endet die Methodenausführung, und das Programm kehrt zur main
-Methode zurück, wo der Wert der Variablen first
ein letztes Mal ausgegeben wird. Das endgültige Ergebnis der Ausgabe ist "First (1972)".
Loading...
Loading...