Objektorientierte Programmierung

Die Objektorientierte Programmierung dient dazu die reale Welt in Objekten abzubilden. Hierzu verwenden wir die in der Informatik bekannte „divide and conquer“ oder zu deutsch das Teile-und-herrsche-Verfahren. Wir brechen einzelne Aspekte oder Problem in kleiner Teil runter und lösen diese in einzelnen Objekten oder Methoden.

Objekte oder Instanzen

Objekte oder auch Instanzen genannt, hat einen Zustand, ein Verhalten und eine Identität. Ein Objekt reagiert nur auf eine Botschaft z.B. einen leeren Aufruf oder einen Aufruf mit einem oder mehreren Parameter.

Ein Objekt wird durch sein Verhalten, also seiner beinhaltenden Methoden beschrieben. Die Identität eines Objektes unterscheidet es von anderen Objekten, auch wenn diese den gleichen Zustand oder auch das gleiche Verhalten haben. Ähnlich wie beispielsweise bei Menschen, es gibt Menschen, die ähnliche oder gleiche Zustände (z.B. Alter, Größe, Nationalität usw.) und Methoden (z.B. laufen, essen, trinken usw. ) haben.

Konstuktor

Ein Konstruktor erzeugt ein Objekt einer Klasse. Ein erstelltes Objekt „lebt“ nur zu einer bestimmten Laufzeit (z.B. vom Start bis zum Programmende). Zur Laufzeit besitzt dann jedes Objekt seine eigenen Werte. Konstruktoren haben keinen Rückgabewert.

public class Mensch {
    private String name;
    private int alter;

    // Standardkonstruktor
    public Mensch() {
   
    }
}
//Diese Beispiel ist nicht optimal, da das alter fest vergeben wird
Mensch mensch1 = new Mensch();

//Hier wird ein neuer Mensch (mensch1) erzeugt, er hat eine Identität, jedoch keine Anfangswerte

Destruktor

Wird ein Objekt nicht mehr benötigt, wird es entweder passiv oder auch aktiv durch den Destruktor aufgelöst.

Instanziierung

Unter der Instanziierung versteht man die Erzeugung eines konkreten Objekts einer bestimmten Klasse.

public class Mensch {
    private String name;
    private int alter;
    
    // Instanzisierung des Menschen
    public KonstruktorClass(String theName, int theAge){
        this.name=theName;
        this.age = theAge;
    }
}

//Hier wird genau diesem Objekt der Name und das Alter zugewiesen
Mensch mensch2 = new Mensch("Tom", 25);

//Hier wird ein neuer Mensch (mensch2) erzeugt, er hat eine Identität und einen Namen: Tom und ein Alter: 25

Kapselung

Unter der Kapselung versteht man, die Sichtbarkeit der Objektattribute nach „aussen“ zum Programm hin. Man kann es sich so vorstellen, wie wenn man in einer Gruppe ein Namensschild trägt, dann ist der Name dieser Person „public“ und das Alter, welches nicht auf dem Schild vermerkt ist „private“. Eine weitere Möglichkeit der Klassifizierung stellt noch „protected“ da. Während „private“ übergestellten Instanzen den zugriff auf diesen Wert gewährt, ist bei „protected“ der Zugriff verwehrt.

Vererbung

Vererbung bedeutet, dass man ein Objekt wiederverwenden kann. So kann man beispielsweise ein Objekt Tier erstellen, welches eine Grundfunktionalität (oder charakteristische Merkmale) welches jedes Tier hat, beinhaltet. Z.B. Alter, Gewicht und Laut (Geräusch was das Tier von sich gibt). Möchte man jetzt einen Hund und eine Katze erstellen, so geschieht dies beides über die Erstellung des Objektes Tier.

Überschreiben

Ein Objekt kann überschreiben werden, in dem man das Objekt anpasst.
Bei unserem Beispiel am Tier-Objekt kann man jetzt den Hund und die Katze überschreiben, da ein Hund bellt und eine Katze miaut.

public class Tier { 

    Tier() {

    }

    public String gibLaut() { 
        System.out.println("...."); 
    }
} 

public class Hund extends Tier { 
    public String gibLaut() { 
        return "Wauu! Wauu!"; 
    }
} 

public class Katze extends Tier { 
public String gibLaut() { 
        return "Miau, miau!"; 
    }
}

//Hier überschreiben wir die Klasse Tier um diese zu spezifizieren.
Tier hund1 = new Hund();
Tier katze1 = new Katze();

System.out.println(hund1.gibLaut()); //prints "Wau! Wau!"

