Universität Hamburg - Fachbereiche - Fachbereich Mathematik

Numerische Mathematik mit Java

Bodo Werner


Inhaltsverzeichnis


Vorbemerkung

Anders als an den meisten anderen deutschen Universitäten ist die Numerische Mathematik schon in den ersten beiden Semestern Pflichtveranstaltung für Studierende des Studiengangs Mathematik-Diplom. Ziel ist es u.a., frühzeitig den Computer in das Studium einzubinden und das Erlernen von Grundtechniken einer höheren Programmiersprache zu ermöglichen. In den letzten Jahren war die begleitende Sprache fast immer Pascal. Einen sehr guten Einblick in dieses Konzept vermittel des Lehrbuch meines Kollegen Gerhard Opfer Numerische Mathematik für Anfänger im Vieweg-Verlag.

Nun scheint es so, daß Pascal keine Zukunft hat. Mittelfristig muß man sich daher umorientieren. Das moderne Programmierungskonzept heißt Objektorientiertes Programmieren (OOP). Ich halte es für sehr sinnvoll, wenn Studierende von Beginn an mit diesem Konzept konfrontiert werden. Hierfür scheint mir Java hervorragend geeignet, weil es dieses Konzept verbindlich vorschreibt, einfacher zu erlernen sein soll als der mächtige(re) Vertreter C++ und plattformunabhängig ist. Daher soll in Hamburg im WiSe 99/00 erstmalig der Versuch unternommen werden, den zweisemestrigen Numerik-Kurs für Anfänger mit Java zu verknüpfen. Um Mißverständnissen vorzubeugen: nicht, weil ich glaube, daß Java in hervorragender Weise die Implementierung numerischer Algorithmen unterstützt, sondern weil mit Java eine sehr moderne Programmierrsprache erlernt wird, die das Tor zum OOP weit aufstößt. Ein weiterer Gesichtspunkt ist, daß das Erlernen von Java ein wichtiger Aspekt der Qualifikation für einen späteren Beruf darstellt - in viel höherem Maße als dies z.B. Pascal vermag.

Die große Popularität verdankt Java dem Internet und der Tatsache, daß Java-Applets von WWW-Browsern interpretiert werden können. Das zur Zeit beste (mathematische) Zeugnis der Mächtigkeit solcher Applets findet man meines Erachtens in der Mathe Online - Galerie

Der Einsatz von Java in der Erstsemster-Numerik soll sich aber nicht an Applets orientieren, sondern der Implementierung von grundlegenden numerischen Algorithmen dienen. Weder sollen Input-Output- noch Grafiktechniken Gegenstand des Unterrichts sein. Hierfür werden Klassen zur Verfügung gestellt werden, die auf den Numerikeinsatz zugeschnitten sind und als Black-Box benutzt werden.

Das hier angebotene Tutorium richtet sich an Studierende, aber auch an Kollegen. Es soll helfen, sich mit Java vertraut zu machen. Vorkenntnisse in Pascal, aber auch ein erstes Verständnis von OOP sind ausgesprochen hilfreich. Wiederholt wird auf Analogien bei Pascal hingewiesen. Dass die Sprachelemente vor einer Einführung in Objekte und Klassen zur Sprache kommen, bedeutet nicht, dass diese Reihenfolge beim Lernen eingehalten werden sollte. Anf&aumL,nger sollten möglichst bald mit dem Konzept des OOP konfrontiert werden. Das wird in den begleitenden Kursmaterialien versucht.

Man findet aber auch viele Dokumentationen und Tutorials im Web:

Der Autor ist alles andere als ein Profi in Sachen Programmiersprachen, insbesondere fehlt ihm der tiefer gehende Einblick in die Struktur von höheren Programmiersprachen. Er ist blutiger Anfänger in Sachen OOP und auch schon relativ alt. Er bittet daher, möglichen Ungereimtheiten, insbesondere in der Fachsprache, mit Nachsicht zu begegnen.


Einführung

Um Java-Anwendungsprogramme laufen lassen zu können, benötigt man ein Java Developer's Kid (JDK), das 1991 von Sun entwickelt wurde und zur Zeit in der Version 1.2 zur Verfügung gestellt wird. Der Javacompiler (javac.exe) erzeugt aus einem Quellcode (*.java) einen Bytecode (*.class), welches danach mit dem Javainterpreter (java.exe) plattformunabhängig ausgeführt wird. Applets sind spezielle in HTML eingebettete Java-Klassen, die von einem WWW-Browser verstanden werden. Wir werden uns hier mit reinen Anwendungsprogrammen befassen, die unabhängig von einer HTML-Umgebung laufen.

Das Grundkonzept von Java beruht auf dem objektorientierten Programmieren (OOP). Ein Objekt ist eine (neuartige) Variable vom Typ einer Klasse , in Abgrenzung zu den primitiven (einfachen) Datentypen (Zahlen: int, double,..., logische Variable: boolean, Zeichen: char). Objekte werden durch ihre Attribute (Eigenschaften, Daten, Variable) und ihre Methoden (Verhalten) charakterisiert. OOP ist des weiteren durch die Möglichkeit der Bildung von Unterklassen mit Vererbung und Polymorphie gekennzeichnet. Dennoch sollen zunächst die sehr Pascal-verwandten Sprachelemente wie Datentypen, Verzweigungen, Schleifen behandelt werden. Wer dennoch sehr schnell ein lauffähiges Programm schreiben will, orientiere sich an der folgenden Variante des unvermeidlichen Hello World-Programms:

class Hallo{
 public static void main (String args[]) {
 System.out.println("Hallo: Ich kann noch nicht Java");
 }
}
Es besteht aus einer Klasse mit Bezeichner Hallo und der Methode main(), die stets für die Ausführung eines Anwendungsprogramms verantwortlich ist. Der Nutzen des Klassenkonzepts kommt hier überhaupt nicht zum Tragen. Der Java-Interpreter interpretiert nur eine Klasse, die die Methode main() enthält, wobei das Argument (der Parameter) String args[] für Kommandozeileneingaben vorgesehen ist und daher häufig gar keine Bedeutung hat, aber dennoch dort immer stehen muß.
println() ist eine Methode des Objekts namens out der Klasse System, die zur offiziellen Java-Bibliothek gehört. Man achte schon jetzt auf die geschweiften Klammern, die den Beginn und das Ende von Blöcken markieren. Man speichere obiges Programm unter dem Dateinamen Hallo.java ab, compiliere es mit
javac Hallo.java
und interpretiere die hierdurch erzeugte Datei Hallo.class mittels
java Hallo.
Dabei soll die Quelldatei genauso heißen wie die Klasse.
Umfang- und inhaltsreichere Anwendungsprogramme, die im wesentlichen ohne OOP-Konzepte auskommen und dennoch Numerik-relevant sind, findet man in Numerik-Beispiele ohne OOP

Sprachelemente

Hier wird der Teil von Java beschrieben, der sich auf Syntaxregeln, Variablen, Strings, Operatoren, Arrays, Verzweigungen und Schleifen bezieht und für jemandem, der schon eine Programmiersprache wie Pascal beherrscht, leicht zu erlernen ist, wenn man von davon absieht, daß das Umlernen in Bezug auf gewohnte Schreibweisen ein umso größeres Problem wird, je älter man ist. Ganz wichtig und fehleranfällig ist z.B. die Tatsache, daß Groß- und Kleinschreibung sehr wohl unterschieden wird.

Kommentare

in nur einer Zeile: // Kommentar, bei mehreren Zeilen umfassenden Blöcken wird der Kommentar durch /* und */ eingeschachtelt.
class Hallo{  //class ist das wichtigste Schlüsselwort
/*
Man achte stets auf die Groß- und Kleinschreibung.
args ist ein Bezeichner für eine Stringvariable und kann auch durch
irgendetwas anderes ersetzt werden.
String und System als Bezeichner vordefinierter Klassen
werden groß, class und out dagegen klein geschrieben.
*/
 public static void main (String args[]) {
 System.out.println("Hallo: Ich kann immer noch nicht Java");
 }
}

Anweisungen

werden wie in Pascal durch ein Semikolon ; abgeschlossen. Eine Anweisung ist der kleinste Baustein, er kann eine Deklaration enthalten, eine Auswertung oder Zuweisung vornehmen oder einen Programmablauf steuern.
Beispiel: int k = 5; ist eine Deklaration einer Variablen namens k vom Integer-Typ int , die mit einer Zuweisung (Initialisierung) verbunden wird.

Bezeichner

benennen Variablen (oder Methoden, Klassen, Objekte), enthalten Buchstaben und Zahlen (allgemeiner Unicode-Zeichen), fangen i.a. mit einem Buchstaben an. Es gibt vordefinierte Bezeichner (z.B. int, boolean, void, ... ), auch Schlüsselworte genannt.

Blöcke

Die geschweiften Klammern {, } markieren den Beginn und Ende eines Blocks und entsprechen den Wörtern begin, end in Pascal. Wie dort können diese Blockklammern bei einem Block mit nur einer Anweisung entfallen.

Variable

haben einen Namen (Bezeichner) und einen Typ, welcher ein Grunddatentyp (primitiver Typ), eine Klasse oder auch ein Array sein kann. Die für die Numerik wichtigsten Grunddatentypen (vordefinierte Typen) sind int, double, char, boolean bzw. ihre nicht ganz so wichtigen Varianten byte, short, long, float.

Deklaration einer Variablen

kann an jeder Stelle, auch simultan mit einer Zuweisung vorgenommen werden: long k=2222222222; oder auch etwas länglicher long k; k=2222222222;.

String, char

Ein String (eine Zeichenkette) kennzeichnet im Gegensatz zu Pascal keinen Grunddatentyp, sondern eine Klasse. Daher ist String s = "rot"; if s == "rot" ... falsch. Richtig ist
String s="rot"; if s.equals("rot") ...

M.a.W.: equals() ist eine Methode eines Objekts der Klasse String. Man achte auf die Punktnotation, die eine Methode an ihr Objekt bindet (Objekt.Methode). Trotz des Klassentyps muß eine Stringvariable nicht mit Hilfe von new (s.u.) initialisiert werden wie bei allgemeinen Objekten (auch bei Arrays). Beispiel: String s="Idiot"; Ein String-Literal (Darstellung des Wertes einer Zeichenketten-Variablen) wird durch doppelte Hochkommata eingeschlossen, ein char-Literal durch einfache Hochkommata:
String s="Eureka - I hob's"; char c='J';

Konstante

Während Variable im Laufe des Programms ihren Wert getreu ihren Namens ändern können, ist dies bei Konstanten nicht der Fall. Ihre Deklaration erfolgt durch das Schlüsselwort final. Beispiel: final double pi=3.1416;. Im Gegensatz zu Variablen können Konstanten nicht lokal definiert werden.

Operatoren

Man unterscheidet zwischen unären (z.B. der Zuweisungsoperator = , aber auch der Inkrementoperator ++) und bin"aren Operatoren (z.B. die arithmetischen Operatoren +, -, *, /, die Vergleichsoperatoren <, <=, == ), die logischen Operatoren: !, &, ^, | ). Die arithmetischen Operatoren liefern als Ergebnis auch bei Eingabeparametern vom Grundtyp byte, short, char stets den Typ int oder - falls einer vom Typ long ist - den Typ long. Kurzoperatoren wie ++, --: Beispiel: a++ oder ++a ist äquivalent mit a=a+1 , ist also eine Verquickung einer Zuweisungs- mit einer arithmetischen Operation. Aber Achtung: b=++a bzw. b=a++ liefert zwar für a jeweils einen um 1 erhöhten Wert, für b jedoch nur im ersten Fall. Im zweiten Fall wird erst b=a gerechnet und dann a um 1 erhöht. Sehr nützlich, gerade für numerische Algorithmen, sind die Kurzoperatoren +=, *=,.... So bedeutet a+=5; dasselbe wie a=a+5;.

Vergleichsoperatoren sind == (im Gegensatz zu Pascal: =) für Gleichheit, != f"ur Ungleichgleichheit (Pascal <>), ansonsten <, <=, >, >= wie in Pascal.

Logische Operatoren werden in Pascal mit and, or, xor, not ausgeschrieben, in Java mit & (and), ! (not), | (or) und ^ (xor) abgekürzt.

Bitweise Operatoren werden hier nicht behandelt, da in der Numerik wohl unbedeutend.

Stringadditionen sind in Java sehr bequem:

String s="Hallo" + "  " + "Numerik-Freunde";
aber auch (!)
int anzahl=5;
String s="Hier sitzen "+anzahl+" Zuhörer";
Dies ist sehr wichtig für numerische Ausgaben. Zahlen werden hierbei automatisch in Zeichenketten umgewandelt.

Array

(Feld) ist ein Liste von angeordneten Elementen eines einheitlichen Typs, seine Deklaration lautet:
int k[]; oder int [] k; Entscheidend ist das Klammerpaar [], nicht etwa wie in Pascal das Wort array. Ein Array ist ein Objekt, seine Deklaration erzeugt es noch nicht, sondern weist einer (Referenz-) Variablen nur einen Typ zu. Die Erzeugung (Speicher-Allokation) geschieht wie bei allen Objekten mit new in Verbindung mit einer Dimensionsangabe:
int[] k = new int[7]; oder auch int[] k; k= new int[7]; Es kann bequemer sein, einen Array manuell (ohne new) zu erstellen:
 String[] Wortliste = {"Frau","Mann","Kind"};
oder
double[][] A={{1,2},{8,9}};
Zum Vergleich in Pascal:
 Wortliste: Array[1..3] of String =("Frau","Mann","Kind");
Achtung! Das erste Listenelement eines Feldes hat stets den Index Null - das ist sehr gewöhnungsbedürftig: int[] k = new int[5]; k[0]=2; k[4]=4; Hier hat k 5 Listenelemente, das letzte hat den Index 4. Ein Array Typ[n] hat stets n Listenelemente, beginnend beim Index 0, endend beim Index n-1.

Mehrdimensionale Arrays:
double[][] A; A = new double[4][6] erzeugt eine 4 x 6 -Matrix mit den Zeilen 0,1,2,3 und den Spalten 0,1,2,...,5. Bei einer solchen Initialisierung erhält A die Elemente Null. Das ist fehleranfällig! Einzelne Elemente werden mit A[i][j] angesprochen (Pascal: A[i,j]).

for-Schleifen

Statt in Pascal for i:=1 to n do schreibt man
for(int i=1;i<=n;i++). Eine Doppelschleife lautet z.B.
 for (i=1;i<=n;i++) for (j=1;j<=n;j++) A[i-1][j-1]=1/(i*j);
 

while-Schleifen

können an Stelle einer for-Schleife verwendet werden:
int i=1; while(i<=6) {System.out.println(i);i++;}
oder
int i=1; do {System.out.println(i);i++;} while (i<=6);
Schleifen werden mit break verlassen (auch mit Verwendung eines Labels), dagegen mit continue durch Sprung an den Anfang fortgesetzt.

if-Anweisung

 if (a<b) s="a ist kleiner als b";  else s="a >= b";

Blockbildung ist sinnvoll! Das Semikolon vor dem else muß stehen! (Es fehlt das then von Pascal. Boolesche Ausdrücke stehen stets in runden Klammern.)

switch

ist analog zum Pascal-case:
char c;
switch(c)
{
case 'J':'j':
    //Java-Anweisungen
    break;
case 'N':'n':
    //Java-Anweisungen
    break;
default:
   //noch mehr Anweisungen
}

Ausgabe auf den Bildschirm:

Die verwendete Methode lautet (lästig länglich!) System.out.println(); (Pascal writeln();). Grundsätzlich werden als Parameter der Methode System.out.println() Parameter in Form von Zeichenketten übergeben, wobei Zahlen automatisch in Zeichenketten (String) umgewandelt werden.
System.out.println("Das Pivotelement der Matrix lautet:"+A[i][i]);
Kompliziert sind formatgerechte Ausgaben von Gleitpunktzahlen. Hierzu muß man sich eigene Methoden schreiben.

Numerik-Beispiele ohne OOP

Alle klassischen Numerikaufgaben kann man ohne Gebrauch der objektorientierten Programmierung (OOP) in Java umschreiben, indem man nur eine Klasse, die neben anderen neu zu programmierenden Methoden die Methode main() enthält, welche automatisch ausgeführt wird, wenn die Klasse interpretiert wird. Ich gebe zwei solche Beispiele.
Diese Beispiele setzen gewisse Numerikkenntnisse voraus. Ein Erstsemester sollte hier direkt zum Kapitel Objektorientierte Programmierung springen.

Lösung eines linearen Gleichungssystems

