Universität Leipzig Institut für Informatik Dr. Monika Meiler Inhalt 4 Einführung in die Programmiersprache Java (Teil III) ................................................... 4-2 4.5 Referenzdatentypen - Felder ..................................................................................... 4-2 4.5.1 4.5.2 4.5.3 4.5.4 4.6 Eindimensionale Felder - Vektoren................................................................... 4-3 Beispiel „Sortieren eines Vektors“ .................................................................... 4-4 Zweidimensionale Felder - Matrizen ................................................................ 4-6 Beispiel „Tic Tac Toe“ ...................................................................................... 4-7 Referenzdatentypen - Klassen ................................................................................. 4-11 4.6.1 4.6.2 Festlegen eines Strukturtyps als Klasse .......................................................... 4-11 Festlegen einer Struktur – eines Objekts einer Klasse .................................... 4-12 Informatik/Numerik 4-1/14 Universität Leipzig Institut für Informatik Dr. Monika Meiler 4 Einführung in die Programmiersprache Java (Teil III) 4.5 Referenzdatentypen - Felder Bisher wurde nur der Umgang mit Elementardatentypen behandelt. Für komplexe Anwendungen reichen diese oft nicht aus. Man benötigt zusammengesetzte, sogenannte strukturierte Daten. Referenzdatentypen ermöglichen den Zugriff auf solche. Java unterscheidet zwei Arten von Referenzdatentypen, Felder und Klassen. Im Unterschied zu Elementardatentypen werden auf Werte von Referenzdatentypen nicht direkt, sondern indirekt über Referenzen, zugegriffen. Eine Referenz ist eine Variable, die als Wert eine Adresse besitzt und somit auf einen Speicherbereich verweist (Zeiger). Definition Feld Feld der Länge n =df n-Tupel über einer Menge von Daten gleichen Typs Felder sind strukturierte Datentypen, die für ein Programm eine Einheit bilden und Daten gleichen Typs zusammenfassen. Die zusammengefassten Daten sind die Feldkomponenten. Arbeitsspeicher Symbolische Adresse im Inhalt der Interpretations-vorschrift Adresse Speicher Speicherzelle ... ... 5e 00 b short (2 Byte) 5f 6a ... ... 65 a3 r char[] (Referenz) ... ... a3 00 char[0] (2 Byte) a4 41 a5 00 char[1] (2 Byte) a6 75 a7 00 char[2] (2 Byte) a8 74 a9 00 char[3] (2 Byte) aa 6f ... ... ... Elementardatentyp short b = 106; b 00 6a b ist eine Variable des Elementardatentypen short: b hat die Speicheradresse (5e)16 = 94, 2 Byte Länge und den Wert (00 6a)16 = 106. Referenzdatentyp char[] r = { 'A', 'u', 't', 'o'}; r a3 00 41 00 75 00 74 00 6f r ist eine Referenzvariable: Informatik/Numerik 4-2/14 Universität Leipzig Institut für Informatik Dr. Monika Meiler r hat im Speicher die Adresse (65)16. Dort steht als Wert wiederum eine Adresse (a3)16. Diese zeigt auf den Anfang eines Feldes. Um das Feld auswerten zu können, muss die Anzahl und der Typ der Feldkomponenten bekannt sein. Im Beispiel handelt es sich um ein Feld mit 4 Komponenten vom Typ char . Die einzelnen Feldkomponenten bestehen jeweils aus 2 Byte. Der Speicherbereich, auf den die Referenz verweist, umfasst damit insgesamt 8 Byte und repräsentiert 4 Zeichen im Unicode, hier „Auto“. 4.5.1 Eindimensionale Felder - Vektoren Analog einfacher Variablen haben Feldvariablen einen Namen, einen Typ und als Wert eine Adresse. Alle durch ein Feld zusammengefasste Daten werden in Feldkomponenten abgespeichert. Deklaration Mit der Deklaration eines Feldnamens wird dem Compiler mitgeteilt, dass es sich um eine Referenzvariable handelt und Speicherplatz für eine Adresse zur Verfügung gestellt werden muss. Komponententyp [ ] Feldname ; float[] vektor; null vektor Definition Um ein Feld für den Gebrauch vorzubereiten, muss der notwendige Speicherplatz für die Komponenten reserviert werden. Dies geschieht mit Hilfe des new-Operators. Feldname = new Komponententyp [ Länge ] ; vektor = new float[ 3]; vektor a3 oder beides zusammen: float[] vektor = new float[ 3]; Zugriff auf die Feldkomponenten Auf einzelne Feldkomponenten wird über einen Index durch den [ ]-Operator zugegriffen. Dieser ist ganzzahlig und muss in den Feldgrenzen liegen, d.h. 0 ≤ Index < Länge. Feldname [ Index ] vektor[ 0] vektor[ 1] vektor[ 2] Durchlaufen eines eindimensionalen Feldes for( int i = 0; i < 3; i++) vektor[ i] = (float)(i / 2.); 1 Die Laufvariable i heißt auch Indexvariable, weil i alle möglichen Komponenten über den Index des Feldes vektor durchläuft. vektor a3 0.0 0.5 1.0 1 Da der Ausdruck (i / 2.) einen double-Wert liefert, muss dieser explizit in einen float-Wert umgewandelt werden. Informatik/Numerik 4-3/14 Universität Leipzig Institut für Informatik Dr. Monika Meiler Zu jedem Feld wird dessen Länge als ganzzahlige Variable length zusätzlich im Speicher abgelegt. Diese kann im Programm abgefragt werden und sollte grundsätzlich, anstatt einer expliziten Längenangabe, verwendet werden. for( int i = 0; i < vektor.length; i++) vektor[ i] = (float)(i / 2.); Explizite Anfangswertzuweisung (Initialisierung) Felder können durch eine Wertemenge bei Ihrer Deklaration definiert und initialisiert werden. Die Länge des Feldes richtet sich nach der Anzahl der Werte in der Wertmenge. Für die Werte wird der notwendige Speicher bereitgestellt. Komponententyp [ ] Feldname = Wertmenge ; Mit der folgenden Initialisierung kann der obige Vektor vereinbart werden: float[] vektor = { 0, (float).5, 1}; Kopieren eindimensionaler Felder 1. Kopie der Datenstruktur float[] vektor = { 0, .5f, 1}; float[] kopie = new float[ vektor.length]; 2. Kopie der Daten for( int i = 0; i < vektor.length; i++) kopie[ i] = vektor[ i]; for( int i = 0; i < kopie.length; i++) kopie[ i] *= 2; vektor a3 0.0 0.5 kopie 1.0 0.0 b4 1.0 2.0 4.5.2 Beispiel „Sortieren eines Vektors“ Beispiel In einem Feld werden Werte eingegeben, sortiert und anschließend wieder ausgegeben. Vektor.java (Grobstruktur) public class Vektor { public static void main( String[] args) { // Deklaration und Definition // Vektoreingabe Informatik/Numerik 4-4/14 Universität Leipzig Institut für Informatik Dr. Monika Meiler // Sortieren mit SelectSort // Vektorausgabe } } Vektor.java // Vektor.java import Tools.IO.*; MM 2009 // Eingaben /** * Sortieren eines Vektors mit Hilfe SelectSort. */ public class Vektor { /** * Eingabe, Sortieren und Ausgabe eines Vektors. */ public static void main( String[] args) { // Deklaration und Definition float[] vektor = new float[ IOTools.readInteger( "Anzahl Werte: ")]; // Vektoreingabe for( int i = 0; i < vektor.length; i++) vektor[ i] = IOTools.readFloat( "v[" + i + "] = "); // Sortieren mit SelectSort for( int i = 0; i < vektor.length - 1; i++) { int k = i; // Suche Minimum for( int j = i + 1; j < vektor.length; j++) if( vektor[ k] > vektor[ j]) k = j; if( i != k) { float temp = vektor[ i]; vektor[ i] = vektor[ k]; vektor[ k] = temp; } // Vertausche } // Vektorausgabe for( int i = 0; i < vektor.length; i++) System.out.print( " " + vektor[ i]); System.out.println(); } } Informatik/Numerik 4-5/14 Universität Leipzig Institut für Informatik Dr. Monika Meiler 4.5.3 Zweidimensionale Felder - Matrizen Eine Matrix ist ein eindimensionales Feld, deren Komponenten eindimensionale Felder sind. Deklaration, Definition und Initialisierung werden als Feld von Feldern ausgeführt. Für die Deklaration und Definition einer Matrix ergibt sich damit: Deklaration und Definition int[][] matrix; matrix = new int[ 4][ 3]; bzw. int[][] matrix = new int[ 4][ 3]; Zugriff auf die Feldkomponenten matrix[ 0][ 0] matrix[ matrix[ 1][ 0] matrix[ matrix[ 2][ 0] matrix[ matrix[ 3][ 0] matrix[ 0][ 1][ 2][ 3][ 1] 1] 1] 1] matrix[ matrix[ matrix[ matrix[ 0][ 1][ 2][ 3][ 2] 2] 2] 2] Durchlaufen eines zweidimensionalen Feldes int k = 0; for( int z = 0; z < matrix.length; z++) for( int s = 0; s < matrix[ 0].length; s++) matrix[ z][ s] = k++; matrix 0 1 2 3 4 5 6 7 8 9 10 11 Das Durchlaufen einer Matrix erfolgt zeilenweise und erfordert eine verschachtelte forSchleife mit zwei Indexvariablen (hier: z Zeilenindex, s Spaltenindex). Dabei gibt matrix.length die Anzahl der Zeilen von matrix an. Um die Anzahl der Spalten je Zeile zu ermitteln, wird mit matrix[ 0].length die Länge der 0. Zeile von matrix zu Hilfe genommen. Explizite Anfangswertzuweisung (Initialisierung) Matrizen können durch eine Menge von Mengen bei Ihrer Deklaration initialisiert werden. Die Zeilen- und Spaltenanzahl richtet sich nach der Anzahl der Wertmengen und der Anzahl der Werte in der Wertmengen. Für die Werte wird der notwendige Speicher bereit gestellt. int[][] matrix = { { 0, 1, 2}, { 3, 4, 5}, { 6, 7, 8}, { 9 , 10, 11}}; Im Beispiel wurde analog eine 4 x 3 - Matrix matrix deklariert, definiert und mit Werten initialisiert. Eine Matrix ist eine Referenz auf ein Feld von Referenzen. Informatik/Numerik 4-6/14 Universität Leipzig Institut für Informatik Dr. Monika Meiler Kopieren zweidimensionaler Felder 1. Kopie der Datenstruktur: int[][] matrix = { { 0, 1, 2}, { 3, 4, 5}, { 6, 7, 8}, { 9 , 10, 11}}; int[][] kopie = new int[ matrix.length][ matrix[ 0].length]; 2. Kopie der Daten: for( int z = 0; z < matrix.length; z++) for( int s = 0; s < matrix[ 0].length; s++) kopie[ z][ s] = matrix[ z][ s]; 4.5.4 Beispiel „Tic Tac Toe“ Tic Tac Toe ist ein Spiel, welches auf einem Spielbrett mit n * n Karos und mit weißen und schwarzen Steinen gespielt wird. In der Ausgangsspielsituation sind alle Karos leer. Weiß und Schwarz besetzen abwechselnd ein leeres Karo, Weiß beginnt. Das Spiel wird in zwei Varianten gespielt: 1. Gewonnen hat der Spieler, dem es als erstem gelingt, m der eigenen Steine in eine waagerechte, senkrechte oder diagonale Reihe zu bringen. 2. Verloren hat der Spieler, der als erster m der eigenen Steine in eine waagerechte, senkrechte oder diagonale Reihe setzen muss. Spielsituation auf einem 3*3 Brett, bei der 3 weiße Steine in die Diagonale gesetzt wurden: 3 2 1 a b c Mit einem guten Partner wird das Spiel am 3*3 - Brett für beide Spielvarianten unentschieden enden. Tic Tac Toe für ein 3*3 – Brett und der ersten Spielvariante: TicTacToe.java (Grobstruktur) public class TicTacToe { public static void main( String[] args) { // Spielerlaeuterung // leeres Spielbrett // 2 Spieler Informatik/Numerik 4-7/14 Universität Leipzig Institut für Informatik Dr. Monika Meiler // Spielstart boolean fertig = false; // Spielrunde do { // aktueller Spieler // Stein setzen // Spielbrett zeigen // Auswerten // Zeilentest // Spaltentest // positiver Diagonalentest // negativer Diagonalentest // Fertig } while( !fertig); // Spielauswertung } } TicTacToe.java // TicTacToe.java import Tools.IO.*; MM 2009 // Eingaben /** * TicTacToe - Spiel fuer zwei Personen. */ public class TicTacToe { /** * Spieler setzen abwechseln drei Steine, * gewonnen hat der Spieler, der seine Steine * in einer Zeile, Spalte oder Diagonalen hat. */ public static void main( String[] args) { // Spielerlaeuterung System.out.println ( "Spieler setzen abwechseln drei Steine, "); System.out.println ("gewonnen hat der Spieler, der seine Steine "); System.out.println ("in einer Zeile, Spalte oder Diagonalen hat."); // leeres Spielbrett final int laenge = 3; char[][] brett = new char[ laenge][ laenge]; // Spielbrett leeren for( int z = 0; z < brett.length; z++) for( int s = 0; s < brett[ 0].length; s++) brett[ z][ s] = ' '; Informatik/Numerik 4-8/14 Universität Leipzig Institut für Informatik Dr. Monika Meiler // 2 Spieler char[] spieler = { 'x', 'o'}; // Spielstart int dran = 0; int runde = 0; boolean fertig = false; boolean gewonnen = false; // aktueller Spieler // Rundenzaehler // Spielrunde do { // aktueller Spieler dran = 1 - dran; runde++; System.out.println ( "Spieler " + spieler[ dran] + " ist dran!"); System.out.println(); // Stein setzen int z, s; do { do { z = IOTools.readInteger( "Zeile (0<=z<=2), z = "); } while( z < 0 || z > 2); do { s = IOTools.readInteger( "Spalte (0<=s<=2), s = "); } while( s < 0 || s > 2); } while( brett[ z][ s] != ' '); brett[ z][ s] = spieler[ dran]; // Spielbrett zeigen System.out.println(); for( z = 0; z < brett.length; z++) { System.out.print( " | "); for( s = 0; s < brett[ 0].length; s++) System.out.print( brett[ z][ s] + " | "); System.out.println(); } System.out.println(); // Auswerten for( z = 0; z < brett.length; z++) if( brett[ z][ 0] != ' ' && Informatik/Numerik // Zeilentest 4-9/14 Universität Leipzig Institut für Informatik Dr. Monika Meiler brett[ z][ 0] == brett[ z][ 1] && brett[ z][ 0] == brett[ z][ 2]) { gewonnen = true; break; } if( !gewonnen) // Spaltentest for( s = 0; s < brett[ 0].length; s++) if( brett[ 0][ s] != ' ' && brett[ 0][ s] == brett[ 1][ s] && brett[ 0][ s] == brett[ 2][ s]) { gewonnen = true; break; } // positiver Diagonalentest if( !gewonnen && brett[ 0][ 0] != ' ' && brett[ 0][ 0] == brett[ 1][ 1] && brett[ 0][ 0] == brett[ 2][ 2]) gewonnen = true; // negativer Diagonalentest if( !gewonnen && brett[ 0][ 2] != ' ' && brett[ 0][ 2] == brett[ 1][ 1] && brett[ 0][ 2] == brett[ 2][ 0]) gewonnen = true; // Fertig fertig = runde == laenge * laenge || gewonnen; } while( !fertig); // Spielauswertung if( gewonnen) System.out.println ( "Sieger: Spieler " + spieler[ dran]); else System.out.println( "Patt!"); System.out.println( "Spiel beendet"); } } Informatik/Numerik 4-10/14 Universität Leipzig 4.6 Institut für Informatik Dr. Monika Meiler Referenzdatentypen - Klassen Eigenschaften von Feldern Ein Feld fasst Daten zusammen, die für ein Programm eine Einheit bilden. Alle Komponenten eines Feldes besitzen den gleichen Typ. Der letzte Punkt ist eine Einschränkung, unterschiedliche Typen sind oft wünschenswert. Im Gegensatz zu Feldern sind Strukturen Sammlungen von Daten verschiedenen Typs. Definition Struktur Struktur =df Tupel über einer Menge von Daten (verschiedenen Typs) Strukturen sind strukturierte Datentypen, die für ein Programm eine Einheit bilden und Daten verschiedenen Typs zusammenfassen. Die zusammengefassten Daten sind die Strukturelemente. Strukturen werden generell in zwei Schritten festgelegt: Zunächst werden ein Strukturtyp und anschließend eine Struktur von diesem Strukturtyp deklariert. 4.6.1 Festlegen eines Strukturtyps als Klasse In Java verwendet man eine Klasse zur Modellierung eines Strukturtyps. Die in einer Klasse zusammengefassten Variablen verschiedenen Typs werden als Attribute bzw. Instanzvariablen bezeichnet. Sie nehmen die Daten (Eigenschaften) einer Struktur auf. Deklaration einer Klasse public class Klassenname { Deklaration . . . } public legt fest, dass die Klasse eine öffentliche, eine für jeden zugreifbare Klasse ist. Die Deklarationen legen die in der Klasse zusammengefassten Datentypen fest. Beispiel „Datum“ Ein Datum besteht aus drei Werten, dem Tag, dem Monat und das Jahr. Man könnte es als int - Feld der Länge 3 auffassen. Dann muss man sich aber merken, dass die 0. Komponente der Tag, die erste Komponente der Monat und die 2. Komponente das Jahr darstellen. Als Klasse bezeichnet man die Attribute mit ihrem Namen, so dass die Daten leichter lesbar werden. Datum.java public class Datum { int tag; int monat; int jahr; } Klasse Datum Datum tag: monat: jahr: int int int Eine Klasse wird in einer eigenen Datei mit dem Namen der Klasse abgespeichert. Informatik/Numerik 4-11/14 Universität Leipzig Institut für Informatik Dr. Monika Meiler 4.6.2 Festlegen einer Struktur – eines Objekts einer Klasse Betrachten wir jetzt ein konkretes Datum, z. B. den Geburtstag von Gottfried Wilhelm Leibniz am 1. 7. 1646. Man benötigt eine Variable, mit der man das Datum verarbeiten kann. Solche Variablen zur Klasse nennt man auch Objekte dieser Klasse. Analog den Feldern werden Objekte in zwei Schritten erzeugt: Deklaration eines Objektes einer Klasse Dem Compiler wird mitgeteilt, dass es sich um eine Referenzvariable handelt und Speicher für eine Adresse benötigt wird. Klassenname Objektname ; Datum datum; Definition eines Objektes einer Klasse Der notwendige Speicherplatz wird mit Hilfe newOperator bereitgestellt. null datum datum Objektname = new Klassenname ( ) ; datum = new Datum(); datum: Datum tag: monat: jahr: 0 0 0 datum: Datum tag: monat: jahr: 1 7 1646 Deklaration und Definition eines Objektes einer Klasse Datum datum = new Datum(); Zugriff auf die Attribute Der Zugriff auf die einzelnen Instanzvariablen erfolgt durch den Punkt-Operator. datum Objektname . Attribut datum.tag = 1; datum.monat = 7; datum.jahr = 1646; Explizite Anfangswertzuweisung (Initialisierung) Datum datum = { 1, 7, 1646} Objekt der Klasse Datum Beispiel Ein Datum soll eingegeben werden, auf Korrektheit überprüft und wieder in einer üblichen Form ausgegeben werden. Dazu ist ein Objekt Datum zu erzeugen und anschließend dessen Attributen kontrolliert einzugeben und auszulesen. DatumsEingabe.java (Grobstruktur) public class DatumsEingabe { public static void main( String[] args) { // Datum // Datumseingabe Informatik/Numerik 4-12/14 Universität Leipzig Institut für Informatik Dr. Monika Meiler // Jahr (1600 .. 3000) // Monat (1 .. 12) // Tag (1 .. Anzahl Tage im Monat) // Datumsausgabe } } DatumsEingabe.java // DatumsEingabe.java import Tools.IO.*; MM 2009 // Eingaben /** * Eingabe eines Datums, * Gregorianischer Kalender gilt seit 1582. */ public class DatumsEingabe { /** * Eingabe eines Datums, * Ueberpruefen der Korrektheit, * Ausgabe des Datum. */ public static void main( String[] args) { System.out.print( "Datumseingabe"); // Datum Datum datum = new Datum(); // Datumseingabe do // Jahr { datum.jahr = IOTools.readInteger( "Jahr (1600 .. 3000): "); } while( datum.jahr < 1600 || datum.jahr > 3000); do { // Monat datum.monat = IOTools.readInteger( "Monat (1 .. 12): "); } while( datum.monat < 1 || datum.monat > 12); boolean schaltJahr = false; // Schaltjahr int cc = datum.jahr / 100; int jj = datum.jahr % 100; if( jj == 0) schaltJahr = cc % 4 == 0); else schaltJahr = ((jj % 4) == 0); int anzahl = 0; switch( datum.monat) { Informatik/Numerik // Anzahl der Tage im Monat 4-13/14 Universität Leipzig Institut für Informatik Dr. Monika Meiler case 1: case 3: case 5: case 7: case 8: case 10: case 12: anzahl = 31; break; case 4: case 6: case 9: case 11: anzahl = 30; break; case 2: if( schaltJahr) anzahl = 29; else anzahl = 28; } do { // Tag datum.tag = IOTools.readInteger( "Tag (1 .. " + anzahl + "): "); } while( datum.tag < 1 || datum.tag > anzahl); // Datumsausgabe System.out.println ( datum.tag + "." + datum.monat + "." + datum.jahr); } } Da man Datumseingaben immer überprüfen muss, wäre es günstig, die Datumsüberprüfung mit der Klasse Datum fest zu verbinden. Diese Möglichkeit ergibt sich mit einem neuen Programmierparadigma, der objektorientierten Programmierung. Informatik/Numerik 4-14/14