Java-Vorlesung im Sommersemester 2011

Donnerstags von 10:15 bis 11:45 im kleinen Hörsaal, Renthof 5, Marburg.

Warnung: Im Unterschied zu meinen anderen Vorlesungen, in denen viel originales Wissen geboten wird, werden hier Java-Programme erklärt, wie sie dutzendfach in Lehrbüchern und Internet-Tutorials zu finden sind.

Java-Installation

Tipps zur Installation, nicht ganz aktuell, aber nützlich.
Von wo man jetzt (2011) Java kostenlos runterladen kann. erst JRE, dann JDK.
Das Problem mit gdiplus.dll; betrifft wohl nur Windows 2000.
Java mit dem Mac.

Referenzen im Internet

Offizielle Java-Dokumentation. Auf den bunten Dingern rechts unten rumklicken!
Programmierhandbuch und Referenz für die Java-2-Plattform. Auf Deutsch.
Anleitung und Referenz HTML. Wichtig für Applets.

Lehrbücher


Der Unterschied zwischen Application und Applet

Erste Application:

/* Muss in einer Datei mit dem Namen application1.java stehen. */

import java.util.Date;

class application1 {
 public static void main(String args[]) {
  Date Today = new Date(); 
  System.out.println(Today);
 }
}
Mit javac application1.java übersetzen. Dabei entsteht die ausführbare Bytecode-Datei application1.class. Die kann man mit java application1 starten.

Erstes Applet:

/* Muss in Datei mit Namen applet1.java stehen. */

import java.util.Date;
import java.awt.Graphics;
import java.applet.Applet;

public class applet1 extends java.applet.Applet {
 Date Today;
 String TodayAsString;
 public void paint(Graphics graphic) {
  Today=new Date();
  TodayAsString=Today.toString();
  graphic.drawString(TodayAsString,10,10);
 }
}
Nur mit javac applet1.java übersetzen, aber nicht starten.

Die folgende Datei soll den Namen blumen.html haben und muss in derselben Directory liegen wie applet1.class .

<html>
<head>
<title>Ein anderes Wort für Applet ist TROJANER</title>
</head>
<body>
Blumen ... Blumen ... Blumen ..., während das Applet das System erkundet:
<applet code=applet1.class width=200 height=50></applet>
</body>
</html>
Sobald ein Browser blumen.html lädt, startet der Browser applet1.class

Ähnlich: Your First Java Application, Your First Java Applet.


Elementare mathematische Funktionen und elementare Ein- und Ausgabe

Java ist von Leuten gemacht, die Mathematik wenig kennen und ihr feindlich gegenüberstehen. Schon die Eingabe von Zahlen mit der Tastatur ist ein Krampf. Dagegen werden - wegen der wirtschaftlichen Bedeutung - nationale Interessen berücksichtigt. Reelle Zahlen werden in Frankreich und Deutschland mit Komma (z.B. 3,57), in Großbritannien und den USA aber mit Punkt (z.B. 3.57) geschrieben. Indes sind in Java alle elementaren mathematischen Funktionen direkt verfügbar, wenn auch umständlich als Math.abs, Math.sin, Math.cos, Math.exp usw.
/* Muss in Datei mit Namen math1.java stehen. */

import java.util.Scanner;
import java.text.NumberFormat;

public class math1 {
 public static void main(String args[]) {
  Scanner read=new Scanner(System.in);
  NumberFormat nf=NumberFormat.getInstance();
  System.out.print("Gib Ganzzahl>");
  int j=read.nextInt();
  System.out.print("Gib Realzahl>");
  double y=read.nextDouble();
  System.out.println("|"+nf.format(j)+"|="+nf.format(Math.abs(j)));
  System.out.println("sin("+nf.format(y)+")="+nf.format(Math.sin(y)));
 }
} //Danke, Herr Hausherr!
Mit javac math1.java übersetzen und mit java math1 starten. Wenn dann ein Nutzer auf einem deutsch eingestellten Computer bei der Realzahl eine reelle Zahl mit Punkt eingibt, stürzt das Programm ab. Er muss die reelle Zahl mit Komma eingeben. Der Sinus dieser reellen Zahl erscheint dann auch mit Komma. Das macht nf.format.

Ähnlich: Some elementary mathematical functions using Java.


Objekt-orientiertes Programmieren

Umfassende Klasse mit privaten Daten und öffentlichen Methoden, die auf die privaten Daten zugreifen können. Die Methoden sind: Constructor, Getter, Setter, diverse arithmetische Funktionen und die Umwandlung in Buchstabenketten (String). Die Methoden können mehrfach mit demselben Namen definiert werden, wenn sich die Argumente unterscheiden (overloading). +,-,*,/,= aber können anders als in C++ von den Anwendungsprogrammierern nicht überladen werden.
/* Muss in einer Datei mit Namen Complex.java stehen. */