Das folgende Programm implementiert das Gaußsche Eliminationsverfahren ohne Zeilentausch:
class Matrix{
// METHODEN -----------------------------------------------------------
   static void LRZerlegung(int n, double[][] A){
    int i,j,k;
    for(k=0;k<n-1;k++)
     for(i=k+1;i<n;i++){
       A[i][k]/=A[k][k];
       for (j=k+1;j<n;j++)    A[i][j]-=A[i][k]*A[k][j];
     }//Ende for i
    }// Ende LRZerlegung()
   static double[] LoeseLGS(int n, double[][] A, double[] b){
     int i,j;
     LRZerlegung(n,A);
     //beginn vorwaertseinsetzen
     for(i=1;i<n;i++) for(j=0;j<i;j++)  b[i]-=A[i][j]*b[j];
     //ende Vorwaertseinsetzen
     //beginn rueckwaertseinsetzen
     for(i=n-1;i>=0;i--){
      for(j=n-1;j>i;j--) b[i]-=A[i][j]*b[j];
      b[i]/=A[i][i];
     }//ende Rueckwaertseinsetzen
     return b;
    }//ende LoeseLGS()
    static void main(String args[]){ //so faengt jedes Hauptprogramm an
      int n=3;
      double[] b={3.1, 3, 0};
      double[][] A={{2.1,-1, 1},{2, -4, 3},{-4, 8,-4}};
      b=LoeseLGS(n,A,b);
      System.out.println("Lösungsvektor:");
      for(int i=0;i<n;i++) System.out.println("x["+i+"]="+b[i]);
     }//ende main()
}// ende class Matrix
Dieses Programm ist praktisch eine 1-1 Übersetzung eines Pascal-Programms in Java. Es enthält eine Methode ohne Wertzuweisung (void, eine "Prozedur"), eine mit Wertzuweisung (double[], eine "Funktion") und einen Hauptteil, die Methode main(), in der alle Daten als lokale Variablen dieser Methode festgesetzt und die anderen Methoden benutzt werden. Alle drei Methoden besitzen den Zusatz static, weil diese Methoden ohne Objektbildung der Klasse Matrix benutzt werden. Alle Methoden haben Parameter, wobei die Variable double[][] A durch die Methode LRZerlegung() überschrieben wird: sie ist eine Referenzvariable.
Bemerkenswert ist, daß an keiner Stelle der new-Operator Verwendung findet.
Man achte auf die durch die geschweiften Klammern gegebene Blockstruktur.

Berechnung eines Newtonschen Interpolationspolynoms

class NewtonPolynom{
//Variablen:
  static int Grad=3;
  static double[] x={1,2,3,4}; //Knoten
  static double[] y={1,4,9,16};//Werte
  static double[] c=new double[Grad+1]; //Div. Differenzen
//Methoden:
  static void DividierteDifferenzen(){
   for(int i=0;i<=Grad;i++) c[i]=y[i];
   for(int k=1;k<=Grad;k++)
   for(int i=Grad;i>=k;i--) c[i]=(c[i]-c[i-1])/(x[i]-x[i-k]);
  }//Ende DividierteDifferenzen()
  static double[] Horner(double xi){
   int j;
   double[] b = new double[Grad];
   DividierteDifferenzen();
   b[Grad-1]=c[Grad];
   for(j=Grad-2;j>=0;j--)  b[j]=c[j+1]+(xi-x[j+1])*b[j+1];
   return b;
  }//ende Horner()
  static double getWert(double xi){
   double[] b = new double[Grad];
   if (Grad==0) return c[0];
   else{
    b=Horner(xi);
    return b[0]*(xi-x[0])+c[0];
   }//Ende else
  }//Ende getWert()
  static void main(String[] args){
    System.out.println("Wert an der Stelle 1.1: "+getWert(1.1));
  }//Ende main()
}//ende class NewtonPolynom
Hier gibt es vier Methoden, mit und ohne Wertzuweisung, mit und ohne Parameter. Im Gegensatz zum Matrix-Beispiel wurden hier die Daten nicht als lokale Variable der main-Methode, sondern als globale Variable der Klasse behandelt. Dies erlaubt eine wesentliche Reduktion der Parameter in den Methoden.

Numerik-Beispiele mit einem Hauch OOP

Bevor OOP systematischer behandelt wird, sollen die beiden Beispiele leicht in Richtung OOP modifiziert werden. Der entscheidende Schritt ist, daß ein Objekt (eine Instanz) der jeweiligen Klasse gebildet wird - mit Hilfe eines Konstruktors, der das Schlüsselwort this verwendet, und mit Hilfe des new-Operators. Die Methoden des Objekts werden sodann mit der Punktnotation aufgerufen.
In beiden Beispielen hätte die main()-Methode auch in eine andere Applikations-Klasse ausgelagert werden können, damit auch andere Klassen von den beiden Klassen Matrix oder NewtonPolynom Gebrauch machen können.

Lösung eines linearen Gleichungssystems

class Matrix{
//DATEN (Variable) ---------------------------------------
   int n;  // Abmessung einer quadratischen Matrix
   double[][]  Data, LR;
          //Data: Elemente der Matrix, LR: nach der LR-Zerlegung
//KONSTRUKTOR ---------------------------------------------------------
   Matrix(int n){  //Konstruktor einer quadratischen Matrix
      int i;
      this.n=n;
      Data=new double[n][n];
      LR=new double[n][n];
   }//Ende Konstruktor
// METHODEN -----------------------------------------------------------
   LRZerlegung(){
    int i,j,k;
    for(i=0;i<n;i++) for(j=0;j<n;j++)  LR[i][j]=Data[i][j];
    //LR=Data bewirkt, dass in Folge LR=Data, d.h. Data veraendert wird.
    //Der Grund: LR und Data sind Referenzvariable
    for(k=0;k<n-1;k++)
     for(i=k+1;i<n;i++){
       LR[i][k]/=LR[k][k];
       for (j=k+1;j<n;j++)    LR[i][j]-=LR[i][k]*LR[k][j];
     }//Ende for
    }// Ende LRZerlegung()
    double[] LoeseLGS(double[] b){
     int i,j;
     LRZerlegung();
     //beginn vorwaertseinsetzen
     for(i=1;i<n;i++) for(j=0;j<i;j++)  b[i]-=LR[i][j]*b[j];
     //ende Vorwaertseinsetzen
     //beginn rueckwaertseinsetzen
     for(i=n-1;i>=0;i--){
      for(j=n-1;j>i;j--) b[i]-=LR[i][j]*b[j];
      b[i]/=LR[i][i];
     }//ende Rueckwaertseinsetzen
     return b;
    }//ende LoeseLGS()
     static void main(String args[]){ //so faengt jedes Hauptprogramm an
      int n=3;
      Matrix A=new Matrix(n);
      double[] b={3.1, 3, 0};
      double[][] a={{2.1,-1, 1},{2, -4, 3},{-4, 8,-4}};
      A.Data=a; //besser A.setData(a);
      b=A.LoeseLGS(b); //wird automatisch eine LR-Zerlegung gemacht
      System.out.println("Lösungsvektor:");
      for(int i=0;i<n;i++) System.out.println("x["+i+"]="+b[i]);
     }//ende main()
}// ende class Matrix
Der Konstruktor kann einfach als eine Methode mit dem gleichen Namen wie die Klasse verstanden werden. Die Anweisung this.n=n; bedarf einer Erklärung: Da n eine lokale Variable der Methode Matrix(int n), aber auch eine Variable der Klasse Matrix ist, wird auf letztere mit dem Schlüsselwort this zugegriffen. Bemerkenswert ist auch, daß der neue Datentyp Matrix (eine Klasse) in der Methode main() Verwendung findet, indem eine Variable namens A durch Matrix A=new Matrix(n); deklariert und initialisiert (konstruiert) wird, um dann mittels A.LoeseLGS(b) (beachte die Punktnotation) zu einer Lösung eines linearen Gleichungssystems führt. In der OOP-Sprache sagt man, es wird eine Nachricht an das Objekt code>A gesendet, es möge seine Methode LoeseLGS() einsetzen und das Ergebnis übermitteln.

Berechnung eines Newtonschen Interpolationspolynoms

