CodeGym /Java blogg /Slumpmässig /OOP-koncept i Java
John Squirrels
Nivå
San Francisco

OOP-koncept i Java

Publicerad i gruppen
En av Javas största styrkor är objektorienterad programmering (OOP). Det är anledningen till att detta språk har blivit så populärt och är väl lämpat för projekt av alla storlekar. Vad är objektorienterad programmering? Det är inte magi, men det kan verka magiskt om du verkligen kommer in i det. OOP handlar om hur du bygger din mjukvara. Det är ett koncept, eller snarare ett gäng oop-koncept i Java, som låter dig skapa vissa specifika interaktioner och relationer mellan Java-objekt för att effektivt utveckla och använda programvara. OOP-koncept i Java - 1Klassisk OOP innehåller 3 + 1 huvudkoncept. Låt oss börja med klassikerna.

Objektet

Java-objekt såväl som verkliga objekt har två egenskaper: tillstånd och beteende.

Till exempel har ett mänskligt objekt tillstånd (namn, kön, sömn eller inte...) och beteende (studerar Java, går, pratar...). Alla Java-objekt lagrar sitt tillstånd i fält och exponerar dess beteende genom metoder.

Inkapsling

Datainkapsling är att dölja interna data från omvärlden och endast få tillgång till dem genom offentligt exponerade metoder. Vad betyder det? Vilken data? Gömmer sig för vem? Dölja innebär att begränsa direkt åtkomst till datamedlemmarna (fälten) i en klass.

Hur det fungerar i Java:

  1. Fälten görs privata
  2. Varje fält i en klass får två speciella metoder: en getter och en setter. Getter-metoder returnerar fältets värde. Settermetoder låter dig ändra fältets värde på ett indirekt men tillåtet sätt.

Exempel på inkapsling i Java-kod:


public class Student {
private int age;
private String name;

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

public class Test{
public static void main(String[] args) {
Student firstStudent = new Student();
firstStudent.setName("John");
// The name field is private, so you can no longer do this:  firstStudent.name = "John"; 
}
}

Varför ska man använda inkapsling?

Det främsta skälet är att göra det lättare att ändra din kod. Föreställ dig att du har en ansökan till en hockeyskola och det finns en HockeyStudent- klass med två fält som lagrar elevens namn och ålder när han eller hon skrevs in på skolan. Något som det här:

public class HockeyStudent {
public String name;
public  int ageOfEnrollment;
}
ageOfEnrollment - fältet är offentligt, inga getters eller setters... Den här klassen används av många andra klasser, och allt var ok tills någon utvecklare beslutade att ett enda int-fält inte räckte. Vissa hockeyspelare i en kohort är nästan ett år äldre än sina kamrater, så det skulle vara bekvämare att dela upp dem i två grupper beroende på vilken månad de föddes. Så ageOfEnrollment -fältet bör ändras till en int-matris (int[][]) : det första talet är för hela år och det andra är för månader. Nu måste du refaktorera all kod som använder Studentklassen ! Men om din ålder OfEnrollmentfältet är privat och du har getters och setters, då är allt lättare. Om kravet för att ställa in en elevs ålder ändras, uppdatera bara logiken i setAgeOfEnrollment() -inställningsmetoden så kan dina klasser fortsätta använda Student utan problem! Det här exemplet är något konstruerat, men jag hoppas att det förklarar varför det är en bra idé att använda inkapsling.

Arv

Denna princip är lättare att förstå även utan praktisk erfarenhet. Repetera inte dig själv (DRY) kan vara mottot för arvskonceptet. Med arv kan du skapa en underordnad klass som ärver den överordnade klassens fält och metoder utan att omdefiniera dem. Visst, du kan åsidosätta föräldraklassens fält och metoder i den underordnade klassen, men det är inte en nödvändighet. Dessutom kan du lägga till nya tillstånd och beteenden i barnklassen. Föräldraklasser kallas ibland superklasser eller basklasser, och barnklasser kallas underklasser. Javas extends nyckelord används för att implementera principen om arv i kod.

Hur det fungerar i Java:

