Java Stellt dies eine mögliche Schwäche der objektorientierten Programmierung dar?

vram78

Lieutenant
Registriert
Dez. 2015
Beiträge
712
Hallo,

Es geht konkret um die Programmiersprache Java.

Ich habe diesbezüglich 3 Fragen.

Ich lese mich derzeit in ,,Java ist auch eine Insel" ein.

Dort steht folgendes:

Da eine Methode immer einer Klasse oder einem Objekt zugeordnet ist, muss der Eigentümer beim Aufruf angegeben werden. Im Fall von System.out.println() ist println() eine Methode vom out-Objekt. Wenn wir das Maximum zweier Fließkommazahlen mit Math.max(a, b) bilden, dann ist max(...) eine (statische) Methode der Klasse Math. Für den Aufrufer ist damit immer ersichtlich, wer diese Methode anbietet, also auch, wer diese Nachricht entgegennimmt. Was der Aufrufer nicht sieht, ist die Arbeitsweise der Methode. Der Methodenaufruf verzweigt in den Programmcode, aber der Aufrufer weiß nicht, was dort geschieht. Er betrachtet nur das Ergebnis.

Mir geht es erstmal konkret um diesen Satz hier:

Was der Aufrufer nicht sieht, ist die Arbeitsweise der Methode. Der Methodenaufruf verzweigt in den Programmcode, aber der Aufrufer weiß nicht, was dort geschieht. Er betrachtet nur das Ergebnis.

Genau das ist mein Problem mit der Objektorientierung. Es ist ja alles schön und gut, wenn es bereits Lösungen für Probleme gibt. beispielsweise ist die println() Methode eine Lösung dafür, wie ich etwas in der Konsole ausgebe. Ich muss dank der Objektorientierung keinen Code diesbezüglich schreiben, wenn ich etwas ausgeben möchte. Dafür hat es bereits jemand anderes getan.

Allerdings weiß ich gar nicht, wie die println() Methode denn nun funktioniert. Und als Anfänger möchte ich nun mal wissen, was im Methodenrumpf von println() steht. Kann man dies als mögliche Schwäche des Programmierparadigmas verstehen?


Meine 2. Frage:

Ich verstehe immer noch nicht ganz, wozu man
Java:
static
verwendet.

In ,,Java ist auch eine Insel" steht folgendes dazu:
Das Schlüsselwort static zeigt an, dass sich die Methode mit dem Klassennamen nutzen lässt, also kein Exemplar eines Objekts nötig ist.

Aber was bringt mir denn jetzt eine Methode, die statisch ist? Was ist der Vorteil darin, eine Methode als statisch zu deklarieren? Wieso denn nicht einfach ein Objekt dazu erzeugen?

Beispiel:

Java:
public class Rücklauf {

    public static void main(String[] args) {
       
        Rücklauf test = new Rücklauf();
       
        test.Konsole();
       
       
    }
   
   
    public void Konsole() {
   
        int i = 499;
       
        while(!(i == 1)) {
           
            if(i == 600) {
               
            }
           
           
            if(i == 1000) {
               
                break;
            }
           
           
            i++;
            System.out.println(i);
           
           
        }
       
    }
}

Hier habe ich eine nicht statische Methode, eine Objektmethode. Ich könnte es aber genau so gut folgendermaßen ausführen:

Java:
public class Rücklauf {

    public static void main(String[] args) {
       
       
       
        Konsole();
       
       
    }
   
    public static void Konsole() {
   
        int i = 499;
       
        while(!(i == 1)) {
           
            if(i == 600) {
               
            }
           
           
            if(i == 1000) {
               
                break;
            }
           
           
           
            i++;
            System.out.println(i);
           
           
           
        }

    }
}

Ich verstehe aber den Sinn dahinter überhaupt nicht. Außer, dass man weniger schreiben muss..

Meine 3. Frage:

Java:
Scanner scanner = new Scanner(System.in);

