Objektorientierte Programmierung: Unterschied zwischen den Versionen

Aus CCWiki
Zur Navigation springen Zur Suche springen
Der Seiteninhalt wurde durch einen anderen Text ersetzt: „== Exceptions == '''Exceptions''' oder '''Ausnahmen''' können von {{Link|Methoden}} geworfen werden. Eine '''Exception''' tritt auf…“
Markierung: Ersetzt
Zeile 1: Zeile 1:
<cite>Die objektorientierte Programmierung (kurz OOP) ist ein auf dem Konzept der Objektorientierung basierendes Programmierparadigma. Die Grundidee besteht darin, die Architektur einer Software an den Grundstrukturen desjenigen Bereichs der Wirklichkeit auszurichten, der die gegebene Anwendung betrifft. Ein Modell dieser Strukturen wird in der Entwurfsphase aufgestellt. Es enthält Informationen über die auftretenden Objekte und deren Abstraktionen, ihre Typen. Die Umsetzung dieser Denkweise erfordert die Einführung verschiedener Konzepte, insbesondere [[{{PAGENAMEE}}#Klasse|Klassen]], [[{{PAGENAMEE}}#Vererbung|Vererbung]], [[{{PAGENAMEE}}#Polymorphismus|Polymorphie]] und [[{{PAGENAMEE}}#Has_A|spätes Binden (dynamisches Binden)]].</cite><ref>https://de.wikipedia.org/wiki/{{PAGENAMEE}}</ref>
{{TOC limit|4}}
= Java =
Java ist eine objektorientierte Programmiersprache. In einigen Punkten nimmt Sie die '''Objektorientierung''' nicht so streng wie andere Sprachen. Ein Beispiel hierfür wären die '''primitiven Datentypen'''. Somit ist in Java nicht alles ein '''Objekt'''.<br>
== Primitive Datentypen ==
In Java gibt es eine vielzahl von '''primitiven Datentypen'''. Sie unterscheiden sich darin, dass Sie kein '''Object''' sind. Sie haben keine '''Methoden''' und auch keine '''Attribute'''. Wird eine Variable mit einem '''primitiven Datentyp''' erstellt und dieser Variable ein Wert zugewiesen, so enthält diese Variable den wirklichen Wert und nicht nur eine '''Referenz''' auf das eigentliche Objekt:
{{JML|code=//Variable a enthält den Wert 10
int a = 10;
//Variable b enthält nur den Verweis auf das Integer Objekt mit dem Wert 10
Integer b = Integer.valueOf(10);
}}
Zu jedem primitiven Datentyp, gibt es einen entsprechende Klasse:
{| class="wikitable"
|+ Entsprechende Klasse für primitive Datentypen
|-
! primitiver Datentyp !! Klasse
|-
| {{JSL|int}} || {{JSL|java.lang.Integer}}
|-
| {{JSL|long}} || {{JSL|java.lang.Long}}
|-
| {{JSL|float}} || {{JSL|java.lang.Float}}
|-
| {{JSL|double}} || {{JSL|java.lang.Double}}
|-
| {{JSL|char}} || {{JSL|java.lang.Character}}
|-
| {{JSL|boolean}} || {{JSL|java.lang.Boolean}}
|}
Primitive Datentypen können automatisch in ihr entsprechendes Klassenäquivalent umgewandelt werden und natürlich umgekehrt. Dieser Prozess nennt sich '''Autoboxing'''. Dies ist vorallem von Vorteil, wenn mit '''Collections (Listen,...)''' gearbeitet wird, da diese nur mit Klassen funktionieren.
{{JML|code=
//Umwandlung der Zahl 10 in ein Integer Object
Integer b = 10;
//Integer Object wird in primitive Zahl 10 umgewandelt
int c = b;
}}
== Klasse ==
<cite>Unter einer Klasse (auch Objekttyp genannt) 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 die Abbildung von realen Objekten in Softwareobjekte und beschreibt Attribute (Eigenschaften) und Methoden (Verhaltensweisen) der Objekte. Verallgemeinernd könnte man auch sagen, dass eine Klasse dem Datentyp eines Objekts entspricht.<ref>https://de.wikipedia.org/wiki/Klasse_(Objektorientierung)</ref></cite><br>
Wir sehen, eine [[{{PAGENAMEE}}#Klasse|Klasse]] kann man sich wie eine Vorlage vorstellen, aus dieser dann '''Instanzen / Objekte''' mittels des Schlüsselworts '''new''' erstellt werden. Weiters sei erwähnt, dass jede [[{{PAGENAMEE}}#Klasse|Klasse]] egal ob es hingeschrieben wird oder nicht, von der [[{{PAGENAMEE}}#Klasse|Klasse]] {{JSL|java.lang.Object}} erbt.
=== Konstruktor ===
Jede [[{{PAGENAMEE}}#Klasse|Klasse]] hat einen Konstruktor, ist dieser leer, d.h. er nimmt keine Parameter, muss dieser nicht geschrieben werden. Der Konstruktor heißt immer gleich wie die Klasse selbst.
{{JML|code=
public class Animal {
  private String name;
  //Standardkonstruktor wird verwendet.
}
...
//Instanz von Animal erstellen
Animal a = new Animal();
}}
Wollen wir nur beispielsweise garantieren, dass ein '''Animal''' bei der Instanzierung einen Namen hat, so könnten wir den '''Konstruktor''' verändern.
{{JML|code=
public class Animal {
  private String name;
  public Animal(String name) {
    this.name = name;
  }
}
...
//Instanz von Animal erstellen
Animal a = new Animal("Alfons");
}}
Es können auch mehrere '''Konstruktoren''' existieren. Diese können sich auch gegenseitig aufrufen. Sobald ein '''Konstruktor''' definiert wird, so kann der Standardkonstruktor nicht mehr zur Instanzierung verwendet werden, dieser muss explizit hingeschrieben werden, wenn nötig.
{{JML|code=
public class Animal {
  private String name;
  public Animal() {
    this("John Doe")
  }
  public Animal(String name) {
    this.name = name;
  }
}
...
//Instanz von Animal erstellen
Animal a = new Animal(); //John Doe
//Weitere Instanz mit gewähltem Namen
Animal b = new Animal("Alfons"); //Alfons
}}
=== Attribute ===
Klassen haben Attribute (Eigenschaften). Wir unterscheiden hier zwei Varianten von Attributen. '''Instanz Attribute''' und '''Klassen Attribute'''.
==== Instanz Attribute ====
{{JML|code=
public class Animal {
  public String name;
}
...
//Korrekter Zugriff
Animal a = new Animal();
System.out.println(a.name);
}}
Jede Instanz verfügt über eigene Instanzvariablen, diese werden nicht untereinander geteilt.
==== Klassen Attribute ====
{{JML|code=
public class Animal {
  public static double PI = 3.14;
}
//Korrekter Zugriff
System.out.println(Animal.PI);
}}
Attribute die mit dem Schlüsselwort '''static''' gekennzeichnet werden, existieren nur einmal. Vereinfacht gesagt, diese Attribute sind an die Klasse und nicht an die Instanz gebunden.
=== Methoden ===
'''Methoden''' sind Prozeduren (Abläufe) die in [[{{PAGENAMEE}}#Klasse|Klassen]] Definiert werden. Durch Methoden können '''Objekte''' miteinander interagieren, bzw. deren Zustand kann modifiziert werden. Genauso wie bei den '''Attributen''' können '''Methoden''' für eine '''Instanz''' oder mit dem Schlüsselwort '''static''' für die [[{{PAGENAMEE}}#Klasse|Klasse]] definiert werden.<br><br>
'''Methoden''' haben zumindest:
* Methodennamen
* Rückgabetype
* 0 - * Parameter
* Methodenrumpf (Code der Ausgeführt wird)
==== Methodensignatur ====
Die '''Methodensignatur''' einer Methode, besteht aus '''Methodenname''', '''Übergabeparameter'''. Weiters muss die Signatur jeder '''Methode''' in einer [[{{PAGENAMEE}}#Klasse|Klasse]] eindeutig sein.<br>
Warum gehört der '''Rückgabetyp''' nicht zur '''Methodensignatur'''? Angenommen wir haben folgende [[{{PAGENAMEE}}#Klasse|Klasse]]:
{{JML|code=
public class Calc {
  public int add(float a, float b) {
    return (int) (a + b);
  }
  public float add(float a, float b) {
    return a + b;
  }
}
}}
In '''Java''' ist es nicht zwingend nötig den Rückgabewert einer Methode zu verarbeiten:
{{JML|code=
public static void main(String[] args) {
  Calc c = new Calc();
  c.add(10.0f, 20.0f);
}
}}
Der '''Compiler''' hat keine Möglichkeit herauszufinden, welche '''Methode''' in '''Calc''' nun verwendet werden soll. Deswegen gehört der '''Rückgabetyp''' nicht zur '''Methodensignatur''' und das Beispiel würde nicht compilieren.
==== Instanzmethode ====
Folgendes Beispiel zeigt den klassischen '''getter''' und '''setter''', '''Attribute''' sollten nach Möglichkeit nur über diese geholt/verändert werden und die '''Attribute''' sollten nach außen hin nicht sichtbar sein ('''private'''). Warum? Durch den '''setter''' ist es möglich, eine inkorrekte Modifikation des '''Attributs''' zu unterbinden.
{{JML|code=
public class Animal {
  private String name;
  public void setName(String name) {
    this.name = name;
  }
  public void getName(String name) {
    this.name = name;
  }
}
...
//Korrekter Aufruf
Animal a = new Animal();
a.setName("Alfons");
System.out.println(a.getName());
}}
==== Klassenmethode ====
Eine klassische '''Helper''' Methode. Diese Methode wird über die Klasse selbst aufgerufen, sie existiert nur ein mal. Die Methode soll nicht über eine Instanz der Klasse aufgerufen werden.
{{JML|code=
public class Animal {
  private float weight;
  public Animal(float weight) {
    this.weight = weight;
  }
  public static float calculateWeight(List<Animal> animals) {
    float weight = 0;
    for(Animal a : animal) {
      weight += a.weight;
    }
  }
}
...
//Korrekter Aufruf
List<Animal> animals = new ArrayList<>();
animals.add(new Animal(10));
animals.add(new Animal(20));
animals.add(new Animal(30));
System.out.println(Animal.calculateWeight(animals));
}}
== Objekt/Instanz VS. Klasse ==
Jede [[{{PAGENAMEE}}#Klasse|Klasse]] erbt von {{JSL|java.lang.Object}}. Somit ist jede [[{{PAGENAMEE}}#Klasse|Klasse]] ein {{JSL|java.lang.Object}}. Wenn jedoch von einem '''Object''' gesprochen wird, so meint man zumeist die '''Instanz''' einer '''Klasse'''.
== Abstrakte Klasse ==
'''Abstrakte Klassen''' sind unfertige [[{{PAGENAMEE}}#Klasse|Klassen]]. Sie können nicht direkt '''instanziert''' werden. Es können nur '''Subklassen''' (Klassen die davon [[{{PAGENAMEE}}#Vererbung|erben]]) davon'''instanziert''' werden. Zusätzlich besteht die Möglichkeit eine '''Instanz''' durch eine '''Anonyme Implementierung''' der '''abstrakten Klasse''' zu erstellen.
<br>Sie einzusetzen ist sinnvoll, wenn sich eine Gruppe von [[{{PAGENAMEE}}#Klasse|Klassen]] Funktionalität teilt, doch gewisse Funktionalität von jeder einzelnen [[{{PAGENAMEE}}#Klasse|Klasse]] implementiert werden muss.<br>
Die noch zu implementierenden '''Methoden''' und die [[{{PAGENAMEE}}#Klasse|Klasse]] selbst werden mit dem Schlüsselwort '''abstract''' gekennzeichnet, .
{{JML|code=public abstract class Animal {
  private String name;
  private float weight;
  public Animal(String name, String weight) {
    this.name = name;
    this.weight = weight;
  }
  //Abstrakte Methode, muss implementiert werden
  public abstract void eat();
}
public class Cat extends Animal {
  public Animal(String name, String weight) {
    //Konstruktor der Superklasse muss aufgerufen werden
    super(name, weight);
  }
  @Override
  public void eat() {
    System.out.println("I eat mice!");
  }
}
public class Bird extends Animal {
  public Animal(String name, String weight) {
    //Konstruktor der Superklasse muss aufgerufen werden
    super(name, weight);
  }
  @Override
  public void eat() {
    System.out.println("I eat worms!");
  }
}
...
//Instanzierung
Bird b = new Bird('Tweety');
b.eat();
//Folgendes ist auch möglich weil, durch die Vererbung, ein Bird ist ein Animal
Animal b1 = new Bird('Tweety');
b1.eat();
//Anonyme Implementierung
Animal a = new Animal("Anonymous", 30) {
  public void eat() {
    System.out.println("I eat nothing.");
  }
};
}}
== Interface ==
'''Interfaces''' oder auch '''Schnittstellen''' bieten die Möglichkeit Methoden zu definieren welche von Klassen die diese Schnittstelle implementieren, implementiert werden müssen. Der Vorteil von Schnittstellen gegenüber der '''Vererbung''' ist, dass eine [[{{PAGENAMEE}}#Klasse|Klasse]] mehrere '''Interfaces''' implementieren kann. Genauso wie bei der '''Abstrakten Klasse''' gilt, '''Interfaces''' können nicht direkt '''instanziert''' werden, eine '''Anonyme Implementierung''' ist jedoch möglich. Ein [[{{PAGENAMEE}}#Interface|Interface]] ist also eine Zusicherung dass eine [[{{PAGENAMEE}}#Klasse|Klasse]] eine gewisse Funktionalität beherrscht.
{{JML|code=public interface CanDrive {
  void drive();
}
public interface CanFly {
  void fly();
}
public class Car implements CanDrive {
  @Override
  public void drive() {
    System.out.println("Driving on the road");
  }
}
public class Plane implements CanFly {
  @Override
  public void fly() {
    System.out.println("Flying in the sky");
  }
}
public class KnightRider implements CanDrive, CanFly {
  @Override
  public void drive() {
    System.out.println("Driving on the road");
  }
  @Override
  public void fly() {
    System.out.println("Flying in the sky");
  }
}
...
//Neues Flugzeug wird instanziert
Plane plane = new Plane();
//Zuweisung ist möglich, da die Klasse Plane CanFly implementiert
CanFly canFly = plane;
//Neues Auto wird instanziert
Car car = new Car();
//Zuweisung ist möglich, da die Klasse Car CanDrive implementiert
CanDrive canDrive = car;
//Neuer KnightRider wird instanziert
KnightRider kit = new KnightRider();
//Zuweisung ist möglich, da die Klasse KnightRider CanDrive implementiert
canDrive = kit;
//Zuweisung ist möglich, da die Klasse KnightRider CanFly implementiert
canFly = kit;
}}
== Vererbung ==
Bei der '''Vererbung''' gibt es immer zwei Protagonisten, die '''Subklasse''' und die '''Superklasse'''. Die '''Subklasse''' erbt von der '''Superklasse'''. Das heißt Sie übernimmt alle [[{{PAGENAMEE}}#Attribute|Attribute]] und alle [[{{PAGENAMEE}}#Methoden|Methoden]]. Die '''Instanz Methoden''' können auch überschrieben werden, wenn diese nicht mit dem Schlüsselwort '''final''' markiert wurden. Vererbung geschieht mittels des Schlüsselworts '''extends''', das heißt eine [[{{PAGENAMEE}}#Klasse|Klasse]] erweitert eine andere [[{{PAGENAMEE}}#Klasse|Klasse]]. In '''Java''' ist keine '''Mehrfachvererbung''' möglich!
Die '''Superklasse''' kann entweder eine normale '''[[{{PAGENAMEE}}#Klasse|Klasse]]''' oder eine '''[[{{PAGENAMEE}}#Abstrakte_Klasse|Abstrakte Klasse]]''' sein. Von einem '''[[{{PAGENAMEE}}#Interface|Interface]]''' wird nicht geerbt, dieses wird implementiert.
{{JML|code=public class Animal {
  private String name;
  public Animal(String name) {
    this.name = name;
  }
  //Kann nicht überschrieben werden
  public final String getName() {
    return this.name
  }
  public void sayHello() {
    System.out.println("Hallo, ich bin " + name);
  }
}
public class Cat extends Animal {
  public Cat(String name) {
    //Der Konstruktor der Superklasse muss aufgerufen werden
    super(name);
  }
  @Override
  public void sayHello() {
    System.out.println("Hallo mein Name ist " + getName() + " und ich bin eine Katze!");
  }
}
}}
Folgendes ist wichtig bei der Vererbung zu erwähnen. Die '''Subklasse''' muss den '''Konstruktor''' der '''Superklasse''' aufrufen. Wenn es nur den '''Standard Konstruktor''' (keine Parameter) gibt, muss auch nicht '''super()''' aufgerufen werden.
== Package ==
[[{{PAGENAMEE}}#Klasse|Klassen]] und '''Interfaces''' sind in '''Java''' immer in '''Packages''' unterteilt. Diese dienen zum gruppieren nach verschiedenen zugehörigkeiten. Die '''Package''' Definition befindet sich immer bei der [[{{PAGENAMEE}}#Klasse|Klasse]] oder dem [[{{PAGENAMEE}}#Interface|Interface]] ganz oben. Die Dateien werden intern in Ordnern abelegt.
{{JML|code=package at.drlue.matura;
public class Matura {
  public void printNote() {
    System.out.println("Eins");
  }
}
}}
Die Klasse '''Matura''' befindet sich in der Datei '''Matura.java''', diese Datei befindet sich im Ordner '''at/drlue/matura/'''.
== Sichtbarkeit ==
Die Sichtbarkeit einer [[{{PAGENAMEE}}#Klasse|Klasse]], [[{{PAGENAMEE}}#Methode|Methode]] oder eines [[{{PAGENAMEE}}#Attribute|Attributs]] kann über Schlüsselwörter verändert werden. Diese beschreiben von wo aus der Zugriff darauf erfolgen darf. Die Folgende Tabelle zeigt diese Schlüsselwörter und von wo aus der Zugriff erfolgen kann:
[[Datei:Sichtbarkeit.png|mini|ohne|400px|Sichtbarkeit<ref>https://wuselsnej.wordpress.com/coding/android/android-sichtbarkeit-java/</ref>]]
'''Datei: at/drlue/matura/packageA/A.java'''
{{JML|code=public class A {
  public String hi() {
    System.out.println("Hallo");
  }
  private String bye() {
    System.out.println("Bye");
  }
  protected String what() {
    System.out.println("Was geht?");
  }
  //Ohne Sichtbarkeitsmodifier ist es Default
  String howDoYouDo() {
    System.out.println("Wie geht es dir?");
  }
}
}}
'''Datei: at/drlue/matura/packageB/B.java'''
{{JML|code=public class B {
  private A a = new A();
  public String hiAccess() {
    a.hi();
  }
  public String byeAccess() {
    //Nicht möglich da Zugriff private
    //a.bye();
  }
  public String whatAccess() {
    //Zugriff möglich, da nicht im selben Package
    a.what();
  }
  public String howDoYouDoAccess() {
    //Zugriff nicht möglich, da nicht im selben Package
    a.howDoYouDo();
  }
}
}}
'''Datei: at/drlue/matura/packageA/C.java'''
{{JML|code=public class C {
  private A a = new A();
  public String hiAccess() {
    a.hi();
  }
  public String byeAccess() {
    //Nicht möglich da Zugriff private
    //a.bye();
  }
  public String whatAccess() {
    //Zugriff möglich, da selbes Package
    a.what();
  }
  public String howDoYouDoAccess() {
    //Zugriff möglich, da selbes Package
    a.howDoYouDo();
  }
}
}}
'''Datei: at/drlue/matura/packageD/D.java'''
{{JML|code=public class D extends A {
  private A a = new A();
  public String hiAccess() {
    a.hi();
  }
  public String byeAccess() {
    //Nicht möglich da Zugriff private
    //a.bye();
  }
  public String whatAccess() {
    //Zugriff möglich, da geerbt wird
    a.what();
  }
  public String howDoYouDoAccess() {
    //Zugriff nicht möglich, da nicht im selben Package obwohl geerbt wird
    a.howDoYouDo();
  }
}
}}
=== Casten ===
Mittels '''Casting''' können untereinander kompatible Datentypen umgewandelt werden.
==== Casten von primitiven Datentypen ====
Primitive Datentypen enthalten werden Sie einer Variable zugewiesen, wirklich den Wert den Sie repräsentieren. Ein Casten führt wirklich zu einem neuen Wert (verändert aber nicht den alten). Alle primitiven Datentypen können untereinander umgewandelt werden, außer '''boolean'''.
{{JML|code=long a = 10;
//long wird in int umgewandelt
int b = (int)a;
//einem long kann aber ohne Casting ein int zugewiesen werden
a = b;
}}
==== Casten von Objekten ====
'''Objekte''' können nur in den Datentyp anderer kompatibler Klassen gecastet werden. Vereinfacht gesagt, es muss möglich sein, dass der Cast erfolgreich ist. Weiters ist hier zu erwähnen, dass keine Umwandlung der Daten erfolgt. Wird ein '''Objekt''' einer Variable zugewiesen, so ist dies '''nicht''' der Wert selbst, sondern nur eine '''Referenz''' darauf. Mit dem '''Cast''' wird nur das erwartete geändert, dass sich an der Stelle, an die diese Referenz zeigt.
{{JML|code=public class A {}
public class B {}
...
public static void main(String[] args) {
  A a = new A();
  //Zuweisung nicht möglich, da A niemals ein B sein kann
  //B b = (B)a;
  //Jede Klasse erbt von java.lang.Object kein Cast ist erforderlich da A ein Object ist
  Object obj = a;
  //Dieser Cast ist möglich, wir wissen zwar dass dies zu einer ClassCastException führt
  B b = (B) obj;
}
}}
== Exceptions ==
== Exceptions ==
'''Exceptions''' oder '''Ausnahmen''' können von {{Link|Methoden}} geworfen werden. Eine '''Exception''' tritt auf, wenn eine Methode außerhalb ihrer gewünschten Funktionalität operiert.<br>
'''Exceptions''' oder '''Ausnahmen''' können von {{Link|Methoden}} geworfen werden. Eine '''Exception''' tritt auf, wenn eine Methode außerhalb ihrer gewünschten Funktionalität operiert. Im folgenden Beispiel wird eine {{Link|Methoden|Methode}} erstellt, welche einen Dateinamen nimmt und die Länge der Datei zurückgibt. Existiert die Datei nicht, so wird eine {{JSL|java.lang.FileNotFoundException}} geworfen:
In folgendem Beispiel wird eine {{Link|Methoden|Methode}} erstellt, welche die Länge einer Datei zurückgibt. Wird die Datei nicht gefunden, so wird eine {{JSL|java.io.FileNotFoundException}} geworfen:
{{JML|code=
{{JML|code=
public static long getFileLength(String fileName) throws FileNotFoundException {
public static long getFileLength(String fileName) throws FileNotFoundException {
Zeile 478: Zeile 9:
   return f.length();
   return f.length();
}
}
}}
== Weitere wichtige Dinge ==
Hier sollen noch einige Weitere Dinge abgeklärt werden.
=== Überladen von Methoden ===
Wir sprechen von überladen einer [[{{PAGENAMEE}}#Methode|Methode]], wenn mehrere Methoden in einer Klasse existieren und den den selben Namen haben, sich aber die Parameter, entweder in ihrem Datentyp, oder der Anzahl unterscheiden. Ein [[{{PAGENAMEE}}#Konstruktor|Konstruktor]] kann ebenfalls überladen werden.<br>
Im folgenden Beispiel wird die Methode ''add'' überladen. Der '''Compiler''' entscheidet anhand des Datentyps der Parameter welche Methode verwendet werden soll, '''der Rückgabetyp spielt keine Rolle.'''
{{JML|code=
public class Calc {
  public int add(int a, int b) {
    return a + b;
  }
  public float add(float a, float b) {
    return a + b;
  }
}
...
public static void main(String[] args) {
  Calc c = new Calc();
  //Die add Methode mit den floats wird verwendet, weil die Parameter floats sind
  float a = c.add(3.0f, 2.0f);
  //Die add Methode mit den ints wird verwendet, weil die Parameter ints sind
  float a = c.add(3, 2);
}
}}
=== Polymorphismus ===
'''Polymorphismus''' oder Vielgestaltigkeit, beschreibt den Vorgang bei einem Methodenaufruf auf eine '''Instanz''' einer '''Subklasse'''. Eine Methode ist '''Polymorph''' wenn diese in verschiedenen Klassen die selbe Signatur aufweist.
{{JML|code=public class A {
  public void doSomething() {
    System.out.println("My name is A and I do something!");
  }
}
public class B extends A {
  @Override
  public void doSomething() {
    System.out.println("My name is B and I do something!");
  }
}
...
public static void main(String[] args) {
  doSomething(new B());
}
public static void reallyDoSomething(A a) {
  a.doSomething();
}
}}
Obwohl in der Methode {{JSL|(A a)}} eine '''Instanz''' des Typs '''A''' als Parameter erwartet wird, ist es in Wirklichkeit eine '''Instanz''' des Typs '''B'''. Ein Aufruf von  {{JSL|a.doSomething()}} führt nicht den Code in '''Klasse A''' sondern '''Klasse B''' aus.
=== Beziehungen ===
Hier sollen noch die Beziehungen zwischen [[{{PAGENAMEE}}#Klasse|Klassen]] bzw. '''Objekten''' geklärt werden.
==== Is A ====
Wenn eine '''Klasse Cat''' eine '''Klasse Animal''' erweitert, d.h. von ihr erbt. Somit ist '''Cat''' ein '''Animal''' und die folgende Zuweisung ist möglich:
{{JML|code=
Cat cat = new Cat();
Animal animal = cat;
}}
Umgekehrt ist das nicht zwingend so. Wenn ich ein '''Objekt''' vom Typ '''Animal''' habe, so könnte es ein '''Object''' vom Type '''Cat''' sein, muss es aber nicht. Um dies zu prüfen gibt es das Schlüsselwort '''instanceof''' und es kann ein [[{{PAGENAMEE}}#Cast|Cast]] erfolgen. Die Umwandlung ist auch ohne Prüfung möglich, dies kann jedoch zu Abstürzen durch eine '''ClassCastException''' führen.
{{JML|code=
public void doSomething(Animal animal) {
  if(animal instanceof Cat) {
    Cat cat = (Cat) animal;
    cat.miau();
  } else {
    System.out.println("Can't miau");
  }
}
}}
Das selbe gilt genau gleich, wenn eine [[{{PAGENAMEE}}#Klasse|Klasse]] ein [[{{PAGENAMEE}}#Interface|Interface]] implementiert.<br>
'''Is A''' ist somit erweiterung einer [[{{PAGENAMEE}}#Klasse|Klasse]] oder Implementierung einer [[{{PAGENAMEE}}#Interface|Schnittstelle]]. Es handelt sich hierbei um eine '''starke Bindung''', d.h. diese kann zur Laufzeit des Programmes '''nicht''' verändert werden.
==== Has A ====
Eine '''Has A''' Beziehung wird auch '''Komposition''' oder '''Zusammenstellung''' genannt. '''Komposition''' ist eine '''schwache Bindung'''. Sie kann während der Laufzeit des Programmes verändert werden. Folgendes Beispiel soll eine '''Has A''' Beziehung verdeutlichen:
{{JML|code=
public class Cat {
  private List<Spielzeug> spielzeuge = new ArrayList();
  public void addSpielzeug(Spielzeug spielzeug) {
    this.spielzeuge.add(spielzeug);
  }
  public void removeSpielzeug(Spielzeug spielzeug) {
    this.spielzeuge.remove(spielzeug);
  }
}
public class Spielzeug {
}
...
public static void main(String[] args) {
  Cat cat = new Cat();
  Spielzeug ball = new Spielzeug();
  cat.addSpielzeug(ball);
 
  cat.removeSpielzeug(ball);
}
}}
Hier wird gezeigt, dass Objekten zur Laufzeit andere Objekte hinzugefügt und entfernt werden können.
== Multithreading ==
'''Threading''' oder '''Nebenläufigkeit''' bezeichnet den Vorgang in einem Programm das unterschiedliche Handlungsstränge aufweist. D.h. verschieden Programmabläufe laufen parallel. Diese Parallelität kann wirklich parallel ablaufen, d.h. 2 oder mehr '''CPUs''' sind involviert, oder es kann nur pseudoparallel Ablaufen, zwischen den unterschiedlichen Abläufen wird sehr schnell hin und her gewechselt. Je nach implementierung der '''Java''' '''V'''irtual '''M'''achine, läuft es wirklich parallel oder nur pseudo parallel ('''green Threads''').  Folgendes Beispiel soll einen parallelen Ablauf verdeutlichen:
{{JML|code=
public static void main(String[] args) {
  Thread a = new Thread(new Runnable() {
    public void run() {
      for(int i=0; i<10; i++) {
        System.out.println("Hallo von Thread A: "+i);
        try {
          Thread.sleep(100);
        } catch(InterruptedException exc) { }
      }
    }
  });
  Thread b = new Thread(new Runnable() {
    public void run() {
      for(int i=0; i<10; i++) {
        System.out.println("Hallo von Thread B: "+i);
        try {
          Thread.sleep(100);
        } catch(InterruptedException exc) { }
      }
    }
  });
  a.start();
  b.start();
}
}}
Zuerst werden die '''Threads''' erstellt, diesen wird ein '''Objekt''' des Typs '''java.lang.Runnable''' mitgegeben. Alternative kann auch eine [[{{PAGENAMEE}}#Klasse|Klasse]] erstellt werden, welche von '''java.lang.Thread''' erbt und die '''run() Methode''' überschreibt. Nach dem initialisieren, können die '''Thread''' Instanzen mittels '''start()''' gestartet werden, '''run()''' wird nun jeweils in einem anderen '''Thread''' aufgerufen.
=== Synchronisierung ===
Verwendet ein Programm mehrere '''Threads''' so kann dies oft zu Problemen führen, wenn '''kritische Bereiche''' welche '''nicht atomare Operationen''' enthalten gleichzeitig verwendet werden.
Atomare Operationen sind nicht Teilbar. Eine int Variable zu erhöhen ist Teilbar.<br>
1) Nimm die Zahl und erhöhe sie um 1
2) Überschreibe den Speicherbereich der Zahl
{{JML|code=
private static int count = 0;
public static void main(String[] args) {
  Thread a = new Thread(new Runnable() {
    public void run() {
      for(int i=0; i<1000; i++) {
        count++;
      }
    }
  });
  Thread b = new Thread(new Runnable() {
    public void run() {
      for(int i=0; i<1000; i++) {
        count++;
      }
    }
  });
  a.start();
  b.start();
 
  //Warten bis die Threads beendet wurden
  try {
    a.join();
  } catch(InterruptedException exc) {}
  try {
    b.join();
  } catch(InterruptedException exc) {}
  System.out.println("Increments: "+(2000));
  System.out.println("Actual count: "+count);
  System.out.println("Difference: "+(2000 - count));
}
}}
In diesem Beispiel ist ersichtlich, dass die Variable am Ende nicht ''2000'' ist, aufgrund dieser '''nicht atomaren''' Erhöhung der Zahl um 1.<br>
Um '''nicht atomare Bereiche''' sicher zu machen, kann das Schlüsselwort '''synchronized''' verwendet werden. '''synchronized''' verwendet ein sogenanntes '''Object Lock''' um einen bereich abzusichern, beim betreten wird es geholt, beim Verlassen zurückgegeben. Dieses '''Lock''' existiert bei jedem '''Objekt''' nur einmal. Kann es nicht geholt werden, so muss gewartet werden, bevor der geschützte bereich verwendet werden darf.
{| class="wikitable"
|+ Wie kann '''synchronized''' verwendet werden
|-
! Code !! Beschreibung
|-
|
{{JML|code=
synchronized(object) {
  //kritischer Bereich
}
}}
|| Das '''Lock''' wird von einem bestimmten '''Object''' geholt.
|-
|
{{JML|code=
public synchronized void doSomething() {
  //kritischer Bereich
}
}}
|| Das '''Lock''' wird von der '''Instanz''' geholt, zu der dieser '''Methodenaufruf''' gehört.
|-
|
{{JML|code=
public static synchronized void doSomething() {
  //kritischer Bereich
}
}}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
|| Synchronized kann auch bei '''statischen Methoden''' verwendet werden. Das '''Lock''' wird von der [[{{PAGENAMEE}}#Klasse|Klasse]] selbst geholt. Die geladene Klasse ist ebenfalls eine Instanz (von der eigenen Definition), diese existiert aber nur einmal.
|}
Somit kann das oben genannte Beispiel gelöst werden:
{{JML|code=
private static int count = 0;
private static synchronized void increment() {
  count++;
}
public static void main(String[] args) {
  Thread a = new Thread(new Runnable() {
    public void run() {
      for(int i=0; i<1000; i++) {
        increment();
      }
    }
  });
  Thread b = new Thread(new Runnable() {
    public void run() {
      for(int i=0; i<1000; i++) {
        increment();
      }
    }
  });
  a.start();
  b.start();
 
  //Warten bis die Threads beendet wurden
  try {
    a.join();
  } catch(InterruptedException exc) {}
  try {
    b.join();
  } catch(InterruptedException exc) {}
  System.out.println("Increments: "+(2000));
  System.out.println("Actual count: "+count);
  System.out.println("Difference: "+(2000 - count));
}
}}

Version vom 27. Januar 2021, 15:55 Uhr

Exceptions

Exceptions oder Ausnahmen können von Methoden geworfen werden. Eine Exception tritt auf, wenn eine Methode außerhalb ihrer gewünschten Funktionalität operiert. Im folgenden Beispiel wird eine Methode erstellt, welche einen Dateinamen nimmt und die Länge der Datei zurückgibt. Existiert die Datei nicht, so wird eine java.lang.FileNotFoundException geworfen: {{JML|code= public static long getFileLength(String fileName) throws FileNotFoundException {

 File f = new File(fileName);
 if(!f.exists()) {
   throw new FileNotFoundException();
 }
 return f.length();

}