  1. Skapa föräldraklassen.
  2. Skapa barnklassen med nyckelordet extends .
  3. I Child-klassens konstruktor, använd metoden super(parentField1, parentField2, ...) för att ställa in förälderns fält.

En konstruktor är en speciell metod som används för att initiera ett nyskapat objekt. En konstruktör har samma namn som dess klassnamn. Det finns två typer av konstruktorer: standard (no-arg konstruktor) och parametriserad konstruktor. En klass måste ha minst en konstruktor (den har standardkonstruktorn om inte andra konstruktorer har definierats) och den kan ha många av dem.

Varje gång du skapar ett nytt objekt anropar du dess konstruktor. I exemplet ovan gör du detta på denna rad:


Student firstStudent = new Student();

Du använder det nya nyckelordet för att anropa Studentklassens standardkonstruktor: tudent() .

Några regler:

  1. En klass kan bara ha en förälder.
  2. En föräldraklass kan ha många barnklasser.
  3. En barnklass kan ha sina egna barnklasser.

Exempel på arv i Java-kod

Låt oss skapa en telefonklass .

public class Phone {
    int price;
    double weight;

// Constructor
public Phone(int price, double weight) {
        this.price = price;
        this.weight = weight;
    }

    void orderPhone(){
        System.out.println("Ordering phone...");
    }
}
Naturligtvis finns det olika typer av telefoner, så låt oss skapa två barnklasser: en för Android-telefoner och en andra för iPhones. Sedan lägger vi till några fält och metoder som föräldern inte har. Och vi kommer att använda super() för att anropa konstruktorer för att initiera fälten som den överordnade klassen har.

Exempel på arv i Java


public class Android extends Phone {

// Some new fields     
String androidVersion;
int screenSize;

    String secretDeviceCode;

// Constructor 
    public Android(int price, double weight, String androidVersion, int screenSize, String secretDeviceCode) {
        super(price, weight); // Android inherits Phone’s fields

        //this - reference to the current object
        //super - reference to the parent object

        this.androidVersion = androidVersion;
        this.screenSize = screenSize;
        this.secretDeviceCode = secretDeviceCode;
    }

	// New Android-specific method, does not exist in the Phone class 
    void installNewAndroidVersion() {
        System.out.println("installNewAndroidVersion invoked...");

    }

}

public class IPhone extends Phone {
   
    boolean fingerPrint;

    public IPhone(int price, double weight, boolean fingerPrint) {
        super(price, weight);
        System.out.println("IPhone constructor was invoked...");
        this.fingerPrint = fingerPrint;
    }

    void deleteIPhoneFromDb() {
        System.out.println("deleteIPhoneFromDb invoked...");
    }

@Override // This is about polymorphism, see below
void orderPhone(){
        System.out.println("Ordering my new iPhone and deleting the old one...");
    }
}
Så, för att upprepa: i Java låter arv dig utöka en klass med underordnade klasser som ärver fälten och metoderna för den överordnade klassen. Det är ett utmärkt sätt att uppnå kodåteranvändbarhet.

Polymorfism

Polymorfism är ett objekts förmåga att förvandla sig, anta olika former eller snarare agera på olika sätt. I Java uppstår polymorfism vanligtvis när en överordnad klassreferens används för att referera till ett underordnat klassobjekt.

Vad det betyder och hur det fungerar i Java:

Vad är polymorfism i Java? I allmänhet betyder det att du kan använda samma metodnamn för olika ändamål. Det finns två typer av polymorfism i Java: metodöverstyrning (dynamisk polymorfism) och metodöverbelastning (statisk polymorfism).

Åsidosättande av metod

Du kan åsidosätta en föräldraklass metod i en barnklass, vilket tvingar den att fungera på ett annat sätt. Låt oss skapa en musikerförälderklass med en play()- metod.

Exempel på polymorfism i Java-kod


   public class Musician {
    String name;
    int age;

    // Default constructor
    public Musician() {
    }

    // Parameterized constructor
    public Musician(String name, int age) {
        this.name = name;
        this.age = age;
    }

    void play() {
        System.out.println("I am playing my instrument...");
    }
}
Olika musiker använder olika instrument. Låt oss skapa två barnklasser: pianist och violinist . Tack vare polymorfism kommer var och en att köra sin egen version av play()- metoden. När du åsidosätter kan du använda @Override- kommentaren, men det är inte nödvändigt.

public class Pianist extends Musician {
    
    String favoritePianoType;

    public Pianist(String name, int age, String favoritePianoType) {
        super(name, age);
        this.favoritePianoType = favoritePianoType;
    }