Hier wird ein Objekt ,,scanner" vom Typ ,,Scanner" erzeugt. Ich verstehe aber nicht, was System.in nun zu bedeuten hat. Was bedeutet System.in? Bedeutet es etwa, dass Objekte miteinander quasi kommunizieren können und ich das Objekt ,,in" mit dem Objekt ,,scanner" sozusagen verbinde? Ich verstehe es nicht ganz.


MFG
 
vram78 schrieb:
Allerdings weiß ich gar nicht, wie die println() Methode denn nun funktioniert.

Das ist doch bei jeder Prozeduralen Sprache auch so sobald du Bibliotheken verwendest?


vram78 schrieb:
Ich verstehe immer noch nicht ganz, wozu man
Java:
static
verwendet.

"static" wirst du immer dann verwenden, wenn du für eine Klasse Daten halten willst, die für alle Objekte gleich sind bzw. auf die alle Objekte gemeinsam zugreifen sollen z.B. eine ID die für jedes erzeugte Objekt einzigartig sein soll, die du in einer statischen Variable der Klasse hochzählst.
Aber es ist durchaus auch für eine Bibiothek an Funktionen gut geeignet, die nicht an bestimmten Objektdaten hängen soll (wie zB bei den diversen Math-Funktionen)

Würdest du immer ein Objekt brauchen müsstest du über dein Programm irgendwie sinnvolle mit den Referenzen umgehen, das ist ein unnötiger Aufwand für Attribute und Funktionen die unabhängig genutzt werden können/sollen.


vram78 schrieb:
Java:
Scanner scanner = new Scanner(System.in);

System.in ist schlichtweg der Zugriff auf das statische Attribut "in" der System-Klasse, was im Prinzip einen Kanal darstellt auf den das erzeugte Scanner Objekt zugreifen soll.
 
Zu deiner ersten Frage: In nicht Objekt Orientierten Sprachen ist dies nicht anders (bei c printf()). Zudem kannst du mit einem Rechtsklick die eigentliche Klasse öffnen, da kannst du dir dann die Sachen nachlesen.

Zu deiner Zweiten Frage: Weißt du was eine Instance einer Klasse ist? Falls nicht einmal nachlesen, ohne das wird das ziemlich schwer zu erklären. Wenn Variablen static sind, dann greifen alle Instancen einer Klasse auf die gleiche Variable zu. Wenn Methoden static sind bedeutet das einfach nur, dass die Methoden nicht auf einem Objekt dieser Klasse aufgerufen werden müssen.
Du kannst zum Beispiel
Math.sqr(9); schreiben statt:
Math mathObject = new Math();
mathObject.sqr(9);
Ich hoffe mal ich habe jetzt hier keinen Fehler gemacht.
 
Zuletzt bearbeitet: (Ergänzung)
vram78 schrieb:
Aber was bringt mir denn jetzt eine Methode, die statisch ist? Was ist der Vorteil darin, eine Methode als statisch zu deklarieren? Wieso denn nicht einfach ein Objekt dazu erzeugen?

Ein Objekt zu erzeugen kostet Zeit und Speicher. Und wenn man es nicht mehr braucht muss man es auch noch löschen, bzw. muss das der Garbage Colleftor machen.
Man kann statische Klassen und prima für Funktionssammlungen nehmen die man unter einem Oberbegriff halten will und deren Funktionen keine eigenen Zustandsinformationen brauchen.
Zum Beispiel die schon erwähnte Math-Klasse. Für die simple Berechnung z.B. von einer Quadratwurzel brauchst du keine Klasseninstanz. Es gibt zwei Eingabewerte und einen Ausgabewert. Die Klasse, bzw. ein theoretisches Objekt der Klasse Math muss keine Zustandsinformationen oder Daten "für später" speichern um die jeweiligen Funktionen auszuführen.
 
Jannis G. schrieb:
Zu deiner ersten Frage: In nicht Objekt Orientierten Sprachen ist dies nicht anders (bei c printf()). Zudem kannst du mit einem Rechtsklick die eigentliche Klasse öffnen, da kannst du dir dann die Sachen nachlesen.

Zu deiner Zweiten Frage: Weißt du was eine Instance einer Klasse ist? Falls nicht einmal nachlesen, ohne das wird das ziemlich schwer zu erklären. Wenn Variablen static sind, dann greifen alle Instancen einer Klasse auf die gleiche Variable zu. Wenn Methoden static sind bedeutet das einfach nur, dass die Methoden nicht auf einem Objekt dieser Klasse aufgerufen werden müssen.
Du kannst zum Beispiel
Math.sqr(9); schreiben statt:
Math mathObject = new Math();
mathObject.sqr(9);
Ich hoffe mal ich habe jetzt hier keinen Fehler gemacht.
Ja. eine Instanz ist ein Objekt der jeweiligen Klasse
 
1. Ich versuch es weiter unten zu beantworten.
Genau darum geht es. Je nachdem um was für eine Klasse es sich handelt ist egal was println macht.

Angenommen du hast die Klasse Base und die hat die Methode print.
Jetzt leitet du von base die Klasse Dev1 ab und implentierterst print. Genau machst du es mit Dev2.

(sorry tippe am Handy deswegen nur pseudo code und ich bin eher in C# unterwegs - das Konzert ist aber das gleiche)
print in Dev1 gibt den Text am Bildschirm aus. Dev2.print auf einen Drucker

Also

List<Base> l =new List<base> ;
l.add(new Dev1) ;
l.add(new Dev2) ;
Foreach (Base element in l)
l.print ;

Ohne OOP müsstes du in der Foreach Schleife immer überprüfen um welchen Typ von Objekt es sich handelt handelt und mit if oder switch die passende Methode aufrufen


d.h du kannst einfach alle Elemente durchgehen und print verwenden ohne zu wissen ob es sich um ein Element der Klasse Dev1 oder Dev2 handelt

2. Wenn du ein Objekt erzeugst wird auch Speicher belegt siehe Antwort von KeyCon
3. System.In ist ein Inputstream normalerweise keyboard input und Scanner kann alle Modell Methoden einer on Inputstream abgeleiteten Klasse verwenden.
(vermute ich jetzt dafür musste ich die Deklaration sehen). Das ist einer der Vorteile von OOP.
Scanner kann einfach die read Methode aufrufen. Wenn du System.in als Parameter verwendest eben vom keyboard.

Wenn du stattdessen sowas schreibst verwendet scanner eben einen Datei stream ohne wissen zu müssen um was es sich handelt - Hauptsache die Methode read existiert

InputStream input = new ;
Scanner scanner = new Scanner(input );
 
vram78 schrieb:
Mir geht es erstmal konkret um diesen Satz hier:
Da haben einige ja schon was zu geschrieben.
Es wird vielleicht klarer, wenn der Sinn etwas besser verstanden wird. Das ist genau wie bei einem Auto. Das bietet dir per Lenkrad, Gangschaltung, etc. seine public methoden bzw. sein public Interface an. Du könntest zwar mit Aufwand reingucken und schauen, wie denn jetzt das ABS, Servolenkung oder die Gangschaltung ganz genau funktioniert, aber das Auto wurde gebaut um damit zu fahren und hat deswegen diese ganze technischen Details weg abstrahiert, sonst könnte kein normaler Mensch damit fahren. Du musst nicht wissen, wie das alles funktioniert, nur wie du Lenkrad und Gangschaltung bedienst.


vram78 schrieb:
Hier wird ein Objekt ,,scanner" vom Typ ,,Scanner" erzeugt. Ich verstehe aber nicht, was System.in nun zu bedeuten hat. Was bedeutet System.in? Bedeutet es etwa, dass Objekte miteinander quasi kommunizieren können und ich das Objekt ,,in" mit dem Objekt ,,scanner" sozusagen verbinde? Ich verstehe es nicht ganz.
Das ist so ähnlich wie mit Auto und Motor. Ein Auto ohne Motor bringt nichts, aber es kommt jetzt nicht so ganz genau darauf an was für ein Motortyp dem Auto eingebaut wird, Hauptsache es ist ein Motor und ein Benziner.
In der Zeile wird gesagt "Gib mir ein neues Scanner-Objekt und zwar eines in dem System.in eingebaut ist."
 
  • Gefällt mir
Reaktionen: ReignInBlo0d
vram78 schrieb:
Und als Anfänger möchte ich nun mal wissen, was im Methodenrumpf von println() steht.

Kann sein dass man das wissen will, aber selbst wenn man das weiss sollte man dieses Wissen nicht benutzten.

Worum es letztendlich geht ist Kopplung zu vermeiden - das heisst verschiedene Teile eines Programmes (die vielleicht von verschiedenden Leuten geschrieben werden) sollen sich unabhängig voneinander weiterenwickeln können.

Wenn Du wüsstest wie eine Methode wirklich implementiert ist würdest Du Dich in Deinem Teil des Programmes vielleicht in irgendeiner Form darauf einstellen. Und später ändert jemand anderes diese Implementierung, weiss aber nicht dass Du genau eine bestimmte Implementierung im Auge hattest und es gibt Probleme...

Deswegen solltest Du das so sehen: Du "rufst" nicht etwa eine Methode "println" eines Objektes auf, sondern Du sendest einem Objekt eine Nachricht "println". Was das Objekt mit dieser Nachricht tut ist alleine Sache des Objektes und geht Dich nichts an.

Das Objekt könnte jederzeit sein Verhalten ändern und garantiert Dir nur gewisse Effekte (wenn Du ihm eine solche Nachricht schickst wird auf dem Terminal ein String erscheinen - aber wie genau das geht ist Sache des Objektes).

Nochmal: Es geht um Entkoppelung. Du programmierst nicht gegen eine Implementierung (das Objekt macht eine gewisse Sache so und so) sondern gegen eine Schnittstelle (wenn ich diese Nachricht schicke wird dieser Effekt erzeugt - wie auch immer).

War das überhaupt verständlich?
 
  • Gefällt mir
Reaktionen: BeBur
Statische Eigenschaften (und Methoden) haben erstmal nichts mit Speicherbedarf oder sowas zu tun. Das ist erstmal alles eine Frage der Modellierung.

Sinngemäß: Wenn Objektorientierung eine Abbildung der Realität ist, dann sind Objekte (als Instanzen einer Klasse) "aktive" Dinge und statische Eigenschaften und Methoden sind passive.
Statische Objekte sind damit der Rahmen, in dem sich dynamische Objekte bewegen, die aber selber nichts tun. Es sind vor allem die "Edge"-Bereiche im OO-Modell.

Beispiel. Ich verschicke einen Brief.
Modell a:
Mail m = new Mail (to: address)
Letter.send();

Modell b:
Letter L = new Letter();
Mailbox.Send(L);

Beide tun dasselbe - es wird ein Brief verschickt. ABER:
Modell A erlaubt es, briefspezifische Senderoutinen zu vergeben. Ein Brief so; ein anderer (per abgeleiteter Klasse) anders.
Modell B erlaubt das nicht. Dafür hab ich aber exakt eine Routine zum Verschicken und nicht (bis zu) Anzahl Briefklassen viele. Der Briefkasten selber steht nur da. Er tut nichts, zumindest nicht in diesem Beispiel. Ginge es aber um verschiedene Briefkästen, zB einer hier und einer dort, dann könnte Mailbox als Klasse nicht statisch sein.

Es kommt also immer drauf an, was man braucht, wie man an ein Problem herangeht, ob/wie man seine Anforderungen am besten strukturiert. Manchmal paßt das eine besser. Manchmal das andere.

Das macht OO gleichzeitig so einfach und so schwer.
Einfach, weil man einfach das programmieren kann, was in der realen Welt auch wirklich passiert.
Schwer, weil man das erstmal ordentlich modellieren muß für dynamische und statische Objekte einerseits und Hierarchien und Ausnahmen andererseits.
 
vram78 schrieb:
Mir geht es erstmal konkret um diesen Satz hier:

Genau das ist mein Problem mit der Objektorientierung. Es ist ja alles schön und gut, wenn es bereits Lösungen für Probleme gibt. beispielsweise ist die println() Methode eine Lösung dafür, wie ich etwas in der Konsole ausgebe. Ich muss dank der Objektorientierung keinen Code diesbezüglich schreiben, wenn ich etwas ausgeben möchte. Dafür hat es bereits jemand anderes getan.

Du hast ja schon viele Antworten bekommen, keine geht aber wirklich auf den Thread-Titel ein.
Das ist keine Schwäche, sondern ganz im Gegenteil eine Stärke.
Diese Stärke ist aber nicht exklusiv für OO-Paradigmen, sondern für so ziemlich jedes andere Paradigma auch, dass nicht mit sequentieller Programmierung gleichgesetzt werden kann.
Sobald die Möglichkeit besteht für ein Stück Code einen Namen zu vergeben, hast du einen ausdrücklichen Vorteil.

Nicht nur ermöglicht es dir, Probleme einmal zu lösen und diese Lösung immer wieder über den entsprechenden Namen anzuwenden, sondern es reduziert auch gleich die Komplexität aller darauf folgender Problemlösungen um dieses bereits gelöste Problem (Abstraktion).
Und Komplexität ist dein größter Feind beim Lösen von Problemen.


Ich kann dich verstehen, dass du gerne wissen willst, wie das alles funktioniert und ich will dich eigentlich gar nicht davon abhalten, deine Neugier zu befriedigen, nur wirst du in diesem Fall auf eine Reise gehen müssen, die im englischen die Bezeichnung "down the rabbit hole" verdient (eine nicht enden wollende Sache).
Ich kenne offensichtlich deinen Stand der Expertise nicht, kann aber eben gut sein, dass du da etwas verloren sein wirst.
Was mir in meinen Anfängen sehr geholfen hat, ist, dass ich so etwas wie einen Mentor zu Verfügung stehen hatte, der mir einen sehr großen Teil meiner Fragen beantworten konnte.
Meine erste Frage in diese Richtung war z.B., wie Windows-Forms "funktionieren" und er konnte mir helfen durch den Source-Code zu navigieren und mit Kommentaren zum Code zusammenfassend ein Bild der Lage zu geben.
Zu dem Zeitpunkt waren da zu viele Dinge, die mir noch nicht bekannt und geläufig waren, sodass die Navigationshilfe und die Kommentare fundamental notwendig waren, um nicht verloren zu gehen.
 
  • Gefällt mir
Reaktionen: ReignInBlo0d, BeBur und vram78
Mein vorheriges Beispiel war vielleicht auch nicht so klar
Java:
abstract class Tier {
    public abstract void laut();
    public abstract void fressen();
}

 class Hund extends Tier {
    public void laut() {
         System.out.println("wuff") ;
    }
    public void fressen() {
         System.out.println("hautsache fleisch") ;
    }
}

class Katze extends Tier {
    public void laut()  {
         System.out.println("miau") ;
    }
    public void fressen() {
         System.out.println("Mäuse natürlich") ;
    }
}

public class MyClass {
    
    public static void FressenUndLautAusgeben (Tier einTier) {
        einTier.fressen();
        einTier.laut();
    }
    public static void FressenUndLautAusgebenNoOOP (Tier einTier) {
        if(einTier instanceof Hund ){
             System.out.println("Hauptsache fleisch") ;System.out.println("wuff") ;
        };
        if(einTier instanceof Katze ){
            System.out.println("Mäuse natürlich") ;System.out.println("miau");
        }
    }
    public static void main(String args[]) {
        Hund bello = new Hund();
        Katze felix = new Katze();
        
        FressenUndLautAusgeben(bello);
        FressenUndLautAusgebenNoOOP(felix);
    }
}

Es geht nicht darum die Implementierung vor dir zu verstecken sondern vor der Methode FressenUndLautAusgeben. Der "Aufrufer" ist die FressenUndLautAusgeben Methode und nicht du als Entwickler - so dürfte es gemeint sein :)

Der Vorteil ist, dass du jetzt eine Klasse Maus von Tier ableiten kannst und in FressenUndLautAusgeben keine Änderung machen musst.
Wenn Du dir dagegen FressenUndLautAusgebenNoOOP ansiehst funktioniert der Code zwar genauso aber du musst in der Methode wissen um was für ein Tier es sich handelt . In dem Beispiel ist es recht trivial, aber wenn du deinen Zoo noch um x andere Tierarten erweitertest musst die jede Method anpassen die Tier verwendet
 
  • Gefällt mir
Reaktionen: BeBur
vram78 schrieb:
....
Kann man dies als mögliche Schwäche des Programmierparadigmas verstehen?


Meine 2. Frage:

Ich verstehe immer noch nicht ganz, wozu man
...
Um es kurz zu machen. Wenn du die Antwort auf Frage 2 nicht kennst solltest du nicht über Frage 1 spekulieren. :freak:
 
Du hast jetzt einige Antworten zu deiner ersten Frage bekommen, die allerdings überwiegend technisch sind. Deswegen hier nochmal ein Beispiel abstrahiert an die Realität - vll damit die Idee etwas einfacher zu verstehen ist:

Stell dir vor du hast heute einen Kinoabend mit deiner Liebsten - damit ein solcher funktioniert, brauchts natürlich Bier und Chips, weils halt ohne nich geht ;D
Also bekommst du den Auftrag: "Kaufe Chips und Bier im Aldi". An sich kein Thema, problematisch wirds aber dann, wenn dein Aldi bereits geschlossen hat. Mit der obigen Formulierung habe ich dich nämlich abhängig von einer konkreten Implementierung gemacht, in diesem Fall von Aldi. Ergo kein Bier und Chips für dich, Kinoabend wird ein Disaster. Besser wäre folgende Formulierung: "Kaufe Chips und Bier". Wo du diese Dinge bekommst (Schnittstelle), ist an sich völlig egal - sie sollen am Ende nur da sein. Es geht also darum harte Abhängigkeiten zu vermeiden.

Diesem Prinzip begegnest du quasi alltäglich, z.B bei der Erläuterung mit dem Auto in einem oberen Beitrag oder beim Kauf einer aktuellen Grafikkarte, die die Schnittstelle PCI-E unterstützt. Die Marke ist dabei auch obsolet oder wie sie intern funktioniert. Wir wissen, solange sie die besagte Schnittstelle anbietet, wird sie wie erwartet arbeiten.
 
  • Gefällt mir
Reaktionen: vram78 und BeBur
VD90 schrieb:
Besser wäre folgende Formulierung: "Kaufe Chips und Bier". Wo du diese Dinge bekommst (Schnittstelle), ist an sich völlig egal - sie sollen am Ende nur da sein. Es geht also darum harte Abhängigkeiten zu vermeiden.
Und das ist dann auch die Schwierigkeit, sobald man die Konzepte verinnerlicht hat.
"Kaufen" hat er geschrieben, aber wenn noch Bier im Kühlschrank ist ist der Ganz zum Supermarkt überflüssig gewesen. Die Alternative, Bier von Zuhause mitzubringen kannst du offen lassen, aber du kannst darauf wetten, dass dann zuhause eine Flasche Bier geöffnet wird und ins Glas geschüttet wird (wie man das zuhause nun einmal macht) und dann das gefüllte Bierglas angeschleppt wird. Alternativ kann auch die leere Bierflasche mitgebracht worden sein.
Programmieren ist ca. so wie einen von Geburt an Blinden Menschen dabei anzuleiten, das Gemälde Mona Lisa von da Vinci zu malen. [/anekdote]
 
  • Gefällt mir
Reaktionen: VD90
Zurück
Oben