public class Complex {
 private double re,im;
 /* constructors */
 public Complex() {
  this.re=0.0;
  this.im=0.0;
 }
 public Complex(double re,double im) {
  this.re=re;
  this.im=im;
 }
 /* getter */
  public double getRe() {
  return(this.re);
 }
 public double getIm() {
  return(this.im);
 }
 /* setter */
 public void setRe(double re) {
  this.re=re;
 }  
 public void setIm(double im) {
  this.im=im;
 }
 /* some elementary complex operations and functions */
 public Complex conj() {
  return(new Complex(this.re,-1.0*this.im));
 }
 public double mod() {
  return(Math.sqrt(this.re*this.re+this.im*this.im));
 }
 public double arg() {
  return(Math.atan2(this.im,this.re));
 }
 public Complex add(Complex operator) {
  Complex result=new Complex();
  result.setRe(this.re+operator.getRe());
  result.setIm(this.im+operator.getIm());
  return(result);
 }
 public Complex sub(Complex operator) {
  Complex result=new Complex();
  result.setRe(this.re-operator.getRe());
  result.setIm(this.im-operator.getIm());
  return(result);
 }
 public Complex mul(Complex operator) {
  Complex result=new Complex();
  double opre=operator.getRe();
  double opim=operator.getIm();
  result.setRe(this.re*opre-this.im*opim);
  result.setIm(this.re*opim+this.im*opre);
  return(result);
 }
 public Complex div(Complex operator) {
  Complex result=new Complex();
  double opre=operator.getRe();
  double opim=operator.getIm();
  double dnom=opre*opre+opim*opim;
  result.setRe((this.re*opre+this.im*opim)/dnom);
  result.setIm((this.im*opre-this.re*opim)/dnom);
  return(result);
 }
 /* transform complex number to string */
 public String toString() {
  if (this.re==0.0) {
   if (this.im==0.0) {
    return("0.0");
   } 
   else {
    return(this.im+"i");
   }
  }
  else {
   if (this.im==0.0) {
    return(String.valueOf(this.re));
   }
   else if (this.im<0.0) {
    return(this.re+" "+this.im+"i");
   }
   else {
    return(this.re+" +"+this.im+"i");
   }
  }  
 }
 /* application that constructs complex numbers,
    does some computing and prints results */
 public static void main(String argv[]) {
  Complex a=new Complex(2.0,5.0);
  Complex b=new Complex(3.0,-4.0);
  System.out.println("a= "+a);
  System.out.println("b= "+b);
  System.out.println("|a|="+a.mod());  
  System.out.println("arg(a)="+a.arg());
  System.out.println("a*b= "+a.mul(b));
  System.out.println("a/b= "+a.div(b));
  System.out.println("a/b*b= "+a.div(b).mul(b));
 }
}
Die Objekte werden mittels new nach Maßgabe der Konstruktoren erzeugt. Die Objekte sind Instanzen der Klassen, von denen sie ihre Namen haben, hier Complex. Ähnlich bei literateprograms

Bekanntlich wurde die objekt-orientierte Programmierung erfunden, um die Programmierer vor ihrer eigenen Dummheit zu schützen. Wer OOP für modischen Firlefanz hält und sich nicht von dekadent-dicklichen Computer-Knaben bevormunden lassen will, kann auch mit Java konservativ-anarchistisch programmieren, z.B. im guten alten Fortran-Stil:

/* Muss in Datei mit Namen noOOP.java stehen. */
class noOOP {
 public static void main(String[] args) {
  float[] a={2.0f,5.0f},b={3.0f,-4.0f};
  float[] w={0.0f,0.0f},z={0.0f,0.0f};
  System.out.println("a= "+a[0]+"+i"+a[1]);
  System.out.println("b= "+b[0]+"+i"+b[1]);
  System.out.println("|a|="+mod(a));
  mul(a,b,z);
  System.out.println("a*b= "+z[0]+"+i"+z[1]);
  div(a,b,z);
  System.out.println("a/b= "+z[0]+"+i"+z[1]);
  mul(z,b,w);
  System.out.println("a*b/b= "+w[0]+"+i"+w[1]);
 }
 static float mod(float[] a) {
  return((float)Math.sqrt(a[0]*a[0]+a[1]*a[1]));
 }
 static void mul(float[] a,float[] b,float[] z) {
  z[0]=a[0]*b[0]-a[1]*b[1]; 
  z[1]=a[0]*b[1]+a[1]*b[0];
 }
 static void div(float[] a,float[] b,float[] z) {
  float denom=1.0f/(b[0]*b[0]+b[1]*b[1]);
  z[0]=denom*(a[0]*b[0]+a[1]*b[1]); 
  z[1]=denom*(a[1]*b[0]-a[0]*b[1]);
 }
}
Der Trick dabei ist die komplexen Zahlen als Felder zu deklarieren, da Java einfache Daten nur per Wert an Unterprogramme übergibt, während bei Feldern deren Pointer übergeben werden.

Überall double zu verwenden, wo float für die Darstellung einer reeller Zahl reicht, ist natürlich auch ein Zeichen von Dekadenz.


Einbeziehung eines Ausnahme-Ereignisses (Exception)

/* Muss in Datei mit Namen exception1.java stehen. */

import java.io.IOException;

class exception1 {
 public static String inputString() {
  int ichar;
  String all="";
  boolean goon=true;
  while (goon) {
   try {
    ichar=System.in.read();
    if ((char)ichar=='\n') goon=false;
    else                   all+=(char)ichar;
   } 
   catch (IOException e) {
    e.printStackTrace();
    return all;
   }
  }
  return all;
 }
 public static void main(String args[]) {
  String result=inputString();
  System.out.println(result);
 }
} 
Normal-Ereignisse geschehen beim Tippen einzelner Tasten. Die Tasten-Codes werden zu Buchstaben, die in der Zeichenkette all gespeichert werden. Sobald man Return drückt, wird die Zeichenkette geschrieben und das Programm beendet. Das Ausnahme-Ereignis IOException kann von System.in.read() ausgelöst werden (read() throws IOException). Doch das Drücken von Strg-c (Control-c) tut das nicht. Mit Control-c wird in Java jedes Programm sofort beendet. Es ist eine Notbremse, die aber keine Ausnahme auslöst.


Mehrere Prozesse (Threads) mit einem Programm starten

Heutzutage enthält fast jeder CPU-Chip mehrere Central Processing Units. Ein Computer kann also mehrere Aufgaben zugleich vollführen. Mit Java kann man das nutzen wie beim folgenden Programm, das einen Kaffee-Automaten mit mehreren Spritzen simuliert. Auch ältere Computer meistern Multi-Threading. Nur werden dort den Threads kurze Zeitintervalle zugeteilt, in denen die Threads nacheinander abgearbeitet werden.
/* Muss in einer Datei mit Namen thread1.java stehen. */

class TextThread extends Thread {
 String text;
 public TextThread(String text) {
  this.text=text;
 }
 public void run() {
  for (int i=0;i<5;i++) {
   try {
    sleep((int)(Math.random()*1000.0));
   }
   catch (InterruptedException e) {
    return;
   }
   System.out.println(text);
  } 
 }
} 
public class thread1 {
 public static void main(String args[]) {
  TextThread muckefuck,milchkaffee;
  muckefuck=new TextThread("Muckefuck");
  milchkaffee=new TextThread("Milchkaffee");
  muckefuck.start();
  milchkaffee.start();
 }
}
Sobald ein Thread gestartet wird, hier muckefuck.start() und milchkaffee.start(), wird run() aktiviert. Mehr über Threads bei Javabeginner.


synchronized Threads mit wait() und notify() koordinieren

Arbeitgeber legen Aufträge auf einen Stapel, Arbeitnehmer holen die Aufträge von dort ab. Immer nur einer darf zu einer Zeit den Stapel beanspruchen.
/* Muss in Datei mit Namen buro.java stehen. */

class Stapel {
 private Object[] Stapelturm;
 private volatile int Stapelkante;
 Stapel(int Kapazitat) {
  Stapelturm=new Object[Kapazitat];
  Stapelkante=-1;
 }
 public synchronized Object nimm() {
  while (leer()) {
   try {
    System.out.println(Thread.currentThread()+": warten aufs Nehmen");
    wait();
   }
   catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
  Object obj = Stapelturm[Stapelkante];
  System.out.println(Thread.currentThread()+": nimmt "+obj);  
  Stapelturm[Stapelkante--]=null;
  System.out.println(Thread.currentThread()+": benachrichtigen nach Nehmen");
  notify();
  return obj;
 }
 public synchronized void gib(Object element) {
  while (voll()) {
   try {
    System.out.println(Thread.currentThread()+": warten aufs Geben");
    wait();
   }
   catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
  Stapelturm[++Stapelkante]=element;
  System.out.println(Thread.currentThread()+": gibt "+element);
  System.out.println(Thread.currentThread()+": benachrichtigen nach Geben");
  notify();
 }
 public boolean voll() {
  return Stapelkante>=Stapelturm.length-1;
 }
 public boolean leer() {
  return Stapelkante<0;
 }
}

abstract class Stapelnutzer extends Thread {
 protected Stapel auftrage;
 Stapelnutzer(String threadName,Stapel auftrage) {
  super(threadName);
  this.auftrage=auftrage;
  System.out.println(this);
  setDaemon(true);
  start();
 }
}
class Stapelnehmer extends Stapelnutzer {
 Stapelnehmer(String threadName,Stapel auftrage) {
  super(threadName,auftrage);
 }
 public void run() {
  while (true) {
   auftrage.nimm();
  }
 }
}
class StapelGeber extends Stapelnutzer {
 StapelGeber(String threadName,Stapel auftrage) {
  super(threadName,auftrage);
 }
 public void run() {
  while (true) {
   int i=(int)(Math.random()*1000.0);
   auftrage.gib(new Integer(i));
  }
 }
}

public class buro {
 public static void main(String args[]) {
  Stapel auftrage=new Stapel(5);
  new StapelGeber("Arbeitgeber1",auftrage);
  new StapelGeber("Arbeitgeber2",auftrage);
  new Stapelnehmer("Arbeitnehmer",auftrage);
  System.out.println("Main Thread pennt");
  try {
   Thread.sleep(10);
  }
  catch (InterruptedException e) {
   e.printStackTrace();
  }
  System.out.println("Main Thread beendet");
 }
}

Eine beliebige Internet-Seite runterladen

/* Muss in Datei mit Namen internet1.java stehen. */

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

import java.net.URL;
import java.net.URLConnection;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;

public class internet1 {
 public static void main(String args[]) throws
 IOException,MalformedURLException,URISyntaxException {
  String sUrl;
  if (args.length!=1) sUrl="http://www.bka.de/";
  else                sUrl=args[0];
  URL url=new URL(sUrl);
  URLConnection con=url.openConnection();
  if (con instanceof HttpURLConnection) {
   HttpURLConnection hcon=(HttpURLConnection)con;
   hcon.setRequestMethod("GET");
   hcon.setRequestProperty("Accept","*/*");
   // Hier nach Bedarf Informationen aus dem Header holen.
   BufferedReader in=new BufferedReader(new InputStreamReader(hcon.getInputStream()));
   String line;
   while ((line=in.readLine())!=null) {
    System.out.println(line);
   }
   in.close();
   hcon.disconnect();
  }
  else System.out.println("No http connection");
 }
} //Danke, Tilman Hausherr!
In der Methode setRequestProperty() stehen Spezifikationen, die fürs Hypertext-Transfer-Protokoll http spezifisch sind: erstens der Schlüssel (key) "Accept", zweitens der Wert (value) "*/*", was bedeutet, dass der Empfänger alles entgegennimmt: Texte, Bilder, binäre Daten jeder Art. Welche Keys und Values möglich sind, steht in einem Dokument der Internet Engineering Task Force ab Seite 99.

Das Programm wie üblich mit javac internet1.java übersetzen. Wenn man es dann nur mit java internet1 startet, wird der Quelltext der Homepage des Bundeskriminalamts auf der eigenen Konsole ausgeschrieben. Man kann es aber auch mit einem Kommandolinien-Argument starten z.B. java internet1 http://www.hessen.de/. Dann bekommt man den Quelltext der offiziellen Homepage des Landes Hessen.

Protokollierte Daten werden mit einer Art Briefkopf verschickt. Wer will, kann an der im Programm bezeichneten Stelle Getter einsetzen, um Informationen aus dem Header zu holen, z.B.

   System.out.println("url="+hcon.getURL());
   System.out.println("type="+hcon.getContentType());
   System.out.println("len="+hcon.getContentLength());
   System.out.println("respcode="+hcon.getResponseCode());
   System.out.println("respmsg="+hcon.getResponseMessage());
   System.out.println();


Wie man vom DNS die IP holt

Internet-Adressen sind Gruppen von Ziffern, zwischen denen Punkte stehen. Weil sich nur wenige Menschen diese Ziffern merken wollen, gibt es den Data Name Server, der die Domain-Namen in die zugehörigen IP-Adressen umwandelt.
/* Muss in Datei mit Namen NameToIP.java stehen. */

import java.net.*;

class NameToIP {
 public static void main(String args[]) {
  if (args.length!=1) System.out.println("Starten mit Domain z.B. hessen.de");
  else try {
   InetAddress ip=InetAddress.getByName(args[0]);
   System.out.println("IP-Adresse von "+args[0]+": "+ip);
  }
  catch(UnknownHostException e) {
   System.out.println(args[0]+" ist dem Data Name Server nicht bekannt");
  }
 }
}
Wie üblich übersetzen, dann z.B. mit java NameToIP bka.de starten. Der DNS antwortet mit 80.245.147.47  Wenn Sie statt http://www.bka.de/ die echte IP-Adresse http://80.245.147.47/ in das Adressfeld Ihres Lieblingsbrowsers eintippen, erscheint die Homepage des Bundeskriminalamts. Analog liefert java NameToIP hessen.de die IP-Adresse der hessischen Staatsdomain 141.90.10.11  Wenn Sie jetzt aber http://141.90.10.11/ in die Adresszeile Ihres Lieblingsbrowsers eingeben, werden Sie mit einem Sperrvermerk abgeblockt. Übungsaufgabe: Finden Sie heraus, ob hier die so genannte Zensursula am Werk war. Ist die hessische Staatsdomain ein Kinderporno-Treff?

Bei der Internet-Kommunikation wird die Botschaft im Allgemeinen durch 7 Schichten hindurchgereicht. Es ist wie bei Behörden, die eine Akte auf dem Dienstweg weiterreichen: Jede Behörde legt ihr eigenes Begleitschreiben hinzu. Die 7 Schichten im OSI-Modell:
7) Application: Wie Anwender auf Daten zugreifen können
6) Presentation: Aus systemabhängigen systemunabhänge Daten machen
5) Session: Wie der Datenaustausch organisiert wird
4) Transport: Aus systemabhängigem systemunabhängigen Datenaustausch machen
3) Network: Schaltung der Verbindungen
2) Data: Fehlerprüfungen und -korrekturen
1) Physical: physische Leitung
Merksatz: Alle Priester Saufen Tequila Nach Der Predigt.

In der Praxis werden oft mehrere Schichten zusammengelegt, z.B. 1-2:Ethernet, 3:IP, 4:TCP, 5-7:http oder https oder smtp oder ftp usw.. Auch in Java wird die Passage durch mehrere Schichten meist zusammengefasst, z.B. in der Klasse InetAddress, siehe oben.

Mehr über Kommunikationsprogrammierung en detail: Reading and Writing through a Socket.


Mauslauscher

Mit Programmen alten Stils sollte der Computer so gesteuert werden, dass er anfangs eingegebene Daten einliest, verarbeitet und die Ergebnisse ausschreibt. Heutzutage hängen am Computer Geräte, die die Aufmerksamkeit des Computers jederzeit beanspruchen können, z.B. die Maus. Der Computer muss sofort darauf reagieren.
/* Muss in Datei mit Namen MouseClick.java stehen. */

import java.awt.*;
import java.awt.event.*;

public class MouseClick {
 static public class Fensteranpasser extends WindowAdapter {
  public void windowClosing(WindowEvent we) {
    System.exit(0);
  }
 }  
 static Label l;
 static public class Mauslauscher extends MouseAdapter {
  public void mouseClicked(MouseEvent me) {
   String str=l.getText();
   if (str.equals("Rose of India")) {
    l.setText("You clicka the button");
   }
   else if (str.equals("You clicka the button")) {
    l.setText("Rose of India");
   }
  }
 }
 public static void main(String[] args) {
  Frame f=new Frame("Checking the mouse click");
  f.addWindowListener(new Fensteranpasser());
  l=new Label("Rose of India");
  f.add(l,BorderLayout.CENTER);
  Panel p=new Panel();
  f.add(p,BorderLayout.NORTH);
  Button b=new Button("Click Me");
  p.add(b,BorderLayout.NORTH);
  b.addMouseListener(new Mauslauscher());
  f.setSize(300,300);
  f.setVisible(true);
 } 
}
Ähnlich bei der Rose von Indien.


Tastenlauscher

Man könnte den Tastenlauscher genauso strukturieren wie den Mauslauscher. Doch soll hier eine moderne Programmstruktur vorgeführt werden: Die Hauptmethode main, mit der das Programm gestartet wird, ist verkümmert. Alle wesentlichen Anweisungen stehen im Konstruktor KeyPress().
/* Muss in Datei mit Namen KeyPress.java stehen. */

import java.awt.*;
import java.awt.event.*;

class KeyPress extends Frame {
 class Fensteranpasser extends WindowAdapter {
  public void windowClosing(WindowEvent we) {
   System.exit(0);
  }
 }
 Label l;
 class Tastenlauscher extends KeyAdapter {
  public void keyReleased(KeyEvent ke) {
   char c=ke.getKeyChar();
   if (c=='x') System.exit(0);
   else if (c=='n'||c=='e') l.setText("Don't wanna echo");
   else l.setText("You typed:"+Character.toString((char)(1+c)));   
  }
 }
 KeyPress() {
  super("Checking Key Press and Release");
  addWindowListener(new Fensteranpasser());
  l=new Label();
  add(l,BorderLayout.NORTH);
  addKeyListener(new Tastenlauscher());
// Wer will, kann hier mit einem TextField die Tastatur überwachen.
  setSize(300,300);
  setVisible(true);
 }
 public static void main(String[] args) {
  new KeyPress();
 }
}
Der Tastenlauscher löst das praktische wichtige, in der Java-Literatur jedoch kaum diskutierte No-Echo-Problem: Die Tastatur soll wahrgenommen werden, die Tastenschläge sollen jedoch gar nicht oder nur abgewandelt auf dem Bildschirm reproduziert werden. Die Lösung besteht darin, den Tastenlauscher direkt an den Frame (der hier zu KeyPress erweitert wird) zu binden. Eine Bindung des Tastenlauschers an ein TextField erzwingt jedoch das Echo, d.h. die übliche Reproduktion der Tastenschläge. Wer das probieren will, kann an der kommentierten Stelle im Programm folgende Anweisungen einfügen:
// Mit TextField die Tastatur überwachen:
  Panel p=new Panel();
  add(p,BorderLayout.SOUTH);
  TextField t=new TextField(20);
  p.add(t,BorderLayout.SOUTH);
  t.addKeyListener(new Tastenlauscher());


Daten grafisch darstellen

/* Muss in Datei mit Namen malen1.java stehen. */

import java.awt.*;
import java.awt.event.*;

class Rahmen extends Frame {
 class Fensteranpasser extends WindowAdapter {
  public void windowClosing(WindowEvent we) {
   System.exit(0);
 }}
 public Rahmen(String Botschaft) {
  super(Botschaft);
  addWindowListener(new Fensteranpasser());
}}

class Leinwand extends Canvas {
 public void paint(Graphics g) {
  Dimension d=getSize();
  int w=d.width,h=d.height;
  /* Achsenkreuz malen und beschriften */
  g.drawLine(10,h/2,w-10,h/2);
  g.drawString("x",w-10,h/2);
  g.drawLine(10,h-10,10,10);
  g.drawString("y",10,10);
  /* darzustellende Daten besorgen */
  int[] x={0,1,2,3,4,5,6,7,8,9};
  int[] y={0,1,2,3,4,5,6,7,8,9};
  int deltax=w/10,deltay=h/2;
  for (int i=0;i<10;i++) {
   x[i]=i*deltax+10; 
   y[i]=(int)(Math.sin((double)i)*deltay)+deltay;
  }
  /* Daten als Linienzug */
  g.drawPolyline(x,y,10);
  /* Daten als Histogramm */
  for (int i=0;i<9;i++) {
   Color C=new Color(25*i,255-25*i,0);
   g.setColor(C);
   /* Breite und Höhe müssen positiv sein */
   if (y[i]>deltay) g.fillRect(x[i]-deltax/2,deltay,deltax,y[i]-deltay);
   else             g.fillRect(x[i]-deltax/2,y[i],deltax,deltay-y[i]);
}}}