    @Override
void play(){
        System.out.println("I am playing the piano...");
    }
}
Violinisten kan vara solist eller medlem i en orkester. Låt oss ta hänsyn till det när vi åsidosätter vår play()- metod.

public class Violinist extends Musician { 
    boolean isSoloist; 

public Violinist(String name, int age, boolean isSoloist) {
            super(name, age);
            this.isSoloist = isSoloist;
        }


    @Override
void play(){
if (isSoloist) 
        System.out.println("I am playing the violin solo...");
else 
System.out.println("I am playing the violin in an orchestra...");

    }
}
Låt oss skapa en demoklass , där vi kommer att skapa tre objekt, en instans av var och en av de tidigare skapade klasserna. Vi får se vilka resultat vi får.

public class Demo {
  public static void main(String[] args) {
  Musician musician = new Musician();
  Violinist violinist = new Violinist("John", 32, true);
  Pianist pianist = new Pianist("Glen", 30, "Acoustic"); 

  System.out.println("Musician said:");
  musician.play();
  System.out.println("Violinist said:");
  violinist.play();
  System.out.println("Pianist said:");
  pianist.play();
    }
}
Här är vad vi får:

Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo…
Pianist said:
I am playing the piano...
Varje violinist och pianist är en musiker, men inte varje musiker är en violinist eller pianist. Det betyder att du kan använda musikerns spelmetod om du inte behöver skapa en ny. Eller så kan du ringa upp förälderns metod från barnet med hjälp av supernyckelordet . Låt oss göra det i Pianists kod:

public class Pianist extends Musician {

    String favoritePianoType;
    
    @Override
    void play(){
        super.play();
        System.out.println("I am playing the piano...");
    }
}
Låt oss nu kalla vår main() -metod i Demo -klassen. Här är resultatet:

Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo...
Pianist said:
I am playing my instrument...
I am playing the piano...

Metodöverbelastning

Metodöverbelastning innebär att man använder olika metoder med samma namn i samma klass. De måste vara olika i fråga om antal, ordning eller typ av parametrar. Anta att en pianist kan spela ett akustiskt piano och ett elpiano. För att spela en elektrisk behöver musikern elektricitet. Låt oss skapa två olika play()- metoder. Den första utan parametrar, för ett akustiskt piano, och den andra med en parameter som indikerar om el finns tillgänglig.

public class Pianist extends Musician {

    String name;
    int age;
    String favoritePianoType;

