Unterprogramme, Methoden
Klassen und Objekte
Parameterübergabe von Referenzen
Kapselung und Zugriffskontrolle
Konstruktoren
Datentyp Complex
Unterprogramme sind Prozeduren und Funktionen, in Java werden sie als Methoden bezeichnet.
Unterprogramme bestehen aus einem Kopf (head) und einem Rumpf (body)
der Rumpf ist eine Zusammenfassung von Anweisungen
in dem Kopf wird der Name des Unterprogramms definiert, sowie die Parameter und der Ergebnistyp
bei Prozeduren ist der Ergebnistyp void
die Parameter (als formale Parameter bezeichnet) definieren im Rumpf lokale Variablen
Aufruf einer Methode erfolgt durch Angabe des Namens, gefolgt
von den aktuellen Parametern (getrennt durch Komma) in Klammern
name( p1, p2, f(p3), p4+5 )
Signatur, Aufrufschnittstelle:
Anzahl und Typen der Parameter sowie des Ergebnistyps
(void, int, String[], Integer)
Überladen:
Methoden mit gleichem Namen, die sich in der Signatur unterschieden
(in Java nur der formalen Parameter),
bei der Auswahl muss die Eindeutigkeit gewährleistet werden
(int, String[], Integer)
,
(double, String[], Double)
Parameterübergabe:
Wertaufruf (call by value):
der berechnete Wert der Ausdrucks wird verwendet
(in Java für elementare Typen und Objektreferenzen)
Referenzaufruf (call by reference):
nur bei Variablen als Parameter,
Ermöglicht Zuweisungen an die Variable im Rumpf
(nicht in Java, aber Objekte sind Referenzen)
Namensaufruf (call by name):
textuelle Substitution wie bei LISP/Scheme
(nicht in Java)
die Prozedur public static void main(String[] args) { }
wird vom Java-Interpreter als erste ausgeführt
bei rekursiven Prozeduren wird im Rumpf die Prozedur selbst wieder aufgerufen,
void prozedur( parameter ) block; typ funktion( parameter ) { ... return (typ) wert; ... } typ methode( typ_1 name_1, ..., typ_n name_n ) { ... int x = (int)name_1; int y = funktion( name_2, 4711 ); int z = name_3 + name_n; ... } public static void main(String[] args) { // die Hauptprozedur, // d.h. die erste die ausgeführt wird }
Beispiele:
public class Unterprogramme { void prozedur() { } int funktion() { return 4711; } int quadrat(int a) { return a*a; } double quadrat(double a) { return a*a; } int name(int a) { int b = quadrat( 2 ); return b+a; } long fakultaet(long n) { if ( n <= 1l ) { return 1l; } else { return n * fakultaet( n-1l); } } double summe(double[] a) { double sum = 0.0; for ( int i = 0; i < a.length; i++ ) { sum += a[i]; } return sum; } void main() { prozedur(); int n = funktion(); int i = quadrat(n); int j = name( 3 ); long k = fakultaet( 10 ); double[] z = { 1.0, 1.2, 3.2, 4.6, 9.2 }; double s = summe( z ); System.out.println("n = " + n); System.out.println("i = " + i); System.out.println("j = " + j); System.out.println("k = " + k); System.out.println("s = " + s); } public static void main(String[] args) { // die Hauptprozedur, (new Unterprogramme()).main(); } }
public class Ueberladen { void problem(int a, long b) { } void problem(long a, long b) { } void problem(long a, int b) { } void ratemal() { problem( 1, 2L ); // ok problem( 1L, 2 ); // ok problem( 1, 2 ); // fehler } long problem(long a, long b) { // fehler return a*b; } }
public class Uebergabe { class Unter { int a = 0; int b = 1; } void wert( int c ) { c = c + 55; } void referenz( Unter u ) { int x = u.a * 3; u.b = x + 14; } void unveraendert( Unter u ) { u = new Unter(); int x = u.a * 3; u.b = x + 14; } void ratemal() { int w = 3; wert( w ); // w == 3 Unter u = new Unter(); referenz( u ); // u.a == 0 // u.b == 14 unveraendert( u ); // u.a == 0 // u.b == 1 } }
public class Rekursion { long fakultaet(int n) { if ( n <= 1 ) { return 1l; } else { return n * fakultaet( n-1); } } double summe(double[] a) { double sum = 0.0; for ( int i = 0; i < a.length; i++ ) { sum += a[i]; } return sum; } double reksumme(double[] a) { return reksumme(a,a.length); } double reksumme(double[] a, int n) { if ( n == 0 ) return 0.0; return reksumme(a,n-1) + a[n-1]; } long potenz(int b, int e) { // berechne b hoch e // if ( e < 0 ) { b = 1/b; e = -e; } if ( e == 0 ) return 1l; if ( e == 1 ) return (long)b; return b * potenz( b, e-1 ); } long quadrat(long b) { // berechne b*b return b*b; } long potenzFast(int b, int e) { // berechne b hoch e // if ( e < 0 ) { b = 1/b; e = -e; } if ( e == 0 ) return 1l; if ( e == 1 ) return (long)b; if ( e % 2 == 0 ) return quadrat( potenzFast( b, e/2 ) ); return b * potenzFast( b, e-1 ); } void main() { long k = fakultaet( 10 ); double[] z = { 1.0, 1.2, 3.2, 4.6, 9.2 }; double s = summe( z ); double r = reksumme( z ); long p = potenz( 10, 15 ); long f = potenzFast( 10, 15 ); System.out.println("k = " + k); System.out.println("s = " + s); System.out.println("r = " + r); System.out.println("p = " + p); System.out.println("f = " + f); } public static void main(String[] args) { // die Hauptprozedur, (new Rekursion()).main(); } }
public class Summe { static double[] betrag = null; static double summe() { double s = 0.0; for (int i = 0; i < betrag.length; i++ ) { s += betrag[i]; } return s; } public static void main(String[] args) { betrag = new double[args.length]; for (int i = 0; i < args.length; i++) { betrag[i] = 0.0; try { betrag[i] = Double.parseDouble( args[i] ); // betrag[i] = Double.valueOf( args[i] ).doubleValue(); } catch (NumberFormatException e) { } } System.out.println("Eingegeben wurden " + args.length + " Zahlen"); double s = summe(); System.out.println("Die Summe ist " + s); } }
Java Syntax aus der Sprachdefinition.
Klassen bieten die Zusammenfassung von Funktionen, Prozeduren und Variablen zu einem bestimmten Zweck. (Abstraktion von einzelnen Methoden zu Modulen oder Gruppen.)
Klassen sind zusammengesetzte Datentypen.
die Klassendeklaration definiert die Bestandteile:
Ein Objekt ist ein Exemplar einer Klasse (auch als Instanz bezeichnet)
Ein Objekt einer Klasse kann durch das Schlüsselwort new erzeugt werden.
ein Objekt enthält die Bestandteile:
Der Zugriff auf Objektbestandteile erfolgt mit dem Punktoperator
'.
'
Variablen einer Klasse deklariert man durch den Namen der Klasse, gefolgt vom Namen der Variablen Klasse var (entspricht Klasse var = null). Der Variablen muss anschliessend ein (neues) Objekt zugewiesen werden.
Konstruktoren sind spezielle Methoden mit dem Namen der Klasse ohne Rückgabeparameter. Bei der Erzeugung eines neuen Objekts wird entsprechend der aktuellen Parameter ein passender Konstruktor ausgewählt, der die Instanz-Variablen initialisieren kann.
Diagramme für Klassen in der Unified Modeling Language (UML):
Name |
Zustand, Attribute |
Funktionalität, Methoden |
public class Klassen { static int klassen = 0; int meineNummer = 0; Klassen() { meineNummer = klassen++; } static String statistik() { return "Erzeugte Objekte = " + klassen; } public String toString() { return "Meine Nummer = " + meineNummer + " von " + klassen; } public static void main(String[] args) { System.out.println( Klassen.statistik() ); Klassen einz = new Klassen(); Klassen zwei = new Klassen(); Klassen drei = new Klassen(); Klassen vier = new Klassen(); Klassen fuenf = new Klassen(); System.out.println( statistik() ); System.out.println("einz: " + einz.toString()); System.out.println("zwei: " + zwei); System.out.println("vier: " + vier); System.out.println("drei: " + drei); System.out.println("fuenf: " + fuenf); } }
Java-Programme werden mittels Klassen definiert. Jede öffentliche (d.h. als public deklarierte) Klasse muß in einer separaten Datei gespeichert werden. Der Name der Datei muß mit dem Namen der public-Klasse übereinstimmen (z.B. muß die Klasse public class Test in der Datei Test.java gespeichert werden).
Der Speicherbereich für das Objekt wird automatisch allokiert. Wenn das Objekt nicht mehr gebraucht wird (d.h. keine Variable referenziert mehr das Objekt), wird der Speicherbereich während eines Garbage-Collection-Vorganges wieder freigegeben (Speicherrecycling). Das heißt, daß im Gegensatz zu C/C++ kein expliziter `delete'-Operator benötigt wird.
In Java können Prozeduren nur innerhalb von Klassen definiert werden. Prozeduren können zu einer Klasse oder zu Objekten gehören. Es gibt in Java keine freien / globalen Variablen oder Methoden.
public class Card { int number; static int base = 5; public static String version = "1.0"; public Card(int n) { number = n; } public int getNumber() { return number; } public static int getBase() { return base; } public int getDiff() { return number - base; } public static void main(String[] args) { Card c = new Card(7); System.out.println(c.version+":"+ c.getNumber()+"-"+c.getDiff()+"="+ Card.getBase()); } }
Die Ausgabe dieses Programms ist 1.0:7-2=5.
Die Übergabe von Objektrefrenzen ermöglicht die Veränderung von übergebenen Objekten in der aufrufenden Methode.
public class Confuse { static class Reference { int val; } public int confuse( int a, int b ) { a = 1; a = b + a; return a; } public Integer confuse( Integer a, Integer b ) { a = new Integer(1); a = new Integer( b.intValue() + a.intValue() ); return a; } public Reference confuse( Reference a, Reference b ) { a.val = 1; a.val = b.val + a.val; return a; } public static void main(String[] args) { Confuse c = new Confuse(); int m = 4; int n = c.confuse( m, m ); System.out.println("m = " + m); System.out.println("n = " + n); Integer i = new Integer(4); Integer j = c.confuse( i, i ); System.out.println("i = " + i); System.out.println("j = " + j); Reference k = new Reference(); k.val = 4; Reference l = c.confuse( k, k ); System.out.println("k = " + k.val ); System.out.println("l = " + l.val ); } }
Das Problem kann durch Zugriffskontrolle gelöst oder beherscht werden.
Klassen bzw. Objekte können ihre Variablen und Methoden in verschiedenen Abstufungen kapseln (einschliessen). Dabei werden nur die Variablen oder Methoden nach aussen, d.h. für andere Objekte sichtbar gemacht, die notwendig sind. Man spricht auch vom Geheimnisprinzip. Die öffentlich zugänglichen Variablen und Methoden werden meist noch durch Interfaces (s.u.) definiert. Vorteil: die Interna können beliebig modifiziert werden solange die Schnittstelle und deren Semantik erhalten bleibt.
Java unterschiedet public
, private
,
protected
und default Zugriffsrechte.
public
völlig öffentlich
private
nur in Objekten der eigenen Klasse sichtbar
protected
in Objekten der Klassenhierarchie sichtbar,
d.h. in abgeleiteten Klassen
nichts, d.h. default: nur im gleichen Package sichtbar
Beispiele siehe Abschnitt "Sichtbarkeit".
Der Kontrakt zwischen dem/der Entwicklerin einer Klasse und einem Anwender besteht aus der Syntax und Semantik der Aufrufschnittstelle.
Konstruktoren dienen der Initialisierung der Objekt-Variablen. Klassen-Variablen werden ein einziges mal vor der ersten Verwendung der Klasse initialisiert.
der Name der Konstruktoren muss der Name der Klasse sein
Konstruktoren sehen aus wie Methoden ohne Rückgabetyp Definition
Konstruktoren können wie Methoden überladen werden
für Konstruktoren gelten die üblichen Zugriffsspezifikationen
Konstruktoren der Superklasse können explizit mit
super(...)
verwendet werden
andere Konstruktoren der eigenen Klasse können explizit mit
this(...)
verwendet werden
Wir entwickeln eine Klasse Complex
, die
für die Arithmetik komplexer Zahlen verwendet werden kann.
Der Konstruktor soll aus einem Paar reeler Zahlen (a,b)
eine komplexe Zahl a + i b
machen.
Methoden und Konstanten:
Null ist 0 + i 0
, Einz ist 1 + i 0
,
Feststellen des Real- und Imaginär-Teils,
Anzeige mit toString()
von Object
,
Test auf Gleichheit,
Addition: (a + i b) + (c + i d)
= (a+c) + i (b+d)
,
Subtraktion analog, negative Zahl,
Multiplikation: (a + i b) * (c + i d)
= (ac-bd) + i (ad+bc)
,
Konjugation: con(a + i b)
= a - i b
,
Betrag: abs(a + i b)
= sqrt( a a + b b )
,
Inverses: inv(a + i b)
= con(a + i b)/abs(a + i b),
Division: (a + i b) / (c + i d)
= (a + i b) * inv(c + i d),
/** * Klasse zum Rechnen mit Komplexen Zahlen * hk, 9.12.2001 * **/ public class Complex { /* Die Datenstruktur */ private double re; // der Realteil private double im; // der Imaginärteil /* Die Konstruktoren erzeugen aus einem Paar von double Zahlen eine Komplexe Zahl */ public Complex(double r, double i) { re = r; im = i; } public Complex(double r) { this(r,0.0); } public Complex() { this(0.0); } /* Konstanten: 1, 0 und i */ public static final Complex ZERO = new Complex(); public static final Complex ONE = new Complex(1.0); public static final Complex I = new Complex(0.0,1.0); /* Bestimmen des Real- und Imaginär-Teils */ public double reTeil() { return re; } public double imTeil() { return im; } /* Darstellung als Zeichenkette */ public String toString() { String s = "" + re; if ( im < 0 ) s += " - " + (-im) + "i"; else s += " + " + im + "i"; return s; } /* Vergleich zweier Komplexer Zahlen */ public boolean equals(Complex b) { return ( re == b.reTeil() && im == b.imTeil() ); } /* Arithmetische Operationen: +, -, - */ public Complex add(Complex b) { return new Complex(re + b.reTeil(), im + b.imTeil()); } public Complex subtract(Complex b) { return new Complex(re - b.reTeil(), im - b.imTeil()); } public Complex negate() { return new Complex(-re,-im); } /* Arithmetische Operationen: Konjugierte, Absolut Betrag */ public Complex conjugate() { return new Complex(re,-im); } public double abs() { return ( Math.sqrt( re * re + im * im ) ); } /* Arithmetische Operationen: *, Inverse, / */ public Complex multiply(Complex b) { return new Complex(re * b.reTeil() - im * b.imTeil(), re * b.imTeil() + im * b.reTeil() ); } public Complex inverse() { double a = re * re + im * im; return new Complex(re/a, -im/a); // Konjugierte } public Complex divide (Complex b) { return this.multiply( b.inverse() ); } }
Anwendungsbeispiel
/** * Testen der Klasse zum Rechnen mit Komplexen Zahlen * hk, 9.12.2001 * **/ public class ComplexTest { public static void main(String[] args) { Complex a = new Complex(2.0,3.0); System.out.println("a(2,3) = " + a); System.out.println("Null(0,0) = " + Complex.ZERO); System.out.println("Eins(1,0) = " + Complex.ONE); Complex b = Complex.ONE; b = b.add(a); System.out.println("b(3,3) = " + b); Complex c = new Complex(3.0,3.0); System.out.println("c(3,3) = " + c); boolean t = c.equals(b); System.out.println("t(true) = " + t); Complex d = c.conjugate(); System.out.println("d(3,-3) = " + d); double e = c.abs(); System.out.println("e(18) = " + e); Complex f = c.multiply(d); System.out.println("f(18,0) = " + f); // c*conjugate(c) = abs(c)^2 t = e == Math.sqrt( f.reTeil() ); System.out.println("t(true) = " + t); // i*i = -1 Complex g = Complex.I.multiply(Complex.I); System.out.println("g(-1,0) = " + g); // 1/i = -i Complex h = Complex.ONE.divide(Complex.I); System.out.println("h(0,-i) = " + h); } }
© Universität Mannheim, Rechenzentrum, 1998-2003.
Heinz Kredel Last modified: Thu Jan 1 16:01:43 CET 2004