public class malen1 {
 public static void main(String[] args) {
  Rahmen f=new Rahmen("Schließbares Fenster");
  f.setSize(300,300);
  f.add(new Leinwand());
  f.setVisible(true);
}}


Packages selber machen

Nach einigen Malen hat man es satt immer wieder dieselben Programm-Teile in neue Programme zu schreiben. Man legt lieber eigene Programm-Sammlungen an, die in Java Packages genannt werden.

Beispiel: Das oben ausprogrammierte schließbare Fenster soll erste Klasse des Pakets "brosa" werden. Dazu legt man in dem Verzeichnis, in dem man Java-Programme entwickelt, das Unterverzeichnis "brosa" an: md brosa. Dorthin kopiert man die Datei Rahmen.java:

/* Muss in Datei mit Namen Rahmen.java stehen. */

package brosa;
import java.awt.*;
import java.awt.event.*;


public class Rahmen extends Frame {
 class Fensteranpasser extends WindowAdapter {
  public void windowClosing(WindowEvent we) {
   System.exit(0);
 }}
 public Rahmen(String Botschaft) {
  super(Botschaft);
  addWindowListener(new Fensteranpasser());
}}
Hingezukommen sind nur public vor class Rahmen extends Frame und als erste Anweisung package brosa;. Der Name des Pakets und des Unterverzeichnisses müssen übereinstimmen. Diese Datei übersetzt man mit javac Rahmen.java.

Dann zurück in das Entwicklungsverzeichnis, wo die Datei malen2.java liegt:

/* Muss in Datei mit Namen malen2.java stehen. */

import java.awt.*;
import java.awt.event.*;
import brosa.Rahmen;

class Leinwand extends Canvas {
 public void paint(Graphics g) {
  Dimension d=getSize();
  int w=d.width,h=d.height;
  /* Achsenkreuz malen und beschriften */
  g.drawLine(10,h/2,w-10,h/2);
  g.drawString("x",w-10,h/2);
  g.drawLine(10,h-10,10,10);
  g.drawString("y",10,10);
  /* darzustellende Daten besorgen */
  int[] x={0,1,2,3,4,5,6,7,8,9};
  int[] y={0,1,2,3,4,5,6,7,8,9};
  int deltax=w/10,deltay=h/2;
  for (int i=0;i<10;i++) {
   x[i]=i*deltax+10; 
   y[i]=(int)(Math.sin((double)i)*deltay)+deltay;
  }
  /* Daten als Linienzug */
  g.drawPolyline(x,y,10);
  /* Daten als Histogramm */
  for (int i=0;i<9;i++) {
   Color C=new Color(25*i,255-25*i,0);
   g.setColor(C);
   /* Breite und Höhe müssen positiv sein */
   if (y[i]>deltay) g.fillRect(x[i]-deltax/2,deltay,deltax,y[i]-deltay);
   else             g.fillRect(x[i]-deltax/2,y[i],deltax,deltay-y[i]);
}}}

public class malen2 {
 public static void main(String[] args) {
  Rahmen f=new Rahmen("Schließbares Fenster");
  f.setSize(300,300);
  f.add(new Leinwand());
  f.setVisible(true);
}}
Sie unterscheidet sich vom obigen malen1.java dadurch, dass class Rahmen fehlt und statt dessen nur import brosa.Rahmen; gefordert wird. Wenn man jetzt javac malen2.java eingibt, holt der Compiler die Rahmen.class aus dem Unterverzeichnis "brosa" und baut sie in eine lauffähige malen2.class ein.

Mit dem direkten Unterverzeichnis erspart man sich Komplikationen mit der CLASSPATH Umgebungsvariablen. CLASSPATH teilt Java mit, wie das Verzeichnis heißt, in dessen Unterverzeichnis das gewünschte Paket liegt.


Interaktiv Zeichnen mit Interfaces, einer Collection und Swing

/* Muss in Datei mit Namen ZeichneRechtecke.java stehen. */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;

class ZeichneRechtecke extends JFrame {
 class Leinwand extends JPanel
 implements MouseListener,MouseMotionListener {
  private class Rechteck {
   private int x,y,b,h;
   public Rechteck(int x,int y,int b,int h) {
    this.x=x; this.y=y; this.b=b; this.h=h;
  }}
  private Rechteck R;
  private Vector Rechtecke=new Vector();
  /* paintComponent ist swing-Ersatz für paint */
  public void paintComponent(Graphics g) {
   super.paintComponent(g); 
   for (Rechteck r:Rechtecke) {
    g.drawRect(r.x,r.y,r.b,r.h);
   }
   if (R!=null) {g.drawRect(R.x,R.y,R.b,R.h);
  }}
  /* Klassen des Interface MouseListener */
  public void mousePressed(MouseEvent e) {
   R=new Rechteck(e.getX(),e.getY(),0,0);
  }
  public void mouseReleased(MouseEvent e) {
   if (R.b>0 && R.h>0) Rechtecke.add(R);
  }
  public void mouseClicked(MouseEvent e) {}
  public void mouseEntered(MouseEvent e) {}
  public void mouseExited(MouseEvent e) {}
  /* Klassen des Interface MouseMotionListener */
  public void mouseDragged(MouseEvent e) {
   int x=e.getX(),y=e.getY();
   if (x>R.x && y>R.y) {
    R.b=x-R.x; R.h=y-R.y;
   }
   repaint();
  }
  public void mouseMoved(MouseEvent e) {}
  public Leinwand() {
   setBackground(Color.white);
   setPreferredSize(new Dimension(400,300));
   addMouseListener(this);
   addMouseMotionListener(this);
 }}
 ZeichneRechtecke() {
  super("Zeichne Rechtecke");
  setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  getContentPane().add(new Leinwand());
  pack();
  setVisible(true);
 }