    @Override
    void play(){
        super.play();
        System.out.println("I am playing the piano...");
    }
    void play(boolean isElectricity){
        if (isElectricity) {
            System.out.println("The electricity is on.");
            System.out.println("I am playing the piano...");
        }
        else System.out.println("I can't play this without electricity.");
    }
}
Förresten, du kan använda den första play() -metoden i den andra play(boolean) -metoden på detta sätt:

void play(boolean isElectricity){
        if (isElectricity) {
            System.out.println("The electricity is on.");
            play();
        }
        else System.out.println("I can't play this without electricity.");
    }
Låt oss lägga till några rader till vår demoklass för att demonstrera vår överbelastning:

public class Demo {
    public static void main(String[] args) {

        Musician musician = new Musician();
        Violinist violinist = new Violinist("John", 23, true);
        Pianist pianist = new Pianist("Glen", 30, "Acoustic"); 

        System.out.println("Musician said:");
        musician.play();
        System.out.println("Violinist said:");
        violinist.play();
        System.out.println("Pianist said:");
        pianist.play();
        System.out.println("The pianist will now try the electric piano:");
        pianist.play(true);
        System.out.println("The electricity has been shut off. Now when trying the electric piano, the pianist says:");
        pianist.play(false);
    }
}
Här är resultatet:

Musician said:
I am playing my instrument...
Violinist said:
I am playing the violin solo...
Pianist said:
I am playing my instrument...
I am playing the piano...
The pianist will now try the electric piano:
The electricity is on.
I am playing my instrument...
I am playing the piano...
The electricity has been shut off. Now when trying the electric piano, the pianist says:
I can't play this without electricity.
Java vet vilken metod som ska användas baserat på dess parametrar och objekttyp. Det är polymorfism.

Abstraktion

När vi definierar en klass försöker vi bygga en modell av något. Anta till exempel att vi skriver ett videospel som heter MyRacer med olika racerbilar. En spelare kan välja en av dem och sedan uppdatera den senare eller köpa en annan. Så... Vad är en bil? En bil är en ganska komplicerad sak, men om vi försöker skapa ett racingspel (i motsats till en körsimulator), så behöver vi inte beskriva alla tusentals växlar och packningar som den innehåller. Vi behöver dess modell, topphastighet, manövrerbarhetsegenskaper, pris, färg... Och det kanske räcker. Det är modellen av en bil för vårt spel. Senare i MyRacer 2, anta att vi bestämmer oss för att lägga till däck som påverkar köregenskaperna på vägen. Här är modellen annorlunda, eftersom vi lagt till fler detaljer. Låta' s definierar dataabstraktion som processen att identifiera endast de viktiga (eller nödvändiga) egenskaperna hos ett objekt och ignorera alla irrelevanta detaljer. Det finns olika abstraktionsnivåer. Om du till exempel är passagerare på en buss behöver du veta hur din buss ser ut och vart den ska, men du behöver inte veta hur du kör den. Om du är busschaufför behöver du inte veta hur man skapar en ny buss – du behöver bara veta hur man kör den. Men om du är en busstillverkare måste du gå till en lägre abstraktionsnivå, eftersom detaljerna i bussdesignen är mycket viktiga för dig. Jag hoppas du förstår vad jag menar. du behöver veta hur din buss ser ut och vart den ska, men du behöver inte veta hur du kör den. Om du är busschaufför behöver du inte veta hur man skapar en ny buss – du behöver bara veta hur man kör den. Men om du är en busstillverkare måste du gå till en lägre abstraktionsnivå, eftersom detaljerna i bussdesignen är mycket viktiga för dig. Jag hoppas du förstår vad jag menar. du behöver veta hur din buss ser ut och vart den ska, men du behöver inte veta hur du kör den. Om du är busschaufför behöver du inte veta hur man skapar en ny buss – du behöver bara veta hur man kör den. Men om du är en busstillverkare måste du gå till en lägre abstraktionsnivå, eftersom detaljerna i bussdesignen är mycket viktiga för dig. Jag hoppas du förstår vad jag menar.

Hur det fungerar i Java:

Låt oss bygga fyra abstraktionsnivåer i Java, eller snarare i OOP — från den lägsta (den mest specifika) till den högsta (den mest abstrakta).
  1. Den lägsta abstraktionsnivån är ett specifikt objekt. Det är en entitet med en uppsättning egenskaper som tillhör en specifik klass. Den har specifika fältvärden

  2. En mall för att skapa objekt är en klass. Det är en beskrivning av en uppsättning objekt med liknande egenskaper och inre struktur.

  3. En abstrakt klass är en abstrakt beskrivning av egenskaperna hos en uppsättning klasser (den fungerar som en mall för nedärvning av andra klasser). Den har en hög abstraktionsnivå, så det är omöjligt att skapa objekt direkt från en abstrakt klass. Endast underordnade klasser av abstrakta klasser kan användas för att skapa objekt. En abstrakt klass kan innehålla metoder med en implementering, men detta är inget krav.

  4. Ett gränssnitt är en konstruktion av Java-programmeringsspråkskonstruktionen som endast innehåller abstrakta offentliga metoder och statiska konstantfält (slutlig statisk). Med andra ord kan varken abstrakta klasser eller gränssnitt användas för att generera objekt.

BTW, i Java 8 eller senare kan gränssnitt inte bara ha abstrakta metoder och konstanter, utan också standardmetoder och statiska metoder. I Java definierar ett gränssnitt ett beteende, medan en abstrakt klass används för att skapa en hierarki. Ett gränssnitt kan implementeras av flera klasser.

Exempel på ett gränssnitt i Java-kod


interface Human {
	public void struggle();
	public void protect();
}

interface Vulcan {
	int angleOfPointyEars; 
	public void turnOffEmotions(boolean isOn);
	public void telepathy();
}
Du kan implementera mer än ett gränssnitt

The Spock class implements Human and Vulcan {
public void struggle() {
System.out.println("I am struggling...");
}
	public void protect() {
System.out.println("You are under my protection!”);
}
public void turnOffEmotions(boolean isOn){
If (isOn) {
System.out.println("I am turning off my emotions.");
isOn= !isOn;
}
}
	public void telepathy() {
System.out.println("Connecting to your brain...");
}

}
För nybörjarstudenter täcker det alla huvudkoncepten för objektorienterad programmering i Java. Förutom de fyra huvudsakliga OOP-principerna har Java också association, aggregering och sammansättning. Du kan kalla dem "ytterligare OOP-principer". De förtjänar en egen separat artikel.
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION