class Polynom{...})Polynom p;)p.Grad)p.getWert(1.1))Polynom p=new Polynom(3,a);)public, private, protected)static)class Polynom extends Funktion{...})Funktion
(Datei: Funktion.java) sowie ihrer Unterklasse
Polynom
(Datei: Polynom.java) orientieren.
Diese Klassen können von irgendeiner Applikation (eine
Klasse mit Methode
main) aufgerufen werden.
Polynom hätte ich auch
in enger Anlehnung an den vorherigen Abschnitt eine Unterklasse
NewtonPolynom wählen können.
main).
Der Programmierer wird daher, allerdings nur in sehr schwacher
Weise (s. die Beispiele
Lösung eines linearen Gleichungssystems
und
Berechnung eines Newtonschen Interpolationspolynoms)
zur OOP gezwungen. Schaut man sich diese Beispiele
oder die allereinfachste Anwendung in Form des
Hello-World-Programms an, kann man keinen Nutzen von OOP
erkennen, eher nur Mystik. Das liegt u.a. daran, daß die
hier definierten Klassen in dem Sinne abstrakt sind, als
daß kein
Exemplar (auch Instanz genannt)
von ihr gebildet wird.
Listing der Klassen Funktion, Polynom
int oder double
sind keine eigentlichen Objekte, alle Variablen von einem anderen als einem
Grunddatentyp sind stets Objekte.
Beispiel:
Mittels Polynom p;
wird ein Objekt mit Bezeichner
p
der Klasse Polynom
deklariert.
Unter Verwendung des
new-Operators
und einem Konstruktor
gleichen Namens wie die Klasse wird das Objekt sodann
allokiert, d.h.
ihm Speicherplatz (dynamisch) reserviert:
Durch
double[] aa={1,2,3,4}; p=new Polynom(3,aa)
wird das Polynom
p(x)=1+2x+3x^2+4x^3 (mit dem Koeffizientenvektor aa)
initialisiert.
Auf die Daten (Mitgliedsvariable) und Methoden eines Objekts wird mit der Punkt-Notation zugegriffen.
Listing der Klassen Funktion, Polynom
static kenntlich gemacht werden.
Beispiel:
Ein Objekt namens p der Klasse Polynom
hat als Mitgliedsvariable
int Grad und den Koeffizientenvektor double[] a.
Mittels
p.Grad, p.a
könnte von außen auf die
Daten von p zugegriffen werden, wenn diese nicht
als
private
deklariert worden wären (s.u. unter
Zugriffsrechte).
Listing der Klassen Funktion, Polynom
void festgelegt
wird). Methoden können in runden Klammern eingeschlossene
Parameter (Argumente), auch in Form von
Parameterlisten, enthalten. Die runden Klammern müssen
immer dem Bezeichner einer Methode angefügt werden - auch wenn es keinen
Parameter gibt.
Polynom (und auch
ihr Objekt p) u.a. über
von denen HornerKoeffizienten() als Wert einen Vektor (private double[] HornerKoeffizienten(double x) public double getWert(double x) public double getAbleitung(double x)
double[])
zurückgibt, während die anderen Methoden
als Wert double
haben. Ferner haben sie alle eine vorgegebene Zahl x
als Parameter. Zu den
Zugriffsrechten später.
double y=p.getWet(3);
kann die Methode
getWert durchgeführt, hier der Wert des Polynoms an der
Stelle 3 berechnet werden.
Man sagt auch, daß eine
Klasse, in der p deklariert wird, sich der (öffentlichen!)
Methode getWert() des Objektes p bedient.
class Polynom extends Funktion{
...
...
private double[] HornerKoeffizienten(double x){
double[] b = new double[this.Grad];
b[Grad-1]=a[Grad];
for(int j=Grad-2;j>=0;j--) b[j]=a[j+1]+x*b[j+1];
return b;
}//ende HornerKoeffizienten
public double getWert(double x){
if (Grad==0) return a[0];
else{
double[] b=HornerKoeffizienten(x);
return b[0]*x+a[0];
}//else
}//Ende getWert
public double getAbleitung(double x){
Polynom q=this.Horner(x);
return q.getWert(x);
}//Ende getAbleitung
...
...
}//Ende class Polynom
Die Werte von Methoden werden mit Hilfe des
return
Operators übergeben.
Die Methode getAbleitung benutzt eine weitere Methode
Horner der Klasse Polynom, die
insofern etwas aus der Rolle fällt, weil sie
als Wert ein Objekt just derselben Klasse
Polynom hat! Es ist das Polynom,
das durch die Koeffizienten b[i] des Horner-Schemas
gebildet wird:
class Polynom extends Funktion{
...
...
public Polynom Horner(double xi){
double[] b=HornerKoeffizienten(xi);
Polynom p=new Polynom(this.Grad-1,b); //rekursiv!
return p; //p_n(x)=p_{n-1}(x)*(x-xi)+p_n(xi), p=p_{n-1}
}//ende Horner
...
...
}//Ende class Polynom
Theoretisch ist eine Anweisung p.Horner(1).Horner(2).getWert(3);
möglich.
this verwendet. Er zeigt
auf das aktuelle Objekt.
Horner wird ein
Konstruktor verwendet.
Listing der Klassen Funktion, Polynom
new.
Konstruktoren sind ganz spezielle Methoden, sie haben
denselben Namen wie die Klasse.
Eine Klasse muß nicht zwingend
einen Konstruktor vorsehen. Dann kann man immer noch mit einem
voreingestellten "minimalen" Default-Konstruktor ein Objekt allokieren.
Der Konstruktor liefert keine Werte, dennoch steht er ohne den Zusatz
void.
Polynom ist folgendermaßen vereinbart:
class Polynom extends Funktion{
...
...
//Konstruktor:
public Polynom(int Grad, double[] a){
this.Grad=Grad;
this.a=a;
}
...
...
}//Ende class Polynom
Das Polynom wird "konstruiert", wenn sein Grad und der Koeffizientenvektor
übergeben werden. Hier ist die Verwendung des Zeigers this
nur dann nicht notwendig, wenn man als Parameter-Bezeichner des
Konstruktors nicht dieselben wie die für die Mitgliedsvariablen
verwendet hätte, z.B.
//Konstruktor:
public Polynom(int n, double[] b){
Grad=n;
a=b;
}
Die Verwendung von this hat den Vorteil, daß man sich
in das Gedächtnis zurückruft, daß Grad, a
Mitgliedsvariable eines Polynom-Objektes sind.
Listing der Klassen Funktion, Polynom
public Polynom(int Grad, double[] a)
hätte durch
public Polynom(double[] a){
this(a.length-1,a);//length ist ein Attribut eines Feldes
}
überladen werden können, indem der erste Konstruktor
mit Hilfe des Zeigers this
aufgerufen wird (Konstruktoren können sich gegenseitig mit Hilfe
von this aufrufen). Danach können beide
Konstruktoren Verwendung finden.
Listing der Klassen Funktion, Polynom
public
(öffentliches Zugriffsrecht, Sichtbarkeit)
oder private lautet.
Ersteres erlaubt jeder Klasse,
bzw. jedem Objekt
auf sie zuzugreifen (und die öffentlichen Daten zu verändern),
letzteres gewährt dieses Recht nur innerhalb der eigenen Klasse.
Für Methoden wird hierdurch die Dienstbarkeit für andere Klassen
festgelegt, in der Regel bieten Objekte ihre Dienste anderen Objekten
an.
protected
erlaubt das Zugreifen auch für
Ober- und Unterklassen (s.u.), also für das gesamte
Paket (package).
public.
private int Grad;
Hätten wir die Variable Grad der Klasse
Polynom
nicht als private deklariert, könnte eine andere Klasse
z.B. durch p.Grad=123; einen "falschen" Grad erzeugen.
Will man jedoch Unterklassen (s.u.) definieren, k"onnen diese
private
Attribute und Methoden nicht erben. Diese müssen wenigstens
protected sein.
Grundsätzlich sind private Daten und Methoden für andere Klassen
nicht sichtbar. Die Methode HornerKoeffizienten ist
private, da sie außerhalb dieser Klasse nicht
benutzt werden soll.
Listing der Klassen Funktion, Polynom
Math. Hier sind naturgemäß
alle Variablen und Methoden statisch (static). Man ruft
sie über den Bezeichner der Klasse auf, z.B.
Math.sin.
Polynom p; ergibt eine (Referenz-) Variable mit
Bezeichner p vom Typ Polynom.
Erst durch Initialisierung in Verbindung mit new
wird ein konkretes
Objekt erzeugt. Man sagt, es wird eine Instanz der Klasse als Vorlage
gebildet: Durch
wird im Heap Platz gemacht für die Daten vonp=new Polynom(3,aa);
p, man sagt p ist initialisiert oder
allokiert.
p ist wie jede Objektvariable einschließlich Arrays und Strings
eine Referenzvariable im Gegensatz zu
int i; char b; double x;, den Grunddatentypen.
Dieser Unterschied kommt bei der Anweisung q=p; für zwei
Objekte p, q zum Tragen: hierdurch werden die Referenzen
gleich, d.h. in Folge weisen beide auf denselben Heap, es wird keine
Kopie angelegt!
Ebenfalls sehr wichtig ist dies, wenn man Referenzvariable
als Parameter von Methoden übergibt ("Call by Reference").
Polynom
ist eine Unterklasse der (abstrakten)
Klasse Funktion.
Dieses erkennt man an der ersten Zeile
in Polynom.java:
class Polynom extends Funktion{
...
...
}
Mit dem Zusatz
extends Funktion
definiert sich die Klasse
Polynom als Unterklasse der Klasse
Funktion.
Der Vorteil dieses Konzepts wird durch Vererbung
und Polymorphie charakterisiert.
Funktion
denkbar. Eine lautet so:
public class HutFunktion extends Funktion{
public double getWert(double x){
double y=0;
if ((x>-1) & (x<=0)) y=x+1;
if ((x>0) & (x<=1)) y=1-x;
return y;
}
}
Listing der Klassen Funktion, Polynom
Funktion
genauer an: es sind die drei Methoden
getWert, getAbleitung,
getWertetabelle. Die ersten beiden Methoden wurden von
der Unterklasse Polynom überlagert, sie
wurden "optimal" auf eine Polynomfunktion zugeschnitten. Die
abstrakte Klasse Funktion erwartet von jeder ihrer
Unterklasse eine Überlagerung
(oder Redefinierung) zumindestens der Methode getWert.
Wird getAbleitung nicht überlagert, so wird der
"Notnagel" der Klasse Funktion (numerische Differentiation)
benutzt. Anders ist es mit der Methode getWertetabelle
von Funktion: auf diese kann und sollte von jedem Objekt
jeder Unterklasse zugegriffen werden:
class Funktion{
...
...
public double[][] getWertetabelle(double a, double b, int Aufloesung){
double dx=(b-a)/Aufloesung;
double[] x=new double[Aufloesung+1];
double[] y=new double[Aufloesung+1];
double[][] z=new double[2][Aufloesung+1];
for(int i=0;i<=Aufloesung;i++){
x[i]=a+i*dx; y[i]=getWert(x[i]);}
z[0]=x; z[1]=y;
return z;
}
...
...
}//Ende class Funktion
Wichtig ist, daß hier als Methode getWert nicht etwa
die "Stellvertreter-Methode" in Funktion verwendet
wird, sondern die jeweils überlagerte Methode der Unterklasse.
Funktion
eingeführt werden, auf die jedes Objekt einer Unterklasse
zugreifen kann.
super. Es ist denkbar, daß
der Konstruktor einer Unterklasse zusätzliche Parameter besitzt.
Listing der Klassen Funktion, Polynom
Funktion enthält, könnte dort
unbedenklich (jedenfalls syntaktisch) ein Objekt der
Unterklasse Polynom
eingesetzt werden.
Als Beispiel sei die Klassenmethode
Tools.Zeichne
genannt, die den Grafen einer Funktion zeichnet. Weitere Methoden
wie Numerische Integration oder Nullstellenbestimmung sind
denkbar.
public abstract class Funktion{
public String Name;
public abstract double getWert(double x); //muss in jeder Unterklasse
//ueberlagert werden
public double getAbleitung(double x){
return (getWert(x+0.001)-getWert(x-0.001))/0.002;
}//Ende getAbleitung
public double[][] getWertetabelle(double a, double b, int Aufloesung){
double dx=(b-a)/Aufloesung;
double[] x=new double[Aufloesung+1];
double[] y=new double[Aufloesung+1];
double[][] z=new double[2][Aufloesung+1];
for(int i=0;i<=Aufloesung;i++)
{x[i]=a+i*dx; y[i]=getWert(x[i]);}
z[0]=x; z[1]=y;
return z;
}//Ende getWertetabelle
}//Ende class Funktion
class Polynom extends Funktion{
//Attribute, Datenelemente, Mitgliedsvariable:
private int Grad;
private double a[]; //Koeffizienten (p=a0+a1*x+....+an*x^n)
//Konstruktor:
public Polynom(int Grad, double[] a){
this.Grad=Grad;
this.a=a;
}//Ende 1. Konstruktor
public Polynom(double[] a){
this(a.length-1,a);
}//Ende 2. Konstruktor
//Methoden:
private double[] HornerKoeffizienten(double xi){
double[] b = new double[this.Grad];
b[Grad-1]=a[Grad];
for(int j=Grad-2;j>=0;j--) b[j]=a[j+1]+xi*b[j+1];
return b;
}//ende HornerKoeffizienten
//Eine Methode, die wieder ein Polynom erzeugt:
public Polynom Horner(double xi){
double[] b=HornerKoeffizienten(xi);
Polynom p=new Polynom(this.Grad-1,b); //Bemerkenswert: Rekursiv
return p; //p_n(x)=p_{n-1}(x)*(x-xi)+p_n(xi), p=p_{n-1}
}//ende Horner
public double getWert(double x){
if (Grad==0) return a[0];
else{
double[] b=HornerKoeffizienten(x);
return b[0]*x+a[0];
}//Ende else
}//Ende getWert
public double getAbleitung(double x){
Polynom q=this.Horner(x);
return q.getWert(x);
}//Ende getAbleitung
}//ende class Polynom
Funktion, Polynom, HutFunktion
nutzt:
public class Test1{
public static void main(String[] args){
//Wertetabelle der Hautfunktion:
double[][] z=new double[2][10];
double a=-2; double b=2; int Aufloesung=10;
HutFunktion f=new HutFunktion();
z=f.getWertetabelle(a,b, Aufloesung);
for(int i=0;i<=10;i++) System.out.println(z[0][i]+" "+z[1][i]);
//Berechnung von Funktionswert und Ableitung eines Polynoms an einer Stelle xi:
double[] aa={1,2,3,4};
Polynom p=new Polynom(3, aa); //oder ...Polynom(aa);
Polynom q=new Polynom(aa);
double xi=1;
int j=1;
for(int i=0;i<=3;i++){//die ersten drei Ableitungen an der Stelle xi
System.out.println("p^"+i+"("+xi+")="+j*q.getWert(xi));
if (i<3) q=q.Horner(xi);
j*=(i+1); //(i+1)!
}//Ende for
}//Ende main
}//Ende Test1
Die letzten Zeilen verdienen eine Bemerkung: Hier werden von dem
Polynom p alle Ableitungen an der Stelle xi
berechnet. Dies geschieht durch mehrmaliges Anwenden des
Horner Schemas, genauer der Methode Horner,
die ja gerade das Polynom liefert, das von den Koeffizienten
des Horner Schemas bestimmt wird. Der Befehl
q=q.Horner(xi)
ist also rekursiv.
Funktion).
java.lang,
andere wie java.applet
oder java.awt (siehe Applets)
müssen importiert werden.
OOP mit der Klasse Matrix und der Unterklasse TridiagonalMatrix