System.out.println(katze1.gibLaut()); //prints "Miau, miau!"

Ruft man jetzt die Methode gibLaut() des Objektes Hund auf, macht dieser „Wau! Wau!“ hingegen gibt das Objekt Katze nun „Miau, miau!“ zurück.

Überladen

Nun überladen wir unsere Tier-Objekt noch mit einer neuen Eigenschaft. Als Beispiel überladen wir nun das Tier zu einem bekannten Tier, welches einen name hat.

Tier(String name)     {         
    this.name=name;     
}

Abstraktion und Superklassen

Unter der Abstraktion versteht man eine nicht vollständig implementierte Klasse oder Methode. Von diese abstrakten Klasse kann kein Objekt erstellt werden. Ein kleine Beispiel verdeutlicht die Idee hinter der Abstraktion.

public abstract class Mensch{
    String sprache;

    abstract void sprechen();
}

Die Methode sprechen hat keinen Methodenrumpf, d.h. wir müssen diesen beim erzeugen implementieren. In meinem Beispiel wenn wir Menschen erzeugen, die englisch, deutsch und / oder französisch sprechen.

public class Englaender extends Mensch{

	public void sprechen(){
		System.out.println("I speek english!");
	}
}
public class Franzose extends Mensch{

	public void sprechen(){
		System.out.println("Je parle francais!");
	}
}
public class Deutscher extends Mensch{

	public void sprechen(){
		System.out.println("Ich spreche deutsch!");
	}
}

Somit ist Mensch die Superklasse. Wichtig ist auch, dass die Methode die selbe Sichtbarkeit hat wie die Methode aus der Superkasse.

Final

mit final kennzeichnen wir in Java beispielsweise eine unverändertere Variable. D.H. dass die Variable zu keiner Zeit verändert / überschrieben werden kann. Der Wert der finalen Variable wird bei der Erstellung einmal gesetzt und dann ist diese für die gesamte Laufzeit fest.

public class Personalausweis {

	private final int seriennummer;
	private String vorname;
	private String nachname;
	private final datum geburtsdatum;

	public Personalausweis(int seriennummer, String vormane, String nachname, datum geburtstag) {
		this.seriennummer = seriennummer;
		this.vorname = vorname;
                this.nachname = nachname;
                this.geburtsdatum = geburtsdatum;
	}

	public int getSeriennummer(){
		return seriennummer;
	}

	public String getNachname(){
		return vorname;
	}

        public String getNachname(){
		return nachname;
	}

	public void setNachname(String nachname){
		this.andereZahl=andereZahl;
	}
}

Der Nachname kann sich z.B. durch eine Heirat ändern, der Vorname, Geburtstag und auch die Seriennummer sollten fest und somit unveränderlich sein.

Der Nachname kann durch einen Setter verändert werden.

Eine finale Klasse ist dadurch fixiert, dass diese Klasse nicht vererben kann, also nicht verändert in Hinsicht auf überladen oder überschreiben nicht verändert werden kann.

Schnittstellen oder auch Interfaces

In Java darf eine abgeleitete Klasse nur von einer (Super-)Klasse erben. Eine Klasse kann nicht von mehreren Klassen gleichzeitig erben. Soll eine Klasse mehre (Ober-)Klassen vereinen, so verwenden wir ein Interface.

Interfaces können als eine Art Ersatzkonstruktor gesehen werden, die eine Vererbung von mehreren Klassen ermöglicht. Dies bedeutet, dass diese klasse mehrere Interfaces implementieren kann. Jedoch muss jedes Interface dieser Klasse vollständig implementiert sein.

Die Deklaration geschieht einfach durch das Schlüsselwort Interface.
Jedoch sind alle enthaltenden Methoden public und abstrakt.

/*
* Beispielanwendung: das Interface Dolmetscher
*/
 
public interface Dolmetscher
{
HashSet<String> sprachen = new HashSet<String>();

  String sprichtSprachen();
String uebersetzt(String sprache, String text);
}
public class EngDeuDolmetscher implements Dolmetscher
{
  public String vorname;
public string nachname;
 
  public EngDeuDolmetscher (String vorname, String nachname, Hashset<String> sprachen)
  {
    this.vorname = vorname;
    this.nachname = nachname;
    this.sprachen = sprachen;
  }
 
  public String getName()
  {
    return nachname+", "+vorname;
  }
  public String getSprachen()
  {
    return sprachen.toString();
  }
  public void lerneSprache(String spache)
  {
    this.sprache.add(sprache);
  }
}