 public static void main(String[] args) {
  new ZeichneRechtecke();
}} // Viel von D.Abts gelernt.
In ZeichneRechtecke ist der Pascal-Programmierstil verwirklicht. Zuerst werden die Variablen und Methoden der untersten Ebene, zuletzt das oberste Hauptprogramm definiert. main konstruiert ZeichneRechtecke, ZeichneRechtecke konstruiert Leinwand, Leinwand installiert die Mauslauscher, die Mauslauscher konstruieren die Rechtecke, paintComponent wird von repaint aktiviert usw..

Dass keine Getter und Setter angewandt, sondern Rechteck.x etc. direkt adressiert werden, ist eine Sünde gegen die objekt-orientierte Programmierung.


Eine eigene Exception (Ausnahme) definieren und verwenden

Das oben erklärte Programm zwecks Berechnung komplexer Zahlen wird erweitert. Hier wird eine Ausnahme-Bedingung eingeführt, die die unmögliche komplexe Teilung durch Null abfängt, während einige oben definierte Methoden weggelassen werden, die fürs Verständnis der Ausnahme-Programmierung entbehrlich sind. Das folgende Programm ist nichtsdestoweniger selbstständig lauffähig.
/* Muss in Datei mit Namen Complex.java stehen. */

class Complex {
 private double re,im;
 /* constructors */
 public Complex() {
  this.re=0.0;
  this.im=0.0;
 }
 public Complex(double re,double im) {
  this.re=re;
  this.im=im;
 }
 /* getter */
 public double getRe() {
  return(this.re);
 }
 public double getIm() {
  return(this.im);
 }
 /* setter */
 public void setRe(double re) {
  this.re=re;
 }  
 public void setIm(double im) {
  this.im=im;
 }
 /* conj, mod, arg, add, sub, mul, etc nach Bedarf eingänzen */
 /* Hier wird die eigene Ausnahme definiert */
 public class ComplexdivException extends Exception {
  public ComplexdivException(String Message) {
   super(Message);
 }}
 /* Hier wird die eigene Ausnahme gegebenenfalls ausgelöst */
 public Complex div(Complex operator) throws ComplexdivException {
  Complex result=new Complex();
  double opre=operator.getRe();
  double opim=operator.getIm();
  double dnom=opre*opre+opim*opim;
  if (dnom==0.0) throw new ComplexdivException("complex divide by zero");
  result.setRe((this.re*opre+this.im*opim)/dnom);
  result.setIm((this.im*opre-this.re*opim)/dnom);
  return(result);
 }
 public String toString() {
  if (this.re==0.0) {
   if (this.im==0.0) {
    return("0");
   } 
   else {
    return(this.im+"i");
  }}
  else {
   if (this.im==0.0) {
    return(String.valueOf(this.re));
   }
   else if (this.im<0.0) {
    return(this.re+" "+this.im+"i");
   }
   else {
    return(this.re+" +"+this.im+"i");
 }}}   

 public static void main(String[] argv) {
  Complex a=new Complex(2.0,5.0);
  Complex b=new Complex(3.0,-4.0);
  Complex c=new Complex();
  System.out.println("a= "+a);
  System.out.println("b= "+b);
  System.out.println("c= "+c);
  /* Hier wird die eigene Ausnahme erprobt und ggf. eingefangen */
  try {
   System.out.println("a/b= "+a.div(b));
   System.out.println("a/c= "+a.div(c));
  }
  catch (ComplexdivException e) {
   e.printStackTrace();
}}}


Einlesen aus einer Datei, String-Manipulation und Ausschreiben in eine Datei

