Kursname: WWI_030_101.1 Grundlagen der Programmierung
|
(Lizenz) |
KompetenzzieleDie Studierenden kennen die Grundprinzipien der Programmierung und Objektorientierung und können diese in einer adäquaten Programmiersprache anwenden. Sie sind in der Lage, einfache Problemstellungen algorithmisch zu formulieren, Algorithmen mit den Sprachelementen der Programmiersprache adäquat umzusetzen und Programme zu implementieren, zu testen und anzuwenden. Die Grundprinzipien der Objektorientierung an modellhaften Szenarien analysiert und implementiert werden. |
|
Prinzipien der Programmerstellung
Aufbau der Programmiersprache
|
Prozedurales und modulares Programmieren:
Grundprinzipien der objektorientierten Programmierung mit
|
Fragen und Antworten zur Selbstkontrolle
Dieser Abschnitt gibt...
Diese Umfrage hilft dem Dozenten Sie besser kennnen zulernen und die nächsten Vorlesungen zu planen.
|
|
|
Im Javaumfeld muss man zwei grundlegende Konzepte unterscheiden:
Programmiersprachen sind eine Ebene über den Maschinensprachen angesiedelt. Sie sind ein Mittel der Abstraktion und Strukturierung.
Die wichtigsten Komponenten einer Programmiersprache (Neuendorf S.40) sind:
Die oben aufgeführten Komponenten sind in allen gängigen Programmiersprachen zu finden.
In Java werden diese Komponenten zu Klassen zusammengeführt:
Der Zusammenhang zwischen diesen Komponenten wird im folgenden UML Diagramm vereinfacht gezeigt:

Bevor man ein System in einer Programmiersprache implementiert, verwendet man objektorientierte Techniken um mit dem Kunden den Problembereich (Anwendungsbereich) im Rahmen einer Systemanalyse zu modellieren.
Anschließend werden die modellierten Objekte und ihre Datentypen in eine Programmiersprache abgebildet.
Im Rahmen des Kurses werden Diagramme in UML (Unified Modeling Language) zur Beschreibung von Javaklassen verwendet. In Java sowohl als UML werden die folgenden Begriffe verwendet:
Objekte haben einen Lebenszyklus. Sie können erzeugt, modifiziert und dereferenziert werden. Dereferenzierte Objekte in Java sind nicht mehr erreichbar und daher nutzlos. Sie sind defacto gelöscht.
Im Folgenden ist eine Klasse Flugzeug in UML und zwei Objekte der Klasse a380 und jumbo modelliert.

Die Modellierung der Klasse Flugzeug in Java ist nachfolgend dargestellt.
Die Erzeugung der beiden Objekte a380 und jumbo erfolgt in der Methode main(). Hierzu muss ein Javaprogramm ausgeführt werden.
Die Methode main() wird als Javaprogramm durch das Kommando java Flugzeug aufgerufen.
public class Flugzeug {
String kennzeichen; // Ein Attribut vom Typ einer Zeichenkette
int leerGewicht; // Ein Attribut vom Type einer Ganzzahl
/**
* Eine Methode zum Drucken der Attributbelegung des Objekts
* Die Methode erfordert keine Eingaben. Sie erzeugt keine
* Aufgaben
*/
public void drucken() {
System.out.println("Kennzeichen " + kennzeichen + " "
+ leerGewicht + "kg");
}
/**
* Die Methode main() wird zum Starten des Programms benutzt
* @param args Übergabe von Konsolenparameter. Hier nicht genutzt
*/
public static void main(String[] args) {
// Erzeugen zweier Objekte
Flugzeug jumbo = new Flugzeug();
Flugzeug a380 = new Flugzeug();
// Belegen der Attribute des ersten Objekts mit Werten
jumbo.kennzeichen = "D-ABYT";
jumbo.leerGewicht = 191000;
// Belegen der Attribute des zweiten Objekts mit Werten
a380.kennzeichen = "D-AIMD";
a380.leerGewicht = 286000;
// Drucken der beiden Objekte auf der Konsole
jumbo.drucken();
a380.drucken();
}
}Das obige, ausführbare Programm hat die folgende Grundstruktur die in allen Javaklassen zufinden ist.
Javaklassen können noch mehr Bestandteile haben. Für die Einführung sind die folgenden Bestandteile die wichtigsten:

(Lizenz)
Java ist:
Netscape führte im Frühjahr 1996 die Browserscriptsprache JavaScript ein. Netscape war zum damaligen Zeitpunkt ein sehr früher Kooperationspartner von Sun Microsystems, der als einer der ersten Partner eine Portierung von Java im Browser auslieferte. Sun Microsystems gewährte Netscape zum damaligen Zeitpunkt das Recht das Markenzeichen "Javascript" zu verwenden. Die Marke gehört noch heute Oracle (als Nachfolger von Sun Microsystems)
Interessant: JavaScript hat technologisch gesehen nichts mit der Programmiersprache Java gemein.
Java-Programme können lokal wie ein gewöhnliches Programm ausgeführt werden.
Java-Applikationsserver die selbst keine direkte Benutzeroberfläche haben und im Hintergrund laufen sind technisch gesehen Programme der Java Standardedition.
Java-Programme die selbst eine graphische Benutzerschnittstelle mit Hilfe der Java Swing/AWT Bibliotheken exponieren nennt man auch "Fat" oder "Rich Clients".

Das Starten eines Javaprogramms geschieht mit dem Kommando java welches die Javalaufzeitumgebung startet:
$ java HelloWorld Hello World!
Java-Programme können über ein Netzwerk transportiert und direkt in einem Browser ausgeführt werden wenn sie als Java-Applet implementiert wurden:

import java.applet.Applet;
import java.awt.Graphics;
public class HelloWorldApplet extends Applet{
// Diese Methode wird aufgerufen wenn das Applet started
public void init() {
// Die Methode ist notwendig. Sie muss aber nicht mit Inhalt gefüllt
// werden.
System.out.println("HelloWorldApplet.init() aufgerfen");
}
// Diese Methode wird aufgerufen wenn das Applet beendet wird.
// Der Benutzer hat die html Seite verlassen
public void stop()
{
System.out.println("HelloWorldApplet.stop() aufgerufen");
}
// Die Standardmethode um etwas auf den Bildschirm zu malen
public void paint(Graphics g)
{
//Diese Methode malt Text auf den Bildschirm
// Text, x Koordinate, y Koordinate
g.drawString("Dies ist ein Text",20,20);
g.drawString("Hello World!",20,40);
}
}
Das Applet weiter unten ist ein Javaprogramm welches solange läuft wie die html Seite angezeigt wird. Es läuft auf dem Computer des Betrachters. Es wurde jedoch vom Internet heruntergeladen.
Der Begriff "Applet" stammt aus dem englischen und beschreibt kleine Programme. Er wurde aus dem Begriff "Application" abgeleitet.
Der Begriff "Plugin" bezieht sich auf den Webbrowser. Die Javafunktionalität ist in einem modernen Browser eine Zusatzfunktion die man als "Plugin" nachinstallieren kann.
Java Web Start ist eine Weiterentwicklung der Applettechnologie. Sie erlaubt es vollständige Javaprogramme über das Netzwerk zu installieren und zu starten.
Ein Beispiel hierfür ist das Demoprogramm Notepad von Oracle.
Applets haben nur sehr eingeschränkte Nutzungsmöglichkeiten da sie innerhalb einer Bowserseite laufen. Ihre Lebensdauer hängt von der Lebensdauer der Browserseite ab.
Java Web Start verbindet die Vorteile des automatischen Ladens eines Applets mit den Vorteilen eines Java Standalone Programms. Java Web Start erlaubt normale Programme (mit Start über main() Methode) über das Netzwerk zu laden und auszuführen. Die Programme werden im Java Web Start Client gecacht und können beim nächsten Aufruf sofort wieder gestartet werden. Ist die Versionsnummer in der zugehörigen jnlp Datei größer als die gecachte Programmversion so wird die neue Version des Programms nach geladen.

Der typische Ablauf des Ladens und Startens eines Java Web Start Programms ist der Folgende.
Java Web Start ermöglicht hiermit dynamisch neue Javaanwendungen an Benutzer zu verteilen.
Die Java Enterprise Edition (JEE) ist eine Applikationservertechnologie die auf der Java Standard Edition aufbaut.

JEE Applikaktionsserver betreiben in der Regel Mehrbenutzeranwendungen die Webseiten für die Benutzer generieren. Die Enterprise Edition wird typischerweise in zwei Varianten eingesetzt.
Hinweis: Java EE ist nicht Gegenstand dieses Kurses.
Prinzipielles Vorgehen ist das Arbeiten in gemeinsamen Untergruppen: JSR (Java Specification Request) nach folgendem Prinzip
Ziel ist die breite allgemeine Verfügbarkeit der Technologie.
Wichtig:
Ein großer Teil der Java Standardedition von Oracle/Sun als Open Source bei OpenJDK.org freigegeben.
Hier findet zur Zeit (Stand 10.2010) die Entwicklung zu JDK 7 statt.
Folge der unterschiedlichen Kollaborationsmodelle sind unterschiedlichste Produkte die zu unterschiedlichen Bedingungen genutzt werden können:
Wichtig: Für Standard-, Micro-, Enterpriseedition: Nur Produkte die Zertifizierungstest mit den entsprechenden TCKs bestanden haben dürfen sich "Java" nennen.
Java Standard Edition (SE) in seiner Distribution von Oracle kann in zwei Varianten benutzt werden:
Das JDK ist eine echte Obermenge des JRE und enhält die folgenden Komponenten die man zur Entwicklung und Ausführung von Java Programmen im Rahmen des Kurses benötigt:
| Program | Bedeutung | Kommentar |
|---|---|---|
| java | Startet Java Programme | Laufzeitsystem |
| javac | Übersetzer | Übersetzt Javaquellcode in Bytecode |
| javadoc | Generieren von Dokumentation | Erzeugt Dokumentation aus speziellen Kommentaren im Javaquellcode |
| jar | Java Archiver | Bündelt Javabytecode und Dateien zu jar Dateien |
| javah | C Schnittstellengenerator | Erzeugt Schnittstellendeklarationen für C Programme die von Java aus benutzt werden sollen |
| javap | Java Class File Disassembler | Extrahiert Schnittstelleninformation aus class Dateien und erzeugt öffentliche Schnittstellen |
| jdb | Java Debugger | Kommandozeilendebugger |
| jps | Anzeige der Prozess Ids von Java Programmen | Hilfswerkzeug für jconsole |
| jconsole | Monitoren von Java Prozessen | graphisches Werkzeug zum monitoren von Java Anwendungen |
| jinfo | Auslesen der Konfiguration eines laufenden Javaprozess | Konsolenausgabe der laufenden Prozessdaten |
| appletviewer | Testen von Applets | Ein Testprogramm für Entwickler von Applets. Erlaubt testen ohne Browser |
Dies ergibt die folgenden Zusammenhänge zwischen den verschiedenen Dateien die bei der Entwicklung beteiligt sind:
Das Übersetzen von Javaquellprogrammen in interpretierbare Bytecodedateien erfolgt mit dem Java-Übersetzer javac. Das Ausführen der übersetzten Dateien erfolgt mit dem Kommando java, dem Javalaufzeitsystem, welches Javaprogramme startet.
Der Javaübersetzer übersetzt lesbare Quelldateien mit der Dateiextension *.java in eine oder mehrere Bytecodedateien mit der Dateiextension *.class.
Beispiel
$ javac HelloWorld.java
Dieser Befehl erzeugt eine Datei mit dem Namen HelloWorld.class im gleichen Verzeichnis
Tipp: DerJavaübersetzer erwartet den vollen Dateinamen inklusive der Dateiextension .java
Das Kommando java erlaubt das Starten von Java-Programmen.
Beispiel:
$ java HelloWorld
Javaprogramme müssen als Bytecodedateien (Dateiextension *.class) oder als zu Java-Archiven (Dateiextension *.jar) gebündelte Bytecodedateien vorliegen.
Tipp: Das Kommando java akzeptiert nur den Namen der auszuführenden Klasse. Es akzeptiert nicht den Namen der gleichnamigen Datei mit dem Bytecode!
Das Kammando java sucht dann standardmässig im aktuellen Verzeichnis nach einer Bytecodedatei (Extension *.class) die den Interpretercode für die gewünschte Klasse enthalten.
Hiermit ergibt sich die Abfolge der Kommandos für ein einfaches Testprogrann HelloWorld.java:

Überprüfen sie ob eine Javalaufzeitumgebung vorhanden ist. Dies geschieht mit der Option -version des Programms java:
$ java -version java version "1.6.0_20" Java(TM) SE Runtime Environment (build 1.6.0_20-b02-279-10M3065) Java HotSpot(TM) Client VM (build 16.3-b01-279, mixed mode)
Erstellen sie eine Textdatei mit dem Namen HelloWorld.java und dem folgenden Inhalt:
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}Zum Beispiel mit gedit:
Das Programm javac (javac.exe in Windows)
$javac HelloWorld.java
Das Ergebnis ist eine Datei mit dem Namen HelloWorld.class. Diese Datei enthält den interpretierbaren Bytecode des Programms.
Der generierte Bytecode der Datei HelloWorld.class wird dann mit Hilfe des Programms java ausgeführt. Die extension .class wird nicht mit angegeben:
$ java HelloWorld Hello World!
Die (vereinfachte) Syntax von javac ist:
$ javac [Optionen] [Quelldateien]
Die wichtigsten Optionen des Javaübersetzers javac sind:
Der Schnittstellengenerator javap wird im gleichen Verzeichnis aufgerufen in dem sich die Datei HelloWorld.class befindet. Rufen Sie ihn mit dem Befehl "javap HelloWorld" auf. Die extension .class wird nicht benötigt.
sschneid@scalingbits:~/l1$ javap HelloWorld
Compiled from "HelloWorld.java"
class HelloWorld extends java.lang.Object{
HelloWorld();
public static void main(java.lang.String[]);
}Es werden die Informationen über alle öffentlichen Eigenschaften generiert jedoch nicht die Implementierung. Die Druckanweisung fehlt.
/**
*
* @author Ihr Name
*/
public class HelloWorld {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
System.out.println("HelloWorld");
}
}
Datei sichern...
sschneid@scalingbits:~/l1$ javadoc HelloWorld.java Loading source file HelloWorld.java... Constructing Javadoc information... Standard Doclet version 1.6.0_18 Building tree for all the packages and classes... Generating HelloWorld.html... Generating package-frame.html... Generating package-summary.html... Generating package-tree.html... Generating constant-values.html... Building index for all the packages and classes... Generating overview-tree.html... Generating index-all.html... Generating deprecated-list.html
Beispiel (Screenshot)

Das Programm javah generiert Schnittstellendateien für C Anwendungen. Hiermit lassen sich plattformabhängige C-Programme aus der Javalaufzeitumgebung aufrufen. Dieses Vorgehen wird von JNI (Java Native Interface) unterstützt.
C-Routinen werden nicht in Java implementiert. Sie werden nur deklariert und die Bibliotheken mit den Objektdateien (keine Javaobjekte...) zur Laufzeit dazu gebunden. Die Deklaration findet mit Hilfe des Schlüsselworts native statt. Im folgenden Beispiel wurde eine C-Routine test() als extern deklariert.
/**
*
* @author Stefan Schneider
*/
public class HelloWorld {
native int test();
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
System.out.println("Hello Gedit");
}
}
Die Datei HelloWorld.java muss zuerst mit javac übersetzt werden um dann auf der Datei HelloWorld.class das Kommanda javah aufzurufen.
javac HelloWorld.java javah HelloWorld
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: test
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_HelloWorld_test
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
jconsole ist eine grafische Javaanwendung die es erlaubt die Konfiguration eines Javapozess' zur Laufzeit zu beobachten. Einige der Eigenschaften die beobachtet werden können sind:
Die Verwendung von jconsole geschieht wie folgt:
Starten eines (länger) laufenden Javaprogramm
Starten des Programm DemoFrame
java DemoFrame
Jeder Prozess des Betriebssystems hat eine eindeutige Nummer den "Process Identifier". Das Kommando jps listet unabhängig vom Betriebssystem alle Javaprozesse.
jps 254 16964 Jps 16959 DemoFrame
jconsole 16959
Wichtig: Das Javaprogramm darf zum Zeitpunkt an dem jps und jconsole aufgerufen werden noch nicht beendet sein!
Laufende jconsole Anwendung:

jinfo liest ebenfalls die wichtigsten Kenndaten eines laufenden Prozesses aus und gibt sie auf der der Konsole aus.
Das erfassen der ProzessId geschieht auch mit dem Hilfsprogramm jps:
Starten (länger) laufenden Javaprogramm
Starten des Programm DemoFrame
java DemoFrame
Pegasus:bin sschneid$ jconsole 16959 Pegasus:bin sschneid$ jps 254 17168 Jps 17166 DemoFrame
Pegasus:bin sschneid$ jinfo 17166 Attaching to process ID 17166, please wait... Debugger attached successfully. Client compiler detected. JVM version is 16.3-b01-279 Java System Properties: java.runtime.name = Java(TM) SE Runtime Environment sun.boot.library.path = /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Libraries java.vm.version = 16.3-b01-279 awt.nativeDoubleBuffering = true gopherProxySet = false mrj.build = 10M3065 java.vm.vendor = Apple Inc. java.vendor.url = http://www.apple.com/ path.separator = : java.vm.name = Java HotSpot(TM) Client VM file.encoding.pkg = sun.io sun.java.launcher = SUN_STANDARD user.country = DE sun.os.patch.level = unknown java.vm.specification.name = Java Virtual Machine Specification user.dir = /Users/sschneid/Documents/JavaKurs/beispiele/l1/HelloWorld java.runtime.version = 1.6.0_20-b02-279-10M3065 java.awt.graphicsenv = apple.awt.CGraphicsEnvironment java.endorsed.dirs = /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/endorsed os.arch = i386 apple.awt.graphics.UseOpenGL = false java.io.tmpdir = /var/folders/UO/UOnPVFsvGEO5k3UJnjadeE+++TI/-Tmp-/ line.separator = java.vm.specification.vendor = Sun Microsystems Inc. os.name = Mac OS X sun.jnu.encoding = MacRoman java.library.path = .:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java java.specification.name = Java Platform API Specification java.class.version = 50.0 sun.management.compiler = HotSpot Client Compiler os.version = 10.6.4 http.nonProxyHosts = local|*.local|169.254/16|*.169.254/16 user.home = /Users/sschneid user.timezone = java.awt.printerjob = apple.awt.CPrinterJob file.encoding = MacRoman java.specification.version = 1.6 java.class.path = . user.name = sschneid apple.awt.graphics.UseQuartz = false java.vm.specification.version = 1.0 java.home = /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home sun.arch.data.model = 32 user.language = de java.specification.vendor = Sun Microsystems Inc. awt.toolkit = apple.awt.CToolkit java.vm.info = mixed mode java.version = 1.6.0_20 java.ext.dirs = /Library/Java/Extensions:/System/Library/Java/Extensions:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/ext sun.boot.class.path = /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/jsfd.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/classes.jar:/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/Resources/Java/JavaRuntimeSupport.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/ui.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/laf.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/sunrsasign.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/jsse.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/jce.jar:/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Classes/charsets.jar java.vendor = Apple Inc. file.separator = / java.vendor.url.bug = http://bugreport.apple.com/ sun.io.unicode.encoding = UnicodeLittle sun.cpu.endian = little mrj.version = 1060.1.6.0_20-279 socksNonProxyHosts = local|*.local|169.254/16|*.169.254/16 ftp.nonProxyHosts = local|*.local|169.254/16|*.169.254/16 sun.awt.exception.handler = apple.awt.CToolkit$EventQueueExceptionHandler sun.cpu.isalist = VM Flags:
Der appletviewer erlaubt das Testen von Applets ohne einen Webserver.
Der appletviewer kann Applets in html Seiten starten solange sie gewissen Bedingungen genügen.
Das folgende Beispiel zeigt die einfachste Anwendung des Appletviewers:
Erzeugen der Quellcodedatei HelloWorldApplet.java:
import java.applet.Applet;
import java.awt.Graphics;
public class HelloWorldApplet extends Applet{
/**
* Diese Methode wird aufgerufen wenn das Applet started
*
*/
public void init() {
// Die Methode ist notwendig. Sie muss aber nicht mit Inhalt gefüllt
// werden.
System.out.println("HelloWorldApplet.init() aufgerufen");
}
/**
* Diese Methode wird aufgerufen wenn das Applet beendet wird.
* Der Benutzer hat die html Seite verlassen
*/
public void stop()
{
System.out.println("HelloWorldApplet.stop() aufgerufen");
}
/**
* Die Standardmethode um etwas auf den Bildschirm zu malen
*/
public void paint(Graphics g)
{
//Diese Methode malt Text auf den Bildschirm
// Text, x Koordinate, y Koordinate
g.drawString("Dies ist ein Text",20,20);
g.drawString("Hello World!",20,40);
}
}Übersetzen der Quelldatei HelloWorldApplet.java und Erzeugen der Bytecodedatei HelloWorldApplet.class
java HelloWordApplet.java
<html> <applet code="HelloWorldApplet.class" width="200" height="50" > </applet> </html>
Der appletviewer muss im Verzeichnis aufgerufen werden in dem die Dateien HelloWorld.html HelloWorldApplet.class gespeichert sind.
appletviewer HelloWorld.html
Ergebnis:

Zur Verwaltung der Objekte(Daten) in einem Programm dienen Datenbehälter die Variable, oder Konstante genannt werden.
| Datenbehälter | Bedeutung | Beispiel |
|---|---|---|
| Konstante | Ein Datenbehälter dem über die gesamte Lebensdauer eines Programms genau ein Wert zugewiesen wird |
pi = 3.14; |
| Variable | Datenbehälter der mit einem bestimmten Wert initialisiert wird: Ihm können im Laufe der Programmausführung unterschiedliche Werte zugewiesen werden |
x = 0; x = 1; x = x +1; |
Variablen müssen in Java immer deklariert sein. Dies erfolgt mit einer Deklaration in der folgenden Syntax:

Beispiele:
int x; float y,z;
Variablen erhalten mit Zuweisungen eine neue Belegung. Hierfür können Konstanten, Variablen oder Operationen benutzt werden deren Ergebnis auf eine Variable zugewiesen wird.
Die Syntax einer Zuweisung ist in Java immer:

Beispiele:
int x = 1; float y = 3.2* 4.1;
Die Bedeutung in Java ist:
| Zuweisungsoperator Java ( Variable = Ausdruck;) |
|---|
| Nimm das Ergebnis der Berechnung der rechten Seite des Operators und weise der Variablen auf der linken Seite das Ergebnis zu. |
Java führt immer zuerst die Berechnung auf der rechten Seite aus und weist erst dann das Ergebnis der Variablen zu.
Der Ausdruck selbst muss nicht unbedingt eine komplexe Berechnung sein. Er kann auch eine Variable oder Konstante sein.
Ein Beispiel sei ein einfaches Programm welches die Eingaben in den Variablen x und y nutzt um durch fortgesetzte Addition zu Multiplizieren.
| Prozessorschritte | x | y | z |
|---|---|---|---|
|
Initialisierung (2*3=?) x=2; y=3; z=0; x = x -1; z = z + y; x = x - 1; z = z + y; |
2 1 1 0 |
3 3 3 3 |
0 0 3 6 |
| Ergebnis in z | 0 | 3 | 6 |
Anmerkung: DIe Prozessorschritte der Überprüfung der Variablen x auf Null wurde hier nicht explizit aufgeführt.
Zuweisung einer Konstanten auf eine Variable. Um genau zu sein: Es wird ein Literal zugewiesen.
pi = 3.14;
Zuweisung einer Variablen auf eine andere Variable:
d = pi;
Zuweisung eines mathematischen Ausdrucks(Term) auf eine Variable. Der Term wird vor der Zuweisung berechnet.
d = 2*pi*r;
Zuweisung eines Terms der die Variable selbst enthält. Der Term wird vor der Zuweisung mit dem alten Wert der Variablen berechnet. Erst nach der Berechnung wird der neue Wert zugewiesen.
d = 2* d;
|
|
1.7.1 Das erste Java Programm
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
} |

|
(Lizenz) |
|
Erzeugen Sie eine Datei mit dem Namen SwingRechner.java und dem folgendem Inhalt:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
public class SwingRechner extends javax.swing.JFrame {
private javax.swing.ButtonGroup buttonGroup1;
private javax.swing.JPanel jPanel1;
private javax.swing.JTextField jTextField1;
private javax.swing.JTextField jTextField2;
private javax.swing.JTextField jTextFieldOut;
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JButton jButton3;
private void initComponents() {
jTextField1 = new javax.swing.JTextField();
jTextField2 = new javax.swing.JTextField();
jTextFieldOut = new javax.swing.JTextField();
jButton1 = new javax.swing.JButton();
jButton2 = new javax.swing.JButton();
jButton3 = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("Swing Rechner");
jTextField1.setText("0");
jTextField1.setColumns(6);
jTextField2.setText("0");
jTextField2.setColumns(6);
jTextFieldOut.setText("0");
jTextFieldOut.setEditable(false);
jButton1.setText("XXXX");
jButton2.setText("YYYY");
jButton3.setText("ZZZZ");
JPanel radioPanel = new JPanel(new GridLayout(1, 0));
radioPanel.add(jButton1);
radioPanel.add(jButton2);
radioPanel.add(jButton3);
jButton1.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == jButton1)
jTextFieldOut.setText(
executeOperation1(jTextField1.getText(),
jTextField2.getText()));
}
}
);
jButton2.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == jButton2)
jTextFieldOut.setText(
executeOperation2(jTextField1.getText(),
jTextField2.getText()));
}
}
);
jButton3.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == jButton3)
jTextFieldOut.setText(
executeOperation3(jTextField1.getText(),
jTextField2.getText()));
}
}
);
this.setBounds(300, 300, 200, 30);
setMinimumSize(new Dimension(200,30));
getContentPane().add(jTextField1, BorderLayout.WEST);
getContentPane().add(jTextField2, BorderLayout.EAST);
getContentPane().add(radioPanel, BorderLayout.NORTH);
getContentPane().add(jTextFieldOut, BorderLayout.SOUTH);
pack();
}
public SwingRechner() {
initComponents();
}
public static void main(String[] args) {
SwingRechner f1 = new SwingRechner();
f1.setVisible(true);
}
public String executeOperation1(String s1,String s2) {
int op1= Integer.parseInt(s1);
int op2= Integer.parseInt(s2);
// Add Application logic here:
int resultInt = 0;
return (Integer.toString(resultInt)) ;
}
public String executeOperation2(String s1,String s2) {
int op1= Integer.parseInt(s1);
int op2= Integer.parseInt(s2);
// Add Application logic here:
int resultInt = 1;
return (Integer.toString(resultInt)) ;
}
public String executeOperation3(String s1,String s2) {
int op1= Integer.parseInt(s1);
int op2= Integer.parseInt(s2);
int resultInt = 2;
return (Integer.toString(resultInt)) ;
}
}
javac SwingRechner.java
java SwingRechner
Passen Sie das Programm an um einen Rechner für drei Grundrechenarten zu erhalten. Ersetzen sie in SwingRechner.java die folgenden Texte
Implementieren Sie die drei entsprechenden Grundrechenarten in den Methoden executeOperation1(), executeOperation2(), executeOperation3().
Ändern Sie hierfür die drei Zuweisungen sinngemäss und ersetzen sie die Zuweisungen von 0, 1, 2 durch den passenden mathematischen Term mit op1 und op2.
...
int resultInt = 0;
...
int resultInt = 1;
...
int resultInt = 2;
...
Das Programm soll anschliesend die Operationen passend zu den Buttonbeschriftungen ausführen
Erzeugen Sie eine Datei mit dem entsprechenden Namen. Setzen Sie den unten aufgeführten Quellcode ein. Übersetzen Sie ihn. Führen Sie ihn aus.
import java.util.Scanner;
public class GGT {
public static void main(String[] args) {
Scanner eingabe = new Scanner(System.in);
System.out.print("Geben Sie die erste Zahl ein: ");
int zahl1 = eingabe.nextInt();
System.out.print("Geben Sie die zweite Zahl ein: ");
int zahl2 = eingabe.nextInt();
int ergebnis = ggT(zahl1, zahl2);
System.out.println("Ergebnis: " + ergebnis);
}
/**
* Euklidscher Algorithmus als Java-Methode
*/
public static int ggT(int x, int y) {
while (x != y) {
System.out.println("Zwischenbelegung, x= " + x + ", y=" + y);
if (x > y) {
x = x - y;
} else {
y = y - x;
}
}
return x;
}
}
![]()
(Lizenz)
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
public class SwingRechner extends javax.swing.JFrame {
private javax.swing.ButtonGroup buttonGroup1;
private javax.swing.JPanel jPanel1;
private javax.swing.JTextField jTextField1;
private javax.swing.JTextField jTextField2;
private javax.swing.JTextField jTextFieldOut;
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JButton jButton3;
private void initComponents() {
jTextField1 = new javax.swing.JTextField();
jTextField2 = new javax.swing.JTextField();
jTextFieldOut = new javax.swing.JTextField();
jButton1 = new javax.swing.JButton();
jButton2 = new javax.swing.JButton();
jButton3 = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("Swing Rechner");
jTextField1.setText("0");
jTextField1.setColumns(6);
jTextField2.setText("0");
jTextField2.setColumns(6);
jTextFieldOut.setText("0");
jTextFieldOut.setEditable(false);
jButton1.setText("+");
jButton2.setText("-");
jButton3.setText("*");
JPanel radioPanel = new JPanel(new GridLayout(1, 0));
radioPanel.add(jButton1);
radioPanel.add(jButton2);
radioPanel.add(jButton3);
jButton1.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == jButton1)
jTextFieldOut.setText(
executeOperation1(jTextField1.getText(),
jTextField2.getText()));
}
}
);
jButton2.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == jButton2)
jTextFieldOut.setText(
executeOperation2(jTextField1.getText(),
jTextField2.getText()));
}
}
);
jButton3.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == jButton3)
jTextFieldOut.setText(
executeOperation3(jTextField1.getText(),
jTextField2.getText()));
}
}
);
this.setBounds(300, 300, 200, 30);
setMinimumSize(new Dimension(200,30));
getContentPane().add(jTextField1, BorderLayout.WEST);
getContentPane().add(jTextField2, BorderLayout.EAST);
getContentPane().add(radioPanel, BorderLayout.NORTH);
getContentPane().add(jTextFieldOut, BorderLayout.SOUTH);
pack();
}
public SwingRechner() {
initComponents();
}
public static void main(String[] args) {
SwingRechner f1 = new SwingRechner();
f1.setVisible(true);
}
public String executeOperation1(String s1,String s2) {
int op1= Integer.parseInt(s1);
int op2= Integer.parseInt(s2);
// Add Application logic here:
int resultInt = op1+op2;
return (Integer.toString(resultInt)) ;
}
public String executeOperation2(String s1,String s2) {
int op1= Integer.parseInt(s1);
int op2= Integer.parseInt(s2);
// Add Application logic here:
int resultInt = op1-op2;
return (Integer.toString(resultInt)) ;
}
public String executeOperation3(String s1,String s2) {
int op1= Integer.parseInt(s1);
int op2= Integer.parseInt(s2);
int resultInt = op1*op2;
return (Integer.toString(resultInt)) ;
}
}
|
(Lizenz) |
1.9.1 Javakommandos des JDK
|
a = 4; a = a; a = b 18 = c c = 18 + a; d = 18 - c; a,b = 18;
Welche Belegung haben die Variablen x,y und z wenn alle Zuweisungen ausgeführt?
Notieren Sie zur Hilfe alle Zwischenzustände
| Prozessorschritte | x | y | z |
|---|---|---|---|
|
x = 2; y = 3; z = 4; |
2
|
3
|
4 |
| Endergebnis | ? | ? | ? |
Ein chaotischer Entwickler sind Ihnen die Binärdatei Raetsel.class und bittet Sie die notwendige native C Methode zu implementieren?
Ansonsten gibt er Ihnen leider keine zusaätzlichen Informationen...
a = 4; // korrekt a = a; // korrekt a = b // falsch. Das Semikolon fehlt 18 = c; // falsch. 18 ist kein gültiger Bezeichner. c = 18; wäre erlaubt (und sinnvoll) c = 18 + a; // korrekt d = 18 - c; // korrekt a,b = 18; // falsch. Man kann nur einer Variablen gleichzeitig einen Wert zuweisen
Welche Belegung haben die Variablen x,y und z wenn alle Zuweisungen ausgeführt?
Hinweis: Es wurden nur geänderte Werte eingetragen. Ist ein Feld leer, so gilt der früherer Wert weiter oben
| Prozessorschritte | x | y | z |
|---|---|---|---|
|
x = 2; y = 3; z = 4; |
2 |
3 |
4 |
|
x = 5; |
5 |
||
|
x = 2 * x; |
10 | ||
|
y= z * x; |
|
40 |
|
|
z= 18 |
|
18 |
|
| Endergebnis | 10 | 40 | 18 |
Laden Sie die Binärdatei auf Ihren Rechner.
Der folgende Befehl dissambliert die Klasse und erzeugt eine Klassenbeschreibung mit allen öffentlichen Komponenten der Klasse.
Rufen Sie javap in dem Verzeichniss aus in dem die Datei Raetsel.class steht:
pegasus:classes sschneid$ javap Raetsel
Compiled from "Raetsel.java"
public class Raetsel extends java.lang.Object{
public int variable1;
public float variable2;
public java.lang.String variable3;
public Raetsel();
public void testMethode();
public native void methodeInC();
}
Der notwendige C-Prototyp Raetsel.h wird mit dem Kommando javah erzeugt. Man muss das Kommando im Verzeichnis aufrufen in dem die Datei Raetsel.class steht:
javah Raetsel
Hierdurch wird eine Datei Raetsel.h erzeugt:
* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Raetsel */#ifndef _Included_Raetsel
#define _Included_Raetsel
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Raetsel
* Method: methodeInC
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_Raetsel_methodeInC
(JNIEnv *, jobject);#ifdef __cplusplus
}
#endif
#endif
Der Name der zu implementierenden Funktion ist fettgedruckt. Alle anderen Zeilen bestehen au Direktiven für die Programmiersprachen C, C++ und aus Kommentaren.
|
(Lizenz) |
Am Ende dieses Blocks können Sie:
|
Sie sind in der Lage die folgenden Fragen zu beantworten:
Dieser Unterrichtsblock beschäftigt sich mit den Grundlagen von Variablen und ihren Belegungen. Am Ende dieses Blocks haben Sie ein Grundverständnis von
Java Schlüsselwörter sind:
| abstract | continue | for | new | switch |
| assert | default | if | package | synchronized |
| boolean | do | goto | private | this |
| break | double | implements | protected | throw |
| byte | else | import | public | throws |
| case | enum | instanceof | return | transient |
| catch | extends | int | short | try |
| char | final | interface | static | void |
| class | finally | long | strictfp | volatile |
| const | float | native | super | while |
Die Schlüsselworte in Kategorien gruppiert ergeben:

The Schlüsselworte "const" und "goto" sind reserviert auch wenn sie im Moment nicht benutzt werden.
"true" und "false" sind technisch gesehen boolsche Literale. (Java 7 Spez. §3.10.3). Ähnliches gilt für das "null" Literal (Java 7 Spez. §3.10.7).
Siehe Java Spezifikation: Identifier
Regeln für Namen in Java:
Literale dienen zum Beschreiben konkreter Werte in der Sprache Java.
Die Syntax wurde mit Hilfe von regulären Ausdrücken beschrieben.
| Syntax | Typ | Beispiel |
|---|---|---|
| number | int | 17, -17 |
| number[l|L] | long | 24l, -24L |
| 0[x|X]hex | int in Hexadezimaldarstellung | 0x01234567890ABCDEF |
| 0octal | int in Oktaldarstellung | 071 |
| 0b{0|1} | int in Binärdarstellung (erst seit Java 7, JSR 334) | 0b101010101010 |
| [+|-][number].number | double (Fließkommazahl) | -3.1415 |
| [+|-]number[f|F] | float (Fließkommazahl) | -3.1415F |
| [+|-]number[d|D] | double (Fließkommazahl) | -3.1415E13D |
| [+|-]number | int mit Vorzeichen (signed number) | -3, +3 |
| [+|-]number.number[e|E][+|-]number | Exponentendarstellung | -3.1415E10 |
| 'character' | einzelnes Zeichen | 'a' |
| "characters" | Zeichenkette | "aB10" |
| "" | Leere Zeichenkette | "" |
| \b | Zeichenposition 1 nach links (back space) | |
| \t | Tabulator | |
| \n | Zeilenvorschub | |
| \f | Seitenvorschub | |
| \r | Wagenrücklauf | |
| \" | Doppeltes Anführungszeichen | |
| \' | Einfaches Anführungszeichen | |
| \\ | Schrägstrich rückwärts | |
| \uNNNN | Unicodezeichen (NNNN in hexadezimal) | |
| true | Boolscher Wert | true |
| false | Boolscher Wert | false |
Seit JDK 7 sind durch die Integration des Projekts "Coin" (JSR 334) die Gruppierung von Zifferngruppen in Zahlenliteralen durch den Tiefstrich '_' möglich.
Der JSR Spezifikation entnommene Beispiele der neuen Syntax:
1234_5678 1_2_3_4__5_6_7_8L 0b0001_0010_0100_1000 3.141_592_653_589_793d 0x1.ffff_ffff_ffff_fP1_023 // Double.MAX_VALUE
nicht erlaubt sind:
_1234 0x_1234 1234_ 0x1.0_p_-1022
Warnung: (Stand 2012) Java 7 ist ein neuer Standard der noch nicht überall eingesetzt wird:
Zeichenketten ohne Namen nennt man Literale.
Kommentare erlauben das Dokumentieren eines Programmes. Sie werden vom Übersetzer ignoriert.
Java benutzt das Kommentarkonzept auch zur Generierung von Dokumentation. Dokumentationskommentare sind eine Sonderform der Kommentare und werden im Abschnitt zur Dokumentation vorgestellt.
Zeilenkommentare beginnen nach dem doppelten Schrägstrich //. Der Javaübersetzer wird alle Zeichen hinter diesem Kommentarzeichen bis zum Ende der Zeile ignorieren.
Beispiel:
int a = 17; // Dies ist ein Zeilenendkommentar // Dieser Kommentar umfasst eine ganze Zeile int b = 18;
Java erlaubt es eine ganze Reihe von Zeilen als Kommentar zu kennzeichnen. Mehrzeilige Kommentare werden mit den Zeichen "/*" (Schrägstrich Stern) eingeleitet und mit der Zeichenkombination "*/" (Stern Schrägstrich) beendet. Hiermit kann man ganze Bereiche als Kommentar markieren.
Beispiel:
/* Hier beginnt ein Kommentar diese Zeile gehört zum Kommentar int i=1; diese Zeile wird nicht als Befehl sondern als Kommentar verarbeitet der Kommentar endet in der nächsten Zeile */
Die Typen die ein Entwickler in Java vewendet sind entweder primitive Datentypen oder komplexe Datentypen. Die beiden Kategorien unterscheiden sich in den folgenden Eigenschaften:
| Primitiver Datentyp | Komplexer Datentyp (Objekt) | |
|---|---|---|
| Operatoren | viele hochoptimierte Operationen | Nur Operatoren für Objekte (Vergleich, Referenzierung). Ansonsten Methoden der Klasse |
| Lebensdauer | hängt vom umgebenden Block, bzw. Objekt ab | liegt im Freispeicher. Lebensdauer hängt von der letzten Referenz auf Objekt ab |
| Speicherverbrauch | konstant | meistens variabel |
| Syntax | immer klein geschrieben | Systemklassenbeginnen immer mit Großbuchstaben |
Im nachfolgenden Diagramm wird die Klassifikation der wichtigsten Typen gezeigt:

Die nächsten beiden Abschnitte behandeln die hier gezeigten Typen. Der dritte Abschnitt beschäftigt sich mit den Risiken von Zuweisungen zwischen Datentypen mit unterschiedlichen Wertebereichen.

(Lizenz)
| Datentyp | Bits | Wertebereich | Wertebereiche | Konstante min. | Konstante max. |
|---|---|---|---|---|---|
| byte | 8=1 byte | -27 bis 27-1 | -128 bis +127 | ||
| short | 16=2 byte | -215 bis 215-1 | -32768 bis +32767 | ||
| char | 16=2 byte | 0 bis 216-1 | 0 bis +65535 (Sonderfall!) | ||
| int | 32=4 byte | -231bis 231-1 | -2147483648 bis +2147483647 | ||
| long | 64=8 byte | -263 bis 263-1 | −9,223,372,036,854,775,808 to +9,223,372,036,854,775,807 |
Die maximalen und minimalen Werte der Ganzzahltypen ergeben sich aus der Anzahl der Bits und der internen binären Präsentation. Dies ist am Beispiel des 8-Bit Typen Byte aufgeführt:

Java verwendet für die Darstellung negativer Ganzzahlen das Zweierkomplement. Der Vorteil des Zweierkomplement besteht in der einfacheren Implementierung von Arithmetikeinheiten in Prozessoren. Durch das Zweierkomplement kann das Vorzeichenbit bei Additionen und Subtraktion wie ein regulärers Bit des Wertebereichs behandelt werden.
Dies kann man gut am Beispiel der Zahl -1 erkennen. Addiert man zur Zahl -1 (Binärdarstellung 11111111) eine 1 (Binärdarstellung 00000001) so ergibt durch den Übertrag eine 0 (Binärdarstellung 00000000).
|
Das Applet zur Rechten ist in der Lage alle Ganzzahltypen in die Binärdarstellung umzuwandeln. Das Vorzeichenbit wird in Rot dargestellt. Bei allen Typen die mehr als 16 Bit erfordern wird die Ausgabe nach 16 Bit umgebrochen. Das Bit mit der höchsten Ordnung wird zuerst ausgegeben. Das Bit mit der niedrigsten Ordnung wird am Ende ausgegeben. Hinweis: Der Typ char ist ein Zahlentyp. Man muss jedoch genau ein beliebiges Zeichen (Buchstabe) im Eingabefenster eingeben. Der Type char unterscheidet sich vom Typ short nur in der Benutzereingabe und im Wertebereich. |
|
Die Zahlendarstellung der Fließkommazahlen geschieht nach der Norm IEEE 754 getrennt nach Bits für Vorzeichen (V), Mantisse und Exponent mit unterschiedlicherAnzahl von Bits abhängig vom Typ nach der Regel
z= (-1)V*Mantisse*2Exponent
| Datentyp | Bits | V (bits) |
Mantisse (bits) |
Exponent (bits) |
Zahlenbereich | Dezimalstellen in Mantisse |
|---|---|---|---|---|---|---|
| float | 32=4 Byte | 1 | 23 | 8 | ≈-3.4*1038 bis +3,4*1038 | 7 |
| double | 64=8 Byte | 1 | 52 | 11 | ≈-1.7*10308 bis +1.7*10308 | 15 |
Die Minimal- und Maximalwerte als Konstanten können über die Attribute MIN_VALUE und MAX_VALUE der Klassen Float und Double abgerufen werden.
|
Die Berechnung der dezimalen Werte ist für den menschlichen Betrachter nicht so einfach wie die Umwandlung von Ganzzahlen.
Der IEEE 754 Standard ist recht anschaulich in Wikipedia beschrieben. Eine 32 Bit Fließkommazahl berechnet sich nach IEEE 754 wie folgt: = (-1)Vorzeichen*2(Exponent-127)*Mantisse |
Für den 32 Bit Typ float ergibt sich so nach dem Standard IEEE 754 das folgende Bitmuster für verschiedene Werte:
|
|
Das Applet zur Rechten ist in der Lage 32 Bit Fließkommazahlen in die Binärdarstellung umzuwandeln. Die Knöpfe auf der rechten Seite erlauben die Eingabe von Extremwerten wie
|
|
| Datentyp | Bits | Wertebereich | Werte | Konstante |
|---|---|---|---|---|
| boolean | 8 | wahr oder falsch | true,false | Boolean.FALSE, Boolean.TRUE |
Java behandelt einzelne Zeichen intern als ganze Zahlen. Man kann auf den Typ char alle Operationen anwenden die auch für Zahlen erlaubt sind. Der wesentliche Unterschied zum Typ short besteht in der Eingabe und Ausgabe, sowei im Wertebereich. Hier werden lexikalische Zeichen ein- oder ausgegeben.
Wichtiger Sonderfall: Der Typ char benutzt 16 Bit zum kodieren wie auch der Typ short. Die Wertebereiche unterscheiden sich jedoch. Der Typ char kodiert nur positive Werte. Im englischen wird ein solcher typ "unsigned" gennannt. Es ist ein Typ ohne Vorzeichenbit.
| Datentyp | Bits | Wertebereich | Werte | Kommentar |
|---|---|---|---|---|
| char | 16 | UC='\u0000' bis '\uffff' | 16 Bit Unicode 4.0 Basic Multilingual Plane (BMP) | "supplementary character" Unterstützung seit JDK 1.5 |

(Lizenz)
Es gibt eine Reihe von oft benutzten Datentypen die jedoch nicht primitiv sind.
Diese Datentypen werden mit Hilfe von Klassen implementiert. Die so erzeugten Datenstrukturen sind Javaobjekte und haben einen anderen Lebenszyklus als primitive Typen die in einer Klasse oder einem Programmierblock benutzt werden.
Zeichenketten sind in Java kein vordefinierter primitiver Typ. Zeichenketten werden im Paket java.lang mit Hilfe der Klasse String implementiert. Die Klasse String kann jedoch ohne eine spezielle Deklaration wie ein primitiver Typ verwendet werden.
Zeichenketten (Strings) sind in Java nicht modifizierbar. Bei jeder Zuweisung wird eine neue Datenstruktur angelegt. Bei primitiven Typen werden die Werte an der gleichen Stelle überschrieben.
Aufzählungstypen sind seit JDK 5.0 Bestandteil der Sprache.
| Aufzählungstypen |
|---|
| Aufzählungstypen in Java haben einen Wertebereich der aus einer geordneten Menge von Konstanten besteht. Der aktuelle Wert einer Variablen besteht aus einem der Aufzählungskonstanten |
Aufzählungstypen können nur als Bestandteil einer Klasse deklariert werden. Sie können nicht wie die anderen Basistypen innerhalb eines Blocks deklariert werden. Die Syntax einer Deklaration in einer Klasse ist die Folgende:

Beispiel einer Deklaration in einer Klasse:
class AufzaehlungsDemo {
enum Kartenfarbe {KARO, HERZ, PIK, KREUZ}
enum Wochentag {MONTAG, DIENSTAG, MITTWOCH, DONNERSTAG, FREITAG, SAMSTAG, SONNTAG}
}Bei der Benutzung von Aufzählungstypen kann nur eine zuvor deklarierte Aufzählungskonstante benutzt werden.
Beispiel der Benutzung in einer Klassenmethode der zugehörigen Klasse:
Wochentag heute = Wochentag.MITTWOCH; Kartenfarbe dame = Kartenfarbe.HERZ;
Aufzählungstypen wurden wie Referenzvariablen (die erst später vorgestellt werden) implementiert.
Referenztypen erlauben es auf andere Objekte zu referenzieren. Sie werden in einem späteren Abschnitt behandelt.
Die Java BigDecimal Klassen erlauben das Rechnen mit einer beliebigen Präzision. BigDecimal Klassen werden im Rahmen dieser Einführung nicht behandelt.
Die Sprache Java und ihre (Laufzeitzeitumgebung) sind typsicher. Typsicher bedeutet, dass bei Zuweisungen für Variablen der Typ der Variable und der zugewiesene Typ eines Wertes geprüft werden und nur sichere Zuweisungen erlaubt werden. Der Übersetzer (javac) erkennt dies schon beim Parsen (lesen) des Quellcodes und meldet dies als Fehler.
Beispiel: Der Wertebereich einer Variable vom Type short endet bei 65535. Der Typ int hat jedoch einen Wertebereich der bis 4294967295 reicht. Im folgenden Fall kann es also zu einem Überlauf kommen:
short a; int b; .... a = b;
Der Übersetzer wird einen Fehler melden, da es hier zu einem Überlauf/Unterlauf aufgrund der unterschiedlichen Wertebereiche kommen kann.
Die Zuweisungskompatibilität kann man als Teilmengen mit dem folgen Symbol darstellen: ⊃ . Es ergibt bei Java die folgenden Beziehung:
double ⊃ float ⊃ long ⊃int ⊃ short/char ⊃ byte
Bei Berechnungen vor der Zuweisung kommen eventuell verschieden Typen in Operationen (z.Bsp.) Addition vor.
Hier verfährt Java nach der folgenden Regel:
Um Zuweisungen zwischen inkompatiblen Typen zu erzwingen kann man den Zieltyp in runden Klammern dem Ausdruck voran stellen. Diesen Operator nennt man Castoperator (cast im englischen: gießen, betonieren).
Beispiel:
short a; int b; .... a = (short)b;
Hiermit erzwingt man Operationen die unsicher sein können! Es kann zu Überläufen in Wertebereichen oder Präzisionsverlusten kommen kann. Der Entwickler übernimmt hier die Verantwortung und überstimmt den Übersetzer. Der Castoperator sollte daher nur wenn nötig eingesetzt werden.
|
Das Applet zur Rechten ist in der Lage einen beliebigen Ganzzahltypen auf einen beliebigen anderen Ganzzahltypen zu zuweisen. Die Variable y enthält den Eingabewert. Den Typ von y kann man in der rechten Spalte wählen. Der Wert von y wird mit Hilfe einer Typkonversion (Cast) auff x zugewiesen. Den Typ von x kann man links wählen. Nach der Wahl der Typen und des Eingabewerts kann die Zuweisung mit dem "Enter" Button gestartet werden. Fragen:
|
|
Ausdrücke in Java sind alles was einen Rückgabewert liefert:
Java verfügt über
|
Unäre Operatoren haben einen einzigen Operanden. Beispiele sind:
|
![]() |
|
Binäre Operatoren haben zwei Operanden. Beispiele sind:
|
![]() |
Die arithmetischen Operatoren können auf die folgenden Typen angewendet werden
| Operator | Beispiel | Semantik |
|---|---|---|
| + | a + b | Addition: Summe von a und b |
| - | a - b | Subtraktion: Differenz an a und b |
| * | a * b | Multiplikation: Produkt von a und b |
| / | a / b | Division: Quotient von a und b |
| % | a % b | Modulo: Rest einer ganzzahligen Division von a durch b |
Die Division von Ganzzahlen ergibt immer ganzzahlige Ergebnisse!
Java arbeitet ohne eine Erkennung des Überlaufs der Wertebereiche. Der Entwickler muss selbst die entsprechenden Vorsichtsmaßnahmen ergreifen.
Weiterhin gibt es einstellige (unäre) arithmetische Operatoren
| Operator | Beispiel | Semantik |
|---|---|---|
| + | +a | Der Wert von a bleibt erhalten (Idempotente Operation) |
| - | -a | Der Wert von a wird negiert |
| ++ |
a++ ++a |
Postinkrement: Der Ausdruck behält ursprünglichen Wert . Der Wert von a wurde um 1 erhöht Präinkrement: Der Wert von a wird um 1 erhöht und der Ausdruck erhält den erhöhten Wert von a |
| -- |
x-- --x |
Postdekrement: Der Ausdruck behält ursprünglichen Wert . Der Wert von a wurde um 1 erniedrigt Präidekrement: Der Wert von a wird um 1 erniedrigt und der Ausdruck erhält den verminderten Wert von a |
Die folgen drei Anweisungen bewirken das gleiche:
a = a + 1; a++; ++a;
Bei Inkrementen mit gleichzeitiger Zuweisung ergeben jedoch unterschiedliche Werte für den zugewiesenen Wert
| Variante 1 | Wert a | Wert b | Variante 2 | Wert a | Wert b |
|---|---|---|---|---|---|
|
a = 10; b = 4; b = a++; |
10 11 |
4 10 (!) |
a = 10; b = 4; b = ++a; |
10 11 |
4 11 (!) |
| Quellcode | Konsolenausgabe |
|---|---|
package block2;
public class Main {
public static void PrePostFixTest(String[] args) {
int x = 10;
int y = 100;
System.out.println("x = " + x + "; y = " + y);
x++;
System.out.println("x++ results in " + x);
++x;
System.out.println("++x results in " + x);
System.out.println("Set x to 0 ");
x=0;
System.out.println("x = " + x + "; y = " + y);
y=x++;
System.out.println("y=x++ (Postfix)");
System.out.println("x = " + x + "; y = " + y);
y=++x;
System.out.println("y=++x (Prefix)");
System.out.println("x = " + x + "; y = " + y);
} |
. . . . . x = 10; y = 100 . x++ results in 11 . ++x results in 12 Set x to 0 . x = 0; y = 100 . y=x++ (Postfix) x = 1; y = 0 . y=++x (Prefix) x = 2; y = 2 . |
Bei der Arithmetik mit Fließkommazahlen werden im Gegensatz zu den Ganzzahlen Überläufe erkannt. Die Fließkommazahlen besitzen eine Reihe Konstanten:
| Konstante | Semantik |
|---|---|
| POSITIVE_INFINITY | Positiv unendlich |
| NEGATIVE_INFINITY | Negativ unendlich |
| MAX_VALUE | Größter darstellbarer Wert |
| MIN_VALUE | Kleinster darstellbarer Wert |
| NaN | "Not a number" Dieser Wert ist ungleich zu allen anderen Werten im Wertebereich |
Gleichheit bzw. Ungleichheit bezieht sich auf den Wert der Variablen x und y
| Operator | Beispiel | Semantik (Bedeutung) |
|---|---|---|
| == | x == y | ist x gleich y ? |
| != | x != y | ist x ungleich y ? |
| < | x < y | ist x kleiner als y ? |
| <= | x <= y | ist x kleiner oder gleich y ? |
| > | x > y | ist x größer als y ? |
| >= | x >= y | ist x igrößer oder gleich y ? |
Die logischen Operatoren wirken auf den Typ Boolean der nur den Wert wahr oder falsch kennt.
| Operator | Beispiel | Semantik (Bedeutung) |
|---|---|---|
| ! | !a | Negation |
| & | a & b | Und |
| | | a | b | Oder (inklusiv) |
| ^ | a ^ b | Entweder-Oder |
| && | a && b | bedingt auswertendes Und |
| || | a || b | bedingt auswertendes Oder |
Der dreistellige Bedingungsoperator (Konditionaloperator) erlaubt eine Zuweisung von dem Ergebnis einer Bedingung abhängig zu machen. Er hat die Form:
<ausdruck1> ? <ausdruck2> : <ausdruck3>
ausdruck1 muss einen boolschen Wert ergeben. Wird ausdruck1 wahr, so wird ausdruck2 der entsprechenden Variable zugewiesen. Wird ausdruck1 unwahr, so wird der ausdruck3 zugewiesen
Hiermit kann man Zuweisungen wie die Folgende formulieren
int maximum; int x = 1; int y =2 ; maximum = (x > y) ? x : y ;
Das Ergebnis ist 2, da y größer als x ist.
Die bedingt auswertenden Operatoren werten Terme nur soweit aus bis das Endergebnis fest steht. Dies macht sie seht effizient.
Im Beispiel:
boolean a = ((1<3) || (4>5));
wird der Term (4>5) nicht mehr ausgewertet. Da (1<3) wahr ist, steht das Endergebnis schon fest.
Die bedingt auswertenden logischen Operatoren wendet man neben Ihrem Geschwindigkeitsvorteil auch gerne an um potentielle Fehler und Ausnahmen zu vermeiden.
Ein Beispiel hierfür ist:
if ((a>0) && (Math.sqrt(a)>2))
Die Wurzel wird nur ausgewertet wenn a größer als Null ist.
Vorsicht: Durch die bedingte Auswertung können unterschiedliche Ergebnisse enstehen wenn in einem Ausdruck gleichzeitig ein Wert verändert wird!
Beispiel:
| bedingter "Oder" Operator | einfacher "Oder" Operator | |
|---|---|---|
| Quellcode |
public static void t1() {
int a = 3;
int b = 5;
if ((a>1) || (a<b++)){ |
public static void t2() {
int a = 3;
int b = 5;
if ((a>1) | (a<b++)){
System.out.println ("Hallo");
}
System.out.println("b= " + b);
} |
| Ausgabe |
Hallo b= 5 |
Hallo b= 6 |
Mit Bitoperatoren werden alle Bits zweier Variablen einzeln manipuliert.
| Operator | Beispiel | Bedeutung |
|---|---|---|
| ~ | ~a | Komplement |
| & | a & b | Und |
| | | a | b | Oder |
| ^ | a ^b | exklusives Oder |
package block2.skript;
public class BitOperator {
public static void main(String[] args) {
int a = 7;
int b = 6;
int result;
result = a & b;
System.out.println("a = " + a + "; b = " + b + " result = " + result);
result = a | b;
System.out.println("a = " + a + "; b = " + b + " result = " + result);
result = a ^ b;
System.out.println("a = " + a + "; b = " + b + " result = " + result);
}
}a = 7; b = 6 result = 6 a = 7; b = 6 result = 7 a = 7; b = 6 result = 1
| Variable | Dezimal | Binär |
|---|---|---|
| a | 7 | 0 0000000 00000000 00000000 00000111 |
| b | 6 | 0 0000000 00000000 00000000 00000110 |
| result = a & b | 6 | 0 0000000 00000000 00000000 00000110 |
| Variable | Dezimal | Binär |
|---|---|---|
| a | 7 | 0 0000000 00000000 00000000 00000111 |
| b | 6 | 0 0000000 00000000 00000000 00000110 |
| result = a | b | 7 | 0 0000000 00000000 00000000 00000111 |
|
Operator |
Beispiel | Bedeutung |
|---|---|---|
| << | a << b | Wert des Ausdrucks sind die Bits von a die um b Positionen nach links verschoben wurden. Es wird mit 0 Bits aufgefüllt. |
| >> | a >> b | Wert des Ausdrucks sind die Bits von a die um b Positionen nach rechts verschoben wurden. Es wird mit dem höchsten Bit aufgefüllt. |
| >>> | a >>> b | Wert des Ausdrucks sind die Bits von a die um b Positionen nach rechts verschoben wurden. Es wird mit dem "0" Bits aufgefüllt. |
|
Das Applet rechts erlaubt die drei Bitschiebeoperationen zu testen. Es kann die Bits um jeweils eine Stelle verschieben. |
|
public class ShiftingBits {
public static void main(String[] args) {
int x = 4;
int result;
int shift = 1;
result = x << shift;
System.out.println("x = " + x + "; shift = " + shift + " result = " + result);
result = x >> shift;
System.out.println("x = " + x + "; shift = " + shift + " result = " + result);
result = result >> shift;
System.out.println("x = " + x + "; shift = " + shift + " result = " + result);
result = result >> shift;
System.out.println("x = " + x + "; shift = " + shift + " result = " + result);
result = result >> shift;
System.out.println("x = " + x + "; shift = " + shift + " result = " + result);
}
}
x = 4; shift = 1 result = 8x = 4; shift = 1 result = 2x = 4; shift = 1 result = 1x = 4; shift = 1 result = 0x = 4; shift = 1 result = 0
Die interne Darstellung der verwendeten Werte:
| Dezimalwert | Binärwert |
|---|---|
| 8 | 0 0000000 00000000 00000000 00001000 |
| 4 | 0 0000000 00000000 00000000 00000100 |
| 2 | 0 0000000 00000000 00000000 00000010 |
| 1 | 0 0000000 00000000 00000000 00000001 |
| 0 | 0 0000000 00000000 00000000 00000000 |
Das Gleichzeichen = dient in Java als Zuweisungsoperator. Die Anweisung
x = y + z;
ist nicht als mathematische Gleichung zuverstehen, sondern als Zuweisung des Ausdrucks auf der echten Seite (y+z) auf die Variable x auf der linken Seite.
Zuweisungen wie:
x = y = 8;
sind auch möglich. Sie haben die gleiche Bedeutung wie
y = 8; x = y;
Für die meisten binären Operatoren gibt es Verbundoperatoren mit denen man einer Variable etwas zuweisen kann und gleichzeitig den alten Wert verwenden kann:
| Verbundoperator | entspricht |
|---|---|
| a += b | a = a + b |
| a -= b | a = a - b |
| a *= b | a = a * b |
| a /= b | a = a / b |
| a %= b | a = a % b |
| a &= b | a = a & b |
| a |= b | a = a | b |
| a ^= b | a = a ^ b |
| a <<= b | a = a << b |
| a >>= b | a = a > b |
| a >>>= b | a = a >>> b |
Für Ausdrücke mit mehreren Operatoren gelten die folgenden Regeln in Bezug auf die Reihenfolge der Auswertung:
Unäre Operatoren haben alle die gleiche Priorität
Die Ausführungsreihenfolge von Operatoren bestimmt wie ein Term aufgelöst wir.
Tipp: Es ist guter Programmstil Terme übersichtlich zu gestalten. Verwenden Sie im Zweifelsfall Klammern!
| Rang | Operator | Beschreibung |
|---|---|---|
| 1 | =, +=, -=, *= ... | Zuweisungsoperator |
| 2 | ?: | Bedingungsoperator |
| 3 | || | Logische Oder |
| 4 | && | Logisches Und |
| 5 | | | logisches oder bitweises Oder |
| 6 | ^ | logisches oder bitweises Entweder-Oder |
| 7 | & | logisches oder bitweises Und |
| 8 | ==, != | Vergleichsoperatoren: Gleich, Ungleich |
| 9 | <, <=, >, >= | Vergleichsoperatoren |
| 10 | <<, >>, >>> | Schiebeoperatoren |
| 11 | +, - | Addition, Subtraktion, Verketten von Zeichenketten |
| 12 | *, /, % | Multiplikation, Division, Rest |
| 13 | ++, --, +, -, ~, ! | unäre (einstellige) Operatoren |
Es kann vorkommen, dass ein Ausdruck mehrere Operatoren der gleichen Priorität besitzt. In diesen Fällen wird die Auswertereihenreihenfolge durch die Assoziativität der Operatoren bestimmt.
| Operatorenasoziativität |
|---|
| Die Assoziativität von Operatoren ist die Reihenfolge in der Operanden durch Operatoren gleicher Priorität verknüpft werden |
Ist ein Operator linksassoziativ, wird zuerst der linke Operand ausgewertet. Das Beispiel zeigt den Plus- und Minusoperator. Beide haben die gleiche Priorität. Hier wird zuerst der Operand a+b ausgewertet.

Einige Operatoren in Java sind rechtsassoziativ. Ein Beispiel hierfür ist der Zuweisungsoperator
| Bewertungsreihenfolge der Operanden |
|---|
| In Java werden die Operanden eines Operators strikt von links nach rechts ausgewertet. |
Diese Regel ist insbesondere wichtig, da Methoden und diverse Operatoren Nebeneffekte haben können. Das bedeutet, dass diese Operatoren den Wert von Variablen während der Auswertung des Gesamtausdrucks verändern. Beispiele sind die Inkrement- und Dekrementoperatoren.
j = i-- -i;
ist ein zulässiger Ausdruck in Java. Der Wert der j zugewiesen wird ist immer 1;
Die Auswertung dieser Zuweisung geschieht in den folgenden Schritten:
Ein Beispielprogramm zum Testen:
public class PrePostInkrement {
public static void main(String[] args) {
int i = 4;
int j;
j=i-- -i;
System.out.println("i: " +i+", j= "+j);
}
}
Ausgabe:
i: 3, j= 1
Die Auswertung des Ausdrucks und der Zuweisung j= i-- -i; findet wie folgt statt:
| i | j | j= i-- -i; | Kommentar |
|---|---|---|---|
| 4 | 0 | j = 4 - i; | Bestimmung des Minuend der Subtraktion |
| 3 | 0 | j = 4 - i; | Postdekrement von i |
| 3 | 0 | j = 4 -3; | Bestimmung des Subtrahend der Subtraktion |
| 3 | 0 | j = 1; | Bestimmung der Differenz |
| 3 | 1 | Zuweisung |
|
(Lizenz) |
2.4.1 Übung: Zuweisungen und TypenErstellen Sie folgende Java-Applikation (Name "Calc"):
|
maxvalue maxValue max_value max value end End 10%ofSum sum10 _10PercentOfSum
Warum übersetzt das folgende Programm nicht?
package block2;
public class Literale {
public static void main(String[] args) {
long i1 = 4000000000;
long i2 = 4000000000L;
System.out.println(i1);
System.out.println(i2);
}
}
Gegeben seien folgende Variablendeklarationen in Java:
long a = 3; int b = 4; short c = 5; byte d = 6;
Welchen Wert liefern die folgenden Ausdrücke und von welchem Typ sind sie?
d / b * a c + b * (d + 1) d / (c - 1) * b / 2 d % b -c % b
long a = 3; int b = 4; short c = 5; byte d = 6;
a = b + 3 * (d + 1); b = c * c; c = b / 3; d = (byte)a + b; d = (byte) ( a + b);
Schreiben Sie einen Java-Ausdruck, der eine ganze Zahl x auf das nächstliegende Vielfache von 100 rundet.
Beispiel:
y = a * x3 + b * x2 + c * x + d
Schreiben Sie ein Java-Programm, das die x- und y-Koordinaten zweier Punkte einliest und den Abstand zwischen ihnen berechnet und ausgibt.
Tipp: Math.sqrt() berechnet eine Wurzel
Die Kreiszahl ∏ kann durch folgende Näherungsformel berechnet werden:
i
∏ n (-1) 1 1 1 1
--- = ∑ ------- = 1 - --- + --- - --- + --- - ...
4 i=0 2*i+1 3 5 7 9
Implementieren sie notwendigen Berechnungen zum Anzeigen von Sekunden-, Minuten, Stundenzeigern einer analogen Uhr.
Implementieren Sie die korrekte Position der Spitze der drei Zeiger abhängig von der aktuellen Zeit in der Klasse Zeiger.java
Die korrekte Spitze wird durch eine x und y Koordinate beschrieben. Der Ursprung des Koordinatensystems liegt in der Mitte der Uhr. Postive X-Koordinaten reichen nach rechts. Positive Y-Koordinaten reichen nach unten. Die Richtung der Y-Koordinaten ist typisch für für Java GUIs in Swing oder AWT.
Hinweis: Implementieren Sie zuerst den Sekundenzeiger. Er erlaubt ihnen binnen 60 Sekunden die visuelle Kontrolle Ihrer Programmierung. Die Programmierung der Minutenzeiger und Stundenzeiger sind dann sehr einfach, da sie auf der gleichen mathematischen Formel basieren.
Empfohlendes Vorgehen:
Beispiele der Ausgangssituation und der korrekten Implementierung:
| Ausgangssituation | Ziel der Übung |
|---|---|
![]() |
![]() |
/*
* Zeichnen einer analogen Uhr in einem JFrame
*/import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;/**
*
* @author sschneid
*/
public class Uhr extends JPanel {static int sekunde = 0;
static int minute = 0;
static int stunde = 0;
static String tzString; // aktuelle Zeitzone
/**
* Hauptprogramm der Anwendung. Es werden keine Eingabeparameter benötigt
* @param args dieser Parameter wird nicht ausgewertet
*/
public static void main(String[] args) {
JFrame hf; // Das Fenster der Anwendung
hf = new JFrame("Uhr");
// Beenden der Anwendung bei Schliesen des Fenster
hf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Uhr dieUhr = new Uhr();
// Aufbau des Contentpanes
Container myPane = hf.getContentPane();
myPane.add(dieUhr, BorderLayout.CENTER);
// Erzeuge einen Menüeintrag zum Beenden des Programms
JMenuBar jmb = new JMenuBar();
JMenu jm = new JMenu("Datei");
JMenuItem exitItem = new JMenuItem("Beenden");
exitItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
jm.add(exitItem);
jmb.add(jm);
hf.setJMenuBar(jmb);
hf.pack();
// Das JFrame sichtbar machen
// Gewünschte Größe setzen
// 1. Parameter: horizontale Größe in Pixel
// 2. Parameter: vertikale Größe in Pixel
hf.setSize(2 * Zeiger.maxRadius + 80, 2 * Zeiger.maxRadius + 80);
hf.setVisible(true);
hf.setAlwaysOnTop(true);
// Update von Panel in zyklischen Intervallen
try {
while (true) {
Thread.sleep(500); // Schlafe x Millisekunden
// Hole Systemzeit und belege statische Variablen
Calendar call = Calendar.getInstance();
tzString = call.getTimeZone().getDisplayName();
sekunde = call.get(Calendar.SECOND);
minute = call.get(Calendar.MINUTE);
stunde = call.get(Calendar.HOUR);
dieUhr.repaint();
}
} catch (InterruptedException e) {
System.out.println(
"Die Anwendung wird wegen einer Ausnahme beendet");
}}
/**
* Überladene Paintmethode. Sie führt alle Zeichenoperationen im Panel aus
* @param g vom Laufzeitsystem übergebenes Graphikobjekt.
*/
@Override
public void paint(Graphics g) {
super.paint(g);
int xCenter = Zeiger.maxRadius + 40;
int yCenter = Zeiger.maxRadius + 20;
int maxRadius = Zeiger.maxRadius;
int charCenterOffSet = 5;
String timeString = stunde + ":" + minute + ":" + sekunde
+ " " + tzString;
// Zeichne Uhrenhintergrung und Koordinatensystem
g.setColor(Color.BLACK); // Farbe
g.drawArc(xCenter - 5, yCenter - 5, 10, 10, 0, 360);
g.drawLine(xCenter, yCenter, xCenter + 40, yCenter);
g.drawLine(xCenter, yCenter, xCenter, yCenter + 40);
g.drawString("X", xCenter + 45, yCenter + +charCenterOffSet);
g.drawString("Y", xCenter - charCenterOffSet, yCenter + 55);
g.drawString("12", xCenter - charCenterOffSet, yCenter - maxRadius);
g.drawString("3", xCenter + maxRadius, yCenter + charCenterOffSet);
g.drawString("6", xCenter, yCenter + maxRadius);
g.drawString("9", xCenter - maxRadius - charCenterOffSet,
yCenter + charCenterOffSet);
// Zeichne aktuelle Zeit zum Debuggen
g.drawString(timeString, 0, yCenter + maxRadius + 10);
// Zeichne Stundenzeiger
g.setColor(Color.BLACK);
g.drawLine(xCenter, yCenter,
xCenter + Zeiger.stundeX(stunde),
yCenter + Zeiger.stundeY(stunde));
g.drawString("h["
+ Zeiger.stundeX(stunde)
+ "," + Zeiger.stundeY(stunde) + "]",
0, yCenter + maxRadius - 40);
// Zeichne Minutenzeiger
g.setColor(Color.RED);
g.drawLine(xCenter, yCenter,
xCenter + Zeiger.minuteX(minute),
yCenter + Zeiger.minuteY(minute));
g.drawString("m["
+ Zeiger.minuteX(minute) + ","
+ Zeiger.minuteY(minute) + "]", 0, yCenter + maxRadius - 25);
// Zeichne Sekundenzeiger
g.setColor(Color.BLUE);
g.drawLine(xCenter, yCenter,
xCenter + Zeiger.sekundeX(sekunde),
yCenter + Zeiger.sekundeY(sekunde));
g.drawString("s["
+ Zeiger.sekundeX(sekunde) + ","
+ Zeiger.sekundeY(sekunde) + "]", 0, yCenter + maxRadius - 10);
}
}
Implementieren sie
import static java.lang.Math.*;/**
*
* @author sschneid
*/
public class Zeiger {
public static final int maxRadius = 100; // Beeinflusst GUI-Größe !/**
* @param int Sekunden der aktuellen Zeit. Ein Wert zwischen 0 und 59
* @return int aktuelle X Koordinate der Zeigerspitze des Sekundenzeigers
* auf dem Bildschirm
*/
public static int sekundeX(int s) {
int xs;
/* Implementierung Beginn */
xs = s; // Proformazuweisung. Sie ist zu ersetzen
/* Implementierung Ende */
return xs;
}
/**
* @param int Sekunden der aktuellen Zeit. Ein Wert zwischen 0 und 59
* @return int aktuelle Y Koordinate der Zeigerspitze des Sekundenzeigers
* auf dem Bildschirm
*/
public static int sekundeY(int s) {
int ys;
/* Implementierung Beginn */
ys = s; // Proformazuweisung. Sie ist zu ersetzen
/* Implementierung Ende */
return ys;
}
/**
* @param int Minuten der aktuellen Zeit. Ein Wert zwischen 0 und 59
* @return int aktuelle X Koordinate der Zeigerspitze des Minutenzeigers
* auf dem Bildschirm
*/
public static int minuteX(int m) {
int xm;
/* Implementierung Beginn */
xm = m; // Proformazuweisung. Sie ist zu ersetzen
/* Implementierung Ende */
return xm;
}
/**
* @param int Minuten der aktuellen Zeit. Ein Wert zwischen 0 und 59
* @return int aktuelle Y Koordinate der Zeigerspitze des Minutenzeigers
* auf dem Bildschirm
*/
public static int minuteY(int m) {
int ym;
/* Implementierung Beginn */
ym = m; // Proformazuweisung. Sie ist zu ersetzen
/* Implementierung Ende */
return ym;
}
/**
* @param int Stunden der aktuellen Zeit. Ein Wert zwischen 0 und 12
* @return int aktuelle X Koordinate der Zeigerspitze des Stundenzeigers
* auf dem Bildschirm
*/
public static int stundeX(int h) {
int xs;
/* Implementierung Beginn */
xs = h; // Proformazuweisung. Sie ist zu ersetzen
/* Implementierung Ende */
return xs;
}
/**
* @param int Stunden der aktuellen Zeit. Ein Wert zwischen 0 und 12
* @return int aktuelle Y Koordinate der Zeigerspitze des Stundenzeigers
* auf dem Bildschirm
*/
public static int stundeY(int h) {
int ys;
/* Implementierung Beginn */
ys = h; // Proformazuweisung. Sie ist zu ersetzen
/* Implementierung Ende */
return ys;
}
}
class ArithmeticTest {
public static void main (String args[]) {
short x = 6;
int y = 4;
float a = 12.5f;
float b = 7f;
System.out.println("x is " + x + ", y is " + y);
System.out.println("x + y = " + (x + y));
System.out.println("x - y = " + (x - y));
/* ??? */
System.out.println("x % y = " + (x % y));
System.out.println("a is " + a + ", b is " + b);
/* ??? */
}
}siehe Spezifikation Java 7 (Identifier)
public class VariableNames {
int maxvalue;
int maxValue;
int max_value;
//int max value;
int end;
int End;
//int 10%ofSum;
int sum10;
int _10PercentOfSum;
public static void main(String[] args) {
}
}Das Programm übersetzt nicht weil
public class Expressions {
public static void main(String[] args) {
long a = 3;
int b = 4;
short c = 5;
byte d = 6;
int result1;
long result2;
result2 = d / b * a;
System.out.println("d / b * a = " + result2 );
result1 = c + b * (d + 1);
System.out.println("c + b * (d + 1) = " + result1 );
result1 = d / (c - 1) * b / 2;
System.out.println("d / (c - 1) * b / 2 = " + result1 );
result1 = d % b;
System.out.println("d % b = " + result1 );
result1 = -c % b;
System.out.println("-c % b = " + result1 );
}
}
d / b * a = 3 c + b * (d + 1) = 33 d / (c - 1) * b / 2 = 2 d % b = 2 -c % b = -1
public class AllowedAssigments {
public static void main(String[] args) {
long a = 3;
int b = 4;
short c = 5;
byte d = 6;
a = b + 3 * (d + 1);
b = c * c;
c = (byte)(b / 3);
d = (byte)(((byte)a + b));
d = (byte) ( a + b);
}
}
public class Runden {
public static void main(String[] args) {
int a, result;
a =149;
result = (a +50)/100*100;
System.out.println("Eingabe = " + a + " Ergebnis = " + result);
a =150;
result = (a +50)/100*100;
System.out.println("Eingabe = " + a + " Ergebnis = " + result);
}
}
Eingabe = 149 Ergebnis = 100 Eingabe = 150 Ergebnis = 200
public class timeCalculation {
public static void main(String[] args) {
int a,h,m,s;
a = 0;
h = a/3600;
m = (a-h*3600)/60;
s = a - h*3600 - m*60;
System.out.println("Sekunden = " + a + " = " + h + ":" + m + ":" +s);
a = 59;
h = a/3600;
m = (a-h*3600)/60;
s = a - h*3600 - m*60;
System.out.println("Sekunden = " + a + " = " + h + ":" + m + ":" +s);
a = 60;
h = a/3600;
m = (a-h*3600)/60;
s = a - h*3600 - m*60;
System.out.println("Sekunden = " + a + " = " + h + ":" + m + ":" +s);
a = 100;
h = a/3600;
m = (a-h*3600)/60;
s = a - h*3600 - m*60;
System.out.println("Sekunden = " + a + " = " + h + ":" + m + ":" +s);
a = 3600;
h = a/3600;
m = (a-h*3600)/60;
s = a - h*3600 - m*60;
System.out.println("Sekunden = " + a + " = " + h + ":" + m + ":" +s);
a = 4000;
h = a/3600;
m = (a-h*3600)/60;
s = a - h*3600 - m*60;
System.out.println("Sekunden = " + a + " = " + h + ":" + m + ":" +s);
}
}Sekunden = 0 = 0:0:0 Sekunden = 59 = 0:0:59 Sekunden = 60 = 0:1:0 Sekunden = 100 = 0:1:40 Sekunden = 3600 = 1:0:0 Sekunden = 4000 = 1:6:40
public class Polynom {
public static void main(String[] args) {
double a, b, c, d, x, y;
a = 1.2;
b = -2.3;
c = 4.5;
d = -6.7;
x = 8.9;
y = a*x*x*x+b*x*x+c*x+d;
System.out.print("Ergebnis = "+ y);
System.out.println("\n oder...");
y = a*Math.pow(x,3)+b*Math.pow(x,2)+c*x+d;
System.out.print("Ergebnis = "+ y);
}
}Ergebnis = 697.1298 oder... Ergebnis = 697.1297999999999
public class Main {
public static void main(String[] args) {
double x1,x2, y1, y2, x, y, d;
x1 = 1; y1 = 1;
x2 = 4; y2 = 5;
x = x1-x2;
y = y1-y2;
d = Math.sqrt(x*x+y*y);
System.out.println("distance = " + d);
}
}distance = 5.0
Hilfe; Java Spezifikation: Division
public class PiCalculation {
public static void main(String[] args) {
double pi;
pi = (1.0 - 1.0/3.0 + 1.0/5.0 - 1.0/7.0 + 1.0/9.0 - 1.0/11.0 + 1.0/13.0)*4;
System.out.println("Pi = "+ pi);
System.out.println("\n oder ...");
pi=1;
for (int i=1;i<=10000;i++) {
pi = pi + Math.pow(-1,i)/(2*i+1);
}
pi=pi*4;
System.out.println("Pi = "+ pi);
}Pi = 3.2837384837384844 oder ... Pi = 3.1416926435905346
import static java.lang.Math.*;
/**
*
* @author sschneid
*/
public class Zeiger {
public static final int maxRadius = 100; // Beeinflusst GUI-Größe !
/**
* @param int Sekunden der aktuellen Zeit. Ein Wert zwischen 0 und 59
* @return int aktuelle X Koordinate der Zeigerspitze des Sekundenzeigers
* auf dem Bildschirm
*/
public static int sekundeX(int s) {
int xs;
/* Implementierung Beginn */
double sx = sin(s*2*PI/60)*maxRadius;
xs = (int)sx;
/* Implementierung Ende */
return xs;
}
/**
* @param int Sekunden der aktuellen Zeit. Ein Wert zwischen 0 und 59
* @return int aktuelle Y Koordinate der Zeigerspitze des Sekundenzeigers
* auf dem Bildschirm
*/
public static int sekundeY(int s) {
int ys;
/* Implementierung Beginn */
double sy = -cos(s*2*PI/60)*maxRadius;
ys = (int)sy;
/* Implementierung Ende */
return ys;
}
/**
* @param int Minuten der aktuellen Zeit. Ein Wert zwischen 0 und 59
* @return int aktuelle X Koordinate der Zeigerspitze des Minutenzeigers
* auf dem Bildschirm
*/
public static int minuteX(int m) {
int xm;
/* Implementierung Beginn */
double mx = sin(m*2*PI/60)*maxRadius*0.75;
xm = (int)mx;
/* Implementierung Ende */
return xm;
}
/**
* @param int Minuten der aktuellen Zeit. Ein Wert zwischen 0 und 59
* @return int aktuelle Y Koordinate der Zeigerspitze des Minutenzeigers
* auf dem Bildschirm
*/
public static int minuteY(int m) {
int ym;
/* Implementierung Beginn */
double my = -cos(m*2*PI/60)*maxRadius*0.75;
ym = (int) my;
/* Implementierung Ende */
return ym;
}
/**
* @param int Stunden der aktuellen Zeit. Ein Wert zwischen 0 und 12
* @return int aktuelle X Koordinate der Zeigerspitze des Stundenzeigers
* auf dem Bildschirm
*/
public static int stundeX(int h) {
int xs;
/* Implementierung Beginn */
double sx = sin(h*2*PI/12)*maxRadius*0.5;
xs = (int)sx;
/* Implementierung Ende */
return xs;
}
/**
* @param int Stunden der aktuellen Zeit. Ein Wert zwischen 0 und 12
* @return int aktuelle Y Koordinate der Zeigerspitze des Stundenzeigers
* auf dem Bildschirm
*/
public static int stundeY(int h) {
int ys;
/* Implementierung Beginn */
double sy = -cos(h*2*PI/12)*maxRadius*0.5;
ys = (int)sy;
/* Implementierung Ende */
return ys;
}
}
Ein graphisch animierter Webcast der die genauen Vorgänge des folgenden Beispielprogramms erläutert:
public class Mitarbeiter {
...
public static void main (String[] args) {
int a;
long b;
String eingabe;
eingabe = args[0];
a = 15;
a++;
b = 2*a;
System.out.println("Ausgabe :" + eingabe + b);
}
}
Das folgende Video dauert 8 Minuten.
Wichtige Aspekte:
|
(Lizenz) |
2.7.1 GanzzahlarithmetikGanzzahloperationen haben eine Reihe von Eigenheiten. Es wird auf eine Variable a eine Variable b zugewiesen die um 25% größer sein soll. Hierfür gibt es drei Möglichkeiten. Im ersten Fall sei a klein (=6). Welches ist die beste Implementierung? Welche Operationen sind heikel? |
| Implementierung | Ergebnis | |
|---|---|---|
|
. |
int a = 0; int b = 6; a = b*5/4; a = b/4*5; a = b*(5/4); |
Was ist die beste Implementierung wenn man weiß das b groß ist? Z.Bsp. b= 230? Welche Operation ist heikel?
Welche der folgenden Zuweisungen sind sicher b.z.w unsicher?
Eine Zuweisung sei unsicher wenn bei der Zuweisung abhängig von der gewählten Variablenbelegung Datenverluster auftreten können.
Analysieren Sie die sechs Zuweisungen auf potentielle Datenverluste. Die Werte von a_short, b_int, c_long wurden absichtlich am oberen Ende des jeweiligen Wertebereichs gewählt um den Präzisionsverlust zu provozieren.
Hinweis: Vergleichen Sie die Wertebereiche der Ausgangs und Zielvariablen.
Ignorieren Sie die Auswertelogik. Das Programm ist ausführbar. Es wird die Belegungen der einzelnen Variablen auf der Konsole drucken. Es wird bei jedem Fall von Präzisionsverlust einen Text "(Fehler)" hinter die Ausgabe anfügen.
public class UebungTypkonversion {public static void main(String[] args) {
// Alle Werte sind der drittgrößte Wert des jeweiligen Wertebereichs
short a_short = Short.MAX_VALUE-2; // short = 16 bit
int b_int = Integer.MAX_VALUE-2; // int = 32 bit
long c_long = Long.MAX_VALUE-2; // long = 64 bit
// cast auf float: Welche Zuweisungen können zu Präzisionsverlusten führen?
float c_float = a_short;
float d_float = b_int;
float e_float = c_long;
// cast auf double:Welche Zuweisungen können zu Präzisionsverlusten führen?
double f_double = a_short;
double g_double = b_int;
double h_double = c_long;
// Auswertelogik und Kontrolle
System.out.println("a_short=" + a_short);
System.out.println("a_int=" + b_int);
System.out.println("a_long=" + c_long);
System.out.print("short auf float= " + c_float + " ");
// Sind die Werte nach der Rückkonvertierung noch gleich?
if (a_short == (short)c_float) {System.out.println("(OK)");}
else {System.out.println("(Fehler)");}
System.out.print("int auf float= " + d_float);
if (b_int == (int)d_float) {System.out.println("(OK)");}
else {System.out.println("(Fehler)");}
System.out.print("long auf float= " + e_float);
if (c_long == (long)e_float) {System.out.println("(OK)");}
else { System.out.println("(Fehler)");}
System.out.print("short auf double= " + f_double + " ");
if (a_short == (short)f_double) {System.out.println("(OK)");}
else {System.out.println("(Fehler)");}
System.out.print("int auf double= " + g_double);
if (b_int == (int)g_double) {System.out.println("(OK)");}
else {System.out.println("(Fehler)");}
System.out.print("long auf double= " + h_double);
if (c_long == (long)e_float) {System.out.println("(OK)");}
else { System.out.println("(Fehler)");}
}
}
Bei Ganzzahldivisionen werden alle Ergebnisse abgerundet.
Option a.) ist hier die optimale Lösung da der Rundungsfehler relativ gesehen am kleinsten ist wenn man zuerst multipliziert um dann eine möglichst große Zahl zu teilen.
Option c.) ist schlecht, da da 5/4 schon innerhalb der Klammer auf 1 abgerundet wird.
Option b.) ist ebenfalls schlecht da 6/4 ebenfalls auf 1 abgerundet wird
| Implementierung | Ergebnis | |
|---|---|---|
|
. |
int a = 0; int b = 6; a = b*5/4; a = b/4*5; a = b*(5/4); |
. . a=7 a=5 a=6 |
Die Variable b ist sehr groß b= 230:
Konsolenausgaben:
a_short=32765
a_int=2147483645
a_long=9223372036854775805
short auf float= 32765.0 (OK)
int auf float= 2.14748365E9(Fehler)
long auf float= 9.223372E18(Fehler)
short auf double= 32765.0 (OK)
int auf double= 2.147483645E9(OK)
long auf double= 9.223372036854776E18(Fehler)
Die Mantisse des Typ float ist nicht groß genug um extrem große (oder kleine) Werte des Typs int und long präzise zu verwalten.
Die Mantisse des Typ double ist nicht groß genug, um extrem große (oder kleine) Werte des Typs long präzise zu verwalten.
|
(Lizenz) |
Am Ende dieses Blocks können Sie:
|
Sie sind in der Lage die folgenden Fragen zu beantworten:
(Nur das Markieren der Schlüsselwörter ist für diesen Abschnitt relevant)
Durch Verzweigungen können im Ablauffluß des Programmes verschiedene Fälle geprüft werden und man kann abhängig davon unterschiedliche Anweisungen ausführen.
Java erlaubt das Zusammenfassen von Javabefehlen durch Blöcke. Blöcke werden geschweiften Klammern beschrieben.
Für Blöcke gilt
{
int i = 1;
i++;
int j = i*2;
}Tipp: Systematisches Einrücken und Klammern die genau übereinander stehen helfen sehr bei der Lesbarkeit
Eine Einfachverzweigung wird in Java mit dem Schlüsselwort if eingeleitet. Anschließend folgt eine Bedingung in runden Klammern. Ist die Bedingung erfüllt, wird die folgende Anweisung bzw. der Anweisungsblock in geschweiften Klammern ausgeführt. Hierdurch ergibt sich die folgende Syntax:
if (Bedingung) Anweisung;
oder
if (Bedingung) {Anweisung(en);}Ist die Bedingung nicht erfüllt wird die Anweisung ausgelassen und der normale Ausführungsfluß wird wieder aufgenommen. Ein Beispiel hierfür ist:
int x = 8;
if (x > 5) System.out.println("Wert x = " + x + " ist größer als 5");
System.out.println ("Wert x = " + x);Hier ergeben sich die Ausgaben:
Wert x = 8 ist größer als 5 Wert x = 8
da die bedingte Anweisung sowie die letzte Anweisung durchlaufen wird.
Es gibt auch die Möglichkeit eines entweder-oder Kontrollflusses mit Hilfe des Schlüsselworts else:
if (Bedingung) {Anweisung(en);}
else {Anweisung(en);};Im Fall der if Bedingung ohne else Klausel kann man die geschweiften Klammer mit einem Anweisungsblock weglassen und durch eine einzelne Anweisung ersetzen.
int x = 8;
int y;
if (x > 5) {
System.out.println("Wert x = " + x + " ist größer als 5");
y = x;
}
else {
System.out.println("Wert x = " + x + " ist kleiner oder gleich 5");
y = -x;
}
System.out.println ("Wert x = " + x ");
if Anweisungen lassen sich schachteln um komplexe Bedingungen prüfen zu können. Anbei ein Beispiel:
int a = 5;
int b = 10;
int maximum =b0;
if (a > b) // Äussere if Bedingung
if (a > 0)
{ maximum = a;} // Innere if Bedingung
else
{ maximum = 0; } //innere else Bedingung
System.out.println ("Wert maximum = " + maximum ");
|
![]() |
Im oben gezeigten Beispiel gibt es nur eine else Bedingung von der nicht unbedingt klar ist zu welchem if Block sie gehört.
| else-Regel |
|---|
| Ein else-Block gehört immer zum direkt vorhergehenden if-Block |
Tipp: Der Quellcode wird bei geschachtelten if Bedingungen schnell unübersichtlich und fehleranfällig.
Verwenden Sie besser die im Folgenden vorgestellte switch Bedingung um komplexe Abfragen zu implementieren.
Eine Alternative zu if-else Anweisungen ist die switch-case Anweisung. Sie erlaubt eine sehr viel übersichtlichere Programmierung wenn es bei einem ganzzahligen Ausdruck mehr als zwei Alternativen gibt.
Im einfachen Fall sieht eine switch-case Anwendung wie folgt aus:
int Wochentag;
...
switch (Wochentag) {
case 7:
System.out.println("Sonntag");
break;
case 6:
System.out.println("Samstag");
break;
case 5: case 4: case 3: case 2: case 1:
System.out.println("Wochentag");
break;
default:
System.out.println("Fehler");
} |
![]() |
int Wochentag;
...
switch (Wochentag) {
case 7:
System.out.println("Sonntag");
break;
case 6:
System.out.println("Samstag");
break;
case 5: case 4: case 3: case 2: case 1:
System.out.println("Wochentag");
break;
default:
System.out.println("Kein gültiger Wochentag!");
}Allgemeine Form:
int Wochentag;
...
switch (Ausdruck) {
case konstante1:
Anweisung(en);
break;
...
case konstante2: case konstante3: ... case konstanteN:
Anweisung(en);
break;
default:
Anweisung(en);
break;
}
int Wochentag;
...
switch (Wochentag) {
case 7:
System.out.println("Sonntag");
break;
case 6:
System.out.println("Samstag");
case 5: case 4: case 3: case 2: case 1:
System.out.println("Wochentag");
break;
default:
System.out.println("Fehler");
} |
![]() |
Schleifen erlauben die Wiederholungen von Einzelanweisung. Schleifen bestehen typischerweise aus den folgenden Komponenten
Die while Schleife überprüft vor dem Schleifeneintritt ob die Ausführungsbedingung (noch) erfüllt ist. Sie enthält die Schleifenbedingung im Kopf der Schleife.
Die Syntax der while Schleife ist die folgende:
while ( Bedingung) { Anweisung(en)} Beispiel: Arithmetische Summe 1+2+3+4+5
int i = 1;
int summe= 0;
while (i<=5) {
summe = summe + i;
i++;
}
|
|
|
Die do-while-Schleife überprüft nach dem ersten Schleifendurchlauf ob die Ausführungsbedingung erfüllt ist. Sie enthält die Schleifenbedingung im Fuß der Schleife. Die Syntax der do-while Schleife ist die folgende: do { Anweisung(en)} while ( Bedingung);Beispiel: Beende Schleife wenn Wert durch 3 ohne Rest teilt
int i = 9;
int j = 0; // Zählt Schleifendurchläufe
do {
j++;
i--;
} while (i%3 != 0);Die Schleife wird dreimal durchlaufen. |
![]() |
Regel:
Diese Unterscheidung ist in verschiedenen Bereichen wichtig
Die for-Schleife überprüft die Schleifenbedingung vor dem Eintritt in die Schleife.
Ihre Syntax ist die anspruchscollste der 3 Schleifenarten:
for (Initialiserung; Bedingung; Veränderung) {Anweisung(en)}Der Kopf der for-Schleife besteht aus den folgenden drei Teilen:
Im folgenden Beispiel wird die for-Schleife "b mal" durchlaufen. Durch das Aufaddieren der Variablen a wird eine Multiplikation von positiven Zahlen ausgeführt.
Ist b gleich Null oder negativ wird die Schleife nicht durchlaufen.

Oft ist es notwendig Schleifen zu schachteln. Dies kommt oft bei mehrdimensionalen Datenstrukturen vor bei denen jedes Feld bearbeitet werden muss. Bei jedem Durchlauf der äusseren Schleife wird die innere aufgerufen, die ihre eigenen Durchläufe komplett bei jedem Durchlauf der äusseren Schleife durchführt. Anbei ein Beispiel einer naiven Multiplikation die auf Inkrementieren beruht:
class MultiplizierenNaiv {
public static void main(String[] args) {
int a = 5;
int b = 10;
int result = 0;
for (int i = 1; i <= a; i++) {
System.out.println("Äussere Schleife i = " + i);
for (int j = 1; j <= b; j++) {
System.out.println("Innere Schleife j = " + j);
result++;
}
}
System.out.println(a +"*"+b+" = "+result);
}
}
Geschachtelte Schleifen sind sehr mächtig und sie haben ein viel größeres Potential Rechnerleistung zu binden. Die Aufwände bei zwei geschachtelten Schleifen können quadratisch steigen im Vergleich zum linearen Aufwand einer einfachen Schleife. Generell gilt, dass eine teure Anweisung in einer Schleife n-mal statt einmal durchlaufen werden kann. Es ist daher wichtig auf die folgenden Dinge zu achten:
Java verfügt über zwei Möglichkeiten Schleifen in der Mitte von Ausführungsblöcken zu verlassen. Diese Programmiertechnik sollte man normalerweise vermeiden.
In manchen Fällen ist sie jedoch nützlich. Schleifenabbrüche werden durch die beiden folgenden Schlüsselwörter gesteuert:
Die break Anweisung ist schon vom switch Befehl her bekannt. Mit ihr kann man auch eine if Bedingung beenden. Beispiel einer continue Anweisung:
...
for (int i=0; i<= 100; i++) {
if (i%2 == 0) continue;
System.out.println("Die Zahl " + i + " ist ungerade");
}Bei geraden Zahlen wird die Druckanweisung nach der if Anweisung nicht mehr ausgeführt. Die Schleifen werden jedoch alle durchlaufen. |
![]() |
Beispiel einer break-Anweisung:
...
for (int i=1; i<= 100; i++) {
if (i== 32) break;Die Zahlen von 1 bis 31 werden ausgedruckt. Anschließend wird die Schleife abgebrochen. Die break und continue Anweisungen beziehen sich immer auf die nächst äussere Schleife. Mit Hilfe von Labels (Marken) kann man auch über mehrere geschachtelte Schleifen nach aussen springen. |
![]() |
Java erlaubt das Benennen von Blöcken mit Hilfe von "Labeln" (Marken). Mit ihnen kann man mit einer break-Anweisung beliebig viele Blöcke auf einmal verlassen.
Das folgende Beispiel zeigt wie man zum Label "Label1" springt um beide Schleifen auf einmal zu verlassen:
class MultiplizierenNaivLabel {
public static void main(String[] args) {
int a = 5;
int b = 10;
int result = 0;
Label1: for (int i = 1; i <= a; i++) {
System.out.println("Äussere Schleife i = " + i);
for (int j = 1; j <= b; j++) {
System.out.println("Innere Schleife j = " + j);
if ((i == 3) && (j == 3)) break Label1;
result++;
}
}
System.out.println(a +"*"+b+" = "+result);
System.out.println("Dieses Ergebnis ist falsch...");
}
}
|
Endlosenschleifen sind Schleifen die nicht terminieren. Ein Programm mit einer Endlosschleife sieht aus wie ein "hängendes" Programm. Es bindet jedoch sehr wahrscheinlich (mindestens) einen physikalischen Prozessor vollständig! Anbei einige Beispiele mehr oder weniger leicht zu erkennende Endloschleifen: while ( true) { /* Anweisung(en) */ }
...
for ( ; ; ) { /* Anweisung(en) */ }
...
int i=10;
while (i>5) i++;
...
int i=0;
while ( i!=99) {i++; i++;}
Der Übersetzer erkennt diverse einfache Endlosschleifen und meldet die darauf folgende Codezeile als "unreachable code". |
Trivia: Welcher Obsthändler hat die Adresse 1 Infinite Loop, Cupertino, CA 95014? |
Die folgende Kurzüberblick von Feldern ist notwendig um die Übungen zu den Kontrollstrukturen zu lösen. Felder werden später noch einmal aufgegriffen und genauer behandelt.
Felder sind Datenbehälter mit einer oder mehreren Dimensionen um eine Anzahl primitive Datentypen gleichen Typs aufzunehmen.
Man kann auf die einzelnen Feldelemente wahlfrei, also direkt mit Hilfe eines Index zugreifen. Java benutzt rechteckige Klammern [] um auf einzelne Elemente eines Feldes zuzugreifen.
Felder sind im Gegensatz zu Variablen mit primitiven Datentyp Objekte. Dies bedeutet, dass die die Variable nur aus einer Referenz besteht. Das Feld selbst muss dynamisch angelegt werden. Hierfür gibt es eine Reihe von Gründen.
Die Deklaration von Feldern geschieht wie folgt:
int[] x; // Anlegen einer Referenz, es ist noch keine Größe vorgeben,
// es können noch keine Feldelemente benutzt werden<
double[] y; // Anlegen einer Referenz, es ist noch keine Größe vorgeben,
//es können noch keine Feldelemente benutz werdenUm das eigentliche Feld anzulegen muss der new Operator verwendet werden wie für reguläre Objekte auch um den Speicher zu allozieren. Dies geschieht wie folgt:
int [] x; x = new int[4]; double[] y; y = new double[200];
Jetzt zeigt x auf 4 Datenbehälter für Ganzzahlen. Der Zugriff auf die vier Elemente erfolgt über einen Index im Bereich von 0 (erster Wert) bis 3.
Das Feld kann nun wie folgt belegt und bearbeitet werden:
int [] x = new int[4];
x[0] = 99; x[1] = 88; x[2] = x[0]+x[1]; x[3] = x[0]*x[1];
...geschieht mit der Methode .length(). Hier ein Beispiel:
int[] x; int groesse; ... groesse=x.length;
Seit Java 5 ist es möglich mit der for Schleife Felder komfortabler abzuarbeiten. Man kann mit einer Laufvariable alle Elemente des gegeben Feldes abarbeiten. Wie zum Beispiel:
int [] xxx = { 11, 22, 33, 44, 55};
for ( int position : xxx) // for each i in xxx[]
System.out.println("Wert: " + position);Wird das folgende Ergebnis liefern:
Wert: 11 Wert: 22 Wert: 33 Wert: 44 Wert: 55
|
(Lizenz) |
3.3.1 Übung: SchleifenterminierungWelche der folgenden Schleifen terminieren?
|
int i = 1, j = 1;
do {
i = i + j;
j++;
} while (i < 200);
int i = 1, j = 20;
while (i + j < i) {
i = i + 2;
j--;
}
int i = 1, j = 20;
while (i + j > i) {
i = i + 2;
j--;
}
int i = 100, j = 27;
while (i != j) {
i = i / 2;
j = j / 3;
}Einlesen von Zahlenwerten beim Starten von der Kommandozeile als Option wie zum Beispiel:
$ java Main 17 19
erfolgt durch Einlesen von Zeichenketten und Umwandlung in Zahlen. Anbei ein Rahmenprogramm welches das Problem löst:
public class Main {
public static void main(String[] args) {
int firstArg=0;
int stellen=0;
if (args.length > 0) {
try {
firstArg = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
System.out.println("Die Zahl firstArg = " + firstArg + " wurde eingelesen");
// Implementierung gehört hierher
System.out.println(" Die Zahl " + firstArg + " hat " + stellen + " Stellen!");
}
}
Eine andere Möglichkeit besteht darin, dass man das Programm startet und bei Bedarf eine Eingabe von der Konsole anfordert.
Achten Sie hier auf die fettgedruckte Importanweisung der Klasse Scanner. Sie ist notwendig um die Klasse zu benutzen:
import java.util.Scanner;
public class Eingabe {
public static void main(String[] args) {
Scanner eingabe = new Scanner(System.in);
System.out.print("Geben Sie die erste Zahl ein: ");
int zahl1 = eingabe.nextInt();
System.out.print("Geben Sie die zweite Zahl ein: ");
int zahl2 = eingabe.nextInt(); System.out.println("Ergebnis: " + zahl1); System.out.println("Ergebnis: " + zahl2);
} }
Das Programm Eingabe kann man auf der Konsole wie folgt bedienen:
$ java Eingabe Geben Sie die erste Zahl ein: 34
Geben Sie die zweite Zahl ein: 56
Ergebnis: 34
Ergebnis: 56
Schreiben Sie ein Java-Programm, das die Quersumme einer positiven ganzen Zahl berechnet und ausgibt.
Beispiel: Zahl 4711 --> Quersumme 13.
int s = 0;
for(;;) {
int x = System.in.read(); //liest ein Zeichen von der Konsole
if (x < 0) break;
s = s + x;
}
Der Quellcode wird erst ausführbar wenn man einen potentiellen IO Fehler behandelt. Siehe
...
import java.io.IOException;
...
try {
x = System.in.read(); //liest ein Zeichen von der Konsole
} catch (IOException ex) {
System.out.println(" Java IO Exception");
}Einlesen eines Feldes (Arrays) von der Kommandozeile
package block3;
public class Zahlenstatistik {
public static void main(String[] args) {
int feld[];
if (args.length > 0) {
feld = new int[args.length];
try {
for (int i=0;i<args.length;i++) {
feld[i] = Integer.parseInt(args[i]);
// Einlesen der Kommandozeilenargumente und umwandeln in Ganzzahlen
}
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
// Ab hier steht das Feld mit allen Werten zur Verfügung
}
}Option: Optimieren sie Ihr Programm auf die kürzeste Laufzeit. Verwenden Sie hierzu den Nanotimer von JDK 6.0 wie folgt:
package block3;
public class Primzahlzerlegung {
public static void main(String[] args) {
int firstArg = 0;
int p=0;
long time;
time= System.nanoTime();
if (args.length > 0) {
try {
p = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
System.out.println("Eingelesene Zahl: " + p);
}
// Die Eingabe der Kommandozeile liegt als Variable p vor.
// Implementierung ...
time= System.nanoTime() - time;
System.out.println("Zeit in m: "+ time);
}
}
// Annahme: j >= 0
i = 0;
while (i != j) i++;
while (a < b) {
c = a;
a = b;
b = c;
}
Lesen Sie ein Datum in Form dreier Zahlen für den Tag, den Monat und das Jahr sowie eine weitere Zahl zwischen 0 und 6 ein, die den Wochentag (Sonntag bis Samstag) des 1. Januars dieses Jahres darstellt. Berechnen Sie den Wochentag des eingelesenen Datums und geben Sie diesen aus. Sie können davon ausgehen, dass die Eingaben korrekt sind. Berücksichtigen Sie auch Schaltjahre.
Lesen Sie zwei 32 Bit Ganzahlen (int) a und b ein und prüfen Sie, ob bei ihrer Addition ein Überlauf stattfindet, also eine Summe entstehen würde, die größer als 231 - 1 oder kleiner als -231 ist.
Lesen Sie die Endpunkte zweier horizontaler oder vertikaler Linien in Form ihrer x- und y-Koordinaten ein und prüfen Sie, ob sich die beiden Linien schneiden. Die beiden Linien befinden sich in einem zweidimensionalen kartesischen System:

Hinweis: Wenn es einen Schnittpunkt gibt ist er (c,b)
a)
if (b == 0)
a = 2 * c;
else
if (c != 0)
a = a * b + 2 * c;
else
a = a * b;
b)
if (x < 0 && y < 0)
a = x * y;
else
if (x < 0)
a = x * (-y);
else
if (y > 0)
a = (-x) * (-y);
else
a = x * (-y);Schreiben Sie ein Java-Programm, das die Seitenlängen eines Dreiecks einliest und prüft, ob es ein
Ein Dreieck ist ungültig, wenn die Summe zweier Seitenlängen kleiner oder gleich der dritten Seitenlänge ist. Beachten Sie, dass ein Dreieck sowohl rechtwinkelig als auch gleichschenkelig sein kann!
Schreiben Sie ein Java-Programm, das 3 Zahlen a, b und c einliest und sie in sortierter Reihenfolge wieder ausgibt. Im Rahmenprogramm muss die letzte Zeile durch einen Sortieralgorithmus und Ausgaben ersetzt werden.
public class Sortieren {
public static void main(String[] args) {
int a = 0;
int b = 0;
int c = 0;if (args.length > 2 ) {
try {
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[1]);
c = Integer.parseInt(args[2]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
System.out.println("Eingelesene Werte: " + a + ", " + b + ", " + c);
System.out.println("Anstatt dieser Zeile muss ein Sortieralgorithmus implementiert werden...";
}
}
Lesen Sie ein Datum in Form dreier Zahlen für den Tag, den Monat und das Jahr ein. Prüfen Sie, ob es sich um ein gültiges Datum handelt. Berücksichtigen Sie auch Schaltjahre.
Hinweis: Gregorianischer Kalender: Ein Jahr ist ein Schaltjahr, wenn es durch 4 teilbar ist. Jahre, die durch 100, aber nicht durch 400 teilbar sind, sind keine Schaltjahre.
Schon im alten Rom verschlüsselte man Nachrichten. Ein einfaches Verschlüsselungsverfahren ist von Julius Cäsar überliefert. Es verschiebt jeden Buchstaben der Nachricht um einen fixen Wert n. Ist n gleich 2, so wird 'A' auf 'C', 'B' auf 'D' und 'Z' auf 'B' verschoben. Ziffern und Sonderzeichen werden nicht verschoben. Schreiben Sie ein Programm, das eine Zahl n und einen beliebigen Text liest und ihn nach obigem Verfahren verschlüsselt wieder ausgibt.
Schreiben Sie ein Java-Programm, das drei Werte x, y und z einliest und prüft, ob
Schreiben Sie ein Programm, das einen Text liest und die Häufigkeit der darinvorkommenden Zeichen berechnet. Geben Sie die Zeichenhäufigkeit als Histogramm aus. Beispiel:
a **** b ** c ******** ...
Hinweis: Dieses Programm setzt Kenntnisse von Feldern vorraus die erst später behandelt werden
Implementieren Sie zwei logische Tests für die Kontrolle der Weckzeit einer Uhr.
Diese Aufgabe baut auf der Übung zur Implementierung einer analogen Uhr auf.
Sie benötigen 3 Klassen für diese Aufgabe die alle im gleichen Verzeichnis stehen müssen:
Es reicht die Klasse Weckeruhr zu übersetzen. Die beiden anderen Klassen werden automatisch rekursiv mitübersetzt.
Nach dem Starten der Anwendung mit java Weckuhr erscheint das folgende Fenster:
|
Uhr im normalen Zustand |
Uhr beim Klingeln (roter pulsierender Kreis) |
Die Klasse Weckzeit enthält eine Methode korrekteWeckzeit(int h, int m, int s). Diese Methode prüft die Eingaben des GUI auf korrekte Wertebereiche. Das Ergebnis wird in einer boolschen Variablen result gespeichert. Die Vorlage liefert bei allen Eingaben false. Belegen Sie die Variable so, dass vernünftige Werte akzeptiert werden.
Fragen:
Der Erfolg Ihrer Implementierung können Sie daran erkennen, dass die Eingaben des GUI nach dem klicken des OK Knopfs in das Anzeigenfeld übernommen worden sind.
Das Lösen der ersten Teilaufgabe ist eine Voraussetzung für den zweiten Teil (Ihr Wecker funktioniert sonst nur Mittags und um Mitternacht...)
Das Hauptprogramm der Klasse WeckerUhr ruft in der aktuellen Implementierung viermal pro Sekunde die Methode klingeln() auf um zu kontrollieren ob der Wecker klingeln soll. Ändern Sie die Referenzimplementierung derart, dass nur nach der eingebenen Weckzeit für eine bestimmte Periode geklingelt wird.
Die zu modifizierende Referenzimplementierung:
result = (aktS%10 == 0);
lässt den Wecker bei allen Sekundenwerten die durch 10 teilbar sind für eine Sekunde klingeln.
Die Variablen aktH, aktM, aktS enthalten die aktuelle Zeit. Die Variablen wzh, wzm, wzs enthalten die Weckzeit.
Fragen:
public class Weckzeit {
int wzh = 0; // Weckzeit in Stunden
int wzm = 0; // Weckzeit in Minuten
int wzs = 0; // Weckzeit in Sekunden
public void setzeWeckzeit(int hh, int mm, int ss) {
if (korrekteWeckzeit(hh,mm,ss)) {
wzh = hh;
wzm = mm;
wzs = ss;
}
}
public boolean korrekteWeckzeit(int h, int m, int s) {
boolean result;
// benutzen die Variablen h,m,s um eine gültige Zeit zu bestimmen
result = false;
return result;
}
public boolean klingeln(int aktH, int aktM, int aktS) {
boolean result;
// benutzen die Variablen der aktuellen Zeit aktH (Stunde),
// aktM (Minute), aktS (Sekunde) und die Weckzeit wzmh, wzm, wzs
// um zu bestimmern ob der Wecker klingeln soll// Verbessern Sie diese Zuweisung
// In der aktuellen Implementieren klingelt der Wecker
// alle 10 Sekunden für 1 Sekunde
result = (aktS%10 == 0);
return result;
}
}
/*
* Zeichnen einer analogen Uhr in einem JFrame
*/
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JTextField;/**
*
* @author sschneid
*/
public class WeckerUhr extends JPanel {int sekunde = 0;
int minute = 0;
int stunde = 0;
boolean klingeln = false;
String tzString; // aktuelle Zeitzone
boolean an = false;
JFrame hf; // Das Fenster der Anwendung
Container myPane;
JTextField h,m,s;
JButton eingabe;
Weckzeit wz;
int klingelRadius = 0;
/**
* Konstruktor der Klasse. Er initialisiert die Grafik
*/
public WeckerUhr() {
wz = new Weckzeit();
hf = new JFrame("Uhr");
h = new JTextField(2);
m = new JTextField(2);
s = new JTextField(2);
eingabe = new JButton("OK");
eingabe.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int stunde, minute, sekunde;
try {
stunde = Integer.parseInt(h.getText());
minute = Integer.parseInt(m.getText());
sekunde = Integer.parseInt(s.getText());
}
catch (NumberFormatException ex) {
// Es wurde keine korrekte Zahl eingegeben
stunde = -100;
minute = -100;
sekunde= -100;
}
wz.setzeWeckzeit(stunde,minute,sekunde);
}
});
// Beenden der Anwendung bei Schließen des Fenster
hf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Aufbau des Contentpanes
myPane = hf.getContentPane();
myPane.add(this, BorderLayout.CENTER);
JPanel wzPanel = new JPanel(new GridLayout(1,0));
wzPanel.add(new JLabel("hh:mm:ss"));
wzPanel.add(h);
wzPanel.add(m);
wzPanel.add(s);
wzPanel.add(eingabe);
myPane.add(wzPanel,BorderLayout.SOUTH);
// Erzeuge einen Menüeintrag zum Beenden des Programms
JMenuBar jmb = new JMenuBar();
JMenu jm = new JMenu("Datei");
JMenuItem exitItem = new JMenuItem("Beenden");
exitItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
jm.add(exitItem);
jmb.add(jm);
hf.setJMenuBar(jmb);
hf.pack();
// Das JFrame sichtbar machen
// Gewünschte Größe setzen
// 1. Parameter: horizontale Größe in Pixel
// 2. Parameter: vertikale Größe in Pixel
hf.setSize(2 * Zeiger.maxRadius + 80,
2 * Zeiger.maxRadius + 130);
hf.setVisible(true);
hf.setAlwaysOnTop(true);
}
/**
* Hauptprogramm der Anwendung. Es werden keine Eingabeparameter benötigt
* @param args dieser Parameter wird nicht ausgewertet
*/
public static void main(String[] args) {
WeckerUhr dieUhr = new WeckerUhr();
dieUhr.tickTack();}
/**
* Diese Methode verwaltet den Zeitgeber-thread. Dieser Thread belegt
* die statischen Variablen der Uhrzeit neu
*/
public void tickTack() {
try {
boolean blinken = false;
while (true) {
Thread.sleep(250); // Schlafe x Millisekunden
// Hole Systemzeit und belege statische Variablen
Calendar call = Calendar.getInstance();
tzString = call.getTimeZone().getDisplayName();
sekunde = call.get(Calendar.SECOND);
minute = call.get(Calendar.MINUTE);
stunde = call.get(Calendar.HOUR);
klingeln = wz.klingeln(stunde,minute,sekunde);
if (blinken){
klingelRadius=100;
}
else {
klingelRadius=30;
}
blinken = !blinken;
repaint();
}
} catch (InterruptedException e) {
System.out.println(
"Die Anwendung wird wegen einer Ausnahme beendet");
}
}
/**
* Überladene Paintmethode. Sie führt alle Zeichenoperationen im Panel aus
* @param g vom Laufzeitsystem übergebenes Graphikobjekt.
*/
@Override
public void paint(Graphics g) {
super.paint(g);
int xCenter = Zeiger.maxRadius + 40;
int yCenter = Zeiger.maxRadius + 20;
int maxRadius = Zeiger.maxRadius;
int charCenterOffSet = 5;
String timeString = stunde + ":" + minute + ":" + sekunde
+ " " + tzString;
String klingelString;
klingelString = wz.wzh +":"+wz.wzm + ":" +wz.wzs +" klingeln";
if (klingeln) {
g.setColor(Color.red);
g.fillOval(xCenter-klingelRadius/2,yCenter-klingelRadius/2,
klingelRadius,klingelRadius);
}
// Zeichne Uhrenhintergrung und Koordinatensystem
g.setColor(Color.BLACK); // Farbe
g.drawArc(xCenter - 5, yCenter - 5, 10, 10, 0, 360);
g.drawLine(xCenter, yCenter, xCenter + 40, yCenter);
g.drawLine(xCenter, yCenter, xCenter, yCenter + 40);
g.drawString("X", xCenter + 45, yCenter + +charCenterOffSet);
g.drawString("Y", xCenter - charCenterOffSet, yCenter + 55);
g.drawString("12", xCenter - charCenterOffSet, yCenter - maxRadius);
g.drawString("3", xCenter + maxRadius, yCenter + charCenterOffSet);
g.drawString("6", xCenter, yCenter + maxRadius);
g.drawString("9", xCenter - maxRadius - charCenterOffSet,
yCenter + charCenterOffSet);
// Zeichne aktuelle Zeit zum Debuggen
g.drawString(timeString, 0, yCenter + maxRadius + 10);
// Zeichne Weckzeit zum Debuggen
g.drawString(klingelString, 0, yCenter + maxRadius + 25);
// Zeichne Stundenzeiger
g.setColor(Color.BLACK);
g.drawLine(xCenter, yCenter,
xCenter + Zeiger.stundeX(stunde),
yCenter + Zeiger.stundeY(stunde));
g.drawString("h["
+ Zeiger.stundeX(stunde)
+ "," + Zeiger.stundeY(stunde) + "]",
0, yCenter + maxRadius - 40);
// Zeichne Minutenzeiger
g.setColor(Color.RED);
g.drawLine(xCenter, yCenter,
xCenter + Zeiger.minuteX(minute),
yCenter + Zeiger.minuteY(minute));
g.drawString("m["
+ Zeiger.minuteX(minute) + ","
+ Zeiger.minuteY(minute) + "]", 0, yCenter + maxRadius - 25);
// Zeichne Sekundenzeiger
g.setColor(Color.BLUE);
g.drawLine(xCenter, yCenter,
xCenter + Zeiger.sekundeX(sekunde),
yCenter + Zeiger.sekundeY(sekunde));
g.drawString("s["
+ Zeiger.sekundeX(sekunde) + ","
+ Zeiger.sekundeY(sekunde) + "]", 0, yCenter + maxRadius - 10);
}
}
int i = 1, j = 1;
do {
i = i + j;
j++;
} while (i < 200);
int i = 1, j = 20;
while (i + j < i) {
i = i + 2;
j--;
}
int i = 1, j = 20;
while (i + j > i) {
i = i + 2;
j--;
}
int i = 100, j = 27;
while (i != j) {
i = i / 2;
j = j / 3;
}
public class Main {
public static void main(String[] args) {
int firstArg = 0;
int stellen = 0;
int a;
if (args.length > 0) {
try {
firstArg = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
System.out.println("Die Zahl firstArg = " + firstArg + " wurde eingelesen");
a = firstArg; while (a !=0) { stellen++; a/=10; }
}
System.out.println(" Die Zahl " + firstArg + " hat " + stellen + " Stellen!");
}
}
package block3;
public class Quersumme {
public static void main(String[] args) {
int firstArg = 0;
int querSumme = 0;
int a;
if (args.length > 0) {
try {
firstArg = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
System.out.println("Die Zahl " + firstArg + " wurde eingelesen");
a = firstArg;
while (a !=0) {
querSumme+= a%10;
a/=10;
}
System.out.println("Die Zahl " + firstArg + " hat die Quersumme " + querSumme + "!");
}
}
}
package block3;
import java.io.IOException;
public class Schleifentransformation {
public static void main(String[] args) {
int s = 0;
int x = 0;
while(true) { //Option 1
try {
x = System.in.read(); //liest ein Zeichen von der Konsole
} catch (IOException ex) {
System.out.println(" Java IO Exception");
}
if (x < 0) break;
s = s + x;
}
do { //Option 2
try {
x = System.in.read(); //liest ein Zeichen von der Konsole
} catch (IOException ex) {
System.out.println(" Java IO Exception");
}
if (x < 0) break;
s = s + x;
} while(true);
}
}
package block3;
public class Zahlenstatistik {
public static void main(String[] args) {
int feld[];
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
float average = 0;
if (args.length > 0) {
feld = new int[args.length];
try {
for (int i=0;i<args.length;i++) {
feld[i] = Integer.parseInt(args[i]);
if (feld[i] < min) {min=feld[i];}
if (feld[i] > max) {max=feld[i];}
average += feld[i];
}
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
average= average/(float)args.length;
System.out.println("Anzahl eingelesene Werte: " + args.length);
System.out.println("Kleinster Wert: " + min);
System.out.println("Größter Wert: " + max);
System.out.println("Durchschnitt: " + average);
}
}
}
package block3;
public class Primzahlzerlegung {
public static void main(String[] args) {
int firstArg = 0;
int p=1;
long time;
time= System.nanoTime();
if (args.length > 0) {
try {
p = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
System.out.println("Eingelesene Zahl: " + p);
while (p>1) {
for (int i=2; i<= p; i++) {
while (p%i == 0) {
System.out.println("Primfaktor: " + i);
p /= i;
}
}
}
}
time= System.nanoTime() - time;
System.out.println("Zeit in m: "+ time);
}
}
Optimiert:
// Gute Kandidaten
// 2^30-1 = 1073741823
// 2^31-1 = 2147483647
public class Main {
public static void Primzahlzerlegung(String[] args) {
int firstArg = 0;
long p = 1;
long time;
time = System.nanoTime();
if (args.length > 0) {
try {
p = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
long i = 2;
System.out.println("Eingelesene Zahl: " + p);
while ((p > 1) && (i <= p)) {
if (p % i == 0) {
System.out.println("Primfaktor: " + i);
p /= i;
i = 2;
}
else {
if (i > Math.sqrt(p)) {
// Beende Suche wenn Wurzel von P erreicht
i = p;
} else {
i++;
}
}
}
}
time = System.nanoTime() - time;
System.out.println("Zeit in m: " + time);
}
}
a.)
// Annahme: j >= 0
i = j;
a = (a<b) ? b : a; b = (a<b) ? a : b; c = (a<b) ? a : c;
Hier wurde nur der julianische Kalender implementiert nicht der (aktuelle) gregorianische Kalender.
package block3;
public class WochentagBerechnung {
public static void main(String[] args) {
int monatOffSet[] = new int[13];
int tag = 0;
int monat = 0;
int jahr=0;
int wochentag0101startJahr= 6;
int jahresOffSet; // Bedeutung siehe Feld tagText
String[] tagText = {"Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"}; int wochentag;
int startJahr = 2010;
int anzSchaltJahre;
tagText[0] = "Sonntag";
tagText[1] = "Montag";
tagText[2] = "Dienstag";
tagText[3] = "Mittwoch";
tagText[4] = "Donnerstag";
tagText[5] = "Freitag";
tagText[6] = "Samstag";
if (args.length > 1 ) {
try {
tag = Integer.parseInt(args[0]);
monat = Integer.parseInt(args[1]);
jahr = Integer.parseInt(args[2]);
wochentag0101startJahr = Integer.parseInt(args[3]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
monatOffSet[1] = 0;
monatOffSet[2] = (monatOffSet[1]+31)%7;
monatOffSet[3] = (monatOffSet[2]+28)%7;
monatOffSet[4] = (monatOffSet[3]+31)%7;
monatOffSet[5] = (monatOffSet[4]+30)%7;
monatOffSet[6] = (monatOffSet[5]+31)%7;
monatOffSet[7] = (monatOffSet[6]+30)%7;
monatOffSet[8] = (monatOffSet[7]+31)%7;
monatOffSet[9] = (monatOffSet[8]+31)%7;
monatOffSet[10] = (monatOffSet[9]+30)%7;
monatOffSet[11] = (monatOffSet[10]+31)%7;
monatOffSet[12] = (monatOffSet[11]+30)%7;
jahresOffSet = (monatOffSet[12]+31)%7;
if (monat>2)
anzSchaltJahre=jahr/4-startJahr/4;
else
anzSchaltJahre=(jahr-1)/4-startJahr/4;
wochentag = (monatOffSet[monat]
+ tag - 1
+ wochentag0101startJahr
+ (jahr-startJahr)*jahresOffSet
+ anzSchaltJahre
)%7;
System.out.println ("Der 1.1."+ startJahr+" war ein "
+ tagText[wochentag0101startJahr]);
System.out.println ("Der "+ tag + "."+monat+"."+jahr+ " ist ein "
+ tagText[wochentag]);
// Optional: Datumsbestimmung mit Hilfe der Java Infrastruktur
Calendar myCal = new GregorianCalendar(jahr,monat+1,tag);
System.out.println ("Gregorianischer Java Kalender:");
System.out.println ("Der "
+ tag + "."
+ monat + "."
+ jahr+ " ist ein "
+ tagText[myCal.get(Calendar.DAY_OF_WEEK)]);
}
}
}Bei der Addition von Ganzzahlen gibt es keine Überlaufsprüfung.
package block3;
public class Ueberlauf {
public static void main(String[] args) {
int a = 0;
int b = 0;
int result;
if (args.length > 1 ) {
try {
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[1]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
System.out.println("Eingelesene Werte: " + a + ", " + b);
result = a + b;
if ((result < a) || (result < b))
System.out.println("Überlauf!");
else
System.out.println(a + " + " + b + " = " + result);
}
}
Eingabereihenfolge der Parameter ist: a1, a2, b ,c , d1, d2
package block3;
public class GeradenSchnitt {
public static void main(String[] args) {
double a1 = 0.0;
double a2 = 0.0;
double b = 0.0;
double c = 0.0;
double d1 = 0.0;
double d2 = 0.0;
double tmp;
if (args.length > 5) {
try {
a1 = Double.parseDouble(args[0]);
a2 = Double.parseDouble(args[1]);
b = Double.parseDouble(args[2]);
c = Double.parseDouble(args[3]);
d1 = Double.parseDouble(args[4]);
d2 = Double.parseDouble(args[5]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Fließkommazahl sein");
System.exit(1);
}
}
System.out.println("Linie 1: (" + a1 + "," + b + ") bis ("
+ a2 + "," + b + ")");
System.out.println("Linie 2: (" + c + "," + d1 + ") bis ("
+ c + "," + d2 + ")");
// Schnittpunkt ist (c,b)
// Sortieren von a1, a2 .
if (a1 > a2) {
tmp = a1; a1 = a2; a2 = tmp;}
// Sortieren von d1, d2 .
if (d1 > d2) {
tmp = d1;
d1 = d2;
d2 = tmp;
}
System.out.println("Nach sortieren...");
System.out.println("Linie 1: (" + a1 + "," + b + ") bis ("
+ a2 + "," + b + ")");
System.out.println("Linie 2: (" + c + "," + d1 + ") bis ("
+ c + "," + d2 + ")");
if ((a1 <= c) && (c <= a2) && (d1 <= b) && (b <= a2))
System.out.println("Die beiden Strecken schneiden sich");
else
System.out.println("Die beiden Strecken schneiden sich nicht");
}
}
Beispielläufe
java block3.GeradenSchnitt 3.1 8.2 4.0 5.2 11.1 4.9 Linie 1: (3.1,4.0) bis (8.2,4.0) Linie 2: (5.2,11.1) bis (5.2,4.9) Nach sortieren... Linie 1: (3.1,4.0) bis (8.2,4.0) Linie 2: (5.2,4.9) bis (5.2,11.1) Die beiden Strecken schneiden sich nicht java block3.GeradenSchnitt 3.1 8.2 6.0 5.2 11.1 4.9 Linie 1: (3.1,6.0) bis (8.2,6.0) Linie 2: (5.2,11.1) bis (5.2,4.9) Nach sortieren... Linie 1: (3.1,6.0) bis (8.2,6.0) Linie 2: (5.2,4.9) bis (5.2,11.1) Die beiden Strecken schneiden sich
a)
a = a * b + 2 * c;
b)
| x<0 | x>=0 | |
|---|---|---|
| y<0 | a=x*y | a=x*(-y) |
| y>=0 | a = x*(-y) | a=(-x)*(-y) |
y = ((x<0) && (y<0)) ? y : -y; x = ((y>=0) && (x>=0))? -x : x; a = x * y;;
package block3;
public class Dreiecksvergleich {
public static void main(String[] args) {
float x[] = new float[3];
int j;
if (args.length > 2 ) {
try {
float a;
for (int i=0; i <3; i++) {
x[i] = Float.parseFloat(args[i]);
j=i; // Sortiere Eingabe in steigender Reihenfolge
while (j>0)
if (x[j] < x[j-1]) {
a = x[j-1];
x[j-1] = x[j];
x[j]= a;
j--; }
else j=0;
}
}
catch (NumberFormatException e) {
System.err.println("Argument muss Fließkommazahl sein");
System.exit(1);
}
System.out.println("Eingebene Werte: " + x[0] + "; "
+ x[1] + "; "
+ x[2]);
// Die folgenden Vergleiche verlassen sich darauf, dass das Feld
// aufsteigend sortiert ist.
if (x[0]+x[1]<=x[2])
System.out.println("Dreieck ist ungültig");
else if (x[0] == x[2])
System.out.println("Dreieck ist gleichseitig (und gleichschenkelig)");
else {
if ((x[0] == x[1]) || (x[1] == x[2]))
System.out.println("Dreieck ist gleichschenkelig (nicht gleichseitig)");
if (x[0]*x[0]+x[1]*x[1]==x[2]*x[2])
System.out.println("Dreieck ist rechtwinklig");
}
}
}
}
package block3;
public class Sortieren {
public static void main(String[] args) {
int a = 0;
int b = 0;
int c = 0;
if (args.length > 2 ) {
try {
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[1]);
c = Integer.parseInt(args[2]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
System.out.println("Eingelesene Werte: " + a + ", " + b + ", " + c);
// alle Moeglichkeiten testen
if ((a <= b) && (b<=c))
System.out.println("Sortierte Werte: " + a + ", " + b + ", " + c);
else if ( (a <= c) && (c <= b) )
System.out.println("Sortierte Werte: " + a + ", " + c + ", " + b);
else if ( (b <= a) && (a <= c) )
System.out.println("Sortierte Werte: " + b + ", " + a + ", " + c);
else if ( (b <= c) && (c <= a) )
System.out.println("Sortierte Werte: " + b + ", " + c + ", " + a);
else if ( (c <= a) && (a <= b) )
System.out.println("Sortierte Werte: " + c + ", " + a + ", " + b);
else if ( (c <= b) && (b <= a) )
System.out.println("Sortierte Werte: " + c + ", " + b + ", " + a);
}
}
package block3;
public class Plausibilitaetspruefung {
public static void main(String[] args) {
int monatTage[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 13};
int tag = 0;
int monat = 0;
int jahr = 0;
if (args.length > 2 ) {
try {
tag = Integer.parseInt(args[0]);
monat = Integer.parseInt(args[1]);
jahr = Integer.parseInt(args[2]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
System.out.println("Eingabe ist: " + tag +
"." + monat +
"." + jahr);
if ((monat>0) && (monat < 13) && (tag>0))
if (monatTage[monat] >= tag)
System.out.println("Datum ist gültig");
else if ((monat == 2)
&& (tag == 29)
&& (jahr%4 == 0)
&& ((jahr%100 !=0) || (jahr%400==0))
)
System.out.println("Datum ist gültig (Schalttag)");
else System.out.println("Datum ist ungültig");
}
}
}Die vorgestellte Lösung ist eine naive, aber in diesem Fall korrekte Lösung da hier der implementierte Wert eines Zeichen im Vordergrund steht und nicht die Bedeutung des Zeichens.
Sie basiert auf der naiven Annahme, dass die den Zeichen (Buchstaben) zugeordnete Zahlenwerte der Sortierreihenfolge der Buchstaben entspricht. Diese Annahme ist für lateinische Zeichensätze meißten, mehr oder weniger richtig. Im Allgemeinen sollte man bei Programmentwicklung an die Internationalisierung denken. Hier gilt:
package block3;
public class Textverschluesselung {public static void main(String[] args) {String myText = "";int offSet = 0;char c;if (args.length > 1 ) {offSet = Integer.parseInt(args[0]);myText = args[1];}System.out.println("Eingabe: <<" + myText + ">>, Verschiebung: " + offSet);System.out.print("Ausgabe: <<");offSet = offSet%26; // Bei 26 Buchstaben kann der Versatz nur module 26 seinfor (int i=0;i < myText.length(); i++) {// Lese jeden Wert der Zeichenkette aus und erhöhe den Zähler im Feldc = myText.charAt(i);if ((c>='A') && (c<='Z')) {c = (char)(c + offSet);if (c > 'Z') c-=(char)26; //Ziehe vom Ergebnis 26 ab da es jenseits von "Z" liegt.System.out.print(c);}}System.out.println(">>");}}
package block3;
public class Verzweigung {
public static void main(String[] args) {
int x[] = new int[3];
if (args.length > 2 ) {
try {
for (int i=0; i<3; i++)
x[i]= Integer.parseInt(args[i]);
}
catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
System.out.println("Eingabe: " + x[0] +", " + x[1] + ", "+ x[2]);
if ((x[0]==x[1]) && (x[1]==x[2]))
System.out.println("Alle Werte sind gleich.");
if ((x[0]==x[1]) || (x[1]==x[2]) || (x[0]==x[2]))
System.out.println("Mindestens zwei Werte sind gleich.");
if ((x[0]!=x[1]) && (x[1]!=x[2]) && (x[0]!=x[2]))
System.out.println("Alle Werte sind verschieden.");
}
}
package block3;
public class Haeufigkeitzeichen {
public static void main(String[] args) {
String myText="";
char c;
int histogram[] = new int[Character.MAX_VALUE];
if (args.length > 0 ) {
myText=args[0];
}
System.out.println("Eingabe: <<" + myText + ">>");
for (int i=0;i < myText.length(); i++) {
// Lese jeden Wert der Zeichenkette aus und erhöhe den Zähler im Feld
c = myText.charAt(i);
histogram[c]++;
}
for (int i=0; i < Character.MAX_VALUE; i++)
if (histogram[i]!= 0) {
// Wichtig: unterdrücke alle leeren Einträge.
// Das Feld hat ~65000 Zellen!
System.out.print((char)i + ": ");
for (int j=0; j< histogram[i]; j++)
System.out.print('*');
System.out.println();
}
}
}
public class WeckzeitLoesung {
int wzh = 0; // Weckzeit in Stunden
int wzm = 0; // Weckzeit in Minuten
int wzs = 0; // Weckzeit in Sekunden
public void setzeWeckzeit(int hh, int mm, int ss) {
if (korrekteWeckzeit(hh,mm,ss)) {
wzh = hh;
wzm = mm;
wzs = ss;
}
}
public boolean korrekteWeckzeit(int h, int m, int s) {
boolean result;
// benutzen die Variablen h,m,s um eine gültige Zeit zu bestimmen
result = ((h>=0) && (h<=12 && (m>=0) && (m<=59)&& (s>=0) && (s<=59)));
return result;
}
public boolean klingeln(int aktH, int aktM, int aktS) {
boolean result;
// benutzen die Variablen der aktuellen Zeit aktH (Stunde),
// aktM (Minute), aktS (Sekunde) und die Weckzeit wzmh, wzm, wzs
// um zu bestimmern ob der Wecker klingeln soll
// Bestimme aktuelle Zeit in Sekunden
int aktZeit = aktH*3600 + aktM*60+aktS;
// Bestimme Weckzeit in Sekunden
int weckZeit = wzh *3600 + wzm *60+wzs;
// Ist die aktuelle Zeit größer aber nicht größer als 10 Sekunden?
result = (aktZeit-weckZeit>=0) && (aktZeit-weckZeit<10);
return result;
}}
|
(Lizenz) |
Am Ende dieses Blocks können Sie:
|
Sie sind in der Lage die folgenden Fragen zu beantworten:
Methoden sind ein wichtiges Strukturierungs- und Modularisierungsmittel.
Die Grundidee einer Methode ist wiederkehrende Aufgaben an einer Stelle zu implementieren und damit Redundanz zu vermeiden.
Ein zweiter wichtiger Aspekt ist die Senkung der Komplexität. Dem Benutzer muss die Implementierung einer Methode nicht bekannt sein um sie zu benutzen.
Der in Java verwendete Begriff wird in anderen Programmiersprachen auch wie folgt bezeichnet:
In Java wurden alle diese Begriffe der Übersichtlichkeit wegen im Begriff der Methode zusammen gefasst. Methoden gehören immer zu Klassen. Man kann in Java keine alleinstehenden Funktionen, Prozeduren etc. implementieren
Methoden bestehen aus
Zum Aufruf der Methode ist nur die Kenntnis der Deklaration notwendig

Durch ihn wird die Methode deklariert. Das bedeutet, das hiermit eine Schnittstelle festgelegt wird die alle Informationen enthält die ein Benutzer der Methode kennen muss um sie zu benutzen.
public static int multiplizieren( int faktor1, int faktor2); ^ ^ ^ ^ ^ 1. 2. 3. 4. 5.
1. Zugriffsspezifikation; die Sichtbarkeit der Methode
2. Statische Methoden: Das Schlüsselwort static erlaubt den Aufruf der Methode auch wenn es für die Klasse keine Instanz gibt. Das Konzept von Klassen, Objekten und Instanzen wird erst später eingeführt.
3. Rückgabetyp: Der Typ des Ergebnis welche die Methode zurückliefert. Es gibt zwei Möglichkeiten:
4. Methodenname: Hiermit werden Methoden in einer Klasse unterschieden.
5. Parameterliste: Sie enthält den Typ und den Namen aller übergebenen Werte innerhalb von runden Klammern.
In Methodenrümpfe kann man:
In Methoden können die im Methodenkopf deklarierten Parameter direkt verwendet werden. Bei einem gegebenen Rückgabeparameter muss dieser mit dem Schlüsselwort return zurückgegeben werden. Mit final bezeichnete Parameter dürfen im Methodenrumpf nicht mehr verändert werden. Beispiel
public static String textverkettung (String text, final int anzahl) {
String s="";
for (int i=0; i<anzahl; i++) {
s = s + text;
}
return s; }Die Rückgabe von Werten mit Hilfe des Schlüsselwort return funktioniert nicht nur mit Variablen, man kann auch Ausdrücke zurückgeben.
Wie zum Beispiel in der Methode textRahmen() die noch ein Textrahmen von jeweils 3 Zeichen um das Ergebnis legt:
public static String textRahmen (String text) {
return ">>>" + text + "<<<";
}Bei einem gegebenen Rückgabeparameter kann der Rumpf an verschiedenen Stellen verlassen werden. Es muss jedoch in jeder Variante der geforderte Typ zurückgegeben werden.
Siehe Beispiel:
public class Textverkettung {
public static void main(String[] args) {
System.out.println(textverkettung("Sonnenschein", 3));
System.out.println(textverkettung("Sonnenschein", 0));
}
public static String textverkettung(String text, final int anzahl) {
String s="";
if (anzahl < 1) {
return "Gewählte Anzahl ist zu klein";
} else {
for (int i = 0; i < anzahl; i++) {
s = s + text;
}
return s;
}
}
}
Der Typ des Rückgabeparameter muss vom Typ her zum deklarierten Rückgabetyp der Methode passen.
Die Methode kann auch ohne einen Rückgabewert definiert werden. Im folgenden Beispiel wird sie den Text direkt selbst ausdrucken:
public class TextverkettungDrucken {
public static void main(String[] args) {
textverkettungDrucken("Sonnenschein", 3);
textverkettungDrucken("Sonnenschein", 0);
}
public static void textverkettungDrucken(String text, final int anzahl) {
String s="";
if (anzahl >0)
for (int i = 0; i < anzahl; i++)
s = s + text;
System.out.println("Ergebnis: " + s);
}
}
Direkter Aufruf: Aufruf nur durch Methodenname und aktuelle Prameter
Aufruf mit vorgestelltem Objekt oder Klassenname (statische Methode)
Beispiel:
public class Flugzeug {
public void drucken () {
//...
}
public static void main(String[] args) {
Flugzeug f = new Flugzeug();
f.drucken();
f.kennzeichenAusgeben(); // Fall 3: Aufruf einer Methode für ein Objekt
eigenschaft(); // Fall 2: Aufruf einer statischen Methode der gleichen Klasse
Flugzeug.eigenschaft(); // Fall4: Aufruf einer statischen Methoden die zu einer anderen Klasse gehören kann
double pi = Math.sqrt(2.0); // Fall4: Aufruf einer statischen Methoden die zu einer anderen Klasse gehört
}
public void kennzeichenAusgeben() {
drucken(); // Fall 1: Nicht statischer Methodenaufruf
}
public static void eigenschaft() {
System.out.println("kann fliegen");
}
}
Methoden mit Eingabeparametern können bei jedem Aufruf mit anderen Werten arbeiten. Wichtig ist hierbei zu wissen, dass es bei mehreren Parameter auf die Reihenfolge der Parameter ankommt. Sie müssen beim Aufruf, in ihrer Reihenfolge, zur Deklaration passen. Der Übersetzer wird Parameter mit nicht passenden Typen als Fehler anzeigen. Sind die Typen mehrerer Parameter gleich oder kompatibel, muss der Entwickler selbst auf die Reihenfolge achten!
| Formalparameter von Methoden |
|---|
| Die in der Deklaration verwendeten Parameter einer Methode werden Formalparameter genannt |
| Aktualparameter von Methoden |
|---|
| Die beim Aufruf einer Methoden verwendeten Parameter werden Aktualparamater (aktuelle Parameter) genannt. |
Die oben gezeigte Methode textverkettung kann bei jedem Aufruf einen anderen Text verketten. Man kann die Methode wie folgt aufrufen.
public class Textverkettung {
public static void main(String[] args) {
String result;
String text1 = "Mustertext";
result= textverkettung("PingPong", 2);
System.out.println("Ergebnis: " + result);
result= textverkettung(text1, 3);
System.out.println("Ergebnis: " + result);
}
public static String textverkettung(String text, final int anzahl) {
String s;
s = text;
if (anzahl < 1) {
return "Gewählte Anzahl ist zu klein";
} else {
for (int i = 1; i < anzahl; i++) {
s = s + text;
}
return s;
}
}
}Das oben gezeigte Codestück sollte auf der Konsole das folgende Ergebnis ausdrucken:
Ergebnis: PingPongPingPong
Ergebnis: MustertextMustertextMustertext
Der Aufruf von Methoden ohne Rückgabeparameter ist noch einfacher. Im folgenden Beispiel wird wie Methode textverketttungDrucken() aufgerufen:
...
String result;
String text1 = "Mustertext";
textverkettungDrucken("PingPong", 2); textverkettungDrucken(text1, 3);
...Man kann die Methode ohne eine Ergebnisvariable mit einer vereinfachten Syntax aufrufen. Ruft man Methoden mit Ergebnisvariablen in der oben gezeigten Form, ohne Zuweisung des Ergebnis zu einer Variablen, auf, so geht das Ergebnis verloren. Die Implementierung wird jedoch übersetzt und abgearbeitet. Es tritt keine Fehlermeldung auf.
Methodenaufrufe mit Ergebnissen können überall benutzt werden wo in einem Ausdruck oder einer Zuweisung der entsprechende Typ gelesen werden soll.
Im folgenden Beispiel wird die Methode textverkettung() verschachtelt mit der Methode zur Rahmenerzeugung textRahmen() aufgerufen. Der geschachtelte Aufruf erfolgt in der Methode testTextRahmen():
public static String textverkettung(String text, final int anzahl) {
String s="";
if (anzahl < 1) {
return "Gewählte Anzahl ist zu klein";
} else {
for (int i = 0; i < anzahl; i++) {
s = s + text;
}
return s;
}
}
public static String textRahmen(String s) {
return "<< " + s + " >>";
}
public static void testTextRahmen() {
String s = "Inhalt";
String result = textRahmen(textverkettung(s,2));
System.out.println(result);
}
Ein Aufruf der Methode testTextRahmen() ergibt die Ausgabe:
<< InhaltInhalt >>
Methoden können nicht nur wie eben beschrieben geschachtelt werden. Man kann innerhalb eines Methodenrumpfes auch eine andere Methode aufrufen. Dies geschah oben in der Methode testTextRahmen.
Überladen von Methoden
Java erkennt eine Methode einer Klasse an den folgenden Merkmalen:
| Überladene Methoden |
|---|
| Überladene Methoden einer Klasse sind Methoden die den gleichen Namen besitzen und Formalparameterlisten mit unterscheidlichen Typen bzw. unterschiedlicher Anzahl von Parametern besitzen |
Überladene Methoden sind nützlich wenn man den prinzipiell gleichen Algorithmus für unterschiedliche Datentypen oder unterschiedliche Parameterkombinationen ausführen will.
Wichtig: Der Rückgabeparameter einer Methode wird in Java nicht bei der Unterscheidung überladener Methoden beachtet!
Methoden mit Rückgabeparameter können auch ohne Zuweisung ihres Rückgabewertes an eine Variable aufgerufen werden.
In diesem Fall kann der Übersetzer nicht feststellen welche Methode benutzt werden soll, wenn es mehrere Methoden gibt die sich nur im Rückgabewert unterscheiden.
Ein typisches Beispiel ist das drucken von unterschiedlichen Werten. Anbei vier überladene Methoden einer Klasse;
public class drucker {
public void drucken(String s) {
System.out.println("String:"+ s);
}
public void drucken(int i) {
System.out.println("Zahl: " + i);
}
public void drucken(String s, int i) {
System.out.println("String/Zahl" + s + "/" + i);
}
public void drucken(int i, String s) {
System.out.println("Anders: String/Zahl" + s + "/" + i);
}
}Die folgenden zwei Methoden sind keine erlaubten (zusätzlichen) überladene Methoden:
public void drucken(String z, int a) {
System.out.println("String/Zahl" + z + "/" + a);
}
public int drucken(String s, int i) {
System.out.println("String/Zahl" + s + "/" + i);
return 2*i;
}
Diese erste Methode besitzt die gleichen Formalparametertypen wie die dritte Methode. Die Namen der Parameter sind nicht relevant.
Die zweite Methode hat zwar einen anderen Rückgabeparameter, jedoch die gleichen Formalparameter wie die dritte Methode. Java ignoriert die Rückgabeparameter und verweigert das Übersetzen der Methode.
Methoden sind einer der Bausteine die die Entwicklung komplexer Anwendungen erlauben. Sie geben Strukturierungsmöglichkeiten um die Gesamtkomplexität von Anwendungen zu reduzieren:
Methoden sind nur das erste von mehreren Strukurierungshilfsmittel der Sprache Java die im Rahmen dieser Vorlesung vorgestellt werden. Klassen, Pakete, Vererbung und globale vs. lokale Variablen sind andere Möglichkeiten.
Variablen sind immer nur in einem gewissen Umfang sichtbar. Sichtbar bedeutet hier benutzbar, gültig.
Die Sichbarkeit von Variablen hängt vom Kontext, dem Block ab in dem sie deklariert worden sind.
Blöcke können sein:
Es gibt hier abhängig von der Sichtbarkeit verschiedene Bereiche

Die Gültigkeit von Variablen steht im Zusammenhang mit dem Stapel (Stackkonzept) des Laufzeitsystems.
Das Laufzeitsystem legt beim Beginn eines jeden Blocks die neuen Variablen hinter den Variablen des gerade aktuellen Blocks an.
Nach dem Verlassen eines Blocks wird der Zeiger auf das obere Ende des Stapels wieder auf die Variablen des letzen Blocks zurückgesetzt.
Der Speicher der Variablen des verlassenen Blocks ist hierdurch zur Wiederverwendung wieder freigegeben worden.
Das Verfahren eines Stapels entspricht der üblichen Handhabung von Tellern im Schrank.
Das Stackkonzept des Laufzeitssystems:

Konstruktoren sind spezielle Methoden mit denen Objekte einer Klasse initialisert werden. Das Java Laufzeitsystem legt automatisch einen Standardkonstruktor an wenn kein klassenspezifischer Konstruktor implementiert wird.
Konstruktoren haben die folgende Eigenschaften
Beispiel:
package bock4;
public class Employee {
public String surName;
public String firstName;
public int employeeId;
public double salary;
public Employee (String ln, String fn, int id, double sal) {
surName = ln;
firstName = fn;
employeeId = id;
if (sal > 100000) salary = 100000;
else salary= sal;
}
public void printRecord() {
System.out.println(employeeId + ", " + surName + " " + firstName);
System.out.println("Salary :" + salary);
}
}
public class Main {
public static void main(String[] args) {
Employee ceo = new Employee("Doe","John",1,80000.0);
Employee cio = new Employee("Doe","Jane",1,70000.0);
ceo.printRecord();
cio.printRecord();
}
} In den vorangegangen Abschnitten wurden verschiedene Schleifenkonstrukte vorgestellt mit denen man Codestrecken wiederholt durch laufen kann. Dieses Verfahren nennt man Iteration.
Methoden können aber nicht nur andere Methoden aufrufen, sie können auch sich selbst aufrufen. Dieses Verfahren nennt man Rekursion. Beide Verfahren sind von der Theorie her gleichwertig. Sie können wechselseitig eingesetzt werden. Im folgenden Beispiel wird die Multiplikation durch fortgesetzte Addition nach dem folgenden Prinzip iterativ und rekursiv gelöst.
Rekursive Algorithmen wenden das "Teile und Herrsche" Prinzip an indem Sie ein gegebenes Problem zerlegen in
|
Die rekursive Methode fib() basiert auf den folgenden Definition von Fibonaccifolgen fib(0) = 0 (ein trivial lösbares Problem) fib(1) = 1 (ein trivial lösbares Problem) für alle n > 1 fib(n) = fib(n-1) + fib(n-2) (das einfachere Restproblem) |
Fibonacciberechnung |
package block4;public class Fibonacci {
public static long fib(int f) {
long ergebnis=0;
switch (f) {
case 0: { ergebnis = 0;break;}
case 1: { ergebnis = 1;break;}
default: { // Die Rekursion
ergebnis = fib(f - 1) + fib(f - 2);
break;
}
}
return ergebnis;
}
public static void main(String[] args) {
int a = 10; //ANzahl der berechneten Fibonaccizahlen
System.out.println("Fibonacciberechnung von fib(0) bis fib(" + a + ")");
for (int i=0; i<=a; i++)
System.out.println("fib("+i+")= " + fib(i));
}
}
Anmerkung: Diese naive Implementierung ist sehr ineffizient. Das Programm berechnet zu jeder Fibonaccizahl die beiden vorhergehenden Zahlen neu. Der Aufwand zur Berechnung der Fibonaccizahlen steigt daher exponentiell mit der Potenz 2. Dies macht den hier gewähltenAlhorithmus, zur Berechnung für größerer Fibonaccizahlen, ungeeignet.
Die Türme von HanoiDie Türme von Hanoi sind ein einfaches Solitärspiel bei dem die folgenden Regeln gelten:
Siehe Wikipedia für weiterführende Erklärung und Animation. Eine detaillierte Diskussion des Problemes mit vielen Beispielen ist auch unter mister-mueller verfügbar. Die Strategie
|
Lösung für 3 Scheiben
|
package block4;
public class Hanoi {
public static void bewegeScheiben(int scheiben,
String von,
String nach,
String hilfsstab){
if (scheiben >0) {
bewegeScheiben(scheiben-1, von, hilfsstab,nach);
System.out.println(scheiben + ".te Scheibe von " + von + " nach " + nach );
bewegeScheiben(scheiben-1, hilfsstab, nach, von);
}
}
public static void main(String[] args) {
bewegeScheiben(3, "links", "mitte", "rechts");
}
}
Konsolenausgabe1.te Scheibe von links nach mitte 2.te Scheibe von links nach rechts 1.te Scheibe von mitte nach rechts 3.te Scheibe von links nach mitte 1.te Scheibe von rechts nach links 2.te Scheibe von rechts nach mitte 1.te Scheibe von links nach mitte |
Rekursive Aufrufe der Methoden |
|
(Lizenz) |
4.5.1 Übung: ArithmetikmethodenImplementieren Sie eine einfache Arithmetik für den Ganzzahltyp int sowie wie den Fließkommatyp double. Implementieren Sie für beide Typen die folgenden 4 Methoden:
Benutzen Sie die unten aufgeführte Klasse Main mit dem gegebenen Hauptprogramm um einige Tests auszuführen. |
Was geschieht wenn man die Operationen von verschiedenen Typen mischt?
public class Rechenarten {
/* Implementierung */
public static void main(String[] args) {
System.out.println(" 5.0 + 4.0 = " + add(5.0,4.0));
System.out.println(" 9.0 / 4.0 = " + div(9.0,4.0));
System.out.println(" 9.0 - 4.0 = " + sub(9.0,4.0));
System.out.println(" 9.0 / 4.0 + 3.0 = " + add(div(9.0,4.0),3.0));
System.out.println(" 5 + 4 = " + add(5,4));
System.out.println(" 9 / 4 = " + div(9,4));
System.out.println(" 9 - 4 = " + sub(9,4));
System.out.println(" 9 / 4 + 3 = " + add(div(9,4),3));
}
}Implementieren Sie eine Klasse mit Namen Dreieck. Die Klasse soll Methoden zu Flächenberechnung enthalten. Implementieren sie einzelne Methoden zur Berechnung der folgenden Dreieckstypen. Passen Sie die Anzahl der Parameter an die Gegebenheiten an
Nutzen Sie wenn möglich andere Methoden zur Berechnung der Fläche.
Tipp:
1. Übersetzen Sie das gegebene Beispielprogramm und testen Sie die Funktionsweise
public class Arithmetik { /**
* Liest von der Kommazeile zwei Ganzzahlen ein und multipliziert sie
* @param args
*/
public static void main(String[] args) {
int a=5;
int b=8;
int c=0;
if (args.length > 1) {
try {
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
// Kontrolle der Eingaben
System.out.println("Eingabe a: " + a +"; b: " +b);
c = a *b;
// Ergebnisprüfung
System.out.print("Ergebnis c: " + c);
if (c == a*b)
System.out.println(" (korrekt)");
else
System.out.println(" (falsch. Richtig ist " + (a*b)+")");
}
}
2. Benutzen einer for-Schleife mit Addition
Ersetzen Sie die Anweisung:
c = a*b;
durch eine for Schleife in der nur noch addiert wird
3. Einführen einer Methode mult()
Ersetzen die die Berechnung des Ergebnises mit einer for-Schleife durch einen Methodenaufruf:
c = mult(a,b);
Implementieren Sie eine dazu passende Methode mult() die die Multplikation mit der for-Schleife durchführt
4. Eine rekursive Methode mult()
Ersetzen Sie die for-Schleife in der Methode mult() durch einen rekursiven Aufruf.
Tipps:
5. Ersetzen der Addition durch eine rekursive Additionsmethode add() die nur die Inkrementfunktion benutzt
In gesamten Anwendung sollte jetzt kein * oder + Operator mehr vorkommen...
Typische klausurrelevante Fragen:
public class Rechenarten {
public static double add (double a, double b) {
return (a+b);
}
public static double sub (double a, double b) {
return (a-b);
}
public static double mul (double a, double b) {
return (a*b);
}
public static double div (double a, double b) {
return (a/b);
}
public static int add (int a, int b) {
return (a+b);
}
public static int sub (int a, int b) {
return (a-b);
}
public static int mul (int a, int b) {
return (a*b);
}
public static int div (int a, int b) {
return (a/b);
}
public static void main(String[] args) {
System.out.println(" 5.0 + 4.0 = " + add(5.0,4.0));
System.out.println(" 9.0 / 4.0 = " + div(9.0,4.0));
System.out.println(" 9.0 - 4.0 = " + sub(9.0,4.0));
System.out.println(" 9.0 / 4.0 + 3.0 = " + add(div(9.0,4.0),3.0));
System.out.println(" 5 + 4 = " + add(5,4));
System.out.println(" 9 / 4 = " + div(9,4));
System.out.println(" 9 - 4 = " + sub(9,4));
System.out.println(" 9 / 4 + 3 = " + add(div(9,4),3));
}
public class l4dreiecksflaeche {
public static double flaeche(double a, double b, double c) {
double s = (a + b + c) / 2;
return Math.sqrt(s * (s - a) * (s - b) * (s - c));
}
public static double flaeche(double gleicherSchenkel, double basis) {
return flaeche(gleicherSchenkel, gleicherSchenkel, basis);
}
public static double flaeche(double gleicheSeite) {
return flaeche(gleicheSeite, gleicheSeite);
}
public static void main(String[] args) {
System.out.println(" 3,4,5 = " + flaeche(3.0, 4.0, 5.0));
System.out.println(" 2*4,5 = " + flaeche(4.0, 5.0));
System.out.println(" 3*3 = " + flaeche(3.0));
}
}
Multiplikation mit Hilfe einer addierenden for-Schleife:
public class Arithmetik1 {
public static void main(String[] args) {
int a=5;
int b=8;
int c=0;
if (args.length > 1) {
try {
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
System.out.println("Eingabe a: " + a +"; b: " +b);
for (int i=0; i<b; i++) {
c += a;
}
System.out.print("Ergebnis c: " + c);
if (c == a*b)
System.out.println(" (korrekt)");
else
System.out.println(" (falsch. Richtig ist " + (a*b)+")");
}
}
Delegation an eine Methode
public class Arithmetik2 {
public static void main(String[] args) {
int a=5;
int b=8;
int c=0;
if (args.length > 1) {
try {
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
System.out.println("Eingabe a: " + a +"; b: " +b);
c = mult(a,b);
System.out.print("Ergebnis c: " + c);
if (c == a*b)
System.out.println(" (korrekt)");
else
System.out.println(" (falsch. Richtig ist " + (a*b)+")");
}
public static int mult(int x, int y) {
int ergebnis=0;
for (int i=0; i<x; i++) {
ergebnis += y;
}
return ergebnis;
}
}
Eine rekursive Multiplikation
public class Arithmetik3 {
public static void main(String[] args) {
int a=5;
int b=8;
int c=0;
if (args.length > 1) {
try {
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
System.out.println("Eingabe a: " + a +"; b: " +b);
c = mult(a,b);
System.out.print("Ergebnis c: " + c);
if (c == a*b)
System.out.println(" (korrekt)");
else
System.out.println(" (falsch. Richtig ist " + (a*b)+")");
}
/**
* Diese Methode multipliziert zwei Zahlen rekursiv.
* Sie funktioniert nur für x Werte die nicht negativ sind!
* @param x darf nicht negativ sein
* @param y
* @return Ergebnis einer Multiplikation
*/
public static int mult(int x, int y) {
int ergebnis=0;
if (x==0)
ergebnis=0;
else
ergebnis=mult(y,(x-1))+y;
return ergebnis;
}
}
Multiplikation mit rekursiver Addition und Multiplikation
public class Arithmetik4 {
public static void main(String[] args) {
int a=5;
int b=8;
int c=0;
if (args.length > 1) {
try {
a = Integer.parseInt(args[0]);
b = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
System.out.println("Eingabe a: " + a +"; b: " +b);
c = mult(a,b);
System.out.print("Ergebnis c: " + c);
if (c == a*b)
System.out.println(" (korrekt)");
else
System.out.println(" (falsch. Richtig ist " + (a*b)+")");
}
/**
* Diese Methode multipliziert zwei Zahlen rekursiiv.
* Sie funktioniert nur für x Werte die nicht negativ sind!
* @param x darf nicht negativ sein
* @param y
* @return Ergebnis einer Multiplikation
*/
public static int mult(int x, int y) {
int ergebnis=0;
if (x==0)
ergebnis=0;
else
ergebnis=add(mult(y,(x-1)),y);
return ergebnis;
}
/**
* Diese Methode addiert zwei Zahlen rekursiv.
* Sie funktioniert nur für x Werte die nicht negativ sind!
* @param x darf nicht negativ sein
* @param y
* @return Ergebnis einer Multiplikation
*/
public static int add(int x, int y) {
int ergebnis=0;
if (y==0)
ergebnis=x;
else {
ergebnis=add(x,(y-1));
ergebnis++;
}
return ergebnis;
}
}
Die folgenden Fragen können auch in der Klausur gestellt werden.
im Test werden 24 Punkte vergeben. Sie haben 24 Minuten zum Lösen der Aufgaben. In der Klausur werden 100 Punkte in 90 Minuten vergeben.
|
(Lizenz) |
Am Ende dieses Blocks können Sie:
|
Sie sind in der Lage die folgenden Fragen zu beantworten:

(Lizenz)
Die objektorientierte Programmierung hilft große Softwareentwicklungsprojekte überschaubarer und handlicher zu machen.
Die Grundidee besteht darin die zu verarbeitenden Daten und die Algorithmen (Methoden) mit denen die Daten verarbeitet werden zu handlichen, wartbaren Einheiten zusammenzufassen.
Objektorientierte Programmierung fasst man mit den folgenden Konzepten zusammen:
Das erste Konzept, die Datenkapselung, kann man als eine technische Weiterentwicklung der Datentypen sehen.
Ein Bestandteil der Objektorientierung ist das "information Hiding" welches schon von den abstrakten Datentypen her bekannt ist. Der Zustand des Objekts wird durch seine Attribute bestimmt. Die Attribute sollen aber nicht beliebig geändert werden können. Die Methoden agieren als Wächter für die Zustandsübergänge und "bewachen" so zu sagen die Attribute des Objekts. Dies hat zwei wesentliche Vorteile

Methoden erfüllen in diesem Kontext mehrere Aufgaben:
Information Hiding: Ein Teilsystem darf nichts von der Implementierung eines anderen Teilsystems wissen
Nach Wikipedia:
Unter einer Klasse versteht man in der objektorientierten Programmierung ein abstraktes Modell bzw. einen „Bauplan“ für eine Reihe von ähnlichen Objekten.
Die Klasse dient als Bauplan für Abbildung von realen „Objekten“ in Softwareobjekten und enthält Attribute (Eigenschaften) und Methoden (Verhaltensweisen) der Objekte. Verallgemeinernd könnte man auch sagen, dass eine Klasse dem Datentyp eines Objekts entspricht.
Klassen
Nach Wikipedia:
Ein Objekt bezeichnet in der objektorientierten Programmierung (OOP) ein Exemplar eines bestimmten Datentyps oder einer bestimmten Klasse (auch „Objekttyp“ genannt). In diesem Zusammenhang werden Objekte auch als „Instanzen einer Klasse“ bezeichnet. Objekte sind also konkrete Ausprägungen („Instanzen“) eines Objekttyps.
Bisher wurden Klassen nur benutzt um mit Methoden zu arbeiten. Die main() Methode wurde immer als Hauptprogramm genutzt um Methoden aufzurufen. Klassen sind jedoch Strukturen die auch Variablen und Konstanten in Form von Attributen aufnehmen können.
Im folgenden Beispiel wird ein Kraftwagen modelliert:
class Kraftwagen {
public String nummernschild;
public double leergewicht;
public int neupreis;
/**
* Ein selbstgewählter Konstruktor
* @param String Das Kennzeichen des Fahrzeugs
*/
public Kraftwagen(String kennzeichen) {
nummernschild = kennzeichen;
}
public void drucken() {
System.out.println("Nummernschild: " + nummernschild);
System.out.println("Leergewicht: " + leergewicht );
System.out.println("Neupreis: " + neupreis );
}
}Die Klasse Kraftwagen unterscheidet sich in einer Reihe von Aspekten von den bisher benutzten Klassen:
Die Klasse Kraftwagen kann nun von anderen Klassen verwendet werden:
class Fuhrpark{
public static void main( String[] args ) {
Kraftwagen wagen1 = new Kraftwagen("M-O-4711");
Kraftwagen wagen2 = new Kraftwagen("MA-Y-11");
Kraftwagen wagen3 = new Kraftwagen("S-AY-7");
wagen1.leergewicht = 890.0d;
wagen1.neupreis = 20000;
wagen2.leergewicht = 1050.0d;
wagen2.neupreis = 15000;
wagen3.leergewicht = 1250.0d;
wagen3.neupreis = 28000;
wagen1.drucken();
wagen2.drucken();
wagen3.drucken();
}
}Die Methode main() der Klasse Fuhrpark legt hier drei Objekte (Instanzen) der Klasse Kraftwagen an. Die Attribute der drei Objekten können mit individuellen Werten belegt werden. Die Attribute wagen1, wagen2 und wagen3 nennt man Objektvariablen. Objektvariablen können auf Objekte eines bestimmten Typs zeigen. Im Beispiel oben können sie auf ein beliebiges Objekt vom Typ Kraftwagen zeigen.
Die Beziehung zwischen einer Klasse und ihren Instanziierungen, den Objekten beschreibt man in UML wie folgt:

Die Instanzen von Klassen, also die Objekte haben einen anderen Lebenszyklus als normale Variablen mit einfachen Typen. Instanzen (Objekte) können auch ausserhalb eines Blocks oder einer Methode existieren. Sie liegen nicht auf dem Stapel (Stack). Sie werden dynamisch im (prozess-)globalen Adressraum angelegt und sie bestehen so lange sie von Objektvariablen referenziert werden.
Mit dem new() Operator werden neue Objekte von Klassen angelegt. Beim Anlegen eines Objekts werden alle Attribute durch die Konstruktoren initialisiert. Im Beispiel oben wird ein Konstruktor ohne Übergabeparameter benutzt. In diesem Fall wird der selbst definierte Konstruktor ohne Parameter der Klasse zur Initialisierung aufgerufen. Existiert dieser Konstruktor nicht, so wird ein Standardkonstruktor aufgerufen.
|
Der Standardkonstruktor initialisiert alle Basistypen auf Nullwerte. Nach der Initialisierung steht das Objekt zur Verfügung und belegt Platz im Prozessspeicher. Die erzeugten Objekte liegen auf dem "Heap", dem Freispeicher. Im Gegensatz zu Variablen können mehrere Referenzvariablen auf das gleiche Objekt zeigen wie im folgenden Beispiel zu sehen ist: |
|
Kraftwagen wagen1 = new Kraftwagen ("M-O-4711)";
Kraftwagen wagen2 = wagen1;
Kraftwagen wagen3 = new Kraftwagen("S-AY-7"); |
|
Wie im Beispiel oben gezeigt kann man in der Klasse Fuhrpark mit der Objektvariablen wagen1 auch auf die Attribute und Methoden der Klasse Kraftwagen zugreifen. Hierzu dient der Punktoperator:
...
wagen1.leergewicht = 980.0d;
wagen1.drucken();
...Man kann mit dem Punkt sowohl auf Attribute (hier leergewicht) zugreifen sowie man Methoden (hier drucken() ) aufrufen kann.
Java erlaubt es den Zugriff auf Methoden und Variablen mit Hilfe des Schlüsselworts private zu beschränken. Ist eine Objektvariable oder eine Methode mit private gekennzeichnet, so kann man sie nur innerhalb der Klasse benutzen. Der Zugriff ist dann nur noch über öffentlich zugängliche Methoden möglich.
Mit dem Schlüsselwort public wird der öffentliche Zugriff explizit gewährt. Ein Schlüsselwort das den Zugriff regelt ist protected. Es wird später behandelt.
Im folgenden Beispiel sieht man wie man die Variable neupreis der Klasse Kraftwagen vor dem Zugriff der Klasse Fuhrpark schützt. Die Methode getNeupreis erlaubt den Zugriff von ausserhalb der Klasse.
class Kraftwagen {
public String nummernschild;
public double leergewicht;
private int neupreis;
public int getNeupreis() { return neupreis;}
...
}In UML werden private Attribute mit einem vorgestellten Minus "-" gekennzeichnet. Öffentliche Attribute erhalten ein vorgestelltes Plus "+": |
|
Beispiel:
class Fuhrpark{
static int anzahlFahrzeuge;
public static void main( String[] args ) {
int a = Fuhrpark.anzahlFahrzeuge; // Aufruf ausserhalb der Klasse Fuhrpark
a = anzahlFahrzeuge; // Aufruf innerhalb der Klasse Fuhrpark
...
}
}Hinweis: Die "normalen" nicht mit static gekennzeichneten Attribute einer Klasse nennt man zur Unterscheidung auch Instanzvariablen, da sie nur im Kontext einer Instanz (Objekt) existieren können.
Klassenmethoden werden ebenfalls mit dem Schlüsselwort static gekennzeichnet. Sie können wie normale Methoden im Kontext von Objekten aufgerufen werden. Sie können jedoch auch ohne die Existenz von Objekten benutzt werden. Will man eine Methode einer Klasse ohne eine existierende Instanz aufrufen, muss die Methode vorher mit static gekennzeichnet worden sein.
Im Falle der Klasse Fuhrpark kann man sie zum Beispiel zum Auslesen der Anzahl der Fahrzeuge benutzen:
class Fuhrpark{
private static int anzahlFahrzeuge;
public static int getAnzahlFahrzeuge() {return anzahlFahrzeuge;}
...
}
}Die main() Methode sowie die Methoden der Klasse Math sind typische Methoden, die mit static deklariert sind, da sie ohne Objektinstanzen auskommen (müssen).
(Lizenz) |
5.3.1 Übung: VektorrechnungEin Vektor im zweidimensionalen Raum kann durch 2 reelle Zahlen dargestellt werden.
Erstellen Sie eine Klasse "Vektor", die Operationen(Methoden) für
Schreiben Sie auch einen geeigneten Konstruktor und implementieren sie eine Ausgabemethode drucken().
Normalisieren Sie die Vektoren nach jeder Berechnung, so dass ihr Betrag den Wert 1 hat.
|
Eine komplexe Zahl (z.B. 3.2 + i1.75) besteht aus einem reellen und einem imaginären Teil, beide vom Typ double. Erstellen Sie eine Klasse Complex, die komplexe Zahlen implementiert. Als Operationen (Methoden) sollen die vier Grundrechenarten sowie ein geeigneter Konstruktor angeboten werden. Hinweis:
(a + ib) + (c + id) = (a + c) + i(b + d) (a + ib) - (c + id) = (a - c) + i(b - d) (a + ib) * (c + id) = (a*c - b*d) + i(a*d + b*c) (a + ib) / (c + id) = (a*c + b*d)/(c*c + d*d) + i(b*c - a*d)/(c*c + d*d)
Benutzen Sie die Klasse Main als Hauptprogramm um auf die Klasse Complex zuzugreifen:
public class Main {
public static void main(String[] args) {
Complex a = new Complex (1.0,2.0);
Complex b = new Complex (3.0,4.0);
Complex c,d,e,f;
c = a.add(b);
d = a.sub(b);
e = a.mul(b);
f = a.div(b);
System.out.println (" a = " + a.toText());
System.out.println (" b = " + b.toText());
System.out.println (" c = " + c.toText());
System.out.println (" d = " + d.toText());
System.out.println (" e = " + e.toText());
System.out.println (" f = " + f.toText());
}
}
|
![]() |
Die Klasse Complex soll neben den Methoden add(), sub(), mul(), div() auch eine Methode toText() besitzen, die eine Zeichenkette mit dem Wert der komplexen Zahl im Format "(1.1 + i 2.2)" für einen Realteil von 1.1 und einem Imaginärteil von 2.2 ausgibt.
Erweitern Sie eine Klasse Flugzeug.java von einer einfachen Klasse mit einem öffentlichen Attribut zu einer objektorientierten Klasse mit geschützten Attributen und Zugriffsmethoden.
Vorsicht: Der unten aufgeführte Quellcode liegt nicht im default package. Der Quellcode liegt in package block5! Achten Sie auf das korrekte Paket beim Anlegen der Klassen.

Gegeben:
package block5;
public class Flugzeug {public String kennzeichen; // Ein Attribut vom Typ einer Zeichenkette
// 1. Privates Attribut zur Verwaltung der Passagierkapazität
// Tipp: Untersuchen Sie die Druckmethode zur Wahl de6
// Variablennamen (1-5)!
// 2. Privates Attribut zur Verwaltung der aktuellen Pasagiere
// 3. Leergewicht in privates Attribut ändern
// Ein Attribut vom Typ einer Ganzzahl
// 4. Maximales Gewicht des Flugzeugs
// 5. Öffentliche Konstante für durchschn. Passagiergewicht
/**
* 8. Konstruktor implementieren
* Konstruktor der Klasse Flugzeug
* @param kennz Kennzeichen des Flugzeugs
* @param kapazitaet Passagierkapazität
* @param leergew Leergewicht in kg
* @param maxgew Maximalgewicht in kg
*/
/**
* einsteigen()
* 10. Fügt einen Passagier zum aktuellen Flugzeug hinzu
*/
/**
* aussteigen()
* 11. Entfernt einen Passagier des aktuellen Flugzeugs
*/
/**
* anzahlPassagiere()
* 12. Ausgabe der aktuellen Anzahl der Passagiere
* @return aktuelle Anzahl der Passagiere
*/
/**
* gewicht()
* 6. Berechnen des aktuellen Gewichts
* @return aktuelles Gewicht
*/
/**
* passagierkapazität()
* 13. Ausgabe der maximalen Anzahl der Passagiere
* @return Maximale Anzahl der Passagiere
*/
/**
* Eine Methode zum Drucken der Attributbelegung des Objekts
* Die Methode erfordert keine Eingaben. Sie erzeugt keine
* Aufgaben
*/
public void drucken() {
// 7. Vervollständigen der Druckmethode
System.out.println("*****************************");
System.out.println("Kennzeichen: " + kennzeichen);
//System.out.println("Leergewicht: " + leergewicht + "kg");
//System.out.println("Maximalgewicht: " + maxgewicht + "kg");
//System.out.println("Aktuelles Gewicht : " + gewicht() + "kg");
//System.out.println("Passagiere: " + passagiere);
//System.out.println("Maximal Anzahl P.: " + maxPassagiere);
System.out.println("*****************************");
}
}
package block5;public class FlugzeugTest {
public static Flugzeug jumbo;
public static Flugzeug a380;
/**
* Die Methode main() wir zum Starten des Programms benutzt
* @param args Übergabe von Konsolenparameter. Hier nicht genutzt
*/
public static void main(String[] args) {
//phase1(); // 9. Phase 1 testen
//phase2(); // 14. Phase 2 testen
//phase3(); // 15. Phase 3 testen
//phase4();
}
/* Entfernen zum Testen von Phase 1
public static void phase1() {
System.out.println(" Phase 1: 2 Flugzeuge");
// Erzeugen zweier Objekte
jumbo = new Flugzeug("D-ABYT",360,191000,400000);
a380 = new Flugzeug("D-AIMD",560,286000,500000);// Drucken der beiden Objekte auf der Konsole
jumbo.drucken();
a380.drucken();
}
*/
/* Entfernen zum Testen von Phase 2
public static void phase2() {
// 7. Testen des vorangehenden Hauptprogramms
System.out.println(" Phase 2: Einsteigen mit Überbuchung");
System.out.println("Ein Passagier in Jumbo einsteigen");
jumbo.einsteigen();
jumbo.drucken();
System.out.println("300 Passagiere in Jumbo einsteigen");
for (int i=0; i<300; i++) jumbo.einsteigen();
jumbo.drucken();
System.out.println("200 Passagiere aus Jumbo aussteigen");
for (int i=0; i<200; i++) jumbo.aussteigen();
jumbo.drucken();
System.out.println("200 Passagiere aus Jumbo aussteigen");
for (int i=0; i<200; i++) jumbo.aussteigen();
jumbo.drucken();
}
*/
/* Entfernen zum Testen von Phase 3
public static void phase3() {
System.out.println(" Phase 3: Jumbo Flugzeugdefekt");
Flugzeug jumboAlt = new Flugzeug("D-ABYU",360,191000,400000);
Flugzeug a380Neu = new Flugzeug("D-AIME",560,286000,500000);
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("300 Passagiere in JumboAlt einsteigen");
for (int i=0; i<300; i++) jumboAlt.einsteigen();
System.out.println("100 Passagiere in Airbus 380 Neu einsteigen");
for (int i=0; i<100; i++) a380Neu.einsteigen();
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("Jumbo ist defekt. Alle in Airbus umsteigen");
while (jumboAlt.anzahlPassagiere()> 0) {
jumboAlt.aussteigen();
a380Neu.einsteigen();
}
System.out.println("Alle umgestiegen. Bereit zum Start");
jumboAlt.drucken();
a380Neu.drucken();
}
*/
/* Entfernen zum Testen von Phase 4
public static void phase4() {
System.out.println(" Phase 3: A380 Flugzeugdefekt mit 560 Passagieren");
Flugzeug jumbo1 = new Flugzeug("D-ABYV",360,191000,400000);
Flugzeug jumbo2 = new Flugzeug("D-ABYW",360,191000,400000);
Flugzeug a380Defekt = new Flugzeug("D-AIME",560,286000,500000);
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("50 Passagiere in Jumbo 1 und 2 einsteigen");
// 17. Lassen Sie 200 Passagiere in jeden Jumbo einsteigen
// Hiermit ist nicht mehr Platz für alle Airbuspassagiere
// Testen Sie den Fall der Überbuchung
for (int i=0; i<50; i++) {
jumbo1.einsteigen();
jumbo2.einsteigen();
}
System.out.println("560 Passagiere in Airbus 380 (defekt) einsteigen");
for (int i=0; i<560; i++) a380Defekt.einsteigen();
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("Airbus ist defekt. Alle in Jumbos umsteigen");
// 16. Implementieren Sie das Evakuieren des Airbus
// Beide Jumbos sollen falls notwendig benutzt werden
// Drucken Sie eine Warnung aus falls Sie einen Passagier
// nicht umsteigen lassen.
System.out.println("Airbus evakuiert. Bereit zum Start");
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
}
*/}
Die Übung baut auf der vorherigen Übung aus. Bitte benutzen Sie die Musterlösung falls Sie die vorherige Übung nicht gelöst haben.
Implementieren Sie einen Zähler für jedes Flugzeugobjekt der Klasse Flugzeug welches erzeugt wird.
Empfohlenes Vorgehen:
Lernziel: Unterscheiden der Variablen die objektspezifisch sind und derer die klassenspezifisch sind.
Die Übung baut auf der vorherigen Übung aus. Bitte benutzen Sie die Musterlösung falls Sie die vorherige Übung nicht gelöst haben.
Implementieren sie überladene Methoden um das Einsteigen und Aussteigen in der Klasse Flugzeug zu vereinfachen. Die neuen Methoden haben
Testen Sie die Implementierung mit der Klasse FlugzeugTest:
public class Vektor {
private double x;
privatedouble y;
Vektor(double xx, double yy) {
x = xx;
y = yy;
normalisierung();
}
public void normalisierung() {
double laenge = Math.sqrt(x*x+y*y);
x = x/laenge;
y = y/laenge;
}
public void addition(Vektor v) {
x = v.x + x;
y = v.y + y;
normalisierung();
}
public void multi(double skalar) {
x = x * skalar;
y = y * skalar;
// normalisierung();
}
public void skalarproduct(Vektor v) {
x = x * v.x;
y = y * v.y;
normalisierung();
}
public void drucken() {
System.out.println("(" + x + "," + y + ")");
}
}
public class Main {
public static void main(String[] args) {
Vektor a = new Vektor(1.0, 2.0);
Vektor b = new Vektor(2.0, 1.0);
Vektor c = new Vektor(3.0, 3.0);
a.drucken();
b.drucken();
c.drucken();
a.addition(b);
a.drucken();
a.multi(10);
a.drucken();
b.skalarproduct(c);
b.drucken();
}
}
public class Complex {
private double re; // Realteil der Zahl
private double im; // Imaginaerteil der Zahl (i)
public Complex(double r, double i) {
re = r;
im = i;
}
public Complex add(Complex z) {
Complex k = new Complex(re+z.re,im+z.im);
return k;
}
public Complex sub(Complex z) {
Complex k = new Complex(re-z.re,im-z.im);
return k;
}
public Complex mul(Complex z) {
Complex k = new Complex(re*z.re-im*z.im,re*z.im+im*z.re);
return k;
}
public Complex div(Complex z) {
Complex k = new Complex((re*z.re+im*z.im)/(z.im*z.im+z.im*z.im),
(im*z.re-im*z.im)/(z.im*z.im+z.im*z.im));
return k;
}
public String toText() {
String s = "(" + re + " + i" + im + ")";
return s;
}
}
package block5;public class FlugzeugTest {
public static Flugzeug jumbo;
public static Flugzeug a380;
/**
* Die Methode main() wir zum Starten des Programms benutzt
* @param args Übergabe von Konsolenparameter. Hier nicht genutzt
*/
public static void main(String[] args) {
phase1(); // 9. Phase 1 testen
phase2(); // 14. Phase 2 testen
phase3(); // 15. Phase 3 testen
phase4();
}
public static void phase1() {
System.out.println(" Phase 1: 2 Flugzeuge");
// Erzeugen zweier Objekte
jumbo = new Flugzeug("D-ABYT",360,191000,400000);
a380 = new Flugzeug("D-AIMD",560,286000,500000);
// Drucken der beiden Objekte auf der Konsole
jumbo.drucken();
a380.drucken();
}
public static void phase2() {
// 7. Testen des vorangehenden Hauptprogramms
System.out.println(" Phase 2: Einsteigen mit Überbuchung");
System.out.println("Ein Passagier in Jumbo einsteigen");
jumbo.einsteigen();
jumbo.drucken();
System.out.println("300 Passagiere in Jumbo einsteigen");
for (int i=0; i<300; i++) jumbo.einsteigen();
jumbo.drucken();
System.out.println("200 Passagiere aus Jumbo aussteigen");
for (int i=0; i<200; i++) jumbo.aussteigen();
jumbo.drucken();
System.out.println("200 Passagiere aus Jumbo aussteigen");
for (int i=0; i<200; i++) jumbo.aussteigen();
jumbo.drucken();
}
public static void phase3() {
System.out.println(" Phase 3: Jumbo Flugzeugdefekt");
Flugzeug jumboAlt = new Flugzeug("D-ABYU",360,191000,400000);
Flugzeug a380Neu = new Flugzeug("D-AIME",560,286000,500000);
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("300 Passagiere in JumboAlt einsteigen");
for (int i=0; i<300; i++) jumboAlt.einsteigen();
System.out.println("100 Passagiere in Airbus 380 Neu einsteigen");
for (int i=0; i<100; i++) a380Neu.einsteigen();
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("Jumbo ist defekt. Alle in Airbus umsteigen");
while (jumboAlt.anzahlPassagiere()> 0) {
jumboAlt.aussteigen();
a380Neu.einsteigen();
}
System.out.println("Alle umgestiegen. Bereit zum Start");
jumboAlt.drucken();
a380Neu.drucken();
}
public static void phase4() {
System.out.println(" Phase 4: A380 Flugzeugdefekt mit 560 Passagieren");
Flugzeug jumbo1 = new Flugzeug("D-ABYV",360,191000,400000);
Flugzeug jumbo2 = new Flugzeug("D-ABYW",360,191000,400000);
Flugzeug a380Defekt = new Flugzeug("D-AIME",560,286000,500000);
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("50 Passagiere in Jumbo 1 und 2 einsteigen");
// 17. Lassen Sie 200 Passagiere in jeden Jumbo einsteigen
// Hiermit ist nicht mehr Platz für alle Airbuspassagiere
// Testen Sie den Fall der Überbuchung
for (int i=0; i<50; i++) {
jumbo1.einsteigen();
jumbo2.einsteigen();
}
System.out.println("560 Passagiere in Airbus 380 (defekt) einsteigen");
for (int i=0; i<560; i++) a380Defekt.einsteigen();
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("Airbus ist defekt. Alle in Jumbos umsteigen");
// 16. Implementieren Sie das Evakuieren des Airbus
// Beide Jumbos sollen benutzt werden falls notwendig
// Drucken Sie eine Warnung aus falls Sie einen Passagier
// nicht umsteigen lassen.
while (a380Defekt.anzahlPassagiere()> 0) {
if (jumbo1.anzahlPassagiere() < jumbo1.passagierkapazität()) {
a380Defekt.aussteigen();
jumbo1.einsteigen();
}
else // Jumbo 1 is voll...
if (jumbo2.anzahlPassagiere() < jumbo2.passagierkapazität()) {
a380Defekt.aussteigen();
jumbo2.einsteigen();
}
else // Beide Jumbos sind voll
{
a380Defekt.aussteigen();
System.out.println("Ein Passagier bleibt zurück...");
}
}
System.out.println("Airbus evakuiert. Bereit zum Start");
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
}
}
package block5;public class Flugzeug {
public String kennzeichen; // Ein Attribut vom Typ einer Zeichenkette
// 1. Privates Attribut zur Verwaltung der Passagierkapazität
// Tipp: Untersuchen Sie die Druckmethode zur Wahl der
// Variablennamen (1-5)!
private int maxPassagiere;
// 2. Privates Attribut zur Verwaltung der aktuellen Pasagiere
private int passagiere;
// 3. Leergewicht in privates Attribut ändern
public int leergewicht; // Ein Attribut vom Type einer Ganzzahl
// 4. Maximales Gewicht des Flugzeugs
private int maxgewicht;
// 5. Öffentliche Konstante für durchschn. Passagiergewicht
public final static int PASSAGIERGEWICHT = 85;
/**
* 8. Konstruktor implementieren
* Konstruktur der Klasse Flugzeug
* @param kennz Kennzeichen des Flugzeugs
* @param kapazitaet Passagierkapazität
* @param leergew Leergewicht in kg
* @param maxgew Maximalgewicht in kg
*/
public Flugzeug(String kennz, int kapazitaet, int leergew, int maxgew) {
kennzeichen = kennz;
// Prüfen ob Kapazität größere Null ist
if (kapazitaet >= 0) {
maxPassagiere = kapazitaet;
} else {
maxPassagiere = 0;
}
// Prüfen ob Leergewicht größer Null ist
if (leergew > 0) {
leergewicht = leergew;
} else {
leergewicht = 0;
}
// Prüfen ob Maximalgewicht größer-gleich Leergeicht ist.
if (maxgew > leergewicht) {
maxgewicht = maxgew;
} else {
maxgewicht = leergewicht; // Viel Spass...
}
}
/**
* 10. Fügt einen Passagier zum aktuellen Flugzeug hinzu
*/
public void einsteigen() {
if (passagiere < maxPassagiere) {
passagiere++;
}
}
/**
* 11. Entfernt einen Passagier des aktuellen Flugzeugs
*/
public void aussteigen() {
if (passagiere > 0) {
passagiere--;
}
}
/**
* 12. Ausgabe der aktuellen Anzahl der Passagiere
* @return aktuelle Anzahl der Passagiere
*/
public int anzahlPassagiere() {return passagiere;}
/**
* 6. Berechnen des aktuellen Gewichts
* @return aktuelles Gewicht
*/
public int gewicht() {
return (leergewicht+ passagiere*PASSAGIERGEWICHT);}
/**
* 13. Ausgabe der maximalen Anzahl der Passagiere
* @return Maximale Anzahl der Passagiere
*/
public int passagierkapazitaet() {return maxPassagiere;}
/**
* Eine Methode zum Drucken der Attributbelegung des Objekts
* Die Methode erfordert keine Eingaben. Sie erzeugt keine
* Ausgaben
*/
public void drucken() {
// 7. Vervollständigen der Druckmethode
System.out.println("*****************************");
System.out.println("Kennzeichen: " + kennzeichen);
System.out.println("Leergewicht: " + leergewicht + "kg");
System.out.println("Maximalgewicht: " + maxgewicht + "kg");
System.out.println("Aktuelles Gewicht : " + gewicht() + "kg");
System.out.println("Passagiere: " + passagiere);
System.out.println("Maximal Anzahl P.: " + maxPassagiere);
System.out.println("*****************************");
}}
Erweitert um die statische Variable
package block5;
public class Flugzeug {public String kennzeichen; // Ein Attribut vom Typ einer Zeichenkette
// 1. Privates Attribut zur Verwaltung der Passagierkapazität
// Tipp: Untersuchen Sie die Druckmethode zur Wahl der
// Variablennamen (1-5)!
private int maxPassagiere;
// 2. Privates Attribut zur Verwaltung der aktuellen Pasagiere
private int passagiere;
// 3. Leergewicht in privates Attribut ändern
public int leergewicht; // Ein Attribut vom Type einer Ganzzahl
// 4. Maximales Gewicht des Flugzeugs
private int maxgewicht;
// 5. Öffentliche Konstante für durchschn. Passagiergewicht
public final int PASSAGIERGEWICHT = 85;
// Anzahl aller erzeugten Flugzeuge
private static int objekte;
/**
* 8. Konstruktor implementieren
* Konstruktur der Klasse Flugzeug
* @param kennz Kennzeichen des Flugzeugs
* @param kapazitaet Passagierkapazität
* @param leergew Leergewicht in kg
* @param maxgew Maximalgewicht in kg
*/
public Flugzeug(String kennz, int kapazitaet, int leergew, int maxgew) {
kennzeichen = kennz;
objekte++;
// Prüfen ob Kapazität größere Null ist
if (kapazitaet >= 0) {
maxPassagiere = kapazitaet;
} else {
maxPassagiere = 0;
}
// Prüfen ob Leergewicht größer Null ist
if (leergew > 0) {
leergewicht = leergew;
} else {
leergewicht = 0;
}
// Prüfen ob Maximalgewicht größer-gleich Leergeicht ist.
if (maxgew > leergewicht) {
maxgewicht = maxgew;
} else {
maxgewicht = leergewicht; // Viel Spass...
}
}
/**
* 10. Fügt einen Passagier zum aktuellen Flugzeug hinzu
*/
public void einsteigen() {
if (passagiere < maxPassagiere) {
passagiere++;
}
}
/**
* 11. Entfernt einen Passagier des aktuellen Flugzeugs
*/
public void aussteigen() {
if (passagiere > 0) {
passagiere--;
}
}
/**
* 12. Ausgabe der aktuellen Anzahl der Passagiere
* @return aktuelle Anzahl der Passagiere
*/
public int anzahlPassagiere() {return passagiere;}
/**
* 6. Berechnen des aktuellen Gewichts
* @return aktuelles Gewicht
*/
public int gewicht() {
return (leergewicht+ passagiere*PASSAGIERGEWICHT);}
/**
* 13. Ausgabe der maximalen Anzahl der Passagiere
* @return Maximale Anzahl der Passagiere
*/
public int passagierkapazitaet() {return maxPassagiere;}
/**
*
* @return Anzahl aller erzeugten Objekte der Klasse Flugzeug
*/
public int anzahlFlugzeuge() {return objekte;}
/**
* Eine Methode zum Drucken der Attributbelegung des Objekts
* Die Methode erfordert keine Eingaben. Sie erzeugt keine
* Ausgaben
*/
public void drucken() {
// 7. Vervollständigen der Druckmethode
System.out.println("*****************************");
System.out.println("Kennzeichen: " + kennzeichen);
System.out.println("Leergewicht: " + leergewicht + "kg");
System.out.println("Maximalgewicht: " + maxgewicht + "kg");
System.out.println("Aktuelles Gewicht : " + gewicht() + "kg");
System.out.println("Passagiere: " + passagiere);
System.out.println("Maximal Anzahl P.: " + maxPassagiere);
System.out.println("******************** " + objekte + " Flugz.");
}}
package block5;
public class Flugzeug { public String kennzeichen; // Ein Attribut vom Typ einer Zeichenkette// 1. Privates Attribut zur Verwaltung der Passagierkapazität
// Tipp: Untersuchen Sie die Druckmethode zur Wahl der
// Variablennamen (1-5)!
private int maxPassagiere;
// 2. Privates Attribut zur Verwaltung der aktuellen Pasagiere
private int passagiere;
// 3. Leergewicht in privates Attribut ändern
public int leergewicht; // Ein Attribut vom Type einer Ganzzahl
// 4. Maximales Gewicht des Flugzeugs
private int maxgewicht;
// 5. Öffentliche Konstante für durchschn. Passagiergewicht
public final int PASSAGIERGEWICHT = 85;
// Anzahl aller erzeugten Flugzeuge
private static int objekte;
/**
* 8. Konstruktor implementieren
* Konstruktur der Klasse Flugzeug
* @param kennz Kennzeichen des Flugzeugs
* @param kapazitaet Passagierkapazität
* @param leergew Leergewicht in kg
* @param maxgew Maximalgewicht in kg
*/
public Flugzeug(String kennz, int kapazitaet, int leergew, int maxgew) {
kennzeichen = kennz;
objekte++;
// Prüfen ob Kapazität größere Null ist
if (kapazitaet >= 0) {
maxPassagiere = kapazitaet;
} else {
maxPassagiere = 0;
}
// Prüfen ob Leergewicht größer Null ist
if (leergew > 0) {
leergewicht = leergew;
} else {
leergewicht = 0;
}
// Prüfen ob Maximalgewicht größer-gleich Leergeicht ist.
if (maxgew > leergewicht) {
maxgewicht = maxgew;
} else {
maxgewicht = leergewicht; // Viel Spass...
}
}
/**
* 10. Fügt einen Passagier zum aktuellen Flugzeug hinzu
*/
public void einsteigen() {
if (passagiere < maxPassagiere) {
passagiere++;
}
}
/**
*
* @param anzahl Anzahl der Passagiere die einsteigen sollen
*/
public void einsteigen(int anzahl) {
if ((anzahl >0) && (passagiere+anzahl) <= maxPassagiere) {
passagiere+= anzahl;
}
}
/**
* 11. Entfernt einen Passagier des aktuellen Flugzeugs
*/
public void aussteigen() {
if (passagiere > 0) {
passagiere--;
}
}
/**
*
* @param anzahl Anzahl der Passagiere die aussteigen sollen
*/
public void aussteigen(int anzahl) {
if ((anzahl >0) && (passagiere-anzahl) >=0) {
passagiere-= anzahl;
}
}
/**
* 12. Ausgabe der aktuellen Anzahl der Passagiere
* @return aktuelle Anzahl der Passagiere
*/
public int anzahlPassagiere() {return passagiere;}
/**
* 6. Berechnen des aktuellen Gewichts
* @return aktuelles Gewicht
*/
public int gewicht() {
return (leergewicht+ passagiere*PASSAGIERGEWICHT);}
/**
* 13. Ausgabe der maximalen Anzahl der Passagiere
* @return Maximale Anzahl der Passagiere
*/
public int passagierkapazitaet() {return maxPassagiere;}
/**
*
* @return Anzahl aller erzeugten Objekte der Klasse Flugzeug
*/
public static int anzahlFlugzeuge() {return objekte;}
/**
* Eine Methode zum Drucken der Attributbelegung des Objekts
* Die Methode erfordert keine Eingaben. Sie erzeugt keine
* Ausgaben
*/
public void drucken() {
// 7. Vervollständigen der Druckmethode
System.out.println("*****************************");
System.out.println("Kennzeichen: " + kennzeichen);
System.out.println("Leergewicht: " + leergewicht + "kg");
System.out.println("Maximalgewicht: " + maxgewicht + "kg");
System.out.println("Aktuelles Gewicht : " + gewicht() + "kg");
System.out.println("Passagiere: " + passagiere);
System.out.println("Maximal Anzahl P.: " + maxPassagiere);
System.out.println("******************** " + objekte + " Flugz.");
}}
package block5;public class FlugzeugTest {
public static Flugzeug jumbo;
public static Flugzeug a380;
/**
* Die Methode main() wir zum Starten des Programms benutzt
* @param args Übergabe von Konsolenparameter. Hier nicht genutzt
*/
public static void main(String[] args) {
phase1(); // 9. Phase 1 testen
phase2(); // 14. Phase 2 testen
phase3(); // 15. Phase 3 testen
phase4();
phase3a();
phase4a();
}public static void phase1() {
System.out.println(" Phase 1: 2 Flugzeuge");
// Erzeugen zweier Objekte
jumbo = new Flugzeug("D-ABYT", 360, 191000, 400000);
a380 = new Flugzeug("D-AIMD", 560, 286000, 500000);
// Drucken der beiden Objekte auf der Konsole
jumbo.drucken();
a380.drucken();
}public static void phase2() {
// 7. Testen des vorangehenden Hauptprogramms
System.out.println(" Phase 2: Einsteigen mit Überbuchung");
System.out.println("Ein Passagier in Jumbo einsteigen");
jumbo.einsteigen();
jumbo.drucken();
System.out.println("300 Passagiere in Jumbo einsteigen");
for (int i = 0; i < 300; i++) {
jumbo.einsteigen();
}
jumbo.drucken();
System.out.println("200 Passagiere aus Jumbo aussteigen");
for (int i = 0; i < 200; i++) {
jumbo.aussteigen();
}
jumbo.drucken();
System.out.println("200 Passagiere aus Jumbo aussteigen");
for (int i = 0; i < 200; i++) {
jumbo.aussteigen();
}
jumbo.drucken();}
public static void phase3() {
System.out.println(" Phase 3: Jumbo Flugzeugdefekt");
Flugzeug jumboAlt = new Flugzeug("D-ABYU", 360, 191000, 400000);
Flugzeug a380Neu = new Flugzeug("D-AIME", 560, 286000, 500000);
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("300 Passagiere in JumboAlt einsteigen");
for (int i = 0; i < 300; i++) {
jumboAlt.einsteigen();
}
System.out.println("100 Passagiere in Airbus 380 Neu einsteigen");
for (int i = 0; i < 100; i++) {
a380Neu.einsteigen();
}
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("Jumbo ist defekt. Alle in Airbus umsteigen");
while (jumboAlt.anzahlPassagiere() > 0) {
jumboAlt.aussteigen();
a380Neu.einsteigen();
}
System.out.println("Alle umgestiegen. Bereit zum Start");
jumboAlt.drucken();
a380Neu.drucken();
}public static void phase4() {
System.out.println(" Phase 4: A380 Flugzeugdefekt mit 560 Passagieren");
Flugzeug jumbo1 = new Flugzeug("D-ABYV", 360, 191000, 400000);
Flugzeug jumbo2 = new Flugzeug("D-ABYW", 360, 191000, 400000);
Flugzeug a380Defekt = new Flugzeug("D-AIME", 560, 286000, 500000);
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("50 Passagiere in Jumbo 1 und 2 einsteigen");
// 17. Lassen Sie 200 Passagiere in jeden Jumbo einsteigen
// Hiermit ist nicht mehr Platz für alle Airbuspassagiere
// Testen Sie den Fall der Überbuchung
for (int i = 0; i < 50; i++) {
jumbo1.einsteigen();
jumbo2.einsteigen();
}
System.out.println("560 Passagiere in Airbus 380 (defekt) einsteigen");
for (int i = 0; i < 560; i++) {
a380Defekt.einsteigen();
}
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("Airbus ist defekt. Alle in Jumbos umsteigen");
// 16. Implementieren Sie das Evakuieren des Airbus
// Beide Jumbos sollen falls notwendig benutzt werden
// Drucken Sie eine Warnung aus falls Sie einen Passagier
// nicht umsteigen lassen.
while (a380Defekt.anzahlPassagiere() > 0) {
if (jumbo1.anzahlPassagiere() < jumbo1.passagierkapazitaet()) {
a380Defekt.aussteigen();
jumbo1.einsteigen();
} else // Jumbo 1 is voll...
if (jumbo2.anzahlPassagiere() < jumbo2.passagierkapazitaet()) {
a380Defekt.aussteigen();
jumbo2.einsteigen();
} else // Beide Jumbos sind voll
{
a380Defekt.aussteigen();
System.out.println("Ein Passagier bleibt zurück...");
}
}
System.out.println("Airbus evakuiert. Bereit zum Start");
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
}public static void phase3a() {
System.out.println(" Phase 3a: Jumbo Flugzeugdefekt");
Flugzeug jumboAlt = new Flugzeug("D-ABYA", 360, 191000, 400000);
Flugzeug a380Neu = new Flugzeug("D-AIMF", 560, 286000, 500000);
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("300 Passagiere in JumboAlt einsteigen");
jumboAlt.einsteigen(300);
System.out.println("100 Passagiere in Airbus 380 Neu einsteigen");
a380Neu.einsteigen(100);
jumboAlt.drucken();
a380Neu.drucken();
System.out.println("Jumbo ist defekt. Alle in Airbus umsteigen");
int warteRaum = jumbo.anzahlPassagiere();
jumboAlt.aussteigen(jumbo.anzahlPassagiere());
// Zu diesem Zeitpunkt hat man ohne warteRaum die Passagiere des
// Jumbos vergessen!
a380Neu.einsteigen(warteRaum);
System.out.println("Alle umgestiegen. Bereit zum Start");
jumboAlt.drucken();
a380Neu.drucken();
}public static void phase4a() {
System.out.println(" Phase 4a: A380 Flugzeugdefekt mit 560 Passagieren");
Flugzeug jumbo1 = new Flugzeug("D-ABYX", 360, 191000, 400000);
Flugzeug jumbo2 = new Flugzeug("D-ABYY", 360, 191000, 400000);
Flugzeug a380Defekt = new Flugzeug("D-AIMG", 560, 286000, 500000);
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("50 Passagiere in Jumbo 1 und 2 einsteigen");
jumbo1.einsteigen(50);
jumbo2.einsteigen(50);
System.out.println("560 Passagiere in Airbus 380 (defekt) einsteigen");
a380Defekt.einsteigen(560);
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
System.out.println("Airbus ist defekt. Alle in Jumbos umsteigen");
// 16. Implementieren Sie das Evakuieren des Airbus
// Beide Jumbos sollen falls notwendig benutzt werden
// Drucken Sie eine Warnung aus falls Sie einen Passagier
// nicht umsteigen lassen.// Alle aus Airbus in den Warteraum
int warteRaum = a380.anzahlPassagiere();
int freiJumbo1 = jumbo1.passagierkapazitaet() - jumbo1.anzahlPassagiere();
a380Defekt.aussteigen(a380.anzahlPassagiere());if (warteRaum <= freiJumbo1) { // Alle Passagiere passen in jumbo1
jumbo1.einsteigen(warteRaum);
} else { // Nur ein Teil der Passagiere kann mitgenommen werden
jumbo1.einsteigen(freiJumbo1); // Jumbo ist voll
warteRaum -= freiJumbo1; // warteRaum reduziert
// Nutzen des zweiten Jumbo...
int freiJumbo2 = jumbo2.passagierkapazitaet() - jumbo2.anzahlPassagiere();
if (warteRaum <= freiJumbo2) { // Alle Passagiere passen in jumbo2
jumbo2.einsteigen(warteRaum);
} else { // Nur ein Teil der Passagiere kann mitgenommen werden
jumbo2.einsteigen(freiJumbo2); // jumbo2 ist jetzt voll
warteRaum -= freiJumbo2; // warteRaum reduziert
System.out.println(warteRaum + " Passagiere können nicht umbebucht werden.");
}
}
System.out.println("Airbus evakuiert. Bereit zum Start");
jumbo1.drucken();
jumbo2.drucken();
a380Defekt.drucken();
}
}
|
(Lizenz) |
Am Ende dieses Blocks können Sie:
|
Sie sind in der Lage die folgenden Fragen zu beantworten:

(Lizenz)
Datenkapselung ist das Verbergen von Attributen und Methoden durch Einschränkung des Zugriffs von Aussen.
Durch dieses Prinzip ergeben sich beim Entwurf und bei der Wartung von Anwendungen eine Reihe von Vorteilen:
Datenkapselung ist die Voraussetzung zur Implementierung von Schnittstellen:
| Schnittstelle |
|---|
| Die Gesamtheit der öffentlichen Strukturen einer Datenstruktur(Klasse) mit der Konsumenten(Verwender) einer Datenstruktur interagieren können. Schnittstellen stellen die Funktionalität einer Datenstruktur(Klasse) nach außen zur Verfügung. |
Entwickler sollten beim Anwendungsentwurf eine:
einplanen. Klassen sollten möglichst autark sein und auf möglichst wenig andere Schnittstellen und Klassen zugreifen
Java erlaubt den Zugriff auf Methoden und Attribute mit Hilfe der Schlüsselwörter private und public vor dem Namen des Attributs oder Methode zu steuern:
Diese Zugriffssteuerung erlaubt die Implementierung der folgenden Empfehlung
|
Attribute und Methoden ohne ein Schlüsselwort werden in Java als public Attribute und Methoden behandelt.
Java verfügt auch über das Schlüsselwort protected welches den Zugriff nur innerhalb eines Pakets erlaubt. Pakete werden erst in der weiterführenden Vorlesung eingeführt werden.
Es ist eine Konvention (und guter Programmierstil) in Java den Zugriff auf private Attribute mit Methoden zu gewähren denen man vor dem Attributnamen get- bzw. set- voranstellt:
class Person() {
private String name;
public void setName(String n) { name = n;}
public String getName() { return name;}
} |
![]() |
Dieser Programmierstil bietet eine Reihe von Vorteilen:
Anmerkung: Laufzeiteinbußen durch solche Trivialmethoden sind in der Regel nicht zu befürchten. Der Java "Just in Time" (JIT) Übersetzer des Laufzeitsystems wird mit hoher Wahrscheinlichkeit solche Methoden zur Laufzeit durch Methoden-inlining wegoptimieren.
Trennung von Zuständigkeiten(Separation of Concerns) ist ein Konzept welches auf Datenkapselung aufbaut.
Die Idee besteht darin Zuständigkeiten Klassen zu zuordnen.
Alle Aufgaben die in einen Bereich fallen sollen möglichst von genau einer Klasse implementiert werden. Ziel ist es, dass unabhängige Dinge auch in der Implementierung unabhängig voneinander bleiben. Hierdurch
Ziel ist es Klassen so zu modellieren, dass sie möglichst in sich abgeschlossen sind.
Ein Beispiel hierfür:
|
MVC ist ein Entwurfmuster welches aus den folgenden drei Einheiten besteht:
|
![]() |
Die Einteilung einer Anwendung in die folgenden drei Bereich ist oft vorteilhaft da häufig
Wichtig ist zu verstehen, dass die drei Komponenten zusammenarbeiten und idealerweise unabhängig voneinander sind. Das Model sollte zum Beispiel auf Aufrufe von View und Controller reagieren, jedoch nicht selbst diese Komponenten aufrufen.
Schichtenarchitekuren sind eine andere Ausprägug der "Separation of Concerns".
Java selbst und Javaanwendungen basieren auf dem Schichtenmodell.
|
|
Eine Javaanwendung soll nur auf die Dienste der Java Runtime zurückgreifen. Die Java Runtime bietet hierfür eine reichhaltige Infrastruktur für GUI Programmierung, Datenbankzugriff, Netzwerkkommunikation, Dateizugriff etc. Beschränkt man sich auf die von der Java Runtime angebotenen Dienste wird man unabhängig vom Betriebssystem und der darunter liegenden Hardware. Ziel bei der Entwicklung einer Anwendung sollte es sein unterschiedliche Schichten zu identifizieren und von Klassen nur auf die eigene Schicht oder die darunter liegende Schicht zu beschränken. |
Java verfügt über das Schlüsselwort this um auf die aktuelle Instanz innerhalb eines Methodenrumpfes zu referenzieren. Mit Hilfe der folgenden Notation kann man mit dem Schlüsselwort this die Methoden und Attribute der eigenen Klasse referenzieren:
Hinweis: Die this Referenz ist final. D.h. man kann ihr keinen anderen Wert zuweisen.
Es gibt eine Reihe von typischen Anwendungsfällen:
Eine Methode einer Klasse soll die Instanz der Klasse einem Konsumenten bekannt machen. Zum Beispiel:
import class AddressBuch;
class Person {
private String nachName;
private String vorName;
...
public Person (String vn, String nn) {
this.setNachName(nn);
vorName = vn;
Adressbuch.eintragen(this);
...
}
public Person setNachName(String nn) {
nachName = nn;
return this;
}
}
Die this Referenz erlaubt auch das Auflösen von Mehrdeutigkeiten innerhalb eines Namensraumes. Im folgenden Beispiel verdecken die Übergabeparameter nachName, vorName im Konstruktor der Klasse Person die gleichnamigen Attribute:
import class AddressBuch;
class Person {
private String nachName;
private String vorName;
...
public Person (String vorName, String nachName) {
this.nachName = nachName;
this.vorName = vorName;
Adressbuch.eintragen(this);
...
}
}
Das Schlüsselwort this kann auch als Methodenaufruf this() verwendet werden. Dies erlaubt den Aufruf von Konstruktoren der eigenen Klasse.
Diese Technik ist sehr nützlich um bei mehreren Konstruktoren die Replikation von Code zu vermeiden. Dies geschieht im folgenden Beispiel der Klasse Person() bei einem Konstruktor ohne Parameter. Er benutzt den Konstruktor mit Parameter zum Setzen des Namens indem er einen Standardnamen verwendet:
import class AddressBuch;
class Person {
private String nachName;
private String vorName;
...
public Person (String vorName, String nachName) {
this.nachName = nachName;
this.vorName = vorName;
Adressbuch.eintragen(this);
...
}
public Person() {
/* Rufe den Konstruktor Person(String vorName, String nachName) auf */
this("John","Doe");
}
}
Der Vorteil dieser Programmiertechnik liegt in der Vermeidung von Redundanzen. Beim Aufruf des Konstruktors ohne Parameter wird durch den Aufruf des Konstruktors mit Name, Vorname zum Beispiel auch das Addressbuch gepflegt.
Die schon früher angesprochenen Konstruktoren sind eine wichtige Komponente der Datenkapselung, da man mit ihnen eine komplexe und korrekte Initialisierung von Objekten erzwingen kann.
Konstruktoren können wie Methoden Übergabeparameter enthalten. Sie werden vom new() Operator erfasst, der dann nach dem Initialisieren der Datenstrukturen den entsprechenden Konstruktor aufruft.
Punkt p1 = new Punkt (3.3, 4.1);
Hier wird ein Konstruktor der Klasse Punkt aufgerufen der folgende Methodenparameter hat
class Punkt {
private double x;
private double y;
public Punkt (double xKoord, double yKoord) {
x = xKoord;
y = yKoord;
}
}Das Schreiben von Konstruktoren ist optional. Es können mehrere Konstruktoren implementiert werden. Alle Konstruktoren müssen sich jedoch in der Parameterliste unterscheiden. Hier zählt die Reihenfolge und Anzahl der Parametertypen, jedoch nicht der Name der Parametervariablen. Der Übersetzer braucht diese Information um die unterschiedlichen Konstruktoren auszuwählen.
| Überladen von Methoden und Konstruktoren |
|---|
| Das Implementieren von mehreren namensgleichen Methoden oder Konstruktoren mit unterschiedlichen Eingabe-Parameterlisten nennt man überladen. |
Java unterscheidet die unterschiedlichen Methoden und Konstruktoren an den Eingabelisten der Parameter jedoch nicht am Rückgabeparameter!
Es kann sehr nützlich sein mehrere Konstruktoren zur Initialisierung einer Klasse zur Verfügung zu stellen wie man am Beispiel der Klasse Punkt sehen kann. Die Klasse Punkt erlaubt hier die folgenden Initialisierungsarten:
class Punkt {
private double x;
private double y;
public Punkt (double xKoord, double yKoord) {
x = xKoord;
y = yKoord;
}
public Punkt (Punkt p) { this(p.x,p.y);}
public Punkt () { this(0,0); }
}
...
Punkt p1 = new Punkt(); // Initialisierung mit (0,0)
Punkt p2 = new Punkt(1.1,2.2); // Initialisierung mit (1.1,2.2)
Punkt p3 = new Punkt(p2); // Initialisierung mit (1.1,2.2) durch Punkt p2
...Der Default-Konstruktor (ohne Parameter) wurde hier selbst implementiert. Er ruft mit Hilfe des Schlüsselworts this den Konstruktor mit den beiden Fliesskommazahlen als Parameter auf. Er initialisiert die Datenstruktur nicht selbst, sondern er delegiert die Initialisierung an einen anderen Konstruktor. Dies ist nicht notwendig aber üblich.
Der Konstruktor mit der Parameterliste Punkt verfährt ähnlich.
| Aufrufe von Konstruktoren durch Konstruktoren (der gleichen Klasse) |
|---|
|
Konstruktoren können andere Konstruktoren der gleichen Klasse mit this(parameter-liste) aufrufen. Wichtig: Der this() Aufruf muss jedoch der erste Befehl in einem Konstruktor sein. |
Ein optionaler this() Aufruf muss das erste Kommando im Codeblock des Konstruktors sein um die Integrität des mit this() aufgerufenen Konstruktors zu gewährleisten.
Ein Konstruktor ist die erste Methode die ein Objekt initialisiert. Das ist aber nicht mehr für den aufgerufenen Konstruktor geährleistet, wenn dem aufrufenden Konstruktor schon Objektmanipulationen vor dem this() Aufruf erlaubt sind.
Java stellt einen Standardkonstruktor (Default-Konstruktor) ohne Parameter zur Verfügung der alle Attribute mit Standardwerten belegt. Implementiert man eigene Konstruktoren gelten die folgenden Regeln für die Benutzer von Konstruktoren:
Dieses Vorgehen ist notwendig um eine Datenkapselung zu erzwingen.
Bei einer Implementierung mit eigenen Konstruktoren aber ohne einen Default-Konstruktor führt der folgende Konstruktoraufruf zu einem Übersetzungsfehler:
class Punkt {
private double x;
private double y;
public Punkt (double xKoord, double yKoord) {...}
public Punkt (Punkt p) { this(p.x,p.y);}
...
}
... Punkt p1 = new Punkt(); // Initialisierung mit Default-Konstruktor ist nicht möglich ...
Erklärung der von Java geforderten Semantik:
Java bietet die Möglichkeit einen Konstruktor als private zu deklarieren. Hiermit kann niemand ausserhalb der Klasse Instanzen erzeugen. Diese Möglichkeit erlaubt die Anzahl von Instanzen bei Bedarf genau zu kontrollieren. Ein Anwendungsfall ist ist das Benutzen einer statischen Methode und eines privaten Kontruktors:
class Punkt {
private double x;
private double y;
public static Punkt createPunkt (double xKoord, double yKoord) {
Punkt pp = new Punkt(xKoord, yKoord);
return pp;
}
private Punkt (double xx, double yy) { x=xx; y=yy;}
}
...
Punkt p1 = Punkt.createPunkt(1.1,2.2);
Punkt p2 = new Punkt(4.4, 5.5); // Fehler!!
...Ein anderer Anwendungsfall ist die Benutzung des Entwurfsmodell "Singleton" d.h. einer Klasse von der es genau eine Instanz gibt. Ein Beispiel ist die Klasse Addressbuch:
class Addressbuch {
private Addressbuch myInstance;
private Addressbuch() {
// Initialisiere Addressbuch
}
public static Addressbuch getAddressbuch() {
if (myInstance == null) myInstance = new Addressbuch();
return myInstance;
}
}
...
Addressbuch ab = getAddressbuch ();
...Mit dieser Implementierung wird beim ersten Anfordern eines Addressbuchs genau einmal ein Addressbuch angelegt.
Begriffsbestimmung:
| Instanziierung einer Klasse |
|---|
| Erzeugen eines neuen Objekts einer Klasse auf dem Java Heap (Freispeicher) |
| Initialisierung (von Datenfeldern) |
|---|
| Das Belegen von existierenden Datenfeldern mit Standardwerten oder mit von Konstruktoren individuell implementierten Werten |
Beim Erzeugen eines Javaobjekts wie zum Beispiel einer Instanz der Klasse Punkt:
class Punkt {
private double x;
private double y;
public Punkt (double xKoord, double yKoord) {
x = xKoord;
y = yKoord;
}
}
...
Punkt p2 = new Punkt(1.1,2.2);
...Mit der Variable p2 l laufen die folgenden Schritte beim Aufruf der Programmzeile Punkt p2=new Punkt() ab:
|
![]() |
|
(Lizenz) |
6.6.1 Teamübung: FuhrparkmanagementDie folgende Übung kann in einem Team von drei Parteien ausgeführt werden. Jede Partei implementiert eine Klasse. Die Übung findet in den folgenden Phasen statt.
Anforderung an alle: Implementieren Sie sinnvolle Konstruktoren |
Anforderungen
Anforderungen
Anforderungen
Hinweis: Die Spezifikation ist nicht vollständig. Das Team muss auf die folgenden Dinge selbst achten:
Benutzen Sie das Beispiel der Klasse Flugzeug und der Klasse FlugzeugTest aus der Übung 5.4.5 Überladenen Methode.
Fügen zur Klasse Flugzeug einen weiteren Konstruktor hinzu. Der Konstruktor soll im Gegensatz zum existierenden Konstruktor keinen Parameter zur Passagierkapazität besitzen.
Der Dokumentationskommentar zum zu implementierenden Konstruktor
/**
* Konstruktur der Klasse Flugzeug
* Berechnet Passagierkapazität automatisch
* @param kennz Kennzeichen des Flugzeugs
* @param leergew Leergewicht in kg
* @param maxgew Maximalgewicht in kg
*/
Hinweis:
Testen Sie das Programm mit der Routine phase6a() der Klasse FlugzeugTest:
package block6;
public class FlugzeugTest {
/**
* Die Methode main() wir zum Starten des Programms benutzt
* @param args Übergabe von Konsolenparameter. Hier nicht genutzt
*/
public static void main(String[] args) {
phase6a();
}
/**
* Testen des überladenen Konstruktors
*/
public static void phase6a() {
Flugzeug b737_500 = new Flugzeug("D-ABIAA", 31900, 52000);
Flugzeug b737_300 = new Flugzeug("D-ABIAB", 12815, 56470);
System.out.println("Kapazität Boing 737-300: " + b737_300.passagierkapazitaet());
System.out.println("Kapazität Boing 737-500: " + b737_500.passagierkapazitaet());
}
}Warum ist die automatisch berechnete Sitzkapazität der Boing 737 viel zu groß?
Was wurde bei der Modellierung der Klasse nicht berücksichtigt?
public class Kraftwagen {
private double verkaufsPreis;
private double einkaufsPreis;
private String kennzeichen;
public Kraftwagen(double ek, double vk, String nummernSchild) {
// Initialisierung der Variablen mit einfachen Konsistenzprüfungen
verkaufsPreis = (ek < vk) ? vk : ek;
einkaufsPreis = (ek < vk) ? ek : vk;
kennzeichen = nummernSchild;
}
public double get_Epreis() {
return einkaufsPreis;
}
public void set_Epreis(double pr) {
einkaufsPreis = pr;
}
public double get_Vpreis() {
return verkaufsPreis;
}
public void set_Vpreis(double pr) {
verkaufsPreis = pr;
}
public String get_kennzeichen() {
return kennzeichen;
}
public void set_kennzeichen(String s) {
kennzeichen = s;
}
public void drucken() {
System.out.println("Einkaufspreis: " + einkaufsPreis);
System.out.println("Verkaufspreis: " + verkaufsPreis);
System.out.println("Kennzeichen: " + kennzeichen);
}
public static void Main (String args) {
}
}
public class Verkaeufer {
private String name;
private double gewinn;
private double gebundeneMittel;
private Kraftwagen[] meineWagen;
public Verkaeufer(String nam) {
meineWagen = new Kraftwagen[2];
name = nam;
gewinn = 0.0;
}
public void neuerWagen(int pos, Kraftwagen w) {
if ((pos >= 0) && (pos < 2)) {
meineWagen[pos] = w;
}
}
public Kraftwagen imBestand(int pos) {
if ((pos >= 0) && (pos < 2)) {
return meineWagen[pos];
} else {
return null;
}
}
public Kraftwagen verkaufeFahrzeug(int pos, int erloes) {
Kraftwagen result;
if (erloes < meineWagen[pos].get_Vpreis()) {
System.out.println("Wagen mit Kennzeichen " +
meineWagen[pos].get_kennzeichen() +
" sollte unter Einkaufspreis verkauft werden.");
result = null;
} else {
gewinn += erloes-meineWagen[pos].get_Epreis();
result = meineWagen[pos];
meineWagen[pos] = null; // Wagen gehört nicht mehr zum Bestand
}
return result;
}
public int fahrzeugeInFlotte() {
int result = 0;
int i = 0;
while (i < meineWagen.length) {
if (meineWagen[i] != null) {
result++;
}
i++;
}
return result;
}
public double gebundeneMittel() {
double result = 0.0;
for (int i = 0; i < meineWagen.length; i++) {
if (meineWagen[i] != null) {
result += meineWagen[i].get_Epreis();
}
}
return result;
}
public double geplanterUmsatz() {
double result = 0.0;
for (int i = 0; i < meineWagen.length; i++) {
if (meineWagen[i] != null) {
result += meineWagen[i].get_Vpreis();
}
}
return result;
}
public void drucken() {
Kraftwagen w1 = imBestand(0);
Kraftwagen w2 = imBestand(1);
System.out.println("<*** Verkäufername : " + name + " ***>");
System.out.println("Erlös bisher: " + gewinn);
System.out.println("Gebundene Mittel: " + gebundeneMittel());
System.out.println("Geplanter Umsatz: " + geplanterUmsatz());
System.out.println("Fahrzeuge in Flotte: " + fahrzeugeInFlotte());
if (w1 != null) w1.drucken();
if (w2 != null) w2.drucken();
System.out.println("<*********************************>");
}
public static void main(String[] args) {
Kraftwagen w1 = new Kraftwagen(20000.00, 30000.00, "M-S 1234");
Kraftwagen w2 = new Kraftwagen(10000.00, 15000.00, "B-A 5678");
Kraftwagen w3 = new Kraftwagen(5000.00, 10000.00, "D-T 8901");
Verkaeufer v1 = new Verkaeufer("Hans");
v1.neuerWagen(0, w1);
v1.neuerWagen(1, w2);
v1.drucken();
Kraftwagen verkWagen = v1.verkaufeFahrzeug(0, 50000);
System.out.println("Verkaufter Wagen: " + verkWagen.get_kennzeichen());
v1.drucken();
}
}
public class Haendler {
private Verkaeufer[] team;
private String name;
public Haendler(String n ) {
name = n;
team = new Verkaeufer[3];
}
public void einstellenTeam(String name1, String name2, String name3) {
team[0] = new Verkaeufer(name1);
team[1] = new Verkaeufer(name1);
team[2] = new Verkaeufer(name1);
}
public void geschäftsEröffnung(Kraftwagen[] k) {
team[0].neuerWagen(0, k[0]);
team[1].neuerWagen(0, k[1]);
team[2].neuerWagen(0, k[2]);
team[0].neuerWagen(1, k[3]);
team[1].neuerWagen(1, k[4]);
team[2].neuerWagen(1, k[5]);
}
public double gesamtGewinn() {
double g =0;
for (int i=0; ipackage block6;
/**
* Eine Flugzeug mit Gewicht und Passagieren
* Am besten mit {@link block6.FlugzeugTest#main FlugzeugTest} testen
* @author sschneid
*/
public class Flugzeug {
public String kennzeichen; // Ein Attribut vom Typ einer Zeichenkette
private int maxPassagiere;
private int passagiere;
public int leergewicht; // Ein Attribut vom Type einer Ganzzahl
private int maxgewicht;
public static final int PASSAGIERGEWICHT = 85;
// Anzahl aller erzeugten Flugzeuge
private static int objekte;
/**
*
* Konstruktur der Klasse Flugzeug
* @param kennz Kennzeichen des Flugzeugs
* @param kapazitaet Passagierkapazität
* @param leergew Leergewicht in kg
* @param maxgew Maximalgewicht in kg
*/
public Flugzeug(String kennz, int kapazitaet, int leergew, int maxgew) {
kennzeichen = kennz;
objekte++;
// Prüfen ob Kapazität größere Null ist
if (kapazitaet >= 0) {
maxPassagiere = kapazitaet;
} else {
maxPassagiere = 0;
}
// Prüfen ob Leergewicht größer Null ist
if (leergew > 0) {
leergewicht = leergew;
} else {
leergewicht = 0;
}
// Prüfen ob Maximalgewicht größer-gleich Leergeicht ist.
if (maxgew > leergewicht) {
maxgewicht = maxgew;
} else {
maxgewicht = leergewicht; // Viel Spass...
}
}
/**
* Konstruktur der Klasse Flugzeug
* Berechnet Passagierkapazität automatisch
* @param kennz Kennzeichen des Flugzeugs
* @param leergew Leergewicht in kg
* @param maxgew Maximalgewicht in kg
*/
public Flugzeug(String kennz, int leergew, int maxgew) {
this(kennz,(maxgew-leergew)/PASSAGIERGEWICHT,leergew,maxgew);
}
/**
* Fügt einen Passagier zum aktuellen Flugzeug hinzu
*/
public void einsteigen() {
if (passagiere < maxPassagiere) {
passagiere++;
}
}
/**
* @param anzahl Anzahl der Passagiere die einsteigen sollen
*/
public void einsteigen(int anzahl) {
if ((anzahl >0) && (passagiere+anzahl) <= maxPassagiere) {
passagiere+= anzahl;
}
}
/**
* Entfernt einen Passagier des aktuellen Flugzeugs
*/
public void aussteigen() {
if (passagiere > 0) {
passagiere--;
}
}
/**
* Entfernt Passagiere des aktuellen Flugzeugs
* @param anzahl Anzahl der Passagiere die aussteigen sollen
*/
public void aussteigen(int anzahl) {
if ((anzahl >0) && (passagiere-anzahl) >=0) {
passagiere-= anzahl;
}
}
/**
* Ausgabe der aktuellen Anzahl der Passagiere
* @return aktuelle Anzahl der Passagiere
*/
public int anzahlPassagiere() {return passagiere;}
/**
* Berechnen des aktuellen Gewichts
* @return aktuelles Gewicht
*/
public int gewicht() {
return (leergewicht+ passagiere*PASSAGIERGEWICHT);}
/**
* Ausgabe der maximalen Anzahl der Passagiere
* @return Maximale Anzahl der Passagiere
*/
public int passagierkapazitaet() {return maxPassagiere;}
/**
*
* @return Anzahl aller erzeugten Objekte der Klasse Flugzeug
*/
public static int anzahlFlugzeuge() {return objekte;}
/**
* Eine Methode zum Drucken der Attributbelegung des Objekts
* Die Methode erfordert keine Eingaben. Sie erzeugt keine
* Ausgaben
*/
public void drucken() {
// 7. Vervollständigen der Druckmethode
System.out.println("*****************************");
System.out.println("Kennzeichen: " + kennzeichen);
System.out.println("Leergewicht: " + leergewicht + "kg");
System.out.println("Maximalgewicht: " + maxgewicht + "kg");
System.out.println("Aktuelles Gewicht : " + gewicht() + "kg");
System.out.println("Passagiere: " + passagiere);
System.out.println("Maximal Anzahl P.: " + maxPassagiere);
System.out.println("******************** " + objekte + " Flugz.");
}
}
|
(Lizenz) |
Am Ende dieses Blocks können Sie:
|
Sie sind in der Lage die folgenden Fragen zu beantworten:
Objektvariablen enthalten Referenzen auf Objekte. Sie unterscheiden sich von Variablen primitiver Datentypen in einer Reihe von Eigenschaften:
| Variablen primitiver Datentypen | Objektvariablen | |
|---|---|---|
| Initialisierung | Standardwert des Typs; direkt benutzbar | Null Zeiger, Objekt muss separat erzeugt werden |
| Exklusivität | Variable gehört exklusiv zum Objekt | referenziertes Objekt gehört nicht exklusiv zum umgebenden Objekt |
| Lebensdauer | die des umgebenden Objekts | referenziertes Objekt existiert so lange es von irgendeiner Referenz referenziert wird |
| Ausnahmebehandlung | nicht relevant | Der Versuch eine Methode oder Objektvariable mit einer Referenz auf Nulll zu benutzen führt zu einer Ausnahme |
| Parameterübergabe bei Methoden | Kopie wird angelegt | Objekt wird nicht kopiert. Nur Referenz wird kopiert |
|
Neue Objekte werden im Adressraum nur mit dem new() Operator angelegt. Referenzen speichern nur die eindeutige Identität unter der das Objekt zu erreichen ist. Die in der Referenz verwaltete eindeutige Objektidentität bleibt für die gesamte Lebensdauer des Objekts konstant. Sie ist eine logische Kennung. Das unten aufgeführte Programmbeispiel mit Diagramm ist jedoch nur als Gedankenmodell zu verstehen. Die konkrete Implementierung in einer virtuellen Java Maschine kann durchaus anders aussehen. Sie ist für den Javaentwickler transparent.
Kraftwagen wagen1 = new Kraftwagen ("0-1");
Kraftwagen wagen2 = wagen1;
Kraftwagen wagen3 = new Kraftwagen("0-2");Das Referenzkonzept von Java ist die einzige Möglichkeit auf Objekte zuzugreifen. Das Referenzenkonzept von Java ist syntaktisch sehr ähnlich zu dem Pointerkonzept von C und C++. Es gibt jedoch wichtige Unterschiede mit signifikanten Auswirkungen auf die Stabilität der Anwendungen: |
|
| Java | C/C++ | |
|---|---|---|
| Begriff | Referenz (Reference) | Zeiger (Pointer) |
| Implementierung | abstrakter Datentyp (Klasse) | Speicherbereich |
| direkter Speicherzugriff | nein | ja |
| Typcheck zum Übersetzungszeitpunkt | ja | ja (Normalerweise) |
| Zugriffskontrolle auf eine nicht initialisierte Referenz | Check mit eventuellem Wurf einer behandelbaren Ausnahme | kein Check auf Existenz oder Gültigkeit des Objekts |
Bemerkung: Der neue C++11 Standard bietet viele Verbesserungen dieser historisch bedingten Defizite von C/C++. Da C++ oft für Anwendungen deren Leistung sehr wichtig ist (Systemprogrammierung) benutzt wird, sind alle zusätzlichen Checks von C++11 immer nur optional.
Java verfügt über eine "null" Konstante mit deren Wert man Referenzen belegen kann um:
|
Im folgenden Beispiel ist zu sehen was geschieht wenn wagen1 und wagen2 mit der null Referenz belegt werden:
package block7.reference;
public class Main {
public static void main(String[] args) {
Kraftwagen wagen1 = new Kraftwagen("0-1");
Kraftwagen wagen2 = wagen1;
Kraftwagen wagen3 = new Kraftwagen("0-2");
wagen2 = null;
wagen3 = null;
}
}Der Wagen"0-2" wird ein Kandidat zum Löschen. Der Wagen "0-1" wird noch über die Referenz wagen1 referenziert. Enthält eine Referenz eine Null Variable so zeigt sie nicht mehr auf ein Objekt und man kann nicht mehr die Methoden und Datenfelder einer Instanz aufrufen. Mit static deklarierte Methoden und Attribute sind hiervon ausgenommen. |
![]() |
Das Javalaufzeitsystem wird in diesem Fall eine NullPointerException werfen und das Programm beenden. Das folgende Beispiel zeigt den Fall einer unbehandelten Ausnahme sowie die Behandlung einer Ausnahme:
package block7.reference;
public class Main {
public static void main(String[] args) {
Kraftwagen wagen1 = new Kraftwagen("O-1");
Kraftwagen wagen2 = wagen1;
Kraftwagen wagen3 = new Kraftwagen("O-2");
wagen2 = null;
wagen3 = null;
try{
System.out.println(wagen2.getKennzeichen());
}
catch (NullPointerException e)
{
System.out.println("Variable wagen2 ist eine Null Referenz");
}
System.out.println(wagen3.getKennzeichen());
//Das Programm wurde wegen der unbehandelten Ausnahme beendet
// Die folgende Zeile nicht nicht mehr erreicht
System.out.println("Diese Zeile des Programms wird nicht erreicht..");
}
}Das Programm wird bei der Ausführung die folgenden Ausgaben auf der Konsole erzeugen;
Variable wagen2 ist eine Null Referenz
Exception in thread "main" java.lang.NullPointerException
at block7.reference.Main.main(Main.java:20)
Die erste Ausnahme, der Aufruf von wagen2.getKennzeichen() wird mit mit einem try-catch Block aufgefangen. Nach der Behandlung im catch-Block wird die Methode weiter ausgeführt.
Der Aufruf von wagen3.getKennzeichen() wird nicht aufgefangen und führt zu einer Ausnahme in Zeile 20 die das Programm beendet. Ausnahmen und Ausnahmebehandlungen werden in einem späteren Abschnitt behandelt.
Es gibt Fälle in denen man als Parameter einer aufzurufenden Methode einer anderen Klasse einen Zeiger auf die aktuelle Instanz, in deren Methoden man gerade arbeitet, mitgeben möchte. Hierzu dient das Schlüsselwort this. Es kann benutzt werden um auf die aktuelle Instanz zu zeigen, solange man sich nicht in einer static Methode befindet. static Methoden können auch ohne eine existierende Instanz benutzt werden. Eine Objektinstanz existiert daher nicht unbedingt.
Beispiel:
class Person {
...
public void sitzPlatzZuweisen (Reservierung r) {
...
r.buchung(this);
...
}
}
...
// Beliebige andere Klasse
Person p = new Person();
Reservierung buchung;
buchung = new Reservierung("LH454","17B");
// Zeitpunkt 1
p.sitzPlatzZuweisen(buchung);
// Zeitpunkt 2
... |
![]() |
Die Java Objekte werden innerhalb des Javaprozesses in einem Speicherbereich mit dem Namen "Java Heap" verwaltet. In diesem Speicherbereich werden alle Datenstrukturen mit einer nicht festen Größe verwaltet. Diese Datenstrukturen sind:
Dieser Bereich
Optionen zum Heapmanagement(siehe auch Java Optionen (Referenzdokumentation)
Beispiel: Starten einer Anwendung Main.class mit 500 Megabytes initialem Heap (Freispeicher), 800 Megabytes maximalem Heap und Protokollierung von Garbagekollektorläufen:
java -Xms500m -Xmx800m -XX:+PrintGC Main
Nicht mehr referenzierte Objekte werden von der JavaVM automatisch, im Hintergrund von einem "Garbage Collector" gelöscht.
Dieser "Garbage Collector" (GC) kann manuell getriggert werden. Dies sollte man aber nicht in einem produktiven Programm durchführen. Der explizit angestossene GC bringt die Anwendung während seiner Arbeit zum Stehen! Automatisch ausgeführte GCs bewirken dies (normalerweise) nicht. Das explizite Anstoßen geschieht mit der statischem Methode System.gc()
System.gc()
Die Konsolenausgabe der Option -XX:+PrintGC sieht wie folgt aus:
[GC 38835K->38906K(63936K), 0.1601889 secs] [GC 39175K(63936K), 0.0050223 secs] [GC 52090K->52122K(65856K), 0.1452102 secs] [GC 65306K->65266K(79040K), 0.1433074 secs]
Sie zeigt an wieviel Speicher die Objekte von der "Garbage Collection" vorher und nachher belegen. Die benötigte Zeit wird ebenfalls angezeigt.
Die Arbeit des "Garbage Collector" und die Füllstände der Pools kann man in den Videos des Java Performance Primer's sehen.
Der Freispeicher (Heap) kann je nach Konfiguration eine konstante Größe haben oder er kann dynamisch bis zu einer vorgegebenen maximalen Größe wachsen. Der Entwickler ist daran interessiert, dass seine Anwendung alle benötigten Objekte im Freispeicher (Heap) verwalten kann. Kann das Javalaufzeitsystem keine neuen Objekte mehr anlegen wird es die Anwendung mit einer OutOfMemoryError Ausnahme beenden.
Dies wird normalerweise durch den Garbage-Collector der automatisch alle Objekte löscht die nicht mehr referenziert werden vermieden.
| Nicht referenzierte Objekte |
|
Nicht referenzierte Objekte können weder von der Anwendung noch vom Javalaufzeitsystem mit Hilfe von Referenzen erreicht werden. Dies bedeutet es gibt keine Kette von Referenzen zu einem Objekt die an den folgenden Orten beginnt:
Nicht referenzierte Objekte werden vom Javalaufzeitsystem bei Bedarf zu einem beliebigen Zeitpunkt gelöscht. Der genaue Zeitpunkt des Löschens ist nicht für den Entwickler vorhersehbar. |
Im folgenden Programm werden eine Reihe von Personen erzeugt, die über eine Vater und Mutter Beziehung aufeinander referenzieren können.
package dereferenzieren;public class Person {public Person vater;public Person mutter;public static void main (String[] args ) {Person p1 = new Person();Person p2 = new Person();
// Zeitpunkt 1p1.vater = p2;p2 = null;
// Zeitpunkt 2aufruf(p1);//Zeitpunkt 5}public static void aufruf(Person p) {Person[] persFeld = new Person[2];
// Zeitpunkt 3persFeld[1] = p;persFeld[0] = new Person();persFeld[0].vater = new Person();persFeld[0].mutter = new Person();// Zeitpunkt 4}}
Das Hauptprogramm main() erzeugt zwei Instanzen der Klasse Person und ruft dann die Methode aufruf() auf die ein kleines Feld und einige weitere Instanzen erzeugt.
Hinweis: Im folgenden Beispiel wird ein Feld verwendet. Javafelder werden im Detail im folgenden Kapitel erklärt.
Zum Zeitpunkt 1 sind in der main() Methode die folgenden Objekte erzeugt:

Zum Zeitpunkt 2 wurde in der main() Methode bereits die Vater-Referenz und der ursprüngliche Zeiger p2 mit null dereferenziert.

Zum Zeitpunkt 3 wurde die Methode aufruf() aufgerufen und das Feld persFeld mit zwei Feldern auf dem Heap angelegt.

Zum Zeitpunkt 4 sind drei weitere Personen angelegt worden. Die drei neuen Personen sind über den Index 0 von perrsFeld erreichbar.
Zu diesem Zeitpunkt können alle Objekte auf dem Heap direkt, oder indirekt von Datenstrukturen auf dem Stapel (stack) erreicht werden.

Zum Zeitpunkt 5 wurde die Methode excute() bereits verlassen.Die lokalen Variablen der Methode aufruf() wurden vom Stack gelöscht und stehen nicht mehr zur Verfügung.
Hierdurch können eine Reihe von Instanzen der Klasse Person und das Feld auf dem Heap (Freispeicher) nicht mehr erreicht werden.

Die nicht mehr erreichbaren Objekte sind Müll (Garbarge) geworden und belegen verfügbaren Speicherplatz. Sie werden bei Bedarf vom Garbage-Collector (GC) gelöscht. Der Garbage-Collector wird alle Objekte löschen die vom Stack und statischen Variablen nicht mehr erreichbar sind. Dies hat für die Anwendung keine Implikationen da die Objekte auch von der Anwendung nicht mehr erreichbar sind.
Objekte die versehentlich bzw. ungewollt referenziert werden können nicht gelöscht werden. Diese Objekte können nach und nach den Heap füllen und zu einem Programmabbruch mangels Hauptspeicher (OutOfMemoryError Ausnahme) führen. Diesen Zustand der früher oder später zum ungewollten Abbruch eines Programms führen kann, nennt man im englischen "Memory Leak" (Speicherleck) da man über die Zeit nutzbaren Speicher verliert wie ein Tank Wasser durch ein Leck verlieren kann.
Ein Speicherleck kann durch eine minimale Änderung im vorhergehenden Beispiel entstehen. Gibt die Methode aufruf() als Ergebnis einen Zeiger auf das Feld von Personen zurück werden die drei Personen und das Feld nicht dereferenziert.
package dereferenzieren;public class Person {public Person vater;public Person mutter;public static void main (String[] args ) {Person p1 = new Person();Person p2 = new Person();p1.vater = p2;p2 = null;Person[] f = aufruf(p1);//Zeitpunkt 7}public static Person[] aufruf(Person p) {Person[] persFeld = new Person[2];persFeld[1] = p;persFeld[0] = new Person();persFeld[0].vater = new Person();persFeld[0].mutter = new Person();// Zeitpunkt 6return persFeld;}}
Zum Zeitpunkt 6 sieht Objektmodell noch aus wie im vorhergehenden Beispiel:

Durch die Rückgabe der Referenz auf das Feld beim Beenden der Methode aufruf(), ist das Feld und die drei Objekte noch vom Stack erreichbar:
Die Variable f in main() referenziert das Feld. Das Feld wiederum referenziert 3 weitere Objekte.
Hier liegt ein Speicherleck nur vor, wenn der Entwickler nicht davon ausgeht, dass das Feld und alle referenzierten Objekte noch erreichbar sind. Ein Speicherleck ist kein Problem des Laufzeitsystems, da das Laufzeitsystem nicht zwischen noch benötigten und nicht mehr benötigten Objekten unterscheiden kann.
Dieses Problem wird vom Javaentwickler durch das Dereferenzieren von Objekten vermieden.
Der Javaentwickler muss nicht (und kann nicht) wie in anderen Programmiersprachen nicht mehr benötigte Objekte selbst löschen. Es verbleibt jedoch die Aufgabe sicherzustellen, dass nicht mehr benötigte Objekte nicht mehr referenziert werden um ein vollaufen des Heap zu vermeiden.
Zeigt eine lokale Variable auf ein Objekt, so verschwindet die Referenz auf das Objekt mit dem Verlassen des Blocks in dem die lokale Referenzvariable definiert war.
Im der unten aufgeführten Methode warePrüfen() gibt es zwei Referenzvariablen w und w1 die auf eine Instanz einer Ware zeigen
public void warePrüfen(Ware w) {
Ware w1 = w;
....
}Beim Aufruf dieser Methode erhöht sich die Anzahl der Referenzen auf eine bestimmte Instanz der Klasse Ware um zwei Referenzen. Da die beiden Variablen aber am Ende des Blocks beim Verlassen der Methode wieder gelöscht werden erniedrigt sich die Anzahl der Referenzen auf ein gegebenes Objekt um zwei.
"Memory Leaks" entstehen daher typischerweise nicht durch lokale Variablen. Werden Methoden jedoch sehr spät verlassen (main() Methode!) werden auch die entsprechenden lokalen Referenzevariablen erst sehr spät gelöscht.
Die erste Variante des Beispiels in dem das Feld auf Personen nur innerhalb des Methodenblocks verwendet wurde ist ein Fall von impliziten Dereferenzieren
Entwickler sollten Referenzvariablen auf Objekte explizit mit dem null belegen wenn Sie wissen, dass ein Objekt sicher nicht mehr benötigt wird.
o.ref = a-reference; ... o.ref = null;
Das Derefenzieren einer Referenzvariable und den null Wert ist insbesondere wichtig wenn die Variable ein Attribut einer Klasse ist. Die Lebensdauer des Objekts o ist nicht unbedingt ersichtlich für den Entwickler. Das Setzen der Referenzvariablen o.ref auf null gewährleistet, dass das Objekt auf das mit der Variable a-reference gezeigt wird bei Bedarf gelöscht werden kann.
Das Speicherleck im zweiten Beispiel kann durch zwei verschiedene Änderungen vermieden werden:
1. Möglichkeit: Dereferenzieren der Variable persFeld in der Methode aufruf()
public static Person[] aufruf(Person p) {
Person[] persFeld = new Person[2];
persFeld[1] = p;
persFeld[0] = new Person();
persFeld[0].vater = new Person();
persFeld[0].mutter = new Person();
// Zeitpunkt 1
persFeld = null;
return persFeld;
}2. Möglichkeit: Dereferenzieren der Variable f in der main() Methode:
public static void main (String[] args ) {
Person p1 = new Person();
Person p2 = new Person();
p1.vater = p2;
p2 = null;
Person[] f = aufruf(p1);
//Zeitpunkt 2
f = null;
}
Objekte kopieren ist nicht trivial. Die folgende Java-Anweisung
Person p1;
Person p2;
p1 = new Person("John", "Doe");
p2=p1;dupliziert nicht das erzeugte Objekt, es dupliziert nur die Referenz auf das gleiche Objekt. Dies bedeutet, dass man das Objekt über p1 sowie über p2 erreichen und modifizieren kann.
Das duplizieren von Objekten muss vom Entwickler explizit implementiert werden. Dies geschieht typischer in einem eigen Konstruktor dem "Copy Construktor". Der typische "Copy Constructor" wird wie folgt implementiert:
Das Vorgehen beim Kopieren der Objektvariablen obliegt dem Implementierer. Er hat hier zwei Möglichkeiten mit verschiedener Semantik:
Das rekursive Kopieren von Objekten wird im Englischen auch als "deep copy" bezeichnet.
Das einfache wiederbenutzen der Objekte ist im folgenden Beispiel implementiert:
public class Person {
Person vater;
Person mutter;
String name;
...
public Person (Person orig) {
vater = orig.vater;
mutter = orig.mutter;
name = orig.name;
}
public Person(String n){
name = n;
}
}
...
Person p1 = new Person("John Doe");
p1.mutter = new Person("mum");
p1.vater = new Person("dad");
Person p2 = new Person(p1); |
Das Kopieren einer Person "John Doe" ohne das Duplizieren der referenzierten Objekte führt zum folgenden Objektmodell:
|
|
Das tiefe, rekursive Klonen (Kopieren) wird wie folgt implementiert:
public class Person {
Person vater;
Person mutter;
String name;
...
public Person (Person orig) {
if (orig.vater != null)
vater = new Person(orig.vater);
if (orig.mutter != null)
mutter = new Person(orig.mutter);
name = orig.name;
}
public Person(String n){
name = n;
}
}
...
Person p1 = new Person("John Doe");
p1.mutter = new Person("mum");
p1.vater = new Person("dad");
Person p2 = new Person(p1); |
|
In Java muss man bei Vergleichen die folgenden, unterschiedlichen Fälle unterscheiden:
Bei primitiven Typen ist der Vergleich recht einfach
int i = 1; int j = 2; if (i ==j) ...
Beim Vergleich von Objekten ist hier jedoch Vorsicht geboten. Beispiel:
class Person {
String name;
public Person (String nameparam) { ...}
}
...
Person p1 = new Person ("John Doe");
if (p1 == "John Doe")
...
p1 ist eine Referenz auf ein Objekt. p1 wird hier direkt mit dem Zeichenliteral "John Doe" verglichen. Dieser Vergleich ist erlaubt, er führt jedoch nicht zum gewünschten Ergebnis. Die Zeichenkette "John Doe" hat einen anderen Typ und ein anderes Objekt als p1.
In Java erben alle Klassen die Methoden der Basisklasse Object. Alle Klassen erben eine Implementierung der equals() Methode von der Klasse Object.
Diese erlaubt das vergleichen von Objekten wie im folgenden Beispiel
Person p1; Person p2; ... if (p1.equals(p2)) ...
Die Methode equals()
Vorsicht: Implementiert man diese Methode selbst, dann sollte auch die Methode hashCode() selbst implementiert (überschrieben) werden. Sie wird von vielen Hilfsklassen im Zusammenhang mit equals() gleichzeitig benutzt.
Beispiel:
class Person {
String name;
...
public boolean equals(Object obj) {
boolean result;
Person target = (Person) obj;
result = (name.equals(target.name));
return result;
}
} Das Schlüsselwort final erlaubt es zu spezifizieren, dass Variablen nur genau einmal belegt werden dürfen (Siehe Javaspezifikation).
Hiermit kann man erzwingen, dass Variablen später nicht mehr modifiziert werden können.
Siehe Beispiel:
public class Person {
...
final String name;
...
final Adresse geburtsort;Das Attribut name ist eine Zeichenkette. Man kann genau einmal eine Zeichenkette zuweisen. Eine spätere Änderung ist nicht mehr möglich.
Der Modifizierer final (englisch modifier) hat bei Referenztypen (hier die Klasse Adresse) eine besondere Wirkung:
Zeichenketten werden in Java mit der Klasse String verwaltet. Sie sind Objekte.
Sie wurden bisher wie die Basistypen Character, Integer oder Float behandelt. Zeichenketten werden jedoch ähnlich wie Referenzen behandelt da Zeichenketten unterschiedlich lang sein können. Man kann Zeichenketten daher nicht die Zahlentypen mit fester Größe einfach in den Speicherbereich eines Objekts einbetten.
Zeichenketten werden bei Bedarf im Hauptspeicher (Heap) angelegt und referenziert. Die Referenz auf die Zeichenkette selbst hat eine konstante Größe.
Zeichenketten können mit einer direkten Zuweisung erzeugt werden. Sie können jedoch auch gleichwertig mit dem new() Operator und dem Konstruktor der Klasse String angelegt werden. Beide Varianten sind nur in der Syntax unterschiedlich. Der einzige Unterschied besteht darin, dass das Objekt "John" schon beim Laden der Klasse angelegt wird. Das Objekt "Doe" wird erst beim Aufruf des Befehls angelegt.
String a = "John";
String b = new String("Doe");
|
![]() |
a sowie b sind Objektreferenzen nicht aber der direkte Datenbehälter!
Zeichenketten(Strings) können einfach mit dem plus "+" Operator verkettet werden. Der plus Operator konvertiert automatisch andere Basistypen in Zeichenketten bevor die neue verkettete Zeichenkette zugewiesen wird:
String a = "Das Ergebnis ist "; double pi = 3.1415; String b = "***"; String c = a + pi + b;
Tipp: Die Klasse String verfügt über eine ganze Reihe von hilfreichen Methoden. Hier eine Auswahl:
Zeichenketten werden von der virtuellen Maschine als unveränderbare ("immutable") Objekte gehandhabt. Sie können daher nicht überschrieben oder verändert werden. Die Implikationen können am folgenden Beispiel gezeigt werden.
String a = "John"; String b = a; String c = " Doe"; |
![]() |
|
Eine Verkettung der Zeichenketten mit dem plus Operator ergibt das folgende Speicherabbild c = a + c; Die Variable c zeigt jetzt auf ein neues Objekt. Die Zeichenkette " Doe" ist eventuell nicht mehr referenziert und wird dann vielleicht irgendwann gelöscht. Bei der oben gezeigten Verkettung entstehen typischerweise sehr viele neue Objekte und es werden sehr viele Stringobjekte zum Löschen freigesetzt! |
![]() |
Da ein Objekt vom Typ String eine Referenz auf eine Zeichenkette ist, muss man beim Vergleich von Zeichenketten unterscheiden, ob man die Referenz oder den Inhalt der Zeichenkette vergleichen möchte um Fehler zu vermeiden.
Der Javaübersetzer und das Javalaufzeitsystem speichern Literale wenn möglich nur einmal im Hauptspeicher. Konstante Literale die man zur Übersetzungszeit erkennen kann werden in Referenzen auf ein einziges Objekt zusammengefasst. Dynamisch erzeugte Zeichenketten werden in eigenen Objekten verwaltet. Die genaue Semantik ist in der Java Sprachspezifikation (3.te Version von 2005, Paragraph 3.10.5 String Literals) beschrieben.
Wichtig: Identische Zeichenketten haben nicht unbedingt die gleiche Objektidentität. Ein Vergleich mit Hilfe der Objektidentität ist daher im Normalfall nicht ratsam.
String a = "John";
String b = a;
String c = "John";
String d = "Jo"+"hn"; // Der konstante Ausdruck
// wird schon bei der Übersetzung aufgelöst!
String e = new String("John"); |
das Speicherabbild zum Quellcode links:
|
Ein Vergleich mit dem == Operator vergleicht nur die Adressen der referenzierten Objekte nicht aber deren Inhalt. Zum Vergleich der Inhalte wird die .equals() Methode benötigt. Siehe
if (a == b) System.out.println(" a und b zeigen auf das gleiche Objekt");
else System.out.println(" a und b zeigen nicht auf das gleiche Objekt");
if (a == c) System.out.println(" a und c zeigen auf das gleiche Objekt");
else System.out.println(" a und c zeigen nicht auf das gleiche Objekt");
if (a == d) System.out.println(" a und d zeigen auf das gleiche Objekt");
else System.out.println(" a und d zeigen nicht auf das gleiche Objekt");
if (a == e) System.out.println(" a und e zeigen auf das gleiche Objekt");
else System.out.println(" a und e zeigen nicht auf das gleiche Objekt");
if (a.equals(b)) System.out.println(" a und b sind gleiche Zeichenketten");
else System.out.println(" a und b sind nicht gleiche Zeichenketten");
if (a.equals(c)) System.out.println(" a und c sind gleiche Zeichenketten");
else System.out.println(" a und c sind nicht gleiche Zeichenketten");
if (a.equals(d)) System.out.println(" a und d sind gleiche Zeichenketten");
else System.out.println(" a und d sind nicht gleiche Zeichenketten");
if (a.equals(e)) System.out.println(" a und e sind gleiche Zeichenketten");
else System.out.println(" a und e sind nicht gleiche Zeichenketten");
Führt zum Ergebnis:
a und b zeigen auf das gleiche Objekt
a und c zeigen auf das gleiche Objekt
a und d zeigen auf das gleiche Objekt
a und e zeigen nicht auf das gleiche Objekt
a und b sind gleiche Zeichenketten
a und c sind gleiche Zeichenketten
a und d sind gleiche Zeichenketten
a und e sind gleiche Zeichenketten
Der javac Übersetzer erkennt Literale schon beim Übersetzen einer Klasse und speichert sie nur einmal in der .class Datei mit dem Binärcode. Zur Laufzeit eines Javaprogramms wird die entsprechende Klasse bei der ersten Benutzung dynamisch geladen und die konstanten Zeichenketten (Literale) zu einem "Stringpool" hinzugefügt, wenn sie vorher noch nicht im Stringpool existierten. Dieses Verfahren minimiert den Speicherverbrauch und steigert den Durchsatz da Zeichenketten, in der Regel, kleine Objekte sind, die sonst den Freispeicher (Heap) dauerhaft belegen und fragmentieren.
Der Stringpool ist ein spezialisierter Bereich des Freispeichers. Objekte im allgemeinen Freispeicher und im Stringpool verhalten sich gleich. Der einzige Unterschied besteht darin, dass alle Literale im Stringpool nur genau einmal vorkommen.
Wird eine Zeichenkette dynamisch mit new String() angelegt so wird sie wie ein normales Objekt auf dem Freispeicher (Heap) angelegt und nicht im Stringpool. Die Klasse String hat jedoch eine Methode String.intern() die immer auf das kanonisierte Literal im Stringpool zeigt.
Der Zusammenhang zwischen Stringobjekten im Freispeicher wird durch das folgende Beispielprogramm deutlich:
String a = new String("Test");Ausgabe: b,d sind die gleichen Objekte im Stringpool |
![]() |
Die Klasse StringBuffer dient dem effizienten Handhaben von Zeichenketten. Ihre wesentlichen Eigenschaften sind:
Wichtige Methoden Methoden der Klasse Stringbuffer sind:
Die Klasse StringBuffer verfügt über viele weitere Methoden zur Manipulation von Zeichenketten die in der API Dokumentation beschrieben werden.
Tipp: Seit JDK 5.0 steht die Klasse StringBuilder für Anwendungen die extreme Leistung benötigen zur Verfügung. Sie bietet jedoch keine Synchronisation zwischen Threads.
|
(Lizenz) |
7.7.1 Referenzieren, Dereferenzieren und GCImplementieren Sie die Klasse Person mit folgenden Eigenschaften:
|
|
Hauptprogramm mit folgenden Eigenschaften:
|
![]() |
Testen des Programms mit den Optionen -Xms -Xmx -XX:+PrintGC
Beispiel: java -Xms64m -Xmx256m -XX:+PrintGC block7.gc.Main
Die bewirkt das Starten einer Javaanwendung mit
Allgemeine Überlegungen:
Tipp: Benutzen die folgende Beispielmethode zum Auslesen des Speicherverbrauchs.
Variieren Sie den initialen, maximalen Speicher sowie die Anzahl der Generationen um
Anbei ein einfaches Testprogramm welches eine Person ohne Vorfahren erzeugt und dann in einer Schleife neue Personen mit immer mehr Vorfahren erzeugt.
Die Konstante mit den Generationen kann bis zu einem Wert von etwa 20 erhöht werden.
package block7.gc;
public class Test {
/**
* Zu erzeugende Generationen
*/
public static int generationen = 3; // Initial testen
//public static int generationen = 19; // mit -XX:+printGC
//public static int generationen = 22; // mit -Xmx500m -XX:+printGC
//public static int generationen = 23; // mit -Xms500m -Xmx1024m -XX:+printGC
public static void main(String[] args) {
Person p;
systemStatus();
for (int i=0; i<= generationen; i++) {
System.out.println("*** Erzeuge " + i + " Generationen ***");
// Der alte Personenbaum wird implizit dereferenziert
p = new Person(i);
// Verlängern der Laufzeit. Dies erlaubt eine bessere Beobachtung mit jonsole
// Der alte Vorfahrenbaum wird durch die Zuweisung dereferenziert
//p = new Person(i);
//p = new Person(i);
systemStatus();
System.out.println("*** Ende. Erzeuge " + i + " Generationen ***");
}
}
public static void systemStatus(){
Runtime r = Runtime.getRuntime();
System.out.println("*** Systemstatus ***");
System.out.println("* Prozessoren : " + r.availableProcessors());
System.out.println("* Freier Speicher: " + r.freeMemory());
System.out.println("* Maximaler Speicher: " + r.maxMemory());
System.out.println("* Gesamter Speicher: " + r.totalMemory());
System.out.println("*** Ende Systemstatus ***");
}
}
Benutzen Sie den Quellcode der vorhergehenden Übung.
Schreiben Sie eine Klasse mir dem Namen Adresse und den folgenden Eigenschaften
Erweitern Sie die Klasse Person wie folgt:
Wenden Sie den "Copy Constructor" im Hauptprogramm an.
Schreiben Sie einen "Copy Constructor" der maximal n Generationen kopiert.
Erweitern Sie die Klassen aus den vorgehenden Beispielen wie folgt:
Klasse Person
Klasse Adresse
Hauptprogramm
Tipp Die equals() Methode muss aus Typisierungsgründen ein Objekt vom Typ Object übernehmen. Mit dem unten aufgeführten Codestück kann man testen ob der EIngabeparameter vom gleichen Typ wie das aktuelle Objekt ist. Ist dies nicht der Fall so können die Objekte nicht gleich sein.
public boolean equals(Object obj) {
...
if (this.getClass() == obj.getClass() )Die hier gezeigte Übung wird interaktiv in der Vorlesung entwickelt. Die Arbeitsanweisungen sind im Quellcode enthalten.
Der Ausgangspunkt ist die Klasse Ware und die Klasse Lager die als Hauptprogramm dient:

Ziel ist es eine Lösung zu entwickeln in der die Klasse Ware mit einem Lager verwendet werden kann:

package block7;
/**
* Dies ist die Dokumentation der Klasse Ware. Ware dient zum Verwalten von Gütern
* mit Preisen und Namen in einem Lager.
* @author Stefan Schneider
* @version 1.1
* @see Lager
*/
public class Ware {
/*
* 7. Anlegen eine Copy Constructor
* 7.1 Alle Werte des Objekts werden kopiert
* 7.2 Es wird bei Bedarf eine neue Empfehlung angelegt
*/
/**
* Der aktuelle Mehrwertsteuersatz 2010.
* Er liegt zur Zeit bei {@value}.
* @since 1.0
* @version 1.0
*/ public static final double mws = 0.19;
private double nettoPreis; //Deklaration
public boolean halbeMws;
private String name;
public Ware empfehlung;
/**
* Konstruktor fuer die Klasse Ware
* @param n der Name der Ware
* @param np der Nettorpreis
* @param hmws halber Mehrwertsteuersatz für Ware gueltig
*/
public Ware(String n, double np, boolean hmws) {
name = n;
nettoPreis = np;
halbeMws = hmws;
}
/**
* Liefert den Namen einer Ware zurueck.
* @return Name der Ware
*/ public String get_name() {
return name; }
/** * Setzen eines neuen Nettopreis
* @param npr der neue Nettopreis
*/
public void set_nettoPreis(double npr) {
nettoPreis = npr;
}
/**
* Ausdrucken aller Werte auf der Konsole
*/
public void drucken() { drucken(0); }
/**
* Ausdrucken aller Werte auf der Konsole mit vorgebener Einrueckung
* für Empfehlungen
* @param einruecken eingerueckte Stellen für Empfehlungen
*/
private void drucken(int einruecken) {
String leerStellen = "";
for (int i = 0; i < einruecken; i++) {
leerStellen = leerStellen + " ";
}
System.out.println(leerStellen + "Name: " + name);
System.out.println(leerStellen + "netto: " + nettoPreis);
System.out.println(leerStellen + "Brutto: " + bruttoPreis());
System.out.println(leerStellen + "Halbe Mws:" + halbeMws);
if (empfehlung != null) { // Empfohlene Bücher werden eingerückt
empfehlung.drucken(einruecken + 2);
}
}
/**
* Ausgabe des Nettopreis
* @return der Nettopreis
*/ public double nettoPreis() {
return nettoPreis; }
/**
* Ausgabe des Bruttopreis
* @return der Bruttopreis
*/ public double bruttoPreis() {
double bp; //temporaere Variable; keine Klassenvariable
if (halbeMws) {
bp = Math.round(nettoPreis * (mws / 2 + 1) * 100) / 100;
} else {
bp = Math.round(nettoPreis * (mws + 1) * 100) / 100;
}
return bp;
}
}
package block7;
/** * Die Klasse Lager verwaltet eine bestimmte Anzahl von Waren
* @author sschneid
*/
public class Lager {
/* * Aufgaben
* 1. Verwalten von n Waren in einem Feld
* 1.1 Deklarieren eines privaten Feldes
* 1.2 Zugriffsmethoden zum Setzen und Auslesen des Feldes
* 2. Implementieren eines Konstruktors der das Lager
* für n Waren initialisiert
* 3. Anlegen einer Methode die zu einem existierenden Buch
* n verkettete Empfehlungen erzeugt
* 4. Erzeugung eines Singletons zum Erzeugen genau eines Lagers
* 5. Anlegen einer neuen Klasse MainLager
* 5.1 Umkopieren der main() Methode aus der Klasse Lager in die Klasse
* MainLager.main()
* 8. Testen des Copy Constructors
* 8.1 Belegen Sie die ersten 500 Lagerpositionen mit Waren
* 8.2 Klonen Sie die ersten 500 Lagerpositionen und belegen Sie die
* folgenden 500 Lagerpositionen mit Ihnen
* 8.3 Löschen Sie Ihr Lager indem Sie alle Postionen mit null belegen
* 8.4 Implementieren Sie eine Schleife die einige Minuten läuft
* und testen Sie den Speicherverbrauch mit jconsole oder
*/
public static void main(String[] args) {
Ware ware1 = new Ware("Zeitung",12.12,true);
System.out.println("Ware1 ohne Empfehlung:");
ware1.drucken();
double p = ware1.nettoPreis();
Ware ware2 = new Ware("Potter Bd1",31.12,false);
Ware ersterBand = ware2;
ware1.empfehlung= ware2;
System.out.println("Ware1 mit Empfehlung:");
ware1.drucken();
// Erzeugen einer Ware mit 7 verketteten Empfehlungen
Ware ware3;
Ware hp1=ware2;
for (int i=2; i<= 7; i++) {
ware3 = new Ware("Potter Bd" + i,31.25+i,false);
ware2.empfehlung = ware3;
ware2 = ware3;
}
System.out.println("Alle Harry Potter Baende drucken");
hp1.drucken();
}
}
package block7;
/**
* * Eine Hilfsklasse zur Implementierung eines Hauptprogramms
*
* @author sschneid
* @version 1.0
*/
public class MainLager {
/*
* 6. Testen der Klasse Main * 6.1 Aufruf des Singleton der Klasse Lager * 6.2 Einfügen zweier Waren * 6.3 Drucken zweier Waren
*/
public static void main(String[] args) {
Ware ware1 = new Ware("Zeitung", 12.12, true);
ware1.drucken();
double p = ware1.nettoPreis();
Ware ware2 = new Ware("Potter Band 1", 31.12, false);
Ware ersterBand = ware2;
Ware ware3;
for (int i = 2; i <= 7; i++) {
ware3 = new Ware("Potter Band" + i, 31.12, false);
ware2.empfehlung = ware3;
ware2 = ware3;
}
}
}
Das folgende Programm wird wie folgt gestartet
java block7.gc.Main 3 gc
Es erwartet zwei Kommandozeilenargumente. Das erste Argument beschreibt die Anzahl der zu erzeugenden Generationen.
Das zweite Argument is optional. Steht hier GC wird eine System Garbarge Collection angestoßen
package block7.gc;
public class Main {
/**
* Auslesen von Systemparametern
*/
public static void systemStatus(){
Runtime r = Runtime.getRuntime();
System.out.println("*** System Status ***");
System.out.println("Prozessoren : " + r.availableProcessors());
System.out.println("Freier Speicher: " + r.freeMemory());
System.out.println("Maximaler Speicher: " + r.maxMemory());
System.out.println("Gesamter Speicher: " + r.totalMemory());
System.out.println("*** ***");
}
public static void main(String[] args) {
int vorfahren = 0;
boolean systemCollection=false;
Person p1;
systemStatus();
// Parsen des ersten Arguments
// Diese Zeichenkette enthält die ANzahl der Generationen
if (args.length > 0 ) { // Gibt es ein erstes Argument?
try { vorfahren = Integer.parseInt(args[0]);}
catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
// Auslesen des zweiten Arguments
// Steht hier die Zeichenkette GC in Klein- oder Grossschreibung
if (args.length > 1 ) // Gibt es ein zweites Argument
systemCollection = ((args[1].equalsIgnoreCase("gc"));
p1 = new Person(vorfahren);
//p1 = null;
System.out.println();
System.out.println("Erster Schritt erledigt: " + Person.zaehler +
" Instanzen erzeugt");
systemStatus();
if (systemCollection) {
System.out.println("Starte System GC");
System.gc();
}
p1 = new Person(vorfahren);
System.out.println();
System.out.println("Zweiter Schritt erledigt: " + Person.zaehler +
" Instanzen erzeugt");
systemStatus();
}
}
package block7.gc;
public class Person {
Person vater;
Person mutter;
static int zaehler=0;
public Person (int vorfahren)
{
if ( vorfahren>0) {
vater = new Person(vorfahren-1);
mutter = new Person(vorfahren-1);
}
zaehler++;
if (zaehler%1000 == 0) System.out.print(".");
}
}
package block7.gc;
public class Main {
public static void systemStatus(){
Runtime r = Runtime.getRuntime();
System.out.println("*** System Status ***");
System.out.println("Prozessoren : " + r.availableProcessors());
System.out.println("Freier Speicher: " + r.freeMemory());
System.out.println("Maximaler Speicher: " + r.maxMemory());
System.out.println("Gesamter Speicher: " + r.totalMemory());
System.out.println("*** ***");
}
public static void main(String[] args) {
int vorfahren = 0;
boolean systemCollection=false;
Person p1;
systemStatus();
if (args.length > 0 ) {
try { vorfahren = Integer.parseInt(args[0]);}
catch (NumberFormatException e) {
System.err.println("Argument muss Ganzzahl sein");
System.exit(1);
}
}
if (args.length > 1 )
systemCollection = (args[1].equalsIgnoreCase("gc"));
p1 = new Person(vorfahren);
//p1 = null;
System.out.println();
System.out.println("Erster Schritt erledigt: " + Person.zaehler +
" Instanzen erzeugt");
systemStatus();
if (systemCollection) {
System.out.println("Starte System GC");
System.gc();
}
p1 = new Person(p1);
System.out.println();
System.out.println("Zweiter Schritt erledigt: " + Person.zaehler +
" Instanzen erzeugt");
systemStatus();
}
}
package block7.gc;
public class Adresse {
public String ort;
public String plz;
public String strasse;
public String hausnr;
public Adresse (String o, String p, String s, String h){
ort = o;
plz = p;
strasse = s;
hausnr = h;
}
public Adresse (Adresse orig) {
ort = orig.ort;
plz = orig.plz;
strasse = orig.strasse;
hausnr = orig.hausnr;
}
}
package block7.gc;
public class Person {
Person vater;
Person mutter;
String name;
Adresse wohnort;
static int zaehler=0;
public Person (int vorfahren)
{
if ( vorfahren>0) {
vater = new Person(vorfahren-1);
mutter = new Person(vorfahren-1);
}
wohnort = new Adresse("Berlin", "10117","Platz der Republik","1");
name = "John Doe";
zaehler++;
if (zaehler%1000 == 0) System.out.print(".");
}
public Person (Person orig) {
if (orig.vater != null) vater = new Person(orig.vater);
if (orig.mutter != null) mutter = new Person(orig.mutter);
if (orig.wohnort != null) wohnort = new Adresse(orig.wohnort);
name = orig.name;
zaehler++;
if (zaehler%1000 == 0) System.out.print(".");
}
}
public Person (Person orig, int vorfahren) {
if (vorfahren > 0) {
if (orig.vater != null)
vater = new Person(orig.vater, vorfahren-1);
if (orig.mutter != null)
mutter = new Person(orig.mutter, vorfahren-1);
}
if (orig.wohnort != null) wohnort = new Adresse(orig.wohnort);
name = orig.name;
zaehler++;
if (zaehler%1000 == 0) System.out.print(".");
}
class Person {
...
public Person (String name1, String gort, String gplz,
String gstrasse, String gnr){
name = name1;
geburtsort = new Adresse(gort,gplz,gstrasse,gnr);
zaehler++;
if (zaehler%1000 == 0) System.out.print(".");
}
...
public boolean equals(Object obj) {
Person target;
// Type checking is optional for this level of skills
if (this.getClass() == obj.getClass() ) {
target = (Person) obj;
return (name.equals(target.name) &&
(geburtsort.equals(target.geburtsort)));
}
else return false;
}
...
}
public boolean equals(Object obj) {
Adresse target;
// Type checking is optional for this level of skills
if (this.getClass() == obj.getClass() ) {
target = (Adresse) obj;
return (ort.equals(target.ort) &&
plz.equals(target.plz) &&
strasse.equals(target.strasse) &&
hausnr.equals(target.hausnr));
}
else return false;
}package block7;
/**
* * Eine Hilfsklasse zur Implementierung eines Hauptprogramms
*
* @author sschneid
* @version 1.1
*/
public class MainLager {
/*
* 5. Anlegen einer neuen Klasse MainLager * 5.1 Umkopieren der main() Methode
* aus der Klasse Lager in die Klasse MainLager.main()
*/
/*
* 8. Testen des Copy Constructors
* 8.1 Belegen Sie die ersten 500
* Lagerpositionen mit Waren
* 8.2 Klonen Sie die ersten 500 Lagerpositionen und belegen Sie die
* folgenden 500 Lagerpositionen mit Ihnen
* 8.3 Löschen Sie Ihr Lager indem Sie alle Postionen mit null belegen
* 8.4 Implementieren Sie eine Schleife die einige Minuten läuft und testen Sie
* den Speicherverbrauch mit jconsole und jps
*/public static void main(String[] args) {
Lager lager1 = Lager.dasLager();
int position;
for (int j = 0; j < 100000; j++) {
for (int i = 0; i < 500; i++) {
position = lager1.einlagern(new Ware("Buch der Zahl " + i, 2 * i, false));
//System.out.println("Ware auf Position" + position + " eingelagert");
}
for (int i = 0; i < 500; i++) {
position = lager1.einlagern(new Ware(lager1.holen(i)));
//System.out.println("Ware auf Position" + position + " eingelagert");
}
lager1.ausraeumen();System.out.println("Schleife " + j + " von 100000");
}
}
}
package block7.loesung;
/**
* Dies ist die Dokumentation der Klasse Ware. Ware dient zum Verwalten von Gütern
* mit Preisen und Namen in einem Lager.
* @author Stefan Schneider
* @version 1.1
* @see Lager
*/
public class Ware {
/**
* Der aktuelle Mehrwertsteuersatz 2010.
* Er liegt zur Zeit bei {@value}.
*/
public static final double mws = 0.19;
private double nettoPreis; //Deklaration
public boolean halbeMws;
private String name;
public Ware empfehlung;
/**
* Konstruktor fuer die Klasse Ware
* @param n der Name der Ware
* @param np der Nettorpreis
* @param hmws halber Mehrwertsteuersatz für Ware gueltig
*/
public Ware(String n, double np, boolean hmws) {
name = n;
nettoPreis = np;
halbeMws = hmws;
}
/*
* 7. Anlegen eine Copy Constructor
* 7.1 Alle Werte des Objekts werden kopiert
* 7.2 Es wird bei Bedarf eine neue Empfehlung angelegt
*/
/**
* Copy-Konstruktor fuer die Klasse Ware
* @param w die zu klonende Ware
*/
public Ware(Ware w) {
name = w.name;
nettoPreis = w.nettoPreis;
halbeMws = w.halbeMws;
if (w.empfehlung != null)
empfehlung = new Ware(w.empfehlung);
}
/**
* Liefert den Namen einer Ware zurueck.
* @return Name der Ware
*/ public String get_name() {
return name; }
/** * Setzen eines neuen Nettopreis
* @param npr der neue Nettopreis
*/
public void set_nettoPreis(double npr) {
nettoPreis = npr;
}
/**
* Ausdrucken aller Werte auf der Konsole
*/
public void drucken() {
System.out.println("Name: " + name);
System.out.println("netto: " + nettoPreis);
System.out.println("Brutto: " + bruttoPreis());
System.out.println("Halbe Mws:" + halbeMws);
if (empfehlung != null) {
empfehlung.drucken(2);
}
}
/**
* Ausdrucken aller Werte auf der Konsole mit vorgebener Einrueckung
* für Empfehlungen
* @param einruecken eingerueckte Stellen für Empfehlungen
*/ public void drucken(int einruecken) {
String leerStellen = "";
for (int i = 0; i < einruecken; i++) {
leerStellen = leerStellen + " ";
}
System.out.println(leerStellen + "Name: " + name);
System.out.println(leerStellen + "netto: " + nettoPreis);
System.out.println(leerStellen + "Brutto: " + bruttoPreis());
System.out.println(leerStellen + "Halbe Mws:" + halbeMws);
if (empfehlung != null) {
empfehlung.drucken(einruecken + 2);
}
}
/**
* Ausgabe des Nettopreis
* @return der Nettopreis
*/ public double nettoPreis() {
return nettoPreis; }
/**
* Ausgabe des Bruttopreis
* @return der Bruttopreis
*/ public double bruttoPreis() {
double bp; //temporaere Variable; keine Klassenvariable
if (halbeMws) {
bp = Math.round(nettoPreis * (mws / 2 + 1) * 100) / 100;
} else {
bp = Math.round(nettoPreis * (mws + 1) * 100) / 100;
}
return bp;
}
}
package block7;
/** * Die Klasse Lager verwaltet eine bestimmte Anzahl von Waren
* @author sschneid
* @version 1.1
*/
public class Lager {
/*
* Aufgaben
* 1. Verwalten von n Waren in einem Feld
* 1.1 Deklarieren eines privaten Feldes
*/
private int lagerGroesse = 1000;
private Ware[] warenLager;
// 1.2 Zugriffsmethoden zum Setzen und Auslesen des Feldes
public Ware holen(int i) {
if ((i<0) || (i>=lagerGroesse)) return null;
else return warenLager[i];
} public int einlagern(Ware w) { int i=0; while ((i<lagerGroesse) && (warenLager[i] != null)) i++; if (i<lagerGroesse) { warenLager[i] = w; return i; } else return -1; }// 1.3 Methode zum Ausräumen des Lagers
public void ausraeumen() {
for (int i=0; i<lagerGroesse; i++) warenLager[i]=null;
}
/*
* 2. Implementieren eines Konstruktors der das Lager
* für n Waren initialisiert
*/
private Lager() {
// Es wird ein Feld von Referenzen auf Waren angelegt
// Es werden keine Waren angelegt!
warenLager = new Ware[lagerGroesse];
}
/*
* 3. Anlegen einer Methode die zu einem existierenden Buch
* n verkettete Empfehlungen erzeugt
* 4. Erzeugung eines Singletons zum Erzeugen genau eines Lagers
*/
private static Lager meinLager; // Eine Objektvariable die auf das Lager zeigt
static public Lager dasLager() {
if (meinLager== null) meinLager= new Lager();
return meinLager;
}
/*
* 5. Anlegen einer neuen Klasse MainLager
* 5.1 Umkopieren der main() Methode aus der Klasse Lager in die Klasse
* MainLager.main()
* 8. Testen des Copy Constructors
* 8.1 Belegen Sie die ersten 500 Lagerpositionen mit Waren
* 8.2 Klonen Sie die ersten 500 Lagerpositionen und belegen Sie die
* folgenden 500 Lagerpositionen mit Ihnen
* 8.3 Löschen Sie Ihr Lager indem Sie alle Postionen mit null belegen
* 8.4 Implementieren Sie eine Schleife die einige Minuten läuft
* und testen Sie den Speicherverbrauch mit jconsole oder
*/
public static void main(String[] args) {
Ware ware1 = new Ware("Zeitung",12.12,true);
System.out.println("Ware1 ohne Empfehlung:");
ware1.drucken();
double p = ware1.nettoPreis();
Ware ware2 = new Ware("Potter Bd1",31.12,false);
Ware ersterBand = ware2;
ware1.empfehlung= ware2;
System.out.println("Ware1 mit Empfehlung:");
ware1.drucken();
// Erzeugen einer Ware mit 10 verketteten Empfehlungen
Ware ware3;
Ware hp1=ware2;
for (int i=2; i<= 7; i++) {
ware3 = new Ware("Potter Bd" + i,31.25+i,false);
ware2.empfehlung = ware3;
ware2 = ware3;
}
System.out.println("Alle Harry Potter Baende drucken");
hp1.drucken();
// ersterBand.drucken();
}
}
|
(Lizenz) |
Am Ende dieses Blocks können Sie:
|
Sie sind in der Lage die folgenden Fragen zu beantworten:
Definition:
Felder sind Javaobjekte und werden auf dem Heap verwaltet. Dies liegt nahe da Felder variable Größen haben. Sie können also schlecht statisch allokiert werden. Anbei die Speichersicht, die ähnlich zu Strings ist. Das abgebildete Diagramm zeigt ein Feld mit Platz für 6 Ganzzahlen:

Felder sind häufig verwendete Datenstrukturen da man sehr einfach mit Schleifen über den Index alle Feldelemente erreichen kann.
Der Zugriff auf Feldelemente erfolgt über eine Syntax mit Verwendung von rechteckigen [index] Klammern wie man im folgenden Beispiel sehen kann:
int [] d1 = { 1000, 100, 10};
int a = d1[2]; // Auslesen von 10 aus Position 2
d1[0]= 99; // Zuweisen des Werts 99 auf Position 0
Das Definieren und Erzeugen von eindimensionalen Feldern erfolgt nach ähnlichen Prinzipien wie die Verwaltung von Strings.
Es gibt hier zwei Varianten.
Die Deklaration anhand eines Feldes von Ganzzahlen mit Hilfe des new() Operators in 3 Schritten:
int[] array1; |
|
Hiermit wurde nur die Referenz auf ein (zukünftiges) Feld von Ganzzahlen angelegt. Siehe unten: Mit Hilfe des new() Operators wird ein leeres Feld angelegt Nach dem Anlegen des Feldes kann man die Größe des Feldes mit dem Attribut length abfragen.:
array1 = new int[6]; int s = array1.length; |
|
Das Definieren der Variablen array1 und das Aufrufen des new() Operators hätte auch in einem Schritt erfolgen können
int[] array1 = new int[6];
Jetzt kann das Feld mit Werten belegt werden. Zum Beispiel mit Vielfachen der Zahl 17:
for ( int i=0; i<s; i++) {array[i] = i*17;}
|
|
Java erlaubt auch die 3 Schritte von oben in einer einzigen Operation auszuführen. Hier können alle Werte als Aufzählung innerhalb von geschweiften Klammern direkt zugewiesen werden:
int[] array1 = {0,17,34,51,68,85};Arrayvariablen verhalten sich wie Referenzen bezüglich Zuweisungen. Das Zuweisen des Nullwerts löscht die Referenz auf ein Feld(Array). Es wird jetzt ein Kandidat zum Löschen:
int[] array1 = {0,17,34,51,68,85};
int[] array2 = array1;
if (array1[2] == array2[2])
System.out.println("Dieser Text sollte " +
"ausgedruckt werden!"); |
|
Beide Variablen array1 und array2 zeigen auf dasselbe Feld
Durch das Zuweisen des null Werts werden die Referenzen gelöscht. Das Feld bleibt bestehen. Es wird jedoch gelöscht, wenn es keine andere Referenz mehr auf das Feld gibt.
array1 = null; array2 = null; |
|
Das naive Kopieren von Feldern mit vollständiger Replikation kann man mit der erweiterten Notation der for Schleife leicht selbst implementieren:
int[] array1 = {0,17,34,51,68,85};
int[] array2 = new int[array1.length];
for (int i=0; i<array1.length; i++)
{array2[i]= array1[i];}
|
|
Alle Elemente des Felds werden jetzt einzelnen kopiert. Die erweiterte Notation erlaubt es die Anweisung ohne explizite Verwendung der Feldgröße anzugeben. Das Ergebnis hat eine Speicherstruktur wie folgt:
Eine effizientere Möglichkeit bildet die Methode System.arraycopy() aus der System Klasse. Diese Methode hat die folgende Signatur:
public static void arraycopy(Object src,
int srcPos,
Object dest,
int destPos,
int length)
Sie erlaubt:
Das oben aufgeführte Beispiel würde wie folgt mit der Methode arrayCopy() implementiert:
int[] array1 = {0,17,34,51,68,85};
int[] array2 = new int[array1.length];
System.arrayCopy(array1,0,array2,0,array1.length);
Beim Lesen und Manipulieren von Feldern können unterschiedliche Fehler auftreten. Sie können beim Über- oder Unterlauf eines Feldes auftreten oder bei dem Versuch Typen zuzuweisen die das Feld nicht erlaubt. Die folgenden Ausnahmen (Exceptions) können auftreten:
Felder(Arrays) können wie Basistypen oder Referenzen als Übergabe- und Rückgabeparameter von Methoden verwendet werden.
Ein typisches Beispiel ist;
public static void main(String[] args) {
...
if (args != null)
if (args.length > 2 ) {
try {
tag = Integer.parseInt(args[0]);
monat = Integer.parseInt(args[1]);
jahr = Integer.parseInt(args[2]);
...
Die beim Programmstart mitgegebenen Argumente werden in Form eines Feldes von Zeichenketten übergeben. Man erkennt beim Beispiel oben, dass im Quellcode eine Reihe von Fehlerfällen geprüft werden bevor die Anwendung auf das "unbekannte" Feld zugreift und die Zeichenketten in Zahlen verwandelt:
Hierbei ist wichtig, dass die Felder nach dem gleichen Konzept wie Referenzen übergeben werden:
Entsprechend können Felder auch als Rückgabeparameter dienen. Der Rückgabeparameter muss mit Typ und rechteckiger Klammer genannt werden. Hier int[]:
public static int[] createRandom(int size, int range) {
int[] array1 = new int[size];
for (int i=0; i<size;i++)
array1[i] = (int)(Math.random()*range);
return array1;Es gibt Anwendungsfälle bei bei denen es wünschenswert ist eine variable Anzahl von Parameter zu benutzen. Seit Java 5 muss man hier nicht mehr ein Feld übergeben. Für sogenannte "varargs" Methoden gibt es ein "..." Konstrukt (drei Punkte) mit dem man die variable Anzahl der Parameter eines bestimmten Typs deklarieren kann.
Anbei das Beispiel einer Methode, die die Namen von Haustieren entgegen nimmt:
public void hausTiereMelden(String ... tierNamen) {
for ( String t : tierNamen) System.out.println("Haustier: " +t);
}
public static void test () {
hausTiereMelden("Waldi","Bello","Rufus");
hausTiereMelden("Mikesh", "Napoleon");
hausTiereMelden(); // Keine Parameter!
}
Hinweis: "varargs" Methoden dürfen auch ganz ohne Parameter aufgerufen werden. Die Implementierung der Methode muss daher auch mit einer null Belegung umgehen können!
Eindimensionale Felder entsprechen Listen mit einem wahlfreien Zugriff. Java erlaubt auch die Benutzung von zwei- und mehrdimensionalen Feldern.
Ein zweidimensionales Feld besteht entsprechend aus Zeilen und Spalten. Die Java Syntax hierzu ist eine Konkatenierung von eckigen Klammern. Zur Erzeugung eines Feldes mit 3 Zeilen und 6 Spalten nutzt man die folgende Notation:
int[][] array1 = new int[3][6];
Hiermit ergibt sich eine Speicherstruktur die aus einem Spaltenfeld besteht welches alle Zeilenfelder enthält um eine zweidimensionale Struktur aufzuspannen. Man kann hier jetzt 3*6=18 Werte speichern:

Das Setzen und Lesen von Werten geschieht wie folgt:
int k = 222; array2[2][5]= k; int n = array2[0][0];
Auch bei mehrdimensionalen Feldern ist eine aufzählende Initialisierung möglich:
int[][] array1 = {{1,2,3,4,5,6},{5,10,15,20,25,30},{10,20,30,40,50,60}}Hiermit ergibt sich eine zweidimensionale Tabelle mit der folgenden Belegung:
Das Attribut length erlaubt auch bei mehrdimensionalen Feldern die Bestimmung der Größe. Hier liefert das Attribut jedoch nicht die Gesamtgröße der Datenstruktur sondern nur die Größe einer bestimmten Dimension.
int d =array1.length;
liefert im oben gezeigten Beispiel eine 3 für die erste Dimension, die Zeilen.
int d = array1[2].length;
gibt eine 6 zurück. Da die dritte Zeile 6 Elemente hat.
Anmerkung: Höher dimensionale Felder haben nicht unbedingt in allen Dimensionen die gleiche Größe. Dies bedeuted, dass ein zweidimensionales Feld muss nicht rechteckig sein muss!
|
Die folgende Implementierung erzeugt ein "dreieckiges" zweidimensionales Feld: int[][] array1; array1 = new int[3][]; array1[0] = new int[1]; array1[1] = new int[2]; array1[2] = new int[3]; |
Die Speicherstruktur zu diesem Feld sieht wie folgt aus:
|
Eine aufzählende Initialisierung ist auch möglich:
int[][] array1 = {{1},{11,22},{111,222,333}};
Das length Attribut liefert bei dieser Struktur die unterschiedlichsten Ergebnisse.
Nach dem gleichen Verfahren können dreidimensionale oder noch höherdimensionale Felder erzeugt und verwaltet werden.
Person[][][] Mitarbeiter = new Person[10][10][10];
Mitarbeiter[7][8][9] = new Person ("Jane","Doe");Im vorliegenden Beispiel handelt es sich um ein dreidimensionales Feld mit einem Objekttyp (Person). Bei Objektfeldern werden nur die Felder angelegt nicht aber die zugehörigen Objekte. Sie müssen individuell erzeugt werden. Nach der initialen Felderzeugung sind alle Felder mit Nullreferenzen belegt. Mit dieser Belegung lässt sich normalerweise schlecht arbeiten. Bei Basistypen ist dies anders. Sie werden auch auf Null initialisiert und können direkt verwendet werden. Bei Feldern mit Referenzen werden jedoch nicht direkt die benötigten Objekte angelegt.
|
(Lizenz) |
8.4.1 Telefonbuchanwendung (1)Implementieren Sie eine Telefonbuchanwendung die es erlaubt die folgenden Datensätze zu verwalten:
|
Die Klasse Telefonbuch soll die folgenden Eigenschaften haben:
Hinweise:
Tipp: Die Suchanfragen lassen sich mit wenig Aufwand vom einer grafischen Swingoberfläche steuern. Ein Beispielprogramm finden Sie hier.
Überarbeiten Sie die Telefonbuchanwendung aus der vorhergehenden Aufgabe derart, dass Sie:
Welche der beiden Lösungen gefällt Ihnen besser? Warum?
Überarbeiten die vorhergehende Lösung derart, dass Sie statt einem Feld mit unsortierten Personen,
drei Personen-Felder haben, die jeweils immer sortiert sind. Erweitern Sie die Ausdruckmethode derart, dass das Addressbuch nach allen drei Kriterien ausgegeben wird.
Schreiben Sie ein Programm welches die Zuverlässigkeit des Java Zufallszahlengenerator prüft.
Tipp: Machen Sie die Größe des dreidimensionalen Feld konfigurierbar und Beginnen Sie mit einem kleinen Kubus(Feld).
Das "Spiel des Lebens" wurde 1970 vom Mathematiker John Horton Conway 1970 entworfen.
Das Simulationsspiel basiert auf auf einem zweidminensionalen zellulären Automaten. Die Regeln des Automaten sind dem Wikipediaartikel zu entnehmen.
Die gelöste Aufgabe kann man im unten aufgeführten Applet testen.
Das Applet kann auch als eigenständiges Hauptgramm ausgeführt werden. Laden Sie das jar-Archiv Conway.jar und starten Sie es im gleichen Verzeichnis mit dem Kommando
java -jar Conway.jar
Vervollständigen die Klasse Generation.java. Nutzen Sie das Hauptprogramm SpielDesLebens.java zum Testen Ihrer Klasse.
Die Interaktion der Klasse Generation mit dem Rahmenprogramm ist im folgenden Diagramm dargestellt:

Hinweise:
Beim Berechnen der Nachbarn eines Feldes ist auf die Sonderfälle am Rand zu achten:

Weitere Hilfestellungen sind den Kommentaren zu entnehmen. Die Klasse kann natürlich auch ohne die Hilfestellung entwickelt werden. Das Feld kann initial zum Testen sehr klein (2) sein. Die Buttons werden dann erst nach dem Vergrößern des Fenster sichtbar. Eine Größe von 50x50 ist für aktuelle Rechner ausführbar. Pro Zelle werden etwa 10 Pixel benötigt.
package block8;public class Generation {
// Hier ein Feld für alten Zustand deklarieren
// Hier ein Feld für neuen Zustand deklarieren
// die Felder muessen zweidimensional, vom Typ boolean sein, quadratisch sein
/**
* Groesse des quadratischen Feldes
*/
// Variable für Groesse des Feldes anlegen. Empfohlen 50 ==> GUI benötigt dann etwa 500 Pixel
/**
* Anlegen aller benoetigten Felder mit Initialwerten
* Alle Feldelemente sollen mit dem Zustand "false" = leer versehen sein
*/
public Generation() {
// Initialisieren sie die beiden Felder
// alle Felder sollen den Zustand "false" haben. Dies ist ein leeres Feld
}
/**
*
* @return Kantenlaenge des quadratischen Felds
*/
public int groesse() {return 0;} //Richtigen Wert zurueckgeben!!
/**
* Berechnen einer neuen Generation.
* Legen Sie ein neues Feld an. Berechnen Sie den neuen Zustand
* aller Feldelement aus dem alten Feld
*/
void neueGeneration() {
// Tipps:
// Weisen Sie die aktuelle Generation auf die alte zu
// Erzeugen oder wiederverwenden Sie ein Feld für eine neue Generation
// Nutzen Sie eine doppelt geschachtelte Schleife zum Ueberstrichen des aktuellen Felds
// Zaehlen Sie die Nachbarn der aktuellen Position in der alten Generation
// Achten Sie auf die Feldgrenzen!!
// Setzen Sie den Wert des aktuellen Felds auf "true" falls ein Objekt erhalten oder erzeugt werden soll
// Setzen Sie dem Wert des aktuellen Felds auf "false" falls kein Objekt in der neuen Generation existieren soll
}
/**
* Das Feld mit den aktuellen Werten
* @return
*/
public boolean[][] status() {return null;} // Hier das aktuelle Feld zurückgeben
}
Das Hauptprogramm. Achten Sie auf die Paketstruktur!
Beim vorgebenen Package kann das Programm mit dem folgenden Befehl gestartet werden
$ java block8.SpielDesLebens
package block8;import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.JButton;
import javax.swing.Icon;
import javax.swing.*;public class SpielDesLebens extends JApplet implements Runnable {
private int size;
private int xRaster=10;
private int yRaster=10;
private Generation gen;
private JButton[][] buttonFeld;
private static boolean appletMode = true;
private static boolean autoMode = false;
private ImageIcon belegtIcon;
private ImageIcon freiIcon;
private static SpielDesLebens myself;
private int sleeptime = 2000; // Millisekunden im Automode
public class Zelle extends JButton {
public int x;
public int y;
public Zelle (Icon ic, int x, int y) {
super(ic);
this.x=x;
this.y=y;
}
}
/**
* Der Konstruktor ohne Argumente wird nur beim einem Start als Applet
* benutzt. Hier wird ein Applet mit einem Grid erzeugt.
*/
public SpielDesLebens() {
erzeugeIcons();
myself=this;
gen = new Generation();
size = gen.groesse();
JFrame f = null;
if (!appletMode) f = new JFrame("Game");
JPanel jp = new JPanel();
jp.setLayout(new GridLayout(size, size));
buttonFeld = new JButton[size][size];
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
buttonFeld[i][j] = createButton(i, j);
jp.add(buttonFeld[i][j]);
}
}
JButton naechste = new JButton(">");
naechste.setToolTipText("Erzeuge nächste Generation");
naechste.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) { nextGen();}
} // Ende innere Klasse
);
JButton auto = new JButton(">>>");
auto.setToolTipText("Starte Film");
auto.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
sleeptime /=2; // Verdoppeln der Geschwindigkeit
if (!autoMode) {
autoMode=true;
Thread t1 = new Thread(SpielDesLebens.myself);
t1.start();
}
}
} // Ende innere Klasse
);
JButton stop = new JButton("Stop");
stop.setToolTipText("Stoppe Film");
stop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
autoMode=false;
sleeptime=4000;
}
} // Ende innere Klasse
);
JPanel buttonPanel = new JPanel();
buttonPanel.add(naechste);
buttonPanel.add(auto);
buttonPanel.add(stop);
Container co;
if (!appletMode) co =f;
else co=this;
co.setLayout(new BorderLayout());
co.add(jp,BorderLayout.CENTER);
co.add(buttonPanel,BorderLayout.SOUTH);
co.setPreferredSize(new Dimension(size * (xRaster+3),size * (yRaster+3)));
if (!appletMode) {
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
}
/**
* Starten der Anwendung als eigenständiges Programm
* @param args
*/
public static void main(String[] args) {
appletMode = false;
SpielDesLebens k = new SpielDesLebens();
}
/**
* Erzeugen eines JButtons für jede Zelle des Feldes
* @param xx x Koordinate im Feld
* @param yy y Koordinate im Feld
* @return
*/
private JButton createButton(int xx, int yy) {
JButton myButton = new Zelle(freiIcon,xx,yy);
myButton.setToolTipText(("("+xx+","+yy+")"));
myButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
if(!autoMode) {
Zelle f = (Zelle) ae.getSource();
//System.out.println("Action auf" +f.x + " " + f.y);
boolean[][] g = gen.status();
if (g[f.x][f.y]) {
f.setIcon(freiIcon);
g[f.x][f.y]=false;
}
else {
f.setIcon(belegtIcon);
g[f.x][f.y]=true;
}
f.updateUI();
}
}
} // Ende innere Klasse
);
return myButton;
}
/**
* Erzeuge die beiden Ikonen für eine freies und ein belegtes Feld
*/
public void erzeugeIcons() {
BufferedImage belegt =
new BufferedImage(xRaster, yRaster, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g = belegt.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, xRaster-1, yRaster-1);
g.setColor(Color.black);
g.fillOval(1, 1, xRaster-2, yRaster-2);
g.dispose();
belegtIcon = new ImageIcon(belegt);
BufferedImage frei =
new BufferedImage(xRaster, yRaster, BufferedImage.TYPE_4BYTE_ABGR);
g = frei.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, xRaster-1, yRaster-1);
g.dispose();
freiIcon = new ImageIcon(frei);
}
/**
* Erzeugen einer neuen Generation und Abgleich der JButtons mit neuen
* Ikonen
*/
private void nextGen() {
gen.neueGeneration();
boolean[][] stat = gen.status();
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++)
if (stat[i][j]) buttonFeld[i][j].setIcon(belegtIcon);
else buttonFeld[i][j].setIcon(freiIcon);
}
/**
* Lasse neue Generationen automatisiert in einem eigen Thread
* erzeugen
*/
public void run() {
try {
while (autoMode) {
Thread.sleep(sleeptime);
nextGen();
}
} catch (InterruptedException e) {
}
}
}
Erweitern Sie die Methode neueGeneration()
Jetzt sollte das Umkopieren von neuen auf alte Generationen funktionieren. Alle Schleifen sollten fehlerfrei laufen
Erweitern Sie die Methode neueGeneration()
package block8;public class Generation {
private boolean[][] alt;
private boolean[][] aktuell;
/**
* Groesse des quadratischen Feldes
*/
private int size = 50;;
/**
* Anlegen aller benoetigten Felder mit Initialwerten
* Alle Feldelemente sollen mit dem Zustand "false" = leer versehen sein
*/
public Generation() {
aktuell= new boolean[size][size];
alt = new boolean[size][size];
for (int i=0; i<size; i++)
for (int j=0; j<size; j++) {
aktuell[i][j]= false;
alt[i][j]= false;
}
}
/**
*
* @return Kantenlaenge des quadratischen Felds
*/
public int groesse() {return size;}
/**
* Berechnen einer neuen Generation.
* Legen Sie ein neues Feld an. Berechnen Sie den neuen Zustand
* aller Feldelement aus dem alten Feld
*/
void neueGeneration() {
alt=aktuell;
aktuell= new boolean[size][size];
for (int i=0; i<size; i++)
for (int j=0; j<size; j++) {
aktuell[i][j]= false;
// Zaehle Nachbarn
int nachbar=0;
if ((i>0) && alt[i-1][j]) nachbar++; //links
if ((i+1<size)&& alt[i+1][j]) nachbar++; //rechts
if ( (j>0) && alt[i][j-1]) nachbar++; //oben
if ( (j+1<size)&& alt[i][j+1]) nachbar++; //unten
if ((i>0) &&(j>0) && alt[i-1][j-1])nachbar++; //links,oben
if ((i>0) &&(j+1<size) && alt[i-1][j+1])nachbar++; //links, unten
if ((i+1<size)&&(j>0) && alt[i+1][j-1])nachbar++; //rechts, oben
if ((i+1<size)&&(j+1<size) && alt[i+1][j+1])nachbar++; //rechts, unten
// Übernehmen des alten Status als Default
aktuell[i][j] = alt[i][j];
// Geburt einer neuen Zelle
if((!alt[i][j]) && (nachbar==3)) aktuell[i][j]=true;
// Tod einer alten Zelle wegen Einsamkeit
if((alt[i][j]) && (nachbar<2)) aktuell[i][j]=false;
// Tod einer alten Zelle wegen Überbevölerung
if((alt[i][j]) && (nachbar>3)) aktuell[i][j]=false;
}
}
/**
* Das Feld mit den aktuellen Werten
* @return
*/
public boolean[][] status() {return aktuell;}
}
|
(Lizenz) |
Am Ende dieses Blocks können Sie:
|
Sie sind in der Lage die folgenden Fragen zu beantworten:
Vererbung ist ein wichtiger Bestandteil der objektorientierten Programmierung. Vererbung ergänzt die bisher vorgestellten Konzepte der
um ein weiteres Strukturierungsmittel.
Der erste Abschnitt beschäftigt sich mit dern Konzepten der Vererbung, die folgenden beschreiben die Implementierung in Java.
Die Ziele die mit Vererbung in der objektorientierten Programmierung verfolgt werden sind:
|
Beispiel einer Vererbungshierarchie:
|
Die gleiche Begriffshierarchie lässt sich auch als Mengendiagramm darstellen:
|
Alle Mitglieder einer Teilmenge haben die gleichen Eigenschaften.
Das Mengendiagramm verdeutlicht, dass es sich bei der Vererbungshierarchie um eine "Ist-ein" Relation handelt.
|
Die Konzepte der Vererbungshierarchien lässt sich leicht auf Klassen abbilden
Das Diagramm rechts verdeutlicht die Beziehungen zwischen den Klassen, ihrer Bedeutung und der Typisierung. |
![]() |
Programmiersprachen wie C++ erlauben die Benutzung von Mehrfachvererbung. Die Sprache Java erlaubt jedoch nur eine einfache Vererbung.
Das Konzept der Mehrfachvererbung durchbricht zu einem gewissen Grad das Konzept der Spezialisierung-Generalisierung. Bei der Einfachvererbung sind Personen Mitarbeiter oder Berater aber nicht beides gleichzeitig. Bei der Mehrfachvererbung mit einer einzelnen Basisklasse werden Unterklassen zuerst als disjunkte Klassen definiert. Später sollen Unter-Unterklassen jedoch die gleichen Eigenschaften über verschieden Vererbungspfade erben.
Mehrfachvererbung erhöht die Mächtigkeit sowie die Komplexität einer Sprache erheblich. Es kann hier leicht zu Effekten kommen die ein Entwickler nicht als intuitiv empfindet. Das Risiko des Fehlverhaltens einer Anwendung steigt hierdurch.
Beim Entwurf der Sprache Java wurde bewusst auf Mehrfachvererbung verzichtet um die Sprache einfach zu halten und um die Implementierung eines effizienten Laufzeitsystems zu vereinfachen.
Die Java Interface (Schnittstellen) Technologie bietet eine "Hintertür" zur Implementierung von Fällen in denen eine Mehrfachvererbung wünschenswert ist.
Die Vererbungsbeziehung wird in Java mit dem Schlüsselwort extends beschrieben. Beispiel:
class Mitarbeiter extends Person {
/* Klassenimplementierung */
}
Die Klasse Mitarbeiter ist eine Spezialisierung der Klasse Person. Sie erbt die öffentlichen Attribute und Methoden der Klasse Person.
Alle Klassen in Java erben direkt oder indirekt von der Java Basisklasse Object. Wird bei einer Klassendeklaration keine extends Klausel angegeben so wird die Klasse automatisch von der Klasse Object abgeleitet.
... stellt für alle Javaklassen eine minimale Infrastruktur zur Verfügung:
Oberklassen vererben öffentliche (public, protected) Methoden und Attribute an Unterklassen. Das heißt, dass sie wie eigene Attribute verwendet werden können.
Die Beispielklasse Person verfügt über:
public class Person {
private String name;
private String firstName;
public int age;
public Person(String ln, String fn) {
name = ln; firstName = fn; }
public Person() { this("Doe","John");}
public void setName(String ln, String fn) {
name = ln; firstName = fn; }
public String fullName() {return (name + " " + firstName);}
}Die Klasse Employee soll jetzt die öffentlichen Eigenschaften der Oberklasse Person nutzen. So kann eine redundante Programmierung der schon existierenden Funktionalität vermieden werden.
public class Employee extends Person {
private String employeeId;
public Employee(String ln, String fn, String EmpId, int a) {
super(ln,fn); // Java ruft hier den Konstruktor der Oberklasse auf
employeeId = EmpId;
age = a;
}
public String getEmployeeId() {return employeeId;}
public String printAll() {
return (
fullName() + " " +
employeeId + " " +
age);}
}
Die so implementierte Klasse kann wie folgt verwendet werden:
public class MainEmployee {
public static void main(String[] args) {
Employee ceo = new Employee("Doe","Jane", "1", 25);
Employee cto = new Employee("Miller","John","2", 30);
cto.age++;
System.out.println(ceo.printAll());
System.out.println(cto.printAll());
}
}Ergibt die Konsolenausgabe:
Doe Jane 1 25 Miller John 2 31
Alle öffentlichen Attribute und Methoden (Beispiel age) der Klasse Person können in der Klasse Employee genutzt werden als ob es eigene/lokale sind.
Die Vererbung funktioniert jedoch nicht rückwärts. Man kann einer Instanz von Person keine Angestelltennummer (employeeId) zuweisen. Sie existiert in der Klasse Person nicht.
"Vertragsrecht": Alle Instanzen von Employee müssen sich auch genau wie Instanzen von Person verhalten.Sie sind auch Instanzen der Klasse Person. Andere Konsumenten im Programm verlassen sich auf dieses Verhalten, diesen "Vertrag". Dies bedeutet:
| Implementierung ohne Vererbung | Implementierung mit Vererbung |
|---|---|
|
Hier eine Implementierung der Klasse Employee ohne Vererbung. Die Implementierung entspricht der rechten Implementierung mit Vererbung. Die Methoden der Klasse haben hier aber direkten Zugriff auf die privaten Attribute name, firstName. |
Eine Implementierung der Klasse Employee mit Vererbung der Klasse Person Vererbung |
Private Attribute werden nicht vererbt!
Sie sind nur innerhalb der Klasse sichtbar. Eine Unterklasse hat keinen priviligierten Zugriff auf private Methoden. Sie werden nicht vererbt. Eine Unterklasse muss wie jede andere Klasse über vorhandene öffentliche Zugriffsmethoden auf die privaten Attribute zugreifen.
Konstruktoren werden in Java nicht vererbt. Konstruktoren müssen für jede Unterklasse implementiert werden. Die Konstruktoren der Unterklasse müssen jedoch in der Lage sein existierende Konstruktoren der Oberklasse aufzurufen, da sie für die Initialisierung der Oberklasse notwendig sind. Hierzu wird das Schlüsselwort super() verwendet.
Hiermit kann man jetzt die Klasse Employee() eleganter und robuster Initialisieren
public class Employee extends Person {
private String employeeId;
public Employee(String ln, String fn, String EmpId, int a) {
super(fn,ln); // Java ruft hier den Konstruktor der Oberklasse auf
employeeId = EmpId;
age = a;
}
public void printAll() {
System.out.println (
fullName() + " " +
employeeId + " " +
age);
}
Wichtig: Der Aufruf des Konstruktors der Oberklasse muss als erster Befehl im Konstruktor der Unterklasse stehen. Die Attribute der Oberklasse müssen als erstes initialisiert werden, bevor die Attribute der Unterklasse initialisiert werden. Nur so kann gewährleistet werden dass sich Instanzen der Oberklasse korrekt verhalten. Würde der Aufruf des Konstruktors der Oberklasse nicht als erstes geschehen, könnte die Unterklasse die Oberklasse inkorrekt manipulieren.
Konstruktoren müssen nicht einen bestimmten Konstruktor der Oberklasse explizit aufrufen.
Wird kein super() Aufruf implementiert, so generiert der Übersetzer automatisch einen super() Aufruf der den Standard(default)konstruktor aufruft.
Hiermit wird immer ein vollständiges Durchlaufen der Klassenhierachie beim Initialisieren gewährleistet.
Besitzt die Oberklasse nur Konstruktoren mit Übergabeparameter so muss einer dieser Konstruktoren über super() aufgerufen werden.
Im folgenden Beispiel kann man die Initialisierungskette einer Klasse C sehen die aus B abgeleitet wird. B wird aus A abgeleitet:
public class A {
public A() {System.out.println("Constructor A called");}
}
public class B extends A{
public B() {System.out.println("Constructor B called");}
}
public class C extends B {
public C() {System.out.println("Constructor C called");}
}
Das erzeugen einer Instanz von C:
C c = new C();
Triggert die folgenden Ausgaben:
Constructor A called Constructor B called Constructor C called
Das im vorgehenden Abschnitt vorgestellte Konzept des Vererbens ging immer von einer Erweiterung der Klasse aus. Das heißt mehr und mehr Attribute und Methoden kommen hinzu.
Es kommt jedoch vor, das eine spezialisierte Klasse eine existierende Methode der Oberklasse "verfeinern" möchte. Dieses Konzept der Implementierung einer Methode oder Attributs welches eine Methode oder Attribut aus der Oberklasse neu implementiert, wird in der objektorientierten Programmierung Überschreiben (Overriding) genannt.
Java erlaubt die Methode einer Oberklasse zu überschreiben, das bedeutet sie zu verdecken, wenn die folgenden Bedingungen für die Deklaration der Methode erfüllt sind
Der Methodenrumpf kann mit einer beliebigen Implementierung ausgeführt werden. Mit dieser Technik kann eine Unterklasse eine eigene Implementierung für eine allgemeinere Methode einer Überklasse zur Verfügung stellen.
Im vorhergehenden Fall der Klasse Employee kann man eine spezialisierte Klasse Manager implementieren, die ein zusätzliches Budget verwalten kann. Das Budget soll ebenfalls mit der Methode printAll() ausgeben werden.
public class Manager extends Employee {
public int budget;
public Manager(String ln, String fn, String EmpId, int a, int b) {
super(ln, fn,EmpId,a);
budget = b;
}
public String printAll()
{ return (
fullName() + " " +
getEmployeeId() + " " +
age + " " +
budget); }
}Das Hauptprogramm:
public class Main {
public static void main(String[] args) {
Employee ceo = new Employee("Doe","Jane", "1", 25);
Employee cto = new Employee("Miller","John","2", 30);
Employee man1 = new Manager("Johnson","Susan","3", 29, 30000);
cto.age++;
System.out.println(ceo.printAll());
System.out.println(cto.printAll());
System.out.println(man1.printAll());;
}
}...erzeugt die folgenden Ausgaben:
Doe Jane 1 25 Miller John 2 31 Johnson Susan 3 29 30000
In vielen Fällen sind überladene Methoden miteinander verwandt und eine Benutzung der Methode der Oberklasse ist gewünscht um Codereplikation und Redundanz zu vermeiden. Hierfür existiert das Schlüsselwort super. Es erlaubt das Aufrufen der überschriebenen Methode mit der folgenden Syntax:
super.MethodenName(para_1, ..,para_n)
Im Fall der Klasse Manager kann man die printAll() Methode mit dem Schlüsselwort super vereinfachen:
public class Manager extends Employee {
public int budget;
public Manager(String ln, String fn, String EmpId, int a, int b) {
super(ln, fn,EmpId,a);
budget = b;
}
public String printAll()
{ return ( super.printAll() + " " + budget); }
}
Hinweis: Die Syntax super.super.methodenname() ist nicht möglich. Man kann nicht die Methode einer Ober-Oberklasse unter Auslassung der Oberklasse aufrufen.
Da in Java alle Klassen einzeln übersetzt werden können, kann man erst zur Laufzeit entscheiden welche Methode aufgerufen werden muss (dynamic invocation). Die Laufzeitumgebung geht bei jedem Aufruf wie folgt vor
Für das Überschreiben von Attributen gelten die gleichen Regeln wie für das Überschreiben von Methoden:
Private Attribute werden nicht vererbt. Sie sind der Unterklasse weder bekannt noch zugänglich!
Der Zugriff auf ein überschriebenes Attribut der Oberklasse geht symmetrisch zu den Methoden mit dem super Schlüsselwort. Beispiel:
a = super.a * 2;
Java bietet die Möglichkeit Klassen und Methoden zu implementieren die nicht mehr abgeleitet oder überschrieben werden dürfen. Hierzu dient das Schlüsselwort final.
Mit dem Schlüsselwort final man das Überschreiben der Methode in einer Unterklasse unterbinden. Ein typsiches Beispiel hierfür ist:
public final void print() { /* Methodenrumpf */}Unterklassen mit überschriebenen Methoden sind ein sehr mächtiges Konzept für den Entwickler einer Unterklasse. Eine Unterklasse kann inhaltlich eine Oberklasse in weiten Bereichen neu implementieren, solange die Signaturen der Methoden gewahrt bleiben und ein Konstruktor der Oberklasse benutzt. Die Kontrolle liegt fast vollständig in den Händen der Unterklasse. das Schlüsselwort final erlaubt dem Entwickler der Oberklasse
Dem Entwickler der Unterklasse steht es nach wie vor frei Methoden mit anderen Signaturen zu verwenden.
Finale Klassen verbieten die Spezialisierung einer Klasse durch eine Unterklasse.
Das Schlüsselwort final kommt im folgenden Beispiel zum Einsatz:
final class chiefExecutiveOfficer extends Manager { /* Klassen Implementierung */}
Abstrakte Klassen und Java-Schnittstellen (Stoff des zweiten Semester) dienen in Java zur Modularisierung und damit oft zur Arbeitsteilung zwischen Entwicklern.Abstrakte Klassen erlauben es dem Entwickler eine Oberklasse zu implementieren die nicht selbst instanziiert werden darf. Dieses Konzept erlaubt es Klassen als Vorlagen zu implementieren und gleichzeitig zu erzwingen, dass die Entwickler der Unterklasse fehlende oder eigene Methoden und Attribute zur Verfügung stellen. Hiermit lassen sich allgemeine Konzepte implementieren die zu allgemein sind um Instanziierungen zu erlauben, Dies erfolgt mit dem Schlüsselwort abstract bei der Spezifikation der Klasse.
Die Klasse Person im vorhergehenden Beispiel ist eine Klasse von der man nicht erlauben möchte, dass sie instanziiert wird. Dies geschieht zum Beispiel so:
public abstract class Person {
private String name;
private String firstName;
public int age;
public Person(String ln, String fn) {
name = ln; firstName = fn; }
public Person() { this("Doe","John");}
public void setName(String ln, String fn) {
name = ln; firstName = fn; }
public String fullName() {return (name + " " + firstName);}
}
Versucht man nun eine Person zu instanziieren:
Person p1 = new Person("persona","nongrata");
...führt dies beim Übersetzen zu folgendem Fehler:
javac Main.java
Main.java:8: Person is abstract; cannot be instantiated
Person p1 = new Person("persona","nongrata");
^
1 error
Abstrakte Klassen haben die typischen Eigenschaften von normalen Javaklassen. Sie haben Attribute und Methoden die vererbt werden können.
| Wichtige Eigenschaften abstrakter Klassen |
|---|
|
Die Systemklasse Number: Die Klasse Number enthält gemeinsame Methoden für zahlenartige Typen. Man kann sie jedoch nicht direkt instanziieren. Die daraus abgeleiteten Klassen Byte, Float etc. sind final. Sie sind hochoptimiert und sollen aus Performance- und Sicherheitsgründen nicht spezialisiert werden.

Normale Methoden und Attribute werden an die Unterklasse vererbt und können auch bei Bedarf überschrieben werden.
Wird jedoch eine Methode mit dem Schlüsselwort abstract gekennzeichnet so muss sie von einer nicht abstrakten Unterklasse implementiert und überschrieben werden. Die Klasse Person kann so erzwingen, dass eine printAll() Methode für alle Unterklassen implementiert muss ohne sie selbst zu implementieren.
public abstract class Person {
private String name;
private String firstName;
public int age;
public Person(String ln, String fn) {
name = ln; firstName = fn; }
public Person() { this("Doe","John");}
public void setName(String ln, String fn) {
name = ln; firstName = fn; }
public String fullName() {return (name + " " + firstName);}
public abstract String printAll();
}
Regel: Eine Unterklasse einer abstrakten Klasse muss:
oder
Die Möglichkeiten der Vererbung sind sehr ausdrucksstark, haben viele Vorteile, bergen aber auch Risiken. Hierbei muss man mindestens drei verschieden Entwicklerrollen beachten:
Unterklassen müssen Spezialisierungen der Oberklassen sein. Sie müssen sich in allen semantischen Belangen wie die Oberklasse verhalten.
Änderungen an Oberklassen sind heikel, da sie die Implementierungen der Unterklassen invalidieren können:
Wichtig: Die Modellierung einer Klassenhierarchie muss sehr sorgfältig geschehen. Das möglichst exakte Abbilden realer Hierarchien ist für eine langlebige und wartbare Implementierung sehr wichtig.
Vererbung birgt auch Risiken da eine Klassenhierarchie relativ starr ist und individuelle Änderungen weitreichende Auswirkungen haben können.
Vererbung sollte immer dann eingesetzt werden wenn man eine "ist-ein" Beziehung modellieren möchte.
In vielen Fällen liegt aber auch eine "hat-ein" Beziehung vor, eine Assoziation. Hier sollte man erwägen zwei Objekte zu erzeugen und sie über eine Referenz zu verwalten.
Im vorhergehen Beispiel ist es eine Überlegung wert, das Budget der Klasse Manager als Referenz von der Klasse Employee auf eine Klasse Budget zu implementieren. Hierbei sind die folgenden Dinge abzuwägen:
|
(Lizenz) |
9.6.1 Aufruf von überschriebenen MethodenImplementieren sie die Klasse TopClass mit den folgenden Eigenschaften
|
|
Implementieren Sie die Klasse LowClass welche aus TopClass abgeleitet mit den folgenden Eigenschaften
Implementieren Sie ein Hauptprogramm, dass folgende Befehle ausführt
|
![]() |
Zu beobachten:
package block9;
/**
*
* @author sschneid
*/
public class MainTop {
private static int size = 3;
public static void main(String[] args) {
TopClass[] feldT = new TopClass[size];
LowClass[] feldL = new LowClass[size]; for ( int i=0; i<size; i++) feldT[i]= new TopClass(); for ( int i=0; i<size; i++) {feldL[i]= new LowClass();
feldL[i].test();
}
System.out.println(TopClass.getZaehler() + " Instanzen TopClass generiert, "+
LowClass.getZaehler() + " Instanzen LowClass generiert");
}
}
Implementieren Sie eine Klasse Point mit den folgenden Eigenschaften:
Implementieren Sie eine Klasse CircleIsPoint die die Eigenschaften der Klasse Point hat und zusätzlich einen Radius verwalten kann mit folgenden Eigenschaften:
Implementieren Sie eine Klasse CircleHasPoint die die Eigenschaften der Klasse Point hat und zusätzlich einen Radius verwalten kann mit folgenden Eigenschaften:
Was unterscheidet beide Implementierungen? Welche ist die bessere Implementierung?

Die drei obigen Klassen sollten mit der folgenden main() Methode funktionieren:
package block9;
public class Main {
public static void main(String[] args) {
Point p1 = new Point (2.2, 3.3);
Point p2 = new Point (2.22, 3.33);
CircleIsPoint cip1 = new CircleIsPoint(4.4,5.5,6.6);
p1.print();
cip1.print();
CircleHasPoint chp1 = new CircleHasPoint(44.4,55.5,66.6);
chp1.print();
}
}
Dieses Programmierbeispiel wird in der Vorlesung schrittweise entwickelt. Die Arbeitsanweisungen sind als Kommentare im Programmcode eingefügt.
Die Ausgangssituation:

Das Hauptprogramm wird zu Beginn die Klasse Buch noch nicht verwenden.
package block9;
/**
* Eine Hilfsklasse zur Implementierung eines Hauptprogramms
* @author sschneid
* @version 1.0
*/
public class Main {
/**
* Die Methode wareTesten testet die Implementierung der
* von Waren mit tiefem Kopieren, tiefem Vergleichen.
* Sie nutzt nicht die Vererbung aus.
*/
public static void warenTesten() {
Ware ware1, ware2;
Lager dasLager;
// Testen der Klasse Ware
ware1 = new Ware("Zeitung", 12.12, true);
ware1.drucken();
double p = ware1.getNettoPreis();
// Generieren von Empfehlungen
ware2 = new Ware("Potter Band 1", 31.12, false);
ware2.generiereEmpfehlungen(7);
// Abfrage des Lagers
dasLager = Lager.getLager();
dasLager.einlagern(ware1, 0);
dasLager.einlagern(ware2, 1);
// Prüfen der Lagerbestände mit tiefem Vergleichen
Ware testWare = dasLager.holen(0);
if (testWare == ware2)
System.out.println("testware und ware2 sind identisch (gut)");
if (testWare.equals(ware2))
System.out.println("testware und ware2 sind inhaltsgleich (gut)");
// vollständiges Befüllen des Lager
for (int i = 0; i < 1000; i++) {
ware2 = new Ware("Band" + i + "B", 12.12, true);
ware2.generiereEmpfehlungen(7);
dasLager.einlagern(ware2);
}
System.out.println("Lager vollständig gefüllt mit "
+ dasLager.lagerGroesse() + " Waren.");
for (int i = 0; i < 1000; i++) {
ware2 = new Ware("Volume" + i + "B", 12.12, true);
ware2.generiereEmpfehlungen(7);
dasLager.einlagern(ware2,i);
}
System.out.println("Lager erneut vollständig gefüllt mit "
+ dasLager.lagerGroesse() + " Waren.");
}
/**
* Diese Methode dient zum Testen der Klasse Buch.
* Sie nutzt die Veererbung in Java aus.
*/
public static void buecherTesten() {
/*
* 2. Testen: Anlegen einer Testmethode für Bücher
* Erzeugen von Büchern
* Anlegen einer Referenz auf eine alte Auflage
* Drucken zweier Bücher
* 3. Frage: Wie kann man Bücher in das Lager einfügen?
* 5. Einfügen der Bücher in das Lager
* 8. Anpassen der Hauptroutine
* 8.1 Alle Instanzen vom Typ Ware sollen MusikCDs werden da die Klasse
* Ware jetzt abstrakt ist.
*/
Lager dasLager;
dasLager = Lager.getLager();
//Buch buch1 = new Buch("Das Grauen",22.22,"9876543");
//Buch buch2 = new Buch("Das Kapital",33.33,"9876543");
//buch1.setAlteAuflage(buch2);
//buch1.drucken();
//dasLager.einlagern(buch1);
//dasLager.einlagern(buch2);
dasLager.drucken();
}
/**
* Das Hauptprogramm
*
* @param args
*/
public static void main(String[] args) {
warenTesten();
buecherTesten();
}
}
package block9;
/**
* Die Klasse Lager verwaltet eine bestimmte Anzahl von Waren
* @author sschneid
* @version 1.0
*/
public class Lager {
private static Lager instanz = null;
/**
* privates Feld zum Verwalten der Waren
*/
private Ware[] bestand;
/**
* * Groesse des Bestands
*/
private int initBestand = 20;
/**
* Standardkonstruktor der das Lager initialisiert
*/
private Lager() {
bestand = new Ware[initBestand];
}
/**
*
* @return Groesse des Lagers
*/
public int lagerGroesse() {return bestand.length;}
public static Lager getLager() {
if (instanz == null) {
instanz = new Lager();
}
return instanz;
}
/**
* Einlagern von Waren. Existierende Waren werden verdraengt.
* Lager wird dynamisch vergrößert
* @param ware1
* @param pos
*/
public void einlagern(Ware ware1,int pos) {
if (pos>=bestand.length) {
// Erzeuge neues Lager welches 2x größer als das geforderte
// Element ist und kopiere alles die das neue Feld
Ware[] tempBestand = new Ware[pos*2];
System.arraycopy(bestand,0,tempBestand,0,bestand.length);
bestand = tempBestand;
}
bestand[pos] = ware1;
}
/**
* Einlagern von Waren. Es wird die erste freie Position gesucht.
* Lager wird dynamisch vergrößert
* @param ware1
* @return eingelagerte Position
*/
public int einlagern(Ware ware1) {
int freiePosition=0;
while ((freiePosition<bestand.length) && (bestand[freiePosition]!=null))
freiePosition++;
einlagern(ware1,freiePosition);
return freiePosition;
}
/**
*
* @param pos die Position der Ware im Lager
* @return eine Referenz auf die Ware. Der Zeiger kann ein null Zeiger sein.
*/
public Ware holen(int pos) {
return bestand[pos];
} /**** drucken des Lagerbestands. Es werden nur die Namen der Produkte
* ausgegeben
*/
public void drucken() {
System.out.println("*** Lagerbestand ***");
for (int i=0; i < bestand.length; i++) {
if (bestand[i]== null)
System.out.println("bestand[" +i + "]: leer");
else
System.out.println("bestand[" +i + "]: " +
bestand[i].getName());
}
System.out.println("*** Lagerbestand (Ende)***");
}
}
package block9;
/**
* Dies ist die Dokumentation der Klasse Ware. Ware dient zum Verwalten von Gütern
* mit Preisen und Namen in einem Lager.
* @author Stefan Schneider
* @version 1.0
* @see Lager
*/
public class Ware {
/*
* 1. Erzeugen Sie eine Klasse Buch in dem Sie die Klasse
* Ware mit Ihrem Inhalt kopieren
* 1.1 Anpassen Name
* 1.2 Anpassen Konstruktoren
* 1.3 Anpassen equals Methode
* 1.4 Anlegen einer ISBN Nummer
* 1.5 Anlegen einer hardcodierten halben Mehrwertsteuer
* 1.6 Referenz auf eine private, optionale (ältere) Auflage
* Zugriffsmethoden anlegen
* 1.7 Anpassen der Druckenmethode
* 4. Ableiten der Klasse aus der Klasse aus der Klasse Ware
* 4.1 extends Schlüsselwort benutzen
* 4.2 Löschen von allem redundanten Code
* 6. Erzeugen er Klasse MusikCD durch Kopieren der Klasse Ware
* 6.1 Mehrwertsteuer auf vollen Mehrwertsteuer hart kodieren
* 7. Die Klasse Ware soll nicht mehr instanziiert werden. Sie soll
* abstrakt werden
*/
/**
* Der aktuelle Mehrwertsteuersatz 2010.
* Er liegt zur Zeit bei {@value}.
*
* @since 1.0
*/
public static final double mws = 0.19;
private double nettoPreis; //Deklaration
private boolean halbeMws;
private String name;
public Ware empfehlung;
/**
* Konstruktor fuer die Klasse Ware
* @param n der Name der Ware
* @param np der Nettorpreis
* @param hmws halber Mehrwertsteuersatz für Ware gueltig
*/
public Ware(String n, double np, boolean hmws) {
name = n;
nettoPreis = np;
halbeMws = hmws;
// zusätzliche Initialiserungen....
}
/**
* tiefes Vergleichen. Berücksichtigt beim Namen nicht
* die Groß/kleinschreinung. Berücksichtigt rekursiv die
* Empfehlungen
* @param dieAndereWare
* @return wahr wenn beide Waren dem Vergleich entsprechen.
*/
public boolean equals(Ware dieAndereWare) {
boolean result;
result = getName().equalsIgnoreCase(dieAndereWare.getName())
&& (getNettoPreis() == dieAndereWare.getNettoPreis())
&& (getHalbeMws() == dieAndereWare.getHalbeMws())
&& (((getEmpfehlung() == null) && (dieAndereWare.getEmpfehlung() == null))
|| ((getEmpfehlung() != null)
&& (dieAndereWare.getEmpfehlung() != null)
&& (getEmpfehlung().equals(dieAndereWare.getEmpfehlung()))
)
);
return result;
}
/**
* Liefert den Namen einer Ware zurueck.
* @return Name der Ware
*/
public String getName() {
return name;
} /** * Setzen eines neuen Nettopreis * @param npr der neue Nettopreis */public void setNettoPreis(double npr) {
nettoPreis = npr;
}
/**
* liefere wahr zurück wenn Mwssatz reduziert ist
* @return
*/
public boolean getHalbeMws() {return halbeMws;}public Ware getEmpfehlung() { return empfehlung; }
public void setEmpfehlung(Ware empfehlung) {
this.empfehlung = empfehlung;
}
/**
* Ausdrucken aller Werte auf der Konsole
*/
public void drucken() { drucken(0); }
/**
* Ausdrucken aller Werte auf der Konsole mit vorgebener Einrueckung
* für Empfehlungen
* @param einruecken eingerueckte Stellen für Empfehlungen
*/
protected void drucken(int einruecken) {
String leerStellen = "";
for (int i = 0; i < einruecken; i++) {
leerStellen = leerStellen + " ";
}System.out.print(leerStellen + "Name: " + getName());
System.out.print(" ;Netto: " + getNettoPreis());
System.out.print(" ;Brutto: " + getBruttoPreis());
if (halbeMws)
System.out.println(" (Halbe Mws)");
else
System.out.println(" (Volle Mws)");
if (empfehlung != null) {
System.out.println(leerStellen + "Empfehlung:");
empfehlung.drucken(einruecken + 2);
}
}
/**
* Ausgabe des Nettopreis
* @return der Nettopreis
*/
public double getNettoPreis() {
return nettoPreis;
}
/**
* Ausgabe des Bruttopreis
* @return der Bruttopreis
*/
public double getBruttoPreis() {
double bp; //temporaere Variable; keine Klassenvariable
if (halbeMws) {
bp = Math.round(nettoPreis * (mws / 2 + 1) * 100) / 100;
} else {
bp = Math.round(nettoPreis * (mws + 1) * 100) / 100;
}
return bp;
}
/**
* Erzeugt verkette Liste einer Ware
* @param Anzahl der zu verkettenden Waren
*/
public void generiereEmpfehlungen(int empf) {
Ware ware1 = this;
Ware ware2;
for (int i = 1; i <= empf; i++) {
ware2 =
new Ware(ware1.getName() + " " + i, 31.12, false);
ware1.setEmpfehlung(ware2);
ware1 = ware2;
}
}
}
public class TopClass {
protected static int zaehler;
public TopClass() {zaehler++;}
protected static int getZaehler() {return zaehler;}
}
public class LowClass extends TopClass {
protected static int zaehler;
public LowClass() {zaehler++;}
protected static int getZaehler() {return zaehler;}
protected void test (){
System.out.println( getZaehler() + " Instanzen der Klasse LowClass erzeugt");
System.out.println( TopClass.getZaehler() + " Instanzen der Klasse TopClass erzeugt");
}
}/**
*
* @author sschneid
*/
public class Main {
private static int size = 3;
public static void main(String[] args) {
TopClass[] feldT = new TopClass[size];
LowClass[] feldL = new LowClass[size];for ( int i=0; i<size; i++) feldT[i]= new TopClass();
for ( int i=0; i<size; i++) {
feldL[i]= new LowClass();
feldL[i].test();
}
System.out.println(TopClass.getZaehler() + " Instanzen TopClass generiert, "+
LowClass.getZaehler() + " Instanzen LowClass generiert");
}
}
1 Instanzen der Klasse LowClass erzeugt 4 Instanzen der Klasse TopClass erzeugt 2 Instanzen der Klasse LowClass erzeugt 5 Instanzen der Klasse TopClass erzeugt 3 Instanzen der Klasse LowClass erzeugt 6 Instanzen der Klasse TopClass erzeugt 6 Instanzen TopClass generiert, 3 Instanzen LowClass generiert
5 Instanzen der Klasse LowClass erzeugt 5 Instanzen der Klasse TopClass erzeugt 7 Instanzen der Klasse LowClass erzeugt 7 Instanzen der Klasse TopClass erzeugt 9 Instanzen der Klasse LowClass erzeugt 9 Instanzen der Klasse TopClass erzeugt 9 Instanzen TopClass generiert, 9 Instanzen LowClass generiert
package block9;
public class Point {
private double x;
private double y;
public Point(double xx, double yy) {
x = xx;
y = yy;
}
public void setXY(double xx, double yy) {
x = xx;
y = yy;
}
public double getX() { return x;}
public double getY() { return y;}
public void print() {System.out.println(toString());}
public String toString() {return ("x: " + x + " y: " + y);}
}
package block9;
public class CircleIsPoint extends Point{
private double radius;
public int price = 99;
public CircleIsPoint(double xx, double yy, double r) {
super(xx,yy);
radius=r;
}
public double getRadius() {return radius;}
public void setRadius(double r) {radius=r;}
public String toString() {return (super.toString() + " , r: " + radius);}
}
package block9;
public class CircleHasPoint {
private double radius;
private Point p;
public CircleHasPoint(double xx, double yy, double r) {
p = new Point(xx,yy);
radius=r;
}
public double getRadius() {return radius;}
public void setRadius(double r) {radius=r;}
public void setXY(double xx, double yy) { p.setXY(xx,yy);}
public double getX() { return p.getX();}
public double getY() { return p.getY();}
public void print() {System.out.println(toString());}
public String toString() {return ("x: " + p.getX() + " y: " +
p.getY() + " r: "+ radius);}
}
package block9;
public class Main {
public static void main(String[] args) {
Point p1 = new Point (2.2, 3.3);
Point p2 = new Point (2.22, 3.33);
CircleIsPoint cip1 = new CircleIsPoint(4.4,5.5,6.6);
p1.print();
cip1.print();
CircleHasPoint chp1 = new CircleHasPoint(44.4,55.5,66.6);
chp1.print();
}
}
x: 2.2 y: 3.3 x: 4.4 y: 5.5 radius: 6.6 x: 44.4 y: 55.5 radius: 66.6

package block9;
/**
* Dies ist die Dokumentation der Klasse Ware. Ware dient zum Verwalten von Gütern
* mit Preisen und Namen in einem Lager.
* @author Stefan Schneider
* @version 1.2
* @see Lager
*/
public class Buch {
/*
* 1. Erzeugen Sie eine Klasse Buch in dem Sie die Klasse
* Ware mit Ihrem Inhalt kopieren
* 1.1 Anpassen Name
* 1.2 Anpassen Konstruktoren
* 1.3 Anpassen equals Methode
* 1.4 Anlegen einer ISBN Nummer
* 1.5 Anlegen einer hardcodierten halben Mehrwertsteuer
* 1.6 Referenz auf eine private, optionale (ältere) Auflage
* Zugriffsmethoden anlegen
* 1.7 Anpassen der Druckenmethode
* 4. Ableiten der Klasse aus der Klasse aus der Klasse Ware
* 4.1 extends Schlüsselwort benutzen
* 4.2 Löschen von allem redundanten Code
* 6. Erzeugen er Klasse MusikCD durch Kopieren der Klasse Ware
* 6.1 Mehrwertsteuer auf vollen Mehrwertsteuer hart kodieren
* 7. Die Klasse Ware soll nicht mehr instanziiert werden. Sie soll
* abstrakt werden
*/
/**
* Der aktuelle Mehrwertsteuersatz 2010.
* Er liegt zur Zeit bei {@value}.
*
* @since 1.0
*/
public static final double mws = 0.19;
private double nettoPreis; //Deklaration
private boolean halbeMws;
private String name;
public Buch empfehlung;
private String isbn;
private Buch alteAuflage;
/**
* Referenz aus alte Auflage
* @return
*/
public Buch getAlteAuflage() { return alteAuflage; }
/**
* zuweisen einer alten Auflage
* @param alteAuflage
*/
public void setAlteAuflage(Buch alteAuflage) {
this.alteAuflage = alteAuflage;
}
/**
* Auslesen der ISBN-Nummer (eine Zeichenkette)
* @return
*/
public String getISBN() { return isbn; }
/**
* Konstruktor fuer die Klasse Ware
* @param n der Name der Ware
* @param np der Nettorpreis
* @param hmws halber Mehrwertsteuersatz für Ware gueltig
*/
public Buch(String n, double np, String myISBN) {
name = n;
nettoPreis = np;
halbeMws = true; //Bei Büchern immer der Fall
isbn=myISBN;
// zusätzliche Initialiserungen....
}
/**
* tiefes Vergleichen. Berücksichtigt beim Namen nicht
* die Groß/kleinschreinung. Berücksichtigt rekursiv die
* Empfehlungen
* @param dieAndereWare
* @return wahr wenn beide Waren dem Vergleich entsprechen.
*/
public boolean equals(Buch dieAndereWare) {
boolean result;
result = getName().equalsIgnoreCase(dieAndereWare.getName())
&& (getNettoPreis() == dieAndereWare.getNettoPreis())
&& (getHalbeMws() == dieAndereWare.getHalbeMws())
&& (((getEmpfehlung() == null) && (dieAndereWare.getEmpfehlung() == null))
|| ((getEmpfehlung() != null)
&& (dieAndereWare.getEmpfehlung() != null)
&& (getEmpfehlung().equals(dieAndereWare.getEmpfehlung()))
)
&& (isbn.equals(dieAndereWare.isbn))
&& (((alteAuflage == null) && (dieAndereWare.alteAuflage == null))
|| ((alteAuflage != null)
&& (dieAndereWare.alteAuflage != null)
&& (alteAuflage.equals(dieAndereWare.alteAuflage))
)
)
);
return result;
}
/**
* Liefert den Namen einer Ware zurueck.
* @return Name der Ware
*/
public String getName() {
return name;
}
/**
* Setzen eines neuen Nettopreis
* @param npr der neue Nettopreis
*/
public void setNettoPreis(double npr) {
nettoPreis = npr;
}
/**
* liefere wahr zurück wenn Mwssatz reduziert ist
* @return
*/
public boolean getHalbeMws() {return halbeMws;}public Buch getEmpfehlung() { return empfehlung; }
public void setEmpfehlung(Buch empfehlung) {
this.empfehlung = empfehlung;
}
/**
* Ausdrucken aller Werte auf der Konsole
*/
public void drucken() { drucken(0); }
/**
* Ausdrucken aller Werte auf der Konsole mit vorgebener Einrueckung
* für Empfehlungen
* @param einruecken eingerueckte Stellen für Empfehlungen
*/
private void drucken(int einruecken) {
String leerStellen = "";
for (int i = 0; i < einruecken; i++) {
leerStellen = leerStellen + " ";
}
System.out.print(leerStellen + "Name: " + getName());
System.out.print(" ;Netto: " + getNettoPreis());
System.out.print(" ;Brutto: " + getBruttoPreis());
if (halbeMws)
System.out.println(" (Halbe Mws)");
else
System.out.println(" (Volle Mws)");
if (empfehlung != null) {
System.out.println(leerStellen + "Empfehlung:");
empfehlung.drucken(einruecken + 2);
}
System.out.println(leerStellen + "ISBN:" +isbn);
if (alteAuflage != null) {
System.out.println(leerStellen + "Alte Auflage:");
alteAuflage.drucken(einruecken + 2);
}
}
/**
* Ausgabe des Nettopreis
* @return der Nettopreis
*/
public double getNettoPreis() {
return nettoPreis;
}
/**
* Ausgabe des Bruttopreis
* @return der Bruttopreis
*/
public double getBruttoPreis() {
double bp; //temporaere Variable; keine Klassenvariable
if (halbeMws) {
bp = Math.round(nettoPreis * (mws / 2 + 1) * 100) / 100;
} else {
bp = Math.round(nettoPreis * (mws + 1) * 100) / 100;
}
return bp;
}
/**
* Erzeugt verkette Liste einer Ware
* @param Anzahl der zu verkettenden Waren
*/
public void generiereEmpfehlungen(int empf) {
Buch ware1 = this;
Buch ware2;
for (int i = 1; i <= empf; i++) {
ware2 =
new Buch(ware1.getName() + " besser" + i, 31.12, isbn+"-1");
ware1.setEmpfehlung(ware2);
ware1 = ware2;
}
}
}
package block9;
/**
* Eine Hilfsklasse zur Implementierung eines Hauptprogramms
* @author sschneid
* @version 1.1
*/
public class Main {
/**
* Die Methode wareTesten testet die Implementierung der
* von Waren mit tiefem Kopieren, tiefem Vergleichen.
* Sie nutzt nicht die Vererbung aus.
*/
public static void warenTesten() {
Ware ware1, ware2;
Lager dasLager;
// Testen der Klasse Ware
ware1 = new Ware("Zeitung", 12.12, true);
ware1.drucken();
double p = ware1.getNettoPreis();
// Generieren von Empfehlungen
ware2 = new Ware("Potter Band 1", 31.12, false);
ware2.generiereEmpfehlungen(7);
// Abfrage des Lagers
dasLager = Lager.getLager();
dasLager.einlagern(ware1, 0);
dasLager.einlagern(ware2, 1);
// Prüfen der Lagerbestände mit tiefem Vergleichen
Ware testWare = dasLager.holen(0);
if (testWare == ware2)
System.out.println("testware und ware2 sind identisch (gut)");
if (testWare.equals(ware2))
System.out.println("testware und ware2 sind inhaltsgleich (gut)");
// vollständiges Befüllen des Lager
for (int i = 0; i < 1000; i++) {
ware2 = new Ware("Band" + i + "B", 12.12, true);
ware2.generiereEmpfehlungen(7);
dasLager.einlagern(ware2);
}
System.out.println("Lager vollständig gefüllt mit "
+ dasLager.lagerGroesse() + " Waren.");
for (int i = 0; i < 1000; i++) {
ware2 = new Ware("Volume" + i + "B", 12.12, true);
ware2.generiereEmpfehlungen(7);
dasLager.einlagern(ware2,i);
}
System.out.println("Lager erneut vollständig gefüllt mit "
+ dasLager.lagerGroesse() + " Waren.");
}
/**
* Diese Methode dient zum Testen der Klasse Buch.
* Sie nutzt die Veererbung in Java aus.
*/
public static void buecherTesten() {
/*
* 2. Testen: Anlegen einer Testmethode für Bücher
* Erzeugen von Büchern
* Anlegen einer Referenz auf eine alte Auflage
* Drucken zweier Bücher
* 3. Frage: Wie kann man Bücher in das Lager einfügen?
* 5. Einfügen der Bücher in das Lager
* 8. Anpassen der Hauptroutine
* 8.1 Alle Instanzen vom Typ Ware sollen MusikCDs werden da die Klasse
* Ware jetzt abstrakt ist.
*/
Lager dasLager;
dasLager = Lager.getLager();
Buch buch1 = new Buch("Das Grauen",22.22,"9876543");
Buch buch2 = new Buch("Das Kapital",33.33,"9876543");
buch1.setAlteAuflage(buch2);
buch1.drucken();
//dasLager.einlagern(buch1);
//dasLager.einlagern(buch2);
dasLager.drucken();
}
/**
* Das Hauptprogramm
*
* @param args
*/
public static void main(String[] args) {
//warenTesten();
buecherTesten();
}
}

package block9;
/**
* Dies ist die Dokumentation der Klasse Ware. Ware dient zum Verwalten von Gütern
* mit Preisen und Namen in einem Lager.
* @author Stefan Schneider
* @version 1.2
* @see Lager
*/
public class Buch extends Ware {
/*
* 1. Erzeugen Sie eine Klasse Buch in dem Sie die Klasse
* Ware mit Ihrem Inhalt kopieren
* 1.1 Anpassen Name
* 1.2 Anpassen Konstruktoren
* 1.3 Anpassen equals Methode
* 1.4 Anlegen einer ISBN Nummer
* 1.5 Anlegen einer hardcodierten halben Mehrwertsteuer
* 1.6 Referenz auf eine private, optionale (ältere) Auflage
* Zugriffsmethoden anlegen
* 1.7 Anpassen der Druckenmethode
* 4. Ableiten der Klasse aus der Klasse aus der Klasse Ware
* 4.1 extends Schlüsselwort benutzen
* 4.2 Löschen von allem redundanten Code
* 6. Erzeugen er Klasse MusikCD durch Kopieren der Klasse Ware
* 6.1 Mehrwertsteuer auf vollen Mehrwertsteuer hart kodieren
* 7. Die Klasse Ware soll nicht mehr instanziiert werden. Sie soll
* abstrakt werden
*/ /*** Der aktuelle Mehrwertsteuersatz 2010.
* Er liegt zur Zeit bei {@value}.
*
* @since 1.0
*/
private String isbn;
private Buch alteAuflage;
/**
* Referenz aus alte Auflage
* @return
*/
public Buch getAlteAuflage() { return alteAuflage; }
/**
* zuweisen einer alten Auflage
* @param alteAuflage
*/
public void setAlteAuflage(Buch alteAuflage) {
this.alteAuflage = alteAuflage;
}
/**
* Auslesen der ISBN-Nummer (eine Zeichenkette)
* @return
*/
public String getISBN() { return isbn; }
/**
* Konstruktor fuer die Klasse Ware
* @param n der Name der Ware
* @param np der Nettorpreis
* @param hmws halber Mehrwertsteuersatz für Ware gueltig
*/
public Buch(String n, double np, String myISBN) {
super(n,np,true);
isbn=myISBN;
// zusätzliche Initialiserungen....
}
/**
* tiefes Vergleichen. Berücksichtigt beim Namen nicht
* die Groß/kleinschreinung. Berücksichtigt rekursiv die
* Empfehlungen
* @param dieAndereWare
* @return wahr wenn beide Waren dem Vergleich entsprechen.
*/
public boolean equals(Buch dieAndereWare) {
boolean result;
result = super.equals((Ware)dieAndereWare)
&& (isbn.equals(dieAndereWare.isbn))
&& (((alteAuflage == null) && (dieAndereWare.alteAuflage == null))
|| ((alteAuflage != null)
&& (dieAndereWare.alteAuflage != null)
&& (alteAuflage.equals(dieAndereWare.alteAuflage))
)
);
return result;
}
/**
* Ausdrucken aller Werte auf der Konsole mit vorgebener Einrueckung
* für Empfehlungen
* @param einruecken eingerueckte Stellen für Empfehlungen
*/
protected void drucken(int einruecken) {
super.drucken(einruecken);
String leerStellen = "";
for (int i = 0; i < einruecken; i++) {
leerStellen = leerStellen + " ";
}
System.out.println(leerStellen + "ISBN:" +isbn);
if (alteAuflage != null) {
System.out.println(leerStellen + "Alte Auflage:");
alteAuflage.drucken(einruecken + 2);
}
}
}
Die Instanzen der Klasse Buch können jetzt in das Lager eingefügt werden
package block9;
/**
* Eine Hilfsklasse zur Implementierung eines Hauptprogramms
* @author sschneid
*/
public class Main {/**
* Die Methode wareTesten testet die Implementierung der
* von Waren mit tiefem Kopieren, tiefem Vergleichen.
* Sie nutzt nicht die Vererbung aus.
*/
public static void warenTesten() {
Ware ware1, ware2;
Lager dasLager;
// Testen der Klasse Ware
ware1 = new Ware("Zeitung", 12.12, true);
ware1.drucken();
double p = ware1.getNettoPreis();
// Generieren von Empfehlungen
ware2 = new Ware("Potter Band 1", 31.12, false);
ware2.generiereEmpfehlungen(7);
// Abfrage des Lagers
dasLager = Lager.getLager();
dasLager.einlagern(ware1, 0);
dasLager.einlagern(ware2, 1);
// Prüfen der Lagerbestände mit tiefem Vergleichen
Ware testWare = dasLager.holen(0);
if (testWare == ware2)
System.out.println("testware und ware2 sind identisch (gut)");
if (testWare.equals(ware2))
System.out.println("testware und ware2 sind inhaltsgleich (gut)");
// vollständiges Befüllen des Lager
for (int i = 0; i < 1000; i++) {
ware2 = new Ware("Band" + i + "B", 12.12, true);
ware2.generiereEmpfehlungen(7);
dasLager.einlagern(ware2);
}
System.out.println("Lager vollständig gefüllt mit "
+ dasLager.lagerGroesse() + " Waren.");
for (int i = 0; i < 1000; i++) {
ware2 = new Ware("Volume" + i + "B", 12.12, true);
ware2.generiereEmpfehlungen(7);
dasLager.einlagern(ware2,i);
}
System.out.println("Lager erneut vollständig gefüllt mit "
+ dasLager.lagerGroesse() + " Waren.");
}
/**
* Diese Methode dient zum Testen der Klasse Buch.
* Sie nutzt die Veererbung in Java aus.
*/
public static void buecherTesten() {
/*
* 2. Testen: Anlegen einer Testmethode für Bücher
* Erzeugen von Büchern
* Anlegen einer Referenz auf eine alte Auflage
* Drucken zweier Bücher
* 3. Frage: Wie kann man Bücher in das Lager einfügen?
* 5. Einfügen der Bücher in das Lager
* 8. Anpassen der Hauptroutine
* 8.1 Alle Instanzen vom Typ Ware sollen MusikCDs werden da die Klasse
* Ware jetzt abstrakt ist.
*/
Lager dasLager;
dasLager = Lager.getLager();
Buch buch1 = new Buch("Das Grauen",22.22,"9876543");
Buch buch2 = new Buch("Das Kapital",33.33,"9876543");
buch1.setAlteAuflage(buch2);
buch1.drucken();
dasLager.einlagern(buch1);
dasLager.einlagern(buch2);
dasLager.drucken();
}
/**
* Das Hauptprogramm
*
* @param args
*/
public static void main(String[] args) {
//warenTesten();
buecherTesten();
}
}

package block9;
/**
* Dies ist die Dokumentation der Klasse Ware. Ware dient zum Verwalten von Gütern
* mit Preisen und Namen in einem Lager.
* @author Stefan Schneider
* @version 1.3
* @see Lager
*/
public class MusikCD extends Ware {
/*
* 6. Erzeugen er Klasse MusikCD durch Kopieren der Klasse Ware
* 6.1 Mehrwertsteuer auf vollen Mehrwertsteuer hart kodieren
* 7. Die Klasse Ware soll nicht mehr instanziiert werden. Sie soll
* abstrakt werden
*/
/**
* Konstruktor fuer die Klasse Ware
* @param n der Name der Ware
* @param np der Nettorpreis
* @param hmws halber Mehrwertsteuersatz für Ware gueltig
*/
public MusikCD(String n, double np) {
super(n,np,false);
}
/**
* tiefes Vergleichen. Berücksichtigt beim Namen nicht
* die Groß/kleinschreinung. Berücksichtigt rekursiv die
* Empfehlungen
* @param dieAndereWare
* @return wahr wenn beide Waren dem Vergleich entsprechen.
*/
public boolean equals(MusikCD dieAndereWare) {
return super.equals((Ware)dieAndereWare);
}
}
package block9.stufe3;
/**
* Eine Hilfsklasse zur Implementierung eines Hauptprogramms
* @author sschneid
* @version 1.3
*/
public class Main {
/**
* Die Methode wareTesten testet die Implementierung der
* von Waren mit tiefem Kopieren, tiefem Vergleichen.
* Sie nutzt nicht die Vererbung aus.
*/
public static void warenTesten() {
Ware ware1, ware2;
Lager dasLager;
// Testen der Klasse Ware
ware1 = new Ware("Zeitung", 12.12, true);
ware1.drucken();
double p = ware1.getNettoPreis();
// Generieren von Empfehlungen
ware2 = new Ware("Potter Band 1", 31.12, false);
ware2.generiereEmpfehlungen(7);
// Abfrage des Lagers
dasLager = Lager.getLager();
dasLager.einlagern(ware1, 0);
dasLager.einlagern(ware2, 1);
// Prüfen der Lagerbestände mit tiefem Vergleichen
Ware testWare = dasLager.holen(0);
if (testWare == ware2)
System.out.println("testware und ware2 sind identisch (gut)");
if (testWare.equals(ware2))
System.out.println("testware und ware2 sind inhaltsgleich (gut)");
// vollständiges Befüllen des Lager
for (int i = 0; i < 1000; i++) {
ware2 = new Ware("Band" + i + "B", 12.12, true);
ware2.generiereEmpfehlungen(7);
dasLager.einlagern(ware2);
}
System.out.println("Lager vollständig gefüllt mit "
+ dasLager.lagerGroesse() + " Waren.");
for (int i = 0; i < 1000; i++) {
ware2 = new Ware("Volume" + i + "B", 12.12, true);
ware2.generiereEmpfehlungen(7);
dasLager.einlagern(ware2,i);
}
System.out.println("Lager erneut vollständig gefüllt mit "
+ dasLager.lagerGroesse() + " Waren.");
}
/**
* Diese Methode dient zum Testen der Klasse Buch.
* Sie nutzt die Veererbung in Java aus.
*/
public static void buecherTesten() {
/*
* 2. Testen: Anlegen einer Testmethode für Bücher
* Erzeugen von Büchern
* Anlegen einer Referenz auf eine alte Auflage
* Drucken zweier Bücher
* 3. Frage: Wie kann man Bücher in das Lager einfügen?
* 5. Einfügen der Bücher in das Lager
* 8. Anpassen der Hauptroutine
* 8.1 Alle Instanzen vom Typ Ware sollen MusikCDs werden da die Klasse
* Ware jetzt abstrakt ist.
*/
Lager dasLager;
dasLager = Lager.getLager();
Buch buch1 = new Buch("Das Grauen",22.22,"9876543");
Buch buch2 = new Buch("Das Kapital",33.33,"9876543");
buch1.setAlteAuflage(buch2);
buch1.drucken();
dasLager.einlagern(buch1);
dasLager.einlagern(buch2);
dasLager.drucken();
}
/**
* Diese Methode dient zum Testen der Klasse Buch.
* Sie nutzt die Veererbung in Java aus.
*/
public static void CDsTesten() {
/*
* 2. Testen: Anlegen einer Testmethode für Bücher
* Erzeugen von Büchern
* Anlegen einer Referenz auf eine alte Auflage
* Drucken zweier Bücher
* 3. Frage: Wie kann man Bücher in das Lager einfügen?
* 5. Einfügen der Bücher in das Lager
* 8. Anpassen der Hauptroutine
* 8.1 Alle Instanzen vom Typ Ware sollen MusikCDs werden da die Klasse
* Ware jetzt abstrakt ist.
*/
Lager dasLager;
dasLager = Lager.getLager();
MusikCD cd1 = new MusikCD("Thriller",8.88);
MusikCD cd2 = new MusikCD("Peter und der Wolf",9.99);
cd1.setEmpfehlung(cd2);
cd1.drucken();
dasLager.einlagern(cd1);
dasLager.einlagern(cd2);
dasLager.drucken();
}
/**
* Das Hauptprogramm
*
* @param args
*/
public static void main(String[] args) {
//warenTesten();
buecherTesten();
CDsTesten();
}
}
|
(Lizenz) |
Am Ende dieses Blocks können Sie:
|
Sie sind in der Lage die folgenden Fragen zu beantworten:
Polymorphie nach Wikipedia: (gr. πολυμορφία Polymorphia) Vielgestaltigkeit
Durch das Konzept der Vererbung sollen Anwendungen mit Objekten von Unterklassen ohne explizites Wissen über die Unterklassen umgehen können.
Objekte von Unterklassen können sich aber anders verhalten wenn die Methoden der Oberklasse in den entsprechenden Unterklassen überschrieben und neu implementiert wurden. Objekte einer Klassenhierarchie können daher abhängig von der Unterklasse verschieden Methodenimplementierungen verwenden und sich polymorph (Vielgestaltig) verhalten.
Barbara Liskov führte 1987 das nach ihr benannte Prinzip welches sich mit der Substitution (Ersetzbarkeit) in der objektorientierten Programmierung beschäftigt:
| Wenn eine Klasse S eine Unterklasse(Subtyp) von T ist, können alle Instanzen von T (Oberklasse) durch Instanzen von S (Unterklasse) ersetzt werden ohne dass dich das sich Verhalten des Programmes ändert. |
Eng mit diesem Prinzip ist auch Betrand Meyer's in der Programmiersprache Eiffel umgesetztes "Design by contract" verwandt.
Der Aspekt der Erweiterung bei der Vererbung ermöglicht problemlos polymorphes Verhalten von Objekten.
Im unten gezeigten Beispiel wird eine Klassenhierarchie verwendet in der in jeder Unterklasse ein Attribut und eine Methode hinzugefügt wird

Ein Programm welches nur Instanzen der Klasse Kraftwagen kennt hat keine Probleme Instanzen von LastKraftwagen oder Bus zu verwenden. Die Instanzen besitzen alle ein Attribut nummernschild und eine Methode getKennZeichen().
| Polymorphismus funktioniert in Java nur bei Attributen und Methoden die von einer gemeinsamen Basisklasse geerbt worden sind! |
Klassen mit namensgleichen Attributen und Methoden profitieren nicht vom Polymorphismus. Der Übersetzer kontrolliert die Klassenhierarchie beim Übersetzen und unterbindet Zuweisungen zwischen Typen(Klassen) die namensgleiche Methoden besitzen, diese aber nicht von einer gemeinsamen Klasse erben.
Dieses Konzept lässt sich in Java wie folgt umsetzen:
import Kraftwagen;
import LastKraftwagen;
import Bus;
...
Kraftwagen[] flotte = new Kraftwagen[3];
flotte[0] = new Kraftwagen();
flotte[1] = new LastKraftwagen();
flotte[2] = new Bus();
...
for (i=0; i<3; i++) {
// Das Programm kennt im Moment nur Kraftwagen!
System.out.println(flotte[i].getKennZeichen());
}
...Das Programm kann mit Instanzen der Klasse LastKraftwagen und Bus umgehen da es sich auf die in der Klasse Kraftwagen bekannten Eigenschaften des Objekts beschränken muss.
Java erlaubt es Methoden von Oberklassen in Unterklassen zu verdecken und neu zu implementieren. Hierdurch können Methoden an die Anforderungen einer spezialisierten Klasse angepasst werden. Man spricht hier vom Überschreiben von Methoden wenn die folgende Definition gilt:
| Überschreiben einer Methode |
|---|
| Bei einer überschriebenen Methode müssen die Signatur und der Rückgabewert der überschriebenen Methode identisch mit der Signatur und dem Rückgabewert der Methode einer Oberklasse sein. |
Beim Überschreiben von Methoden und Attributen ist das "Liskov Substitution Principle" nicht mehr automatisch garantiert. Hier tritt nun die überschreibende Methode an die Stelle der überschriebenen Methode. Es wird also ein anderer Code ausgeführt.
Anbei das Beispiel einer Klasse Point, die einen zweidimensionalen Punkt modelliert und der Klasse CircleIsPoint die von Punkt erbt und zusätzlich ein Attribut zur Verwaltung des Radius hat:
public class Point {
private double x;
private double y;
public Point(double xx, double yy) {
x = xx;
y = yy;
}
public void setXY(double xx, double yy) {
x = xx;
y = yy;
}
public double getX() { return x;}
public double getY() { return y;}
public void print() {System.out.println(toString());}
public String toString() {return ("x: " + x + " y: " + y);}
}
public class CircleIsPoint extends Point{
private double radius;
public CircleIsPoint(double xx, double yy, double r) {
super(xx,yy);
radius=r;
}
public double getRadius() {return radius;}
public void setRadius(double r) {radius=r;}
public void print() { //Uberschriebene Methode
System.out.println(super.toString() + " radius: " + radius);
}
}
public class Main {
public static void main(String[] args) {
Point[] pf = new Point[3];
pf[0] = new Point (2.2, 3.3);
pf[1] = new Point (7.7, 8.8);
pf[2] = new CircleIsPoint(4.4,5.5,6.6);
pf[0].print();
pf[1].print();
pf[2].print();
}
}Bei der Ausführung des Programms ergibt sich die folgende Objekstruktur:

Die Ausgabe ist die folgende:
x: 2.2 y: 3.3 x: 7.7 y: 7.7 x: 4.4 y: 5.5 radius: 6.6
Die überschriebene print() Methode erzeugt eine sinnvolle Ausgabe die zusätzlich den Radius ausgibt ohne das das komsumierende Hauptprogramm "weiß", das es sich um eine überschriebene Methode handelt.
Da der Begriff einer "sinnvollen" Erweiterung nicht von Werkzeugen abgeprüft werden kann, ergibt sich ein Risiko des Missbrauchs. Ein Beispiel ist die folgende Implementierung der Methode print() in der Klasse CircleIsPoint().
public class CircleIsPoint extends Point{
...
public void print() {
System.out.println("Methode print() muss noch implementiert werden...");
}
}Der Unterschied zwischen der recht menschlichen Implementierung und der Implementierung die auch den Radius ausgibt ist nur gering.
Es gibt im schlimmsten Fall Programme, die diesen Text lesen und weder die zusätzlichen Information über den Radius interpretieren können, noch mit einem anderen Text umgehen können.
Ist eine Klasse und/oder die Methode nicht als final deklariert, muss ein Konsument dieser Klasse mit einem Überschreiben von Methoden rechnen!
Hiermit ergibt sich für den Polymorphismus beim Überschreiben die folgende Abschätzung:
| Vorteil | Nachteil |
|---|---|
| Kostensenkung Entwicklung: Existierender Code kann unmodifiziert mit neuen Unterklassen umgehen | Kostensteigerung QA und Service: Der Polymorphismus kann durch Missbrauch zu unerwarteten Effekten führen, die erst zur Laufzeit auftreten. |
Es gibt in Java überschriebene (englisch overriding) und überladene (englisch overloading) Methoden. Beide Begriffe werden leicht verwechselt!
| Überschriebene Methode | Überladene Methode | |
|---|---|---|
| Methodenname | identisch | identisch |
| Eingabeparameterliste | identisch (Typen, Anzahl, Folge) | unterschiedlich! |
| Rückgabewert | identisch | nicht relevant |
| Klassenbeziehung der Methoden | Methoden gehören zu unterschiedlichen Klassen die aber in einer Ober- Unterklassebeziehung stehen | Methoden gehören zur gleichen Klasse |
Zur vollständigen Begriffsverwirrung: Methoden können andere Methoden der gleichen Klasse überladen während sie eine Methode der Oberklasse überschreiben.
Siehe die Methode mult() im folgenden Beispiel:
Bisher wurden bereits implizit das Konzept von Referenzvariablen verwendet, die wechselseitig auf Objekte ihres Typs (Klasse) oder auf Objekte von Unterklassen gezeigt haben.
Java ist trotz des Polymorphismus eine streng typisierte Sprache. Das bedeutet:
| Strenge Typisierung |
|---|
| Der Übersetzer erlaubt schon beim Übersetzen nur typsichere Zuweisungen und Operationen (und meldet unsichere Operationen als Fehler) |
Dieses Konzept steigert die Qualität der ausgelieferten Anwendungen. Fehler aufgrund inkorrekter Typen können beim Anwender zur Laufzeit nicht mehr auftreten. Der Übersetzer zwingt den Entwickler nur typsichere Operationen durchzuführen. Eine typsichere Operation ist zum Beispiel das Aufrufen einer Methode von der man sicher weiß, das sie für eine gegebene Sprache existiert.
| "Casting" in der Programmierung |
|---|
|
engl. Casting hier: Formen, gießen, krümmen Das implizite oder explizite Ändern des Typs eines Objekts oder Datenstruktur bei einer Zuweisung oder der Methodenauswahl |
Ein "Upcast" ist eine Typkonversionen einer Instanz einer Unterklasse auf den Typ einer Oberklasse. Sie wurden bisher implizit in den vorhergehenden Beispielen verwendet, wie zum Beispiel bei der Klasse CircleIsPoint die aus der Klasse Point abgeleitet wurde:
import Point; import CircleIsPoint; ... Point p1 = new Point (2.2, 3.3); CircleIsPoint c1 = new CircleIsPoint(4.4,5.5,6.6); Point p2 = c1; //Das explizite Wissen über den Objekttyp CircleIsPoint geht verloren ... p1.print(); p2.print();
Die Zuweisung von c1 auf p1 ist
| Upcast |
|---|
| Der "Upcast" ist eine sichere Typkonversation da spezialisierte Klasseninstanzen auf Referenzen von allgemeineren Klassen zugewiesen werden. |
Das umgekehrte Prinzip gilt jedoch nicht.
Eine Instanz einer allgemeineren Klasse hat nicht alle Eigenschaften einer spezialisierten Klasse. Das folgende Beispiel wird nicht vom Javaübersetzer akzeptiert:
import Point; import CircleIsPoint; ... Point p1 = new Point (2.2, 3.3); CircleIsPoint c1 = new CircleIsPoint(4.4,5.5,6.6); CircleIsPoint c2 = p1; ... double r1 = c1.getRadius(); double r2 = c2.getRadius();
Eine Instanz vom Typ Point hat nicht die Eigenschaften einer Instanz vom Type CircleIsPoint. Im UML Diagramm sieht der obige Versuch wie folgt aus:

Der Übersetzer erzeugt schon beim Versuch der Zuweisung eine Fehlermeldung:
Main.java:30: incompatible types found : l9vererbungassoziation.Point required: l9vererbungassoziation.CircleIsPoint CircleIsPoint c2 = p1; 1 error
Es gibt jedoch auch Fälle in denen eine Typkonversion erzwungen werden muss. Im verwendeten Beispiel ist dies der Fall wenn man einen Kreis in einem Feld von Point verwaltet. Wenn man die Referenz benutzt um den Radius auszulesen ergibt sich folgendes Problem wenn man zum Auslesen den Typ CircleIsPoint wie folgt verwendet:
public static void main(String[] args) {
Point[] pf = new Point[3];
pf[0] = new Point (2.2, 3.3);
pf[1] = new Point (2.22, 3.33);
pf[2] = new CircleIsPoint(4.4,5.5,6.6);
pf[0].print();
pf[1].print();
pf[2].print();
CircleIsPoint cip1 = pf[2];
double r2 = cip1.getRadius();
}Der Übersetzer meldet den folgenden Fehler da die beiden Typen keine sichere Typkonversion zulassen:
Main.java:24: incompatible types
found : l9vererbungassoziation.Point
required: l9vererbungassoziation.CircleIsPoint
CircleIsPoint cip1 = pf[2];
1 error
MaDieses Problem kann man versuchen zu Lösen in dem man die Variable cip1 mit dem Typ Point versieht:
public static void main(String[] args) {
Point[] pf = new Point[3];
pf[0] = new Point (2.2, 3.3);
pf[1] = new Point (2.22, 3.33);
pf[2] = new CircleIsPoint(4.4,5.5,6.6);
pf[0].print();
pf[1].print();
pf[2].print();
Point cip1 = pf[2];
double r2 = cip1.getRadius();
}
Dies Maßnahme erlaubt das Auslesen der Feldvariable. Sie scheitert jedoch eine Zeile weiter beim Aufruf der Methode getRadius(). Die Klasse Point kennt keine Methode getRadius()...
Main.java:25: cannot find symbol
symbol : method getRadius()
location: class l9vererbungassoziation.Point
double r2 = cip1.getRadius();
1 error
Die Lösung besteht in einer expliziten Typkonversion (" cast") mit Hilfe einer runden Klammer und Angabe des Typs:
public static void main(String[] args) {
Point[] pf = new Point[3];
pf[0] = new Point (2.2, 3.3);
pf[1] = new Point (2.22, 3.33);
pf[2] = new CircleIsPoint(4.4,5.5,6.6);
pf[0].print();
pf[1].print();
pf[2].print();
CircleIsPoint cip1 = (CircleIsPoint)pf[2];
double r2 = cip1.getRadius();
}
Mit dieser Typkonversion zwingt der Entwickler den Übersetzer zur Zuweisung. Der Übersetzer kann sich dieses Wissen nicht von alleine herleiten. Die Verantwortung liegt jetzt beim Entwickler. Würde der Entwickler den folgenden Cast erzwingen, würde das Programm auch fehlerfrei übersetzen:
CircleIsPoint cip1 = (CircleIsPoint)pf[1];
double r2 = cip1.getRadius();
Beim Aufruf der Methode getRadius() würde jetzt jedoch zur Laufzeit eine Ausnahme (Exception) geworfen werden. Der Entiwckler hat in diesem Fall den Übersetzer zu einem inkorrekten Übersetzungsvorgang gezwungen.
Der Fehler tritt jetzt erst zur Laufzeit auf. Dies kann für den Entwickler sehr teuer werden, da die Software den Fehler eventuell erst beim Kunden zeigt.
Die Syntax
KlasseC variable2; ... KlasseA variable1 = (KlasseB) variable2;
erlaubt die Zuweisung einer Objektreferenz variable2 auf eine Objektreferenz variable1 solange der Typ KlasseB in der Klammer eine sichere Konvertierung von KlasseB auf KlasseA erlaubt. Diese Zuweisung funktioniert unabhängig vom tatsächlichen Typ (KlasseC) von variable2.
Explizite Typkonversionen sind in manchen Fällen aufgrund des Polymorphismus notwendig. Sie haben jedoch eine Reihe von Konsequenzen:
|
Downcasts sind problematische, explizite Typkonvertierungen. Ihre Verwendung sollte wenn möglich vermieden werden, da bestimmte Fehlerklassen zum Übersetungszeitpunkt nicht geprüft werden können. |
Der Polymorphismus funktioniert in Java nicht im Fall von überschriebenen Attributen die einen unterschiedlichen Typ besitzen.
Bei überschriebenen Attributen wendet Java den Typ der Referenz an um das Attribut zu bestimmen.
Im Beispiel der Klassen Point und CircleIsPoint wird dies mit Hilfe des Attributs price gezeigt:
public class Point {
...
public double price = 100.1;
...
public void print() {System.out.println(toString());}
public String toString() {return ("x: " + x + " y: " + y);}
}
public class CircleIsPoint extends Point{
...
public int price = 99;
...
public void print() {
System.out.println(super.toString() + " radius: " + radius);
}
}
public class Main {
public static void main(String[] args) {
Point[] pf = new Point[3];
pf[0] = new Point (2.2, 3.3);
pf[1] = new Point (2.22, 3.33);
pf[2] = new CircleIsPoint(4.4,5.5,6.6);
pf[0].print();
pf[1].print();
pf[2].print();
Point p2 = pf[2];
p2.print(); // toString von CircleIsPoint wird benutzt
double t1 = 1000.0 + p2.price; // price von Point wird benutzt
System.out.println(t1);
CircleIsPoint cip = (CircleIsPoint)pf[2];
cip.print(); // toString von CircleIsPoint wird benutzt
double t2 = 1000.0 + cip.price; // price von CircleIsPoint wird benutzt
System.out.println(t2);
}
}Hiermit ergeben sich die folgenden Belegungen:

x: 2.2 y: 3.3 x: 2.22 y: 3.33 x: 4.4 y: 5.5 radius: 6.6 x: 4.4 y: 5.5 radius: 6.6 1100.1 x: 4.4 y: 5.5 radius: 6.6 1099.0
Im ersten Fall (p2) wird das Attribut price vom Typ double benutzt. Im Fall der Variable cip vom Typ CircleIsPoint wird die Variable price vom Typ int benutzt.
Es kommt kein Polymorphismus zum Tragen, die Auswahl des Attributs richtet sich nach dem Typ der Referenz!
Tipp: Durch das strikte Anwenden von Datenkapselung greift man nur noch über Zugriffsmethoden auf Attribute zu. Hierdurch wird der Polymorphismus auch beim Zugriff auf Attribute (intuitiv) gewährleistet. Das Ausnutzen des nicht polymorphen Verhalten betrifft dann nur noch Sonderfälle in denen man dies explizit wünscht.
Normalerweise sind zur Laufzeit eines Programmes bereits alle Typen bekannt und wurden vom Übersetzer schon beim Übersetzen des Programms kontrolliert.
Durch die Möglichkeit der Typkonvertierung durch "casten", und "Upcasting" kann man die genaue Information über einen Typ einer Instanz zur Laufzeit verlieren. Das bedeutet, sie ist im Kontext einer Methode oder Klasse nicht mehr bekannt.
Zur Bestimmung eines Typs zur Laufzeit bietet Java den instanceof Operator an mit dem man Instanzen auf Ihren Typ prüfen kann.
Im vorhergehenden Beispiel mit den Klassen Point und CircleIsPoint kann man den instanceof Operator verwenden um einen Laufzeitfehler bei einer cast-Operation zu vermeiden:
public class Main {
public static void main(String[] args) {
Point[] pf = new Point[3];
pf[0] = new Point (2.2, 3.3);
pf[1] = new Point (2.22, 3.33);
pf[2] = new CircleIsPoint(4.4,5.5,6.6);
pf[0].print();
pf[1].print();
pf[2].print();
double r2 = 0;
CircleIsPoint cip1;
if (pf[2] instanceof CircleIsPoint) {
cip1 = (CircleIsPoint)pf[2];
r2 = cip1.getRadius();
}
}
}Die Javalaufzeitumgebung verfügt über ein vollständiges, dynamisches System zur Verwaltung von Metainformationen inklusive der Informationen über eine Klasse.
Hierzu dient die Klasse Class die man über die Methode Object.getClass() benutzen kann. Die Methode getClass() wird an alle Klassen vererbt. Hiermit kann man Informationen über die Klasse selbst, wie zum Beispiel ihren Name und die Oberklasse auslesen:
public class Main {
public static void main(String[] args) {
Point[] pf = new Point[3];
pf[0] = new Point (2.2, 3.3);
pf[1] = new Point (2.22, 3.33);
pf[2] = new CircleIsPoint(4.4,5.5,6.6);
pf[0].print();
pf[1].print();
pf[2].print();
double r2 = 0;
CircleIsPoint cip1;
Class myClass = pf[2].getClass();
Class mySuperClass = myClass.getSuperclass();
System.out.println("pf[2] Klassenname: " + myClass.getSimpleName());
System.out.println("pf[2] Oberklasse: " + mySuperClass.getSimpleName());
if (pf[2] instanceof CircleIsPoint) {
cip1 = (CircleIsPoint)pf[2];
r2 = cip1.getRadius();
}
}
}Die Konsolenausgabe des Programmes ist die Folgende:
x: 2.2 y: 3.3 x: 2.22 y: 3.33 x: 4.4 y: 5.5 radius: 6.6 pf[2] Klassenname: CircleIsPoint pf[2] Oberklasse: Point

(Lizenz)

Schreiben Sie ein Hauptprogramm das folgende Aufgaben ausführt:
package block10.TierVererbung;public class TierTest {private static Haustier hausTiere[];public static void main(String[] args) {populate();neuerLieblingsvogel();iterate();}public static void populate() {hausTiere = new Haustier[6];/* Implementierung */}public static void neuerLieblingsvogel() {/* Implementierung */}public static void iterate() {/* Implementierung */
}}
Versuch der Zuweisung eines inkompatiblen Instanz als Lieblingvogel zu einer Katze. Die Routine soll durch casten übersetzen. Zur Laufzeit soll die Zuweisungweisungsmethode der Katze den Fehlversuch auf der Konsole dokumentieren
Die Methode .getClass() der Klasse Object liefert eine Referenz auf die Beschreibung der Klasse. Man kann diese Referenz direkt ausdrucken. Es wird der Name der Klasse inklusive eventueller Paketzugehörigkeit ausgedruckt.
Will man nur den Klassennamen ausdrucken muss man von einer Instanz vom Typ Class die Methode .getSimpleName()aufrufen.
package block10.TierVererbung;public abstract class Haustier {private String name;private boolean steuerpflichtig;private float kostenTierarzt;/*** Get the value of kostenTierarzt** @return the value of kostenTierarzt*/public float getKostenTierarzt() {return kostenTierarzt;}/*** Set the value of kostenTierarzt** @param kostenTierarzt new value of kostenTierarzt*/public void setKostenTierarzt(float kostenTierarzt) {this.kostenTierarzt = kostenTierarzt;}/*** Get the value of steuerpflichtig** @return the value of steuerpflichtig*/public boolean getSteuerpflichtig() {return steuerpflichtig;}/*** Set the value of steuerpflichtig** @param steuerpflichtig new value of steuerpflichtig*/public void setSteuerpflichtig(boolean steuerpflichtig) {this.steuerpflichtig = steuerpflichtig;}/*** Get the value of name** @return the value of name*/public String getName() {return name;}/*** Set the value of name** @param name new value of name*/public void setName(String name) {this.name = name;}public Haustier(String name, boolean steuerpflichtig, float kostenTierarzt) {this.name = name;this.steuerpflichtig = steuerpflichtig;this.kostenTierarzt = kostenTierarzt;}public String beschreibung() {String stpf = (steuerpflichtig) ? ", " : ", nicht ";String b = "Name :" + name+ stpf + "steuerpflichtig, Kosten: "+ kostenTierarzt;return b;}}
package block10.TierVererbung;/**** @author sschneid*/public class Hund extends Haustier{private String rasse;/*** Get the value of rasse** @return the value of rasse*/public String getRasse() {return rasse;}public Hund(String name, float kostenTierarzt, String rasse) {super(name,true,kostenTierarzt);this.rasse = rasse;}public String beschreibung() {return super.beschreibung() + ", Rasse: " + rasse;}}
package block10.TierVererbung;public class Katze extends Haustier {private Vogel lieblingsVogel;public String vogel() {String vname;if (lieblingsVogel != null)vname = lieblingsVogel.getName();else vname = "keinen Vogel";return vname;}/**** @param v setzen des Lieblingsvogel*/public void setVogel(Vogel v) { lieblingsVogel=v;}public Katze(String name, float kostenTierarzt, Vogel lieblingsVogel) {super(name, false, kostenTierarzt);if ((lieblingsVogel !=null) && (lieblingsVogel instanceof Vogel))this.lieblingsVogel = lieblingsVogel;}public String beschreibung() {return super.beschreibung() + ", mag " + vogel();}}
package block10.TierVererbung;/**** @author sschneid*/public class Vogel extends Haustier{private boolean singvogel;/*** Get the value of singvogel** @return the value of singvogel*/public boolean getSingvogel() {return singvogel;}public Vogel(String name, float kostenTierarzt, boolean singvogel) {super(name, false, kostenTierarzt);this.singvogel = singvogel;}public String beschreibung() {String saenger = (singvogel) ? "ein" : "kein";return super.beschreibung() + ", ist "+ saenger + " Singvogel";}}
package block10.TierVererbung;public class TierTest {private static Haustier hausTiere[];public static void main(String[] args) {populate();neuerLieblingsvogel();iterate();}public static void populate() {hausTiere = new Haustier[6];hausTiere[0] = new Vogel("Hansi", 50.55f, true);hausTiere[1] = new Vogel("Piep", 50.44f, false);hausTiere[2] = new Hund("Waldi", 222.22f, "Dackel");hausTiere[3] = new Hund("Fiffi", 202.22f, "Terrier");hausTiere[4] = new Katze("Isis", 88.88f, (Vogel) hausTiere[0]);hausTiere[5] = new Katze("Napoleon", 77.77f, null);}public static void neuerLieblingsvogel() {Vogel v;Katze k;if ((hausTiere[1] instanceof Vogel)&& (hausTiere[4] instanceof Katze)) {v = (Vogel) hausTiere[1];k = (Katze) hausTiere[4];k.setVogel(v);}}public static void iterate() {float kosten = 0;for (int i = 0; i < hausTiere.length; i++) {kosten += hausTiere[i].getKostenTierarzt();System.out.println("Art: " + hausTiere[i].getClass().getSimpleName()+ "; " + hausTiere[i].beschreibung());}
System.out.println("Gesamtjahrekosten "+ kosten +" Euro");}}
Ausgaben:
Art: Vogel; Name :Hansi, nicht steuerpflichtig, Kosten: 50.55, ist ein Singvogel
Art: Vogel; Name :Piep, nicht steuerpflichtig, Kosten: 50.44, ist kein Singvogel
Art: Hund; Name :Waldi, steuerpflichtig, Kosten: 222.22, Rasse: Dackel
Art: Hund; Name :Fiffi, steuerpflichtig, Kosten: 202.22, Rasse: Terrier
Art: Katze; Name :Isis, nicht steuerpflichtig, Kosten: 88.88, mag Piep
Art: Katze; Name :Napoleon, nicht steuerpflichtig, Kosten: 77.77, mag keinen Vogel
Gesamtjahrekosten 692.08 Euro
|
(Lizenz) |
Am Ende dieses Blocks können Sie:
|
Sie sind in der Lage die folgenden Fragen zu beantworten:

Das Java API ist eine plattformunabhängige Bibliothek die zum Javalaufzeitsystem gehört. Das Java API ist ein wichtiger Grund für die Popularität von Java. Man findet hier tausende von Klassen die bekannte Probleme korrekt und effizient lösen. Hieraus ergeben sich eine Reihe von Vorteilen
Der beste Zugang zum Java API findet über die online Dokumentation von JDK Standard Edition 7 statt.
Vorsicht: Will man Anwendungen schreiben, die auch mit älteren JDKs funktionieren sollen so sollte man sich an die Schnittstellen der Klassen der älteren JDKs halten.
Das API wächst von Version zu Version und enthält eventuell Klassen und Methoden die in einer älteren Version von Java nicht enthalten sind.
Im Folgenden werden einige ausgewählte Klassen und Methoden des Java API vorgestellt. Die Liste ist nicht vollständig, sie enthält nützliche Klassen die im Rahmen dieses Kurses verwendet werden:
Die Klasse System gehört zum Paket java.lang und muss nicht mit einem Importbefehl deklariert werden. Man kann sie direkt benutzen. das Attribut System.out ist eine Instanz der Klasse PrintStream. Es erlaubt Ausgaben auf der Konsole in den folgenden Varianten:
Einlesen eines Texts mit Hilfe von System.in und eines InputStreamReader. Dies geschieht am besten mit einer eigenen Methode
import java.io;
...
public static String StringLesen (){
// liest einen vom Benutzer eingegebenen Text (String) ein
BufferedReader keyboard =
new BufferedReader( new InputStreamReader(System.in));
try {
return keyboard.readLine();
} catch (IOException e) {
throw new RuntimeException( e );
}
}Die Klasse Scanner erlaubt das einfache lesen von primitiven Typen und Zeichenketten mit Hilfe regulärer Ausdrücke.
Man kann z.Bsp eine Ganzzahl(Integer) wie folgt einlesen:
Scanner sc = new Scanner(System.in); int i = sc.nextInt();
Der Scanner kann auch mit anderen Begrenzungen als Leerzeichen umgehen. Hier ein Beispiel mit dem Text "Trenner" als Begrenzung:
String input = "47 Trenner 11 Trenner Montag Trenner Dienstag Trenner";
Scanner s = new Scanner(input).useDelimiter("\\s*Trenner\\s*");
System.out.println(s.nextInt());
System.out.println(s.nextInt());
System.out.println(s.next());
System.out.println(s.next());
s.close();Wird das folgende Ergebnis liefern:
47 11 Montag Dienstag
Im folgenden Beispiel wird gezeigt wie man eine beliebige Anzahl von Eingabeparameter in einer eigenen Methode einliest und in Ganzzahlen umwandelt.
public class MethodenDemo {
public static void main(String[] args) {
int[] zahlenFeld;
zahlenFeld = ganzZahlenEingabe(args);
System.out.println(args.length + " arguments found");
for (int i = 0; i < zahlenFeld.length; i++) {
System.out.println("zahlenFeld[" + i + "] = " + zahlenFeld[i]);
}
}
public static int[] ganzZahlenEingabe(String[] myArgs) {
int[] z = new int[myArgs.length];
if (myArgs.length > 0) {
try {
for (int i = 0; i < myArgs.length; i++) {
z[i] = Integer.parseInt(myArgs[i]);
}
} catch (NumberFormatException e) {
System.err.println("Input Error: " + e.getMessage());
System.exit(1);
}
}
return z;
}
}
Das Programm erzeugt die folgenden Ausgaben:
$ java MethodenDemo 33 22 11 3 arguments found zahlenFeld[0] = 33 zahlenFeld[1] = 22 zahlenFeld[2] = 11
Die folgenden Seiten zeigen einen stark vereinfachten Überblick über die wichtigsten Swing Klassen und Methoden. Es werden hier nur die Klassen und Methoden dokumentiert, die im Rahmen des Kurses benötigt werden.
Die Klasse JFrame erlaubt das Erzeugen von eigenen Fenstern für eine Javaanwendung.
Die API Dokumentation zur Klasse JFrame enthält die vollständige Dokumentation aller Methoden dieser Klasse.
Im Folgenden werden die wichtigsten Methoden (für den Kontext des Kurses) in ihrer natürlichen Benutzungsreihenfolge vorgestellt.
| Methode | Beschreibung | Geerbt von |
|---|---|---|
| Konstruktor: JFrame() | Erzeugen eines (noch) unsichtbaren Fensters ohne einen Titel | - |
| Konstruktor: JFrame(String title) | Erzeugen eines (noch) unsichtbaren Fensters mit einem Titel | - |
| void setSize(int breite, int hoehe) | Setzen der Fenstergröße durch Breite und Höhe in Pixel | java.awt.Window |
| setLayout(LayoutManager manager) | Durch das Setzen eines Layout Managers wird die Anordnung der Komponenten vorbestimmt | - |
| Component add(Component comp) | Hinzufügen einer Komponente zum Container | java.awt.Container |
| setJMenuBar(JMenuVar menubar) | Hinzufügen einer Menüleiste zum Fenster | - |
| setDefaultCloseOperation(int Operation) | Aktion die beim Schließen des Fensters geschieht. Mögliche Konstanten als Parameter: DO_NOTHING_ON_CLOSE, HIDE_ON_CLOSE, DISPOSE_ON_CLOSE, EXIT_ON_CLOSE | - |
| pack() | Berechnen des Layouts des Fensters | java.awt.Window |
| setVisible(boolean visible) | Erlaubt ein Fenster anzuzeigen oder auszublenden | java.awt.WIndow |
Beispiel... eines sehr einfachen Fensters welches mit der Klasse JFrame erzeugt wurde: import javax.swing.JFrame; |
Ergibt das folgende Fenster: |
Anmerkung: Das hier gezeigte Fenster benutzt das Standard "Look and Feel" von MacOS. Die Rahmendekoration sieht bei anderen Betriebssystemen bzw. anderen "Loak and Feel" Themen anders aus. Der oben gezeigt Code ist jedoch plattformneutral.
Die Klassen JMenuBar, JMenu, JMenuItem , erlauben Konfiguration von Menüleisten, Pulldownmenüs und Menülisteneinträge für Fenster die mit JFrame erzeugt wurden.
Im Folgenden werden die wichtigsten Methoden (für den Kontext dieses Kurses) aufgeführt. Die obengenannten Klassen sind sehr viel mächtiger als hier beschrieben. Sie haben zusätzliche Methoden für:
| Methode | Beschreibung | Geerbt von |
|---|---|---|
| Konstruktor: JMenuBar() | Erzeugen einer Menüleiste für ein JFrame Fenster | - |
| JMenu add(JMenu c) | Hinzufügen eines Menüs zur Leiste | - |
| Methode | Beschreibung | Geerbt von |
|---|---|---|
| Konstruktor: JMenu() | Erzeugen einer Menüspalte am Ende der Menüleiste | - |
| Konstruktor: JMenu(String title) | Erzeugen einer Menüspalte mit Titel am Ende der Menüleiste | - |
| JMenuItem add(JMenuItem c) | Hinzufügen eines Menüeintrags am Ende des Menüs | - |
| Methode | Beschreibung | Geerbt von |
|---|---|---|
| Konstruktor: JMenuItem(String text) | Erzeugen eines Menüeintrags mit einem gegebenen Text | - |
| void addActionListener(ActionListener l) | Registrieren eines Listeners der aufgerufen wird wenn der Menülisteneintrag geklickt wird | java.swingx.AbstractButton |
| void setEnabled(boolean) | Anzeigen oder ausblenden eines Menülisteneintrags | - |
... Menüs mit einem einzigen Eintrag :
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
...
JFrame hf = new JFrame();
JMenuBar menueleiste;
JMenu ablageMenue;
JMenuItem beendenMenueEintrag;
menueleiste = new JMenuBar();
ablageMenue = new JMenu("Ablage");
beendenMenueEintrag = new JMenuItem("Beenden");
beendenMenueEintrag.addActionListener(this);
beendenMenueEintrag.setEnabled(true);
ablageMenue.add(beendenMenueEintrag);
menueleiste.add(ablageMenue);
hf.setJMenuBar(menueleiste);
hf.setVisible(true);Einen einfachen Actionlistener kann man als Klasse mit der folgenden Methode implementieren:
public void actionPerformed(ActionEvent e) {JMenuItem source = (JMenuItem) (e.getSource());if (source == beendenMenueEintrag)System.exit(0);}
beendenMenueEintrag muss hier auf das gleiche Objekt wie bei der obigen Erzeugung der Menüleiste zeigen. Der Actionlistener beendet die Anwendung bei Aufruf!
Hierdurch ergibt sich das folgende Fenster:

Anmerkung: Das hier gezeigt Fenster benutzt das Standard "Look and Feel" von MacOS. Die Rahmendekoration sieht bei anderen Betriebssystemen bzw. anderen "Look and Feel" Themen anders aus. Der oben gezeigt Code ist jedoch plattformneutral.
Java bietet mit AWT (Another Window Tool Kit) und Swing reichhaltige Bibliotheken zum Entwurf von graphischen Benutzeroberflächen.
Hinweis: "Swing" (Schaukel) ist der Projektname. Swing ist Teil der Java Foundation Classes (JFC).
Das folgende Beispiel dient als einfaches Rahmenprogramm für Text Ein- und Ausgaben.
Es übernimmt die Erzeugung eines Fensters mit:
Der eigene Algorithmus wird in der Methode public String executeOperation1(String) oder der beiden anderen executeOpteration Methoden implementiert.
Hierzu werden die folgenden Swing-Klassen verwendet
Das erscheinende Fenster sieht wie folgt aus:

Das Fenster nach einer Texteingabe ("Eingabe"):

Das Fenster nach dem Drücken des Knopf "Do twice". Es wurde die Methode executeOperation2() aufgerufen:

Die Methoden executeOperation1(), executeOperation2(), executeOperation3() erlauben eine Implementierung des eigenen Algorithmus. Der erforderliche String in der Rückgabe wird dann im Fenster angezeigt.
Anbei das Beispielprogramm mit der Klasse DemoFrame:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
public class DemoFrame extends javax.swing.JFrame {
private javax.swing.ButtonGroup buttonGroup1;
private javax.swing.JPanel jPanel1;
private javax.swing.JLabel jLabel1;
private javax.swing.JTextField jTextField1;
private javax.swing.JTextField jTextField2;
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JButton jButton3;
private void initComponents() {
jLabel1 = new javax.swing.JLabel();
jTextField1 = new javax.swing.JTextField();
jTextField2 = new javax.swing.JTextField();
jButton1 = new javax.swing.JButton();
jButton2 = new javax.swing.JButton();
jButton3 = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("My first JFrame");
jLabel1.setText("Input Text:");
jTextField1.setText("Input");
jTextField2.setText("Output");
jTextField2.setEditable(false);
jButton1.setText("Do once:");
jButton2.setText("Do twice:");
jButton3.setText("Do three times:");
JPanel radioPanel = new JPanel(new GridLayout(1, 0));
radioPanel.add(jButton1);
radioPanel.add(jButton2);
radioPanel.add(jButton3);
jButton1.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == jButton1)
jTextField2.setText(
executeOperation1(jTextField1.getText()));
}
}
);
jButton2.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == jButton2)
jTextField2.setText(
executeOperation2(jTextField1.getText()));
}
}
);
jButton3.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
if(e.getSource() == jButton3)
jTextField2.setText(
executeOperation3(jTextField1.getText()));
}
}
);
this.setBounds(300, 300, 200, 30);
setMinimumSize(new Dimension(200,30));
getContentPane().add(jLabel1, BorderLayout.WEST);
getContentPane().add(jTextField1, BorderLayout.CENTER);
getContentPane().add(radioPanel, BorderLayout.NORTH);
getContentPane().add(jTextField2, BorderLayout.SOUTH);
pack();
}
public DemoFrame() {
initComponents();
}
public static void main(String[] args) {
DemoFrame f1 = new DemoFrame();
f1.setVisible(true);
}
public String executeOperation1(String s) {
// Add Application logic here:
String result = "Button 1:" + s;
return (result) ;
}
public String executeOperation2(String s) {
// Add Application logic here:
String result = "Button 2:" + s + s;
return (result) ;
}
public String executeOperation3(String s) {
// Add Application logic here:
String result = "Button 3:" + s + s +s;
return (result) ;
}
}
Die Klasse DemoFrame1 stellt drei Eingabefelder zum Erfassen von Texten zur Verfügung und erlaubt das Starten von 3 Operationen:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
/**
*
* @author sschneid
*/
public class DemoFrame1 extends javax.swing.JFrame {
private ButtonGroup buttonGroup1;
private JPanel jPanel1;
private JLabel jLabel1;
private JTextField jTextFieldin[];
private JTextField jTextField2;
private JButton jButton1;
private JButton jButton2;
private JButton jButton3;
/**
* Anzahl der Eingabefelder
*/
private int inFields = 3;
/**
* Initialisieren aller Komponenten
*/
private void initComponents() {
jLabel1 = new JLabel();
jTextFieldin = new JTextField[inFields];
for (int i = 0; i < inFields; i++) {
jTextFieldin[i] = new JTextField();
}
jTextField2 = new JTextField();
jButton1 = new JButton();
jButton2 = new JButton();
jButton3 = new JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("My second JFrame");
jLabel1.setText("Input Text:");
// Belegen alle Eingabefelder mit Standwerten
for (int i = 0; i < inFields; i++) {
jTextFieldin[i].setText("Input" + i);
}
// Belegen des Augabefeldes mit Standartwert
jTextField2.setText("Output");
jTextField2.setEditable(false);
// Erzeugen dreier Buttons
jButton1.setText("Do once:");
jButton2.setText("Do twice:");
jButton3.setText("Do three times:");
// Ezeugen einer Datenstruktur(Panel) die drei Buttons aufnimmt
JPanel radioPanel = new JPanel(new GridLayout(1, 0));
radioPanel.add(jButton1);
radioPanel.add(jButton2);
radioPanel.add(jButton3);
// Führe Operation 1 aus wenn Button 1 gedrückt wird
jButton1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == jButton1) {
jTextField2.setText(executeOperation1(
jTextFieldin[0].getText(),
jTextFieldin[1].getText(),
jTextFieldin[2].getText()));
}
}
});
// Führe Operation 2 aus wenn Button 2 gedrückt wird
jButton2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == jButton2) {
jTextField2.setText(executeOperation2(
jTextFieldin[0].getText(),
jTextFieldin[1].getText(),
jTextFieldin[2].getText()));
}
}
});
// Führe Operation 3 aus wenn Button 3 gedrückt wird
jButton3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == jButton3) {
jTextField2.setText(executeOperation3(
jTextFieldin[0].getText(),
jTextFieldin[1].getText(),
jTextFieldin[2].getText()));
}
}
});
// Allgemeine Konfiguration des Fensters
this.setBounds(300, 300, 200, 30);
setMinimumSize(new Dimension(200, 30));
// Einfügen der Eingabeaufforderung links
getContentPane().add(jLabel1, BorderLayout.WEST);
// Panel zum verwalten von mehreren Eingabefelder
JPanel inputPanel = new JPanel(new GridLayout(inFields, 0));
for (int i = 0; i < inFields; i++) {
inputPanel.add(jTextFieldin[i]);
}
// Einfügen des Panels mit Eingabefeldern in der Mitte
getContentPane().add(inputPanel, BorderLayout.CENTER);
// Einfügen des Panels mit Buttons oben
getContentPane().add(radioPanel, BorderLayout.NORTH);
// Einfügen der Ausgabezeile unten
getContentPane().add(jTextField2, BorderLayout.SOUTH);
pack();
}
/**
* Initialierung alle Komponenten
*/
public DemoFrame1() {
initComponents();
}
/**
* Hauptprogramm: Erzeuge eine Instanz von DemoFrame1 und zeige sie an
* @param args
*/
public static void main(String[] args) {
DemoFrame1 f1 = new DemoFrame1();
f1.setVisible(true);
}
/**
* Führe Operation 1 aus (Button 1 wurde gedrückt)
* @param s1
* @param s2
* @param s3
* @return
*/
public String executeOperation1(String s1, String s2, String s3) {
// Add Application logic here:
String result = "Button 1:" + s1 + s2 + s3;
return (result);
}
/**
* Führe Operation 2 aus (Button 2 wurde gedrückt)
* @param s1
* @param s2
* @param s3
* @return
*/
public String executeOperation2(String s1, String s2, String s3) {
// Add Application logic here:
String result = "Button 2:" + s1 + s2 + s3;
return (result);
}
/**
* Führe Operation 3 aus (Button 3 wurde gedrückt)
* @param s1
* @param s2
* @param s3
* @return
*/
public String executeOperation3(String s1, String s2, String s3) {
// Add Application logic here:
String result = "Button 3:" + s1 + s2 + s3;
return (result);
}
}
Die Math Klasse enthält alle mathematischen Hilfsroutinen wie z.Bsp.
...erzeugt zufällige Fliesskommazahlen im Intervall von 0.0 bis 1.0
Die folgende Methode erzeugt ein beliebig großes Feld von zufälligen Ganzzahlen in einem bestimmten Bereich (range)
public static int[] createRandom(int size, int range) {
int[] array1 = new int[size];
for (int i=0; i<size;i++)
array1[i] = (int)(Math.random()*range);
return array1;
} Die Java Runtime bietet die Möglichkeit die folgenden Systemwerte auszulesen:
Die Klasse bietet ebenfalls die Möglichkeit eine Reihe von Operationen zu starten:
Warnung: Alle aufgeführten Operationen haben erheblichen Einfluss auf die gesamte VM. Im normalen Entwicklungsumfeld größerer Anwendungen führen die oben genannten Operationen in der Regel zu erheblichen negativen Seiteneffekten für Sicherheit, Stabilität und Performanz von Anwendungen.
Die folgende Methode nutzt die Klasse Runtime um die wichtigsten Werte des Prozesses auszulesen:
public static void systemStatus(){
Runtime r = Runtime.getRuntime();
System.out.println("*** System Status ***");
System.out.println("Prozessoren : " + r.availableProcessors());
System.out.println("Freier Speicher: " + r.freeMemory());
System.out.println("Maximaler Speicher: " + r.maxMemory());
System.out.println("Gesamter Speicher: " + r.totalMemory());
System.out.println("*** ***");
}
Der Vergleich von Zeichenketten hängt oft von der lexikographischen Ordnung ab. Die lexikographische Ordnung selbst hängt wieder von der Region ab. Java bietet hierfür die Klasse Collator (Collation engl. : Der Textvergleich) an, die Zeichenketten nach bestimmten Kriterien sortieren kann. Die Methode compare() erlaubt das Vergleichen zweier Zeichenketten.
Zur Bestimmung der Sortierreihenfolge wird die Klasse java.util.Locale benötigt, die die entsprechende Region und deren Sprache bestimmt.
Das Vorgehen zum Konfigurieren und Vergleichen von Zeichenketten ist das folgende:
Vergleichen von Vor- und Nachnamen einer Person nach deutschen Sortierregeln inklusive besonderer deutscher Zeichen mit der Methode compare() der Klasse Collator.
import java.text.Collator;import java.util.Locale;public class Person{private String nachname;private String vorname;private static Locale myLocale = Locale.GERMANY;public boolean istKleinerAls(Person p) {boolean kleiner = false;Collator myCollator = Collator.getInstance(myLocale);
// Beim Vergleich auch Gross- und Kleinschreibung unterscheidenmyCollator.setStrength(Collator.TERTIARY);// Konfiguriere die Sortierordnungif (myCollator.compare(nachname, p.nachname) < 0) {kleiner = true;} else if (myCollator.compare(nachname, p.nachname) == 0) {kleiner = myCollator.compare(vorname, p.vorname) < 0;}return kleiner;
}
}
Das Programm Lexikographisch akzeptiert die folgende Kommandozeilensyntax:
java Kurs2.Sort.Lexikographisch String1 String2 [[German|French|UK|US] [PRIMARY|SECONDARY|TERTIARY]]
$ java Kurs2.Sort.Lexikographisch SPULE Spüle German PRIMARYSPULE ist gleich Spüle$ java Kurs2.Sort.Lexikographisch SPULE Spüle German SECONDARYSPULE vor Spüle$ java Kurs2.Sort.Lexikographisch Schmidt SCHMIDT German PRIMARYSchmidt ist gleich SCHMIDT$ java Kurs2.Sort.Lexikographisch Schmidt SCHMIDT German SECONDARYSchmidt ist gleich SCHMIDT$ java Kurs2.Sort.Lexikographisch Schmidt SCHMIDT German TERTIARYSchmidt vor SCHMIDT
package Kurs2.Sort;
import java.text.Collator;
import java.util.Locale;
/**
*
* @author sschneid
*/
public class Lexikographisch {
public static void main(String[] args) {
String s1 = "Test1";
String s2 = "Test2";
Locale myLocale = Locale.GERMANY;
int strength = Collator.TERTIARY;
if (args.length < 2) {
System.out.println("Erforderliche Mindestparameter: String1 String2"); System.out.println("Syntax: java Kurs2.Sort.Lexikographisch " + "String1 String2 " + "[[German|French|UK|US] [PRIMARY|SECONDARY|TERTIARY]]"); } else {
s1 = args[0];
s2 = args[1];
}
if (args.length >= 3) {
String loc = args[2];
if (loc.equalsIgnoreCase("German")
|| loc.equalsIgnoreCase("Germany")) {
myLocale = Locale.GERMAN;
}
if (loc.equalsIgnoreCase("France")
|| loc.equalsIgnoreCase("French")) {
myLocale = Locale.FRENCH;
}
if (loc.equalsIgnoreCase("US")) {
myLocale = Locale.US;
}
if (loc.equalsIgnoreCase("UK")
|| loc.equalsIgnoreCase("English")) {
myLocale = Locale.UK;
}
// Add more locales here...
}
if (args.length >= 4) {
String s = args[3];
if (s.equalsIgnoreCase("PRIMARY")) {
strength = Collator.PRIMARY;
}
if (s.equalsIgnoreCase("SECONDARY")) {
strength = Collator.SECONDARY;
}
if (s.equalsIgnoreCase("TERTIARY")) {
strength = Collator.TERTIARY;
}
}
vergleich(s1, s2, myLocale, strength);
}
private static void vergleich(String s1, String s2, Locale myLocale, int strength) {
Collator myCollator = Collator.getInstance(myLocale);
// Beim Vergleich auch Gross- und Kleinschreibung unterscheiden
// Konfiguriere die Sortierordnung
myCollator.setStrength(strength);
if (myCollator.compare(s1, s2) == 0) {
System.out.println(s1 + " ist gleich " + s2);
}
if (myCollator.compare(s1, s2) < 0) {
System.out.println(s1 + " vor " + s2);
}
if (myCollator.compare(s1, s2) > 0) {
System.out.println(s2 + " vor " + s1);
}
}
}
|
(Lizenz) |
Es gibt ein paar wichtige Regeln um Code auch in Zukunft noch verstehen zu können. In der Javawelt gelten die folgenden Konventionen:
Siehe auch Wikibooks: Standard für Variablen und Bezeichner |
Hier werden nur kostenlose Entwicklungsumgebungen vorgestellt.
Das Ziel ist es den Studenten eine Orientierung für den Programmierkurs zu geben. Die Leistung kommerziell verfügbarer Werkzeuge soll hiermit nicht geschmälert werden.
wikipedia.org verfügt über eine gute Sammlung von Java Entwicklungsumgebungen.

Netbeans (Version 7.1) bietet dem Entwickler die Vervollständigung von Methoden- und Variablennamen von Klassen automatisch an.
Diese Information holt sich Netbeans automatisch aus dem für das Projekt konfigurierten Bibliotheken und den Laufzeitbibliotheken des gewählten JRE/JDK. Netbeans kann aber auch automatisch die Erklärung der entsprechenden Methoden und Variablen anzeigen. Man muss dies aber explizit konfigurieren,
Das folgende Bild zeigt die Eingabe des Texts System.out.prin im Javaeditor von Netbeans. Netbeans wird normalerweise einen Auswahldialog mit den sinnvollen Vervollständigungen erzeugen.
In diesem Fall sind es alle Methoden des Attributs out vom Typ PrintStream die mit prin beginnen. Dieses Verhalten ist Standard in Netbeans. Man kann aber Netbeans auch so konfigurieren, dass man die Bedeutung einer ausgewählten Methode sehen kann. Dies geschieht hier im zweiten Fenster unterhalb der "prin" Liste. Netbeans zeigt die Javadokumentation der Methode java.io.PrintStream() an, da System.out auf ein Objekt der Klasse PrintStream zeigt.

Die Erklärung der Methoden bzw. Variablen fehlt jedoch in der Standardkonfiguration. Netbeans kann hier nicht automatisch auf die entsprechenden Daten zugreifen. Man muss Netbeans zeigen wo diese Daten zu finden sind.
Eine Möglichkeit ist in Netbeans den URL auf die Oracle Javadokumentation im Internet zu konfigurieren. Die geschieht mit den folgenden Schritten:
Die Dokumentation ist eine Eigenschaft der gewählten Javaplattform.
Wählen Sie im Menü "Tools" den Eintrag "Java Platforms".

Im Fenster "Java Platform Manager" muss man die Karteikarte "Javadoc" auswählen:

Hier kann man dann URLs oder Verweise auf lokale Dateien konfigurieren.
Im Bild unten wird auf die Dokumentation von Java 6 gezeigt, da als gewählte Plattform auch Java 6 gewählt wurde

Die gezeigten Fenster stammen von MacOS. Die Dekorationen der Fenster werden auf anderen Plattformen (Windows, Linux, Solaris etc.) unterschiedlich aussehen. Inhalte und Auswahlmöglichkeiten sollten aber auf anderen Plattformen gleich sein (Stand Netbeans 7.0.1; Oktober 2011),
Anbei einige Befehlsabkürzungen in Eclipse und Netbeans
| Abkürzung | Netbeans | Eclipse |
|---|---|---|
| Referenzen |
Keyboard Shortcuts (Spickzettel) Menü->Preferences->Editor->Coe templates |
Menü:preferences ->Java->Editor->Templates |
| System.out.println("") | sout<tab> | sysout<ctrl><Leertaste> |
| String | St<tab> | |
| switch | sw<tab> | switch<ctrl><Leertaste> |
| main | psvm<tab> | main<ctrl><Leertaste> |
|
(Lizenz) |
Nach Wikipedia:
|
Im Rahmen der Vorlesung werden einige wenige, ausgewählte Entwurfsmuster vorgestellt:
Es gibt Anwendungsfälle in denen es gewünscht ist genau ein Objekt global zur Verfügung zu stellen. Dieser Anwendungsfall wird mit dem Entwurfsmuster "Singleton" (Einzelstück) beschrieben. Beispiele für solche Anwendungsfälle sind:
VerwendungEin Einzelstück
Ein Singleton (Einzelstück) implementiert eine ähnliche Struktur wie eine globale Variable. KategorieDas Einzelstück (Singleton) gehört zur Kategorie der Erzeugungsmuster (Creational Pattern). |
UML Diagramm |
/**
* Einfache Implementierung des Einzelstueck (Singleton
*/
public class Einzelstueck {
private static Einzelstueck instanz = null;
/**
* privater Konstruktor der nur innerhalb der Klasse
* aufgerufen werden kann
*/
private Einzelstueck() {
// Individuelle Initialisierung erfolgt hier
}
/**
* Erzeugen des einzigen Objekts falls noch keines existiert.
* Rückgabe des Objekts falls es schon existiert
* Diese Methode ist statisch. Sie kann auch ohne die Existenz einer Instanz aufgerufen werden.
* Die Methode ist die einzige öffentliche Methode
*/
public static Einzelstueck getInstance() {
if (instanz == null) {
instanz = new Einzelstueck();
}
return instanz;
}}Das gezeigte Beispiel verwendet eine "Lazy initialization". Das Objekt wird erst erzeugt wenn es auch wirklich benötigt wird. Hierdurch kann das unnötige Allokieren von Ressourcen vermieden werden.
Der Nachteil dieser Implementierung besteht darin, dass sie nicht threadsicher ist. In einem Javaprogramm mit mehreren Threads (Ausführungseinheiten) können zwei Threads gleichzeitig ein Objekt erzeugen und damit das gewünschte Ziel des Einzelstücks durchkreuzen.
/**
* Threadsichere Implementierung des Entwurfsmuster Einzelstueck (Singleton)
*/
public class Einzelstueck {
private static Einzelstueck instanz = new Einzelstueck();
/**
* privater Konstruktor der nur innerhalb der Klasse
* aufgerufen werden kann
*/
private Einzelstueck() {
// Individuelle Initialisierung erfolgt hier
}
/**
* Diese Methode ist statisch. Sie kann auch ohne die Existenz einer Instanz aufgerufen werden.
* Die Methode ist die einzige öffentliche Methode
*/
public static Einzelstueck getInstance() {
return instanz;
}
}Die hier gezeigte Implementierung ist threadsicher da die Instanz schon beim Laden der Klasse erzeugt wird.
Die Dokumentation von Methoden, Variablen und Klassen ist Teil der Sprache und wird von Standardwerkzeugen des JDK unterstützt.
Die genauen Spezifikationen zum Dokumentieren von Javaklassen sind im Oracle Dokument "Requirements for Writing Java API Specifications" beschrieben. Nach diesen Richtlinien können auch eigene Javaklassen dokumentiert werden. Oracle bietet hierzu ein recht gutes Tutorial an.
Das Format für die Dokumentation ist ein Sonderfall des mehrzeiligen Javakommentars. Es sieht wie folgt aus:
/** * Hier steht Dokumentationskommentar * Hier steht eine weitere Zeile mit Dokumentationskommentar */
Javakommentare können mit html formatiert werden.
Beginnen Sie die Dokumentation Klasse mit einer Zusammenfassung ihrer Funktion.
Für die Klasse Ware.java kann das wie folgt aussehen:
/**
* Ware dient zum Verwalten von Guetern mit Preisen und Namen in einem Lager.
* @author Stefan Schneider
* @version 1.1
* @see Lager
*/
public class Ware {
...
}
Die Klassendokumentation erlaubt die Verwendung von "Tags" mit denen man weitere Informationen beisteuern kann. Man kann für Klassen die folgenden Tags verwenden:
Zur Dokumentation von Attributen wird die Dokumentation der Deklaration vorangestellt. Bei der Klasse Ware kann die zum Beispiel wie folgt geschehen:
public class Ware {
...
/**
* Der aktuelle Mehrwertsteuersatz 2010.
* Er liegt zur Zeit bei {@value} .
*
* @since 1.0
*/
public static final double mws = 0.19;
...
}
Bei Klassenvariablen können die folgenden Tags verwendet werden:
Die Dokumentation von Konstruktoren und Methoden wird ebenfalls direkt der Implementierung der entsprechenden Methode als Javakommentar vorangestellt. Hiermit kann man neben der Bedeutung der Methode auch die Eingabe- und Ausgabeparameter dokumentieren. Siehe folgendes Beispiel:
/**
* Liefert den Namen einer Ware zurueck.
* @return Name der Ware
*/
public String get_name(){return name;}
/**
* Setzen eines neuen Nettopreis
* @param npr der neue Nettopreis
*/
public void set_nettoPreis(int npr) {
...
}
Bei Methoden und Konstruktoren sind die folgenden Tags möglich:
@see@since@deprecated@param@return@throws und @exception@serialData{@link}{@linkplain}{@inheritDoc}{@docRoot}Das JDK Programm javadoc (Oracle Dokumentation) erzeugt aus Javaquelldateien Java API Beschreibungen im html Format.
In seiner einfachsten Form kann man eine Java API Dokumentation mit dem folgenden Kommando erzeugen:
$ javadoc JavaQuelldatei.java ... JavaQuelldatei1.java
Das Kommando javadoc hat zahlreiche Optionen (siehe Oracle Dokumentation) die direkt nach dem Kommando eingefügt werden können. Die wichtigsten sind:
Für eine Klasse Ware.java mit allen Komponenten:
/**
* Dies ist die Dokumentation der Klasse Ware. Ware dient zum Verwalten von Gütern
* mit Preisen und Namen in einem Lager.
* @author Stefan Schneider
* @version 1.1
* @see Lager
*/
public class Ware {
/**
* Der aktuelle Mehrwertsteuersatz 2010.
* Er liegt zur Zeit bei {@value}.
*
* @since 1.0
*/
public static final double mws = 0.19;
private double nettoPreis; //Deklaration
public boolean halbeMws;
private String name;
/**
* Konstruktor fuer die Klasse Ware
* @param n der Name der Ware
* @param np der Nettorpreis
* @param hmws halber Mehrwertsteuersatz für Ware gueltig
*/
public Ware(String n, double np, boolean hmws) {
name = n;
nettoPreis = np;
halbeMws = hmws;
}
/**
* Liefert den Namen einer Ware zurueck.
* @return Name der Ware
*/
public String get_name() {
return name;
}
/**
* Setzen eines neuen Nettopreis
* @param npr der neue Nettopreis
*/
public void set_nettoPreis(double npr) {
nettoPreis = npr;
}
/**
* Ausdrucken aller Werte auf der Konsole
*/
public void drucken() {
System.out.println("Name: " + name);
System.out.println("netto: " + nettoPreis);
System.out.println("Brutto: " + bruttoPreis());
System.out.println("Halbe Mws:" + halbeMws);
}
/**
* Ausgabe des Nettopreis
* @return der Nettopreis
*/
public double nettoPreis() {
return nettoPreis;
}
/**
* Ausgabe des Bruttopreis
* @return der Bruttopreis
*/
public double bruttoPreis() {
double bp; //temporaere Variable; keine Klassenvariable
if (halbeMws) {
bp = Math.round(nettoPreis * (mws / 2 + 1) * 100) / 100;
} else {
bp = Math.round(nettoPreis * (mws + 1) * 100) / 100;
}
return bp;
}
}
Die Dokumentation kann mit dem Kommando javadoc generiert werden. Für das oben gezeigte Beispiel werden zwei Optionen zur Generierung des Autors und der Version benötigt. Die Optionen erlauben die Informationen über Autoren und Versionen auf Wunsch wegzulassen:
$ javadoc -author -version Ware.java
Das Kommando erzeugt eine Reihe von html Dateien im gleichen Verzeichnis. Die generierte Datei index.html sieht wie folgt aus (Screen shot):




|
(Lizenz) |
Am Ende dieses Blocks können Sie:
|
Sie sind in der Lage die folgenden Fragen zu beantworten:
Javaprogramme können aus sehr vielen Klassen und externen Bibliotheken bestehen. "jar" Dateien erlauben das Bündeln von Klassendateien und anderer Dateien um Installation und die Verteilung von Javaprogrammen zu vereinfachen. Mit jar Dateien werden die folgenden Ziele verfolgt:
jar Dateien werden mit dem gleichnamigen Kommando jar des JDK manipuliert. jar steht für Java ARchive.
Das jar Format ist plattformunabhängig und ist daher auf allen Javaplattformen verfügbar.
Hinweis: Das jar Format basiert auf dem zip Dateiformat. Das heißt, dass man auch mit zip,unzip Inhalte von jar Dateien inspizieren kann! Zum Erzeugen von jar Dateien ist zip jedoch nicht geeignet da jar zusätzliche Informationen wie Manifestinformationen etc. erzeugt.
| Operation | Befehl |
|---|---|
| Erzeugen einer jar Datei | jar cf jar-Datei liste-Dateien |
| Auflisten des Inhalts einer jar Datei | jar tvf jar-Datei |
| Auslesen einer jar Datei | jar xf jar-Datei |
| Auslesen bestimmter Dateien einer jar Datei | jar xf jar-Datei auszupackende-Datei |
| Starten einer Javaanwendung mit Hilfe einer jar Datei | java -jar anwendung.jar |
Das allgemeine Format zum Erzeugen einer Datei mit einem jar-Archiv ist:
jar cf jar-Datei Eingabedatei(en)
Die hier verwendeten Optionen und Argumente bedeuten
Beim Erzeugen einer jar Datei legt das Kommando jar immer eine Manifest Datei MANIFEST.MF im folgenden Verzeichnis des Archivs an:
META-INF/MANIFEST.MF
Der Inhalt dieser Datei ist im einfachsten Fall:
Manifest-Version: 1.0 Created-By: 1.6.0 (Sun Microsystems Inc.)
Im Manifest des Archivs werden Metadaten wie zum Beispiel Signaturen, das gewählte Hauptprogramm zum Starten, oder Urheberansprüche verwaltet.
Weitere Optionen:
Gegeben sei eine Anwendung bei der drei Klassen Database.class, Main.class und GUI.class und alle Bilddateien im Verzeichnis bilder in das jar Archiv gepackt werden sollen:

Dies geschieht mit dem Befehl:
$ jar cvf appl.jar *.class bilder Manifest wurde hinzugefügt. Hinzufügen von: Database.class (ein = 10240) (aus = 27) (komprimiert 99 %) Hinzufügen von: GUI.class (ein = 10240) (aus = 27) (komprimiert 99 %) Hinzufügen von: Main.class (ein = 10240) (aus = 27) (komprimiert 99 %) Hinzufügen von: bilder/ (ein = 0) (aus = 0) (gespeichert 0 %) Hinzufügen von: bilder/Bild1.jpg (ein = 10240) (aus = 27) (komprimiert 99 %) Hinzufügen von: bilder/Bild2.jpg (ein = 10240) (aus = 27) (komprimiert 99 %) Hinzufügen von: bilder/Bild3.jpg (ein = 10240) (aus = 27) (komprimiert 99 %)
jar Archive können mit der der jar Option t inspiziert werden:
jar tvf jar-Datei
Die hier verwendeten Optionen und Argumente bedeuten:
Für das oben angeführte Beispiel ergibt sich der folgende Befehl
$ jar tf appl.jar META-INF/ META-INF/MANIFEST.MF Database.class\r\nGUI.class nMain.class bilder/ bilder/Bild1.jpg bilder/Bild2.jpg bilder/Bild3.jpg
Einen detaillierten Überblick kann man mit der v Option (v: verbose, engl. "ausführlich") gewinnen
$ jar tvf appl.jar
0 Sun Dec 12 16:27:56 CET 2010 META-INF/
60 Sun Dec 12 16:27:56 CET 2010 META-INF/MANIFEST.MF
10240 Sun Dec 12 16:26:10 CET 2010 Database.class
10240 Sun Dec 12 16:26:00 CET 2010 GUI.class
10240 Sun Dec 12 16:25:50 CET 2010 Main.class
0 Sun Dec 12 16:27:12 CET 2010 bilder/
10240 Sun Dec 12 16:27:02 CET 2010 bilder/Bild1.jpg
10240 Sun Dec 12 16:27:04 CET 2010 bilder/Bild2.jpg
10240 Sun Dec 12 16:27:12 CET 2010 bilder/Bild3.jpg
jar Archive werden mit dem folgenden Befehl ausgepackt (ausgelesen);
$ jar xvf jar-Datei [archivierte-Datei(en)]
Die hier verwendeten Optionen und Argumente bedeuten:
Der jar Befehl wird beim Extrahieren
Auspacken des jar Archivs mit ausführlicher Protokollierung:
$ jar xvf appl.jar
erstellt: META-INF/
dekomprimiert: META-INF/MANIFEST.MF
dekomprimiert: Database.class
dekomprimiert: GUI.class
dekomprimiert: Main.class
erstellt: bilder/
dekomprimiert: bilder/Bild1.jpg
dekomprimiert: bilder/Bild2.jpg
dekomprimiert: bilder/Bild3.jpg
Die Option u (update) erlaubt das Hinzufügen von Dateien zu Archiven mit der folgenden Syntax
$ jar uf jar-Archiv Datei(en
Das Javalaufzeitsystem sucht beim Aufruf mit Hilfe des "Classpath" (Pfad zu den Klassen) nach ausführbaren Dateien mit der Endung .class .
Wird kein expliziter "Classpath" angegeben, so wird im aktuellen Verzeichnis und den darunterliegenden Verzeichnissen gesucht. Unterverzeichnisse können Pakete mit deren Klassen enthalten.
Mit Hilfe der Option -cp oder -classpath kann man die Suche nach Klassendateien steuern. Man kann hier eine Liste der folgenden Dinge angeben:
Die Elementeliste der jar Archive und Suchverzeichnisse mit mit dem Zeichen ":" getrennt. Hiermit kann man ein Javaprogramm mit einem jar Archiv starten:
$ java -cp appl.jar Main
jar Archive können benutzt werden um direkt Programme aus ihnen heraus anzustarten. Dies geschieht mit der Option -jar im Kommando java:
$ java -jar jar-Datei
Hierzu muss in der Manifestdatei des Archivs ein einzeiliger Eintrag mit der Klasse stehen deren Methode main() aufgerufen werden soll. Dieser Eintrag muss im folgenden Format geschehen:
Main-Class: klassenname
Zum Erzeugen des Manifesteintrags gibt es eine Reihe von Möglichkeiten
Eine Manifestdatei mit den gewünschten Einträgen wird selbst erstellt und dann beim Erzeugen des Archivs mit Hilfe der m-Option mit angegeben.
Beispiel:
$ jar cfm appl.jar Manifest.txt *.class bilder
Die Klasse mit der main() Methode wird direkt angegeben.
Beispiel:
$ jar cfe appl.jar Main *.class bilder
Im Kurs ist es unter Umständen notwendig die Befehle des Java JDK auf der Kommandozeile aufzurufen. Normalerweise wird schon bei der Installation des JDK diese Konfiguration automatisch vorgenommen. Man kann dann die Befehle java, javac, jar etc. wie folgt verwenden:

Ist das JDK jedoch an einer anderen Stelle installiert, kann es sein, dass man den Windowssuchpfad individuell konfigurieren muss.
Als erstes muss man das das bin Verzeichnis des JDK finden. In ihm stehen alle Programme des JDK. Man kann es an den Dateien javac.exe und javadoc.exe erkennen Ein typischer Ort kann zum Beispiel sein:
C:\Program Files\jdk1.6.0_24\bin\
Dieses Verzeichnis muss zum Standardsuchpfad des Benutzers hinzugefügt werden.
Dies geschieht in Windows XP in der Systemsteuerung (Ikone System)

Hier muss die Karteikarte "Advanced" angeklickt werden.

Hier gibt es einen Schalter zum Pflegen der Umgebungsvariablen:

Bei den Umgebungsvariablen muss die Variable PATH gesucht und editiert werden:

Beim Einpflegen des zusätzlichen Suchpfades ist das Folgende zu beachten
Vorsicht:

|
(Lizenz) |
Die klausurrelevanten Abschnitte dieses Kapitels sind das Packen mit dem Java-archive Werkzeug jar und der Befehl javadoc Am Ende dieses Blocks können Sie:
|
Sie sind in der Lage die folgenden Fragen zu beantworten:

(Lizenz)
Gegeben sei das folgende Programm:
Gegeben ist das folgende Java-Programm:
public class Main2 {
static short k;
public static void main(String[] args) {
short i, k;
short imax = 10;
for (i = 1; i < imax; i++) {
for (k = 1; k < imax; k++) {
if ((k == i) || (k == (imax - i))) {
System.out.print("*");
}
else { System.out.print(" "); // ein Leerzeichen
}
} // for k
switch (i) {
case 1:
case 2:
case 3:
case 4:
System.out.println("LINKS");
break;
case 6:
case 7:
case 8:
case 9:
System.out.println("RECHTS");
break;
default:
System.out.println("MITTE");
} // switch
} // for i
} // main
}
Schlüsselwörter im Prgramm:
public class Main2 {
static short k;
public static void main(String[] args) {
short i, k;
short imax = 10;
for (i = 1; i < imax; i++) {
for (k = 1; k < imax; k++) {
if ((k == i) || (k == (imax - i))) {
System.out.print("*");
}
else { System.out.print(" "); // ein Leerzeichen
}
} // for k
switch (i) {
case 1:
case 2:
case 3:
case 4:
System.out.println("LINKS");
break;
case 6:
case 7:
case 8:
case 9:
System.out.println("RECHTS");
break;
default:
System.out.println("MITTE");
} // switch
} // for i
} // main
}Konsolenausgaben des Programms
* *LINKS
* * LINKS
* * LINKS
* * LINKS
* MITTE
* * RECHTS
* * RECHTS
* * RECHTS
* *RECHTS
Beide Befehle erzeugen einer Konsolenausgabe von Texten.
| Quellcode | Ausgabe |
|---|---|
System.out.print("Person: "); |
Person: John Doe **************** |
aber:
| Quellcode | Ausgabe |
|---|---|
System.out.println("Person: "); |
Person: John Doe **************** |
Java kann Zeichenketten nicht als Basistypen verwalten. Hierzu verwendet Java eine Klasse mit dem Namen String. Zeichenketten werden wie Objekte behandelt
Die hat eine Reihe von Konsequenzen:
Mit
i=j;
wird der Wert von j auf i zugewiesen. Mit
if(i==j)
werden zwei Variablen verglichen.
Zuweisungen tauchen nicht in boolschen Termen auf!
Die folgende Syntax ist korrekt:
int i=0; int j=1; boolean b = (i==j);
Die Boolsche Variable b wird mit false belegt da i ungleich j ist.
|
|
Lernen
Javabilder
|