Das folgende Programm berechnet das Newtonsche Interpolationspolynom zu vorgegebenen Knoten und Funktionswerten und wertet es an einer vorgegebenen Stelle mit dem Horner Schema aus.
class NewtonPolynom{
//DATEN (Attribute, Mitgliedsvariable):
  int Grad;
  double x[]; //Knoten
  double y[]; //Werte
  double c[]; //Koeffizienten (p=c0+c1*(x-x0)+....+cn*(x-x0)*...(x-x_n-1)
//KONSTRUKTOR:
  NewtonPolynom(int Grad,double[] x, double[] y){
   this.Grad=Grad;
   this.x=x;
   this.y=y;
   c=DividierteDifferenzen();
  }//Ende Konstruktor
//METHODEN:
  double[] DividierteDifferenzen(){
   double[] c=new double[Grad+1];
   for(int i=0;i<=Grad;i++) c[i]=y[i];
   for(int k=1;k<=Grad;k++)
   for(int i=Grad;i>=k;i--) c[i]=(c[i]-c[i-1])/(x[i]-x[i-k]);
   return c;
  }//Ende DividierteDifferenzen()
  double[] Horner(double xi){
   int j;
   double[] b = new double[this.Grad];
   b[Grad-1]=c[Grad];
   for(j=Grad-2;j>=0;j--)  b[j]=c[j+1]+(xi-x[j+1])*b[j+1];
   return b;
  }//ende Horner()
  double getWert(double xi){
   if (Grad==0) return c[0];
   else{
   double[] b=Horner(xi);
   return b[0]*(xi-x[0])+c[0];
   }//Ende else
  }//Ende getWert()
  static void main(String[] args){
   double[] x={1,2,3,4};
   double[] y={1,4,9,16};
   NewtonPolynom p=new NewtonPolynom(3,x,y);
   System.out.println("Wert an der Stelle 1.1: "+p.getWert(1.1));
  }//Ende main()
}//ende class NewtonPolynom
Hier berechnet ein Konstruktor (wieder mit Hilfe des Schlüsselworts this) das Interpolationsplolynom mit Hilfe der beiden Methoden Horner(), DividierteDifferenzen(), von denen beide als Werte Felder (double[]) haben. Bemerkenswert ist, daß der neue Datentyp NewtonPolynom (eine Klasse) in der Methode main() Verwendung findet, indem eine Variable namens p durch NewtonPolynom p=new NewtonPolynom(3,x,y); deklariert und initialisiert (konstruiert) wird, um dann mittels p.getWert(1.1) (beachte die Punktnotation) an einer Stelle ausgewertet zu werden. In der OOP-Sprache sagt man, es wird eine Nachricht an das Objekt code>p gesendet, es möge seine Methode getWert() einsetzen und das Ergebnis übermitteln.

Später kann die Klasse NewtonPolynom als Unterklasse einer abstrakten Klasse Funktion dargestellt, wodurch weitere Methoden, die z.B. den Graphen zeichnet (Polymorphie), eine Wertetabelle erstellt oder die Ableitung an einer Stelle mit Hilfe eines Differenzenquotienten approximiert (Vererbung), zur Verfügung stehen.

Applets

Die "Hallo World"-Applet-Variante lautet
import java.applet.*; //alle Klassen des package java.applet
import java.awt.Graphics; //nur die Klasse Applet des Pakets java.awt
                          //awt= Abstract Window Toolkit
public class Hello extends Applet
{
  public void paint(Graphics g){
  g.drawString("Hello world!",50,25);
}
Dieses Programm (nach seiner Compilierung als *.class) muß in ein HTML-File eingebaut werden. Die Methode paint() hat dieselbe Funktion wie main() in Anwendungsprogrammen.

Die eigene Klasse Tools

In dieser Klasse werden nur statische Variable und Methoden vereinbart, die später direkt z.B. mit
Tools.SetmeinOut("DateiName")
aufgerufen werden, wobei eine Textdatei namens DateiName geöffnet wird, in die danach mit Tools.meinOut.println() oder aber auch (!) direkt mit den diversen Schreibe()-Methoden geschrieben werden kann. Der Trick ist, daß ohne der Spezifizierung einer Ausgabedatei die Voreinstellung Tools.meinOut=System.out benutzt wird, so daß ohne Verwendung von Tools.SetmeinOut() ganz normal auf den Bildschirm ausgegeben wird. Die öffentliche, statische Methode Schreibe(double[][] A, int m, int n, int vk, int nk) schreibt zeilenweise die Matrix A (mit m Zeilen und n Spalten) in einem Format, das vk Vorkomma- und nk Nachkommastellen verwendet. Diese Methode Schreibe() wird mannigfach überlagert, indem statt A eine Zahl oder ein Vektor (auch Integer-Vektor) ausgegeben wird.
Das Einlesen einer reellen Zahl geht mit double x=Tools.LeseR(); Diese Methode läuft nur unter JDK1.2. Sie stammt aus Krüger, S.98, und ist ziemlich kompliziert.