Jeder, der diese Internet-Seite auf einer eigenen Internet-Seite verlinkt, so wie es in der Quelltext-Datei Verbrecher.html
<html>
<a href="http://home.arcor.de/althand/java.html">Brosas Java-Vorlesung</a>
<a href="http://sci.althand.com/java.html">nochmal Brosas Java-Vorlesung</a>
</html>
geschieht, ist der Mitgliedschaft in einer terroristischen Vereinigung überführt und muss sofort verhaftet werden. Das veranlasst der RoboStaatsanwalt gemäß folgendem Programm:
/* Muss in Datei mit Namen RoboStaatsanwalt.java stehen.*/
import java.io.*;
class RoboStaatsanwalt {
 RoboStaatsanwalt() throws IOException {
  int anf=0,end=0,fund=0;
  String Alles="",Zeile,Ergebnis;
  /* Daten einlesen */
  BufferedReader ein=new BufferedReader(
                      new FileReader("Verbrecher.html")
                     );
  while ((Zeile=ein.readLine())!=null) Alles+=Zeile;
  ein.close();
  /* Daten analysieren */
  do {
   if ((anf=Alles.indexOf("http://",anf))>-1) {
    anf+=7;
    if ((end=Alles.indexOf('"',anf))>-1) {
     Ergebnis=Alles.substring(anf,end);
     /* Ergebnis ausschreiben */
     if (Ergebnis.equals("sci.althand.com/java.html")) {
      fund++;
      PrintWriter aus=new PrintWriter (
                       new BufferedWriter (
                        new FileWriter("AnRoboRichter.txt")
                      ));
      aus.println("Verbrecher entlarvt.");
      aus.println("Haftbefehl ausstellen!");
      aus.close();
  }}}} 
  while (fund<1&&anf>-1&&end>-1);
 }
 public static void main(String[] args) {
  try {new RoboStaatsanwalt();}
  catch(IOException e) {e.printStackTrace();}
}}
Der RoboStaatsanwalt durchsucht die Datei Verbrecher.html nach möglichen Internet-Adressen. Hat er eine gefunden, schneidet er sie heraus und vergleicht sie mit sci.althand.com/java.html. Falls der Vergleich zutrifft, beantragt der RoboStaatsanwalt beim RoboRichter einen Haftbefehl.

Ein technisch ausgereifter RoboStaatsanwalt würde allerdings verdächtige Dateien aus dem Internet runterladen, wie es in einem obigen Beispiel vorgeführt wird, und er würde den Verhaftungsantrag per E-Mail an den RoboRichter schicken, was mit Java ebenfalls leicht programmiert werden kann.

Mehr über String-Manipulation: CodingBat Java Strings, String Manipulation


Analysieren mathematischer Ausdrücke mit split([regulärer Ausdruck])

Objekt-orientiertes Programmieren ist besonders geeignet, um das Rechnen mit höheren mathematischen Ausdrücken zu erleichtern.

Die systematische Lösung nichtlinearer algebraischer Gleichungen ohne Formen dürfte unmöglich sein. (Formen sind homogene Polynome mehrerer Veränderlicher.) Jede Form wird aus Monomen zusammengesetzt, die ihrerseits aus einem bekannten Koeffizienten mit einem Produkt der Veränderlichen in ganzen Potenzen bestehen. Beispiel: 3.0*x_1^17*x_2^4*x_3^9. Der Koeffizient ist 3.0. Die Veränderlichen sind x_1, x_2 und x_3 mit den Potenzen 17, 4 und 9. Das erste Problem besteht darin, ein Objekt monom so aus der menschlich lesbaren Zeichenkette (String) zu konstruieren, dass es maschinlich brauchbare Daten enthält. Das folgende Programm löst das Problem, indem es die Zeichenkette mit split immer dort aufgeteilt, wo ein Sternchen * steht. Ist das geschafft, werden die Faktoren nochmals aufgespalten, und zwar dort, wo ein Tiefstrich _ oder ein Hütchen ^ gefunden wird. Dabei ist zu beachten, dass split als Argument einen regulären Ausdruck (RegEx) erwartet. In den regulären Ausdrücken haben einige Zeichen, z.B. * und ^, Steuerungsfunktionen. Man muss deshalb \\* und \\^ in split einsetzen, wenn man die Zeichen selbst meint.

/* Muss in Datei mit Namen monom.java stehen. */

class monom {
 private int factors=0;
 private double coe=0.0;
 private int[] ind;
 private int[] pow;
 monom(String text) {
  String[] token=text.split("[\\*]");
  this.factors=token.length-1;
  this.ind=new int[this.factors];
  this.pow=new int[this.factors];
  this.coe=Double.parseDouble(token[0]);
  for (int j=0;j<this.factors;j++) {
   String[] subtoken=token[j+1].split("[_\\^]");   
   this.ind[j]=Integer.parseInt(subtoken[1]);
   this.pow[j]=Integer.parseInt(subtoken[2]);
 }}
 int getfactors() {
  return this.factors;
 }
 double getcoe() {
  return this.coe;
 }
 int getind(int position) {
  if (position<0 || position>this.factors) return -1;
  else return this.ind[position];
 }
 int getpow(int position) {
  if (position<0 || position>this.factors) return -1;
  else return this.pow[position];
 }
 public static void main(String[] args) {
  monom m=new monom("3.0*x_1^17*x_2^4*x_3^9");
  int factors=m.getfactors();
  System.out.println("Koeffizient="+m.getcoe());
  for (int i=0;i<factors;i++) 
  System.out.println("Index="+m.getind(i)+" Potenz="+m.getpow(i));
}}
Mehr über Parsing: Parsing Strings with Java.


Anmerkungen

[clebsch] Unentbehrlich für die Forschung im dreidimensionalen Raum sind die Clebsch-Gordan-Koeffizienten.
Clebsch-Gordan-Rechner mit Java programmiert; halbzahlige Werte als "0.5", "-1.5" usw. eingeben!
Clebsch-Gordan-Rechner mit JavaScript programmiert; halbzahlige Werte als "1/2", "-3/2" usw. eingeben!
JavaScript ist Java zwar ähnlich, kann aber weniger als Java. JavaScript wird von allen modernen Browsern direkt verstanden. Eine JRE braucht man daher für JavaScript nicht.

HOME