CodeGym /Java-blogg /Tilfeldig /OOP-konsepter i Java
John Squirrels
Nivå
San Francisco

OOP-konsepter i Java

Publisert i gruppen
En av Javas største styrker er objektorientert programmering (OOP). Det er grunnen til at dette språket har blitt så populært og er godt egnet for prosjekter av alle størrelser. Hva er objektorientert programmering? Det er ikke magi, men det kan virke magisk hvis du virkelig setter deg inn i det. OOP handler om hvordan du bygger programvaren din. Det er et konsept, eller snarere en haug med oop-konsepter i Java, som lar deg lage noen spesifikke interaksjoner og relasjoner mellom Java-objekter for å effektivt utvikle og bruke programvare. OOP-konsepter i Java - 1Klassisk OOP inkluderer 3 + 1 hovedkonsepter. La oss starte med klassikerne.

Objektet

Java-objekter så vel som virkelige objekter har to egenskaper: tilstand og atferd.

For eksempel har et menneskelig objekt tilstand (navn, kjønn, soving eller ikke...) og atferd (studerer Java, går, snakker...). Ethvert Java-objekt lagrer sin tilstand i felt og avslører dets oppførsel gjennom metoder.

Innkapsling

Datainnkapsling er å skjule interne data fra omverdenen, og bare få tilgang til dem gjennom offentlig eksponerte metoder. Hva betyr det? Hvilke data? Gjemmer seg for hvem? Skjul betyr å begrense direkte tilgang til datamedlemmene (feltene) til en klasse.

Slik fungerer det i Java:

  1. Felter gjøres private
  2. Hvert felt i en klasse får to spesielle metoder: en getter og en setter. Getter-metoder returnerer verdien av feltet. Setter-metoder lar deg endre verdien av feltet på en indirekte, men tillatt måte.

Eksempel på innkapsling i Java-kode:


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"; 
}
}

Hvorfor bør du bruke innkapsling?

Hovedgrunnen er å gjøre det enklere å endre koden. Tenk deg at du har en søknad om en hockeyskole og det er en HockeyStudent- klasse med to felt som lagrer elevens navn og alder da han eller hun meldte seg inn på skolen. Noe sånt som dette:

public class HockeyStudent {
public String name;
public  int ageOfEnrollment;
}
ageOfEnrollment - feltet er offentlig, ingen gettere eller settere... Denne klassen brukes av mange andre klasser, og alt var ok inntil en utvikler bestemte at et enkelt int-felt ikke var nok. Noen hockeyspillere i en kohort er nesten ett år eldre enn jevnaldrende, så det ville være mer praktisk å dele dem inn i to grupper avhengig av måneden de ble født. Så ageOfEnrollment -feltet bør endres til en int-matrise (int[][]) : det første tallet er for hele år og det andre er for måneder. Nå må du refaktorere all kode som bruker Student- klassen! Men hvis din alderOfEnrollmentfelt er privat og du har gettere og settere, da er alt enklere. Hvis kravet for å angi alderen til en elev endres, oppdaterer du bare logikken i setAgeOfEnrollment() setter-metoden, så kan klassene dine fortsette å bruke Student uten problemer! Dette eksemplet er noe konstruert, men jeg håper det forklarer hvorfor det er en god idé å bruke innkapsling.

Arv

Dette prinsippet er lettere å forstå selv uten praktisk erfaring. Ikke gjenta deg selv (TØRR) kan være mottoet for arvebegrepet. Arv lar deg opprette en underordnet klasse som arver feltene og metodene til den overordnede klassen uten å omdefinere dem. Visst, du kan overstyre foreldreklassens felt og metoder i barneklassen, men det er ikke en nødvendighet. Dessuten kan du legge til nye tilstander og atferd i barneklassen. Foreldreklasser kalles noen ganger superklasser eller basisklasser, og barneklasser er kjent som underklasser. Javas extends nøkkelord brukes til å implementere prinsippet om arv i kode.

Slik fungerer det i Java:

  1. Opprett foreldreklassen.
  2. Opprett barneklassen ved å bruke nøkkelordet extends .
  3. I Child-klassens konstruktør bruker du metoden super(parentField1, parentField2, ...) for å angi foreldrenes felt.

En konstruktør er en spesiell metode som brukes til å initialisere et nyopprettet objekt. En konstruktør har samme navn som klassenavnet. Det er to typer konstruktører: standard (no-arg konstruktør) og parameterisert konstruktør. En klasse må ha minst én konstruktør (den har standardkonstruktøren hvis ikke andre konstruktører er definert) og den kan ha mange av dem.

Hver gang du oppretter et nytt objekt, ringer du dets konstruktør. I eksemplet ovenfor gjør du dette på denne linjen:


Student firstStudent = new Student();

Du bruker det nye nøkkelordet til å kalle opp studentklassens standardkonstruktør: tudent() .

Noen regler:

  1. Én klasse kan kun ha én forelder.
  2. Én foreldreklasse kan ha mange barneklasser.
  3. En barneklasse kan ha sine egne barneklasser.

Eksempel på arv i Java-kode

La oss lage en telefonklasse .

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...");
    }
}
Selvfølgelig er det forskjellige typer telefoner, så la oss lage to barneklasser: en for Android-telefoner og en andre for iPhones. Deretter legger vi til noen felt og metoder som forelderen ikke har. Og vi bruker super() for å kalle konstruktører for å initialisere feltene som den overordnede klassen har.

Eksempel 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å for å gjenta: i Java lar arv deg utvide en klasse med underordnede klasser som arver feltene og metodene til den overordnede klassen. Det er en utmerket måte å oppnå kodegjenbrukbarhet.

Polymorfisme

Polymorfisme er et objekts evne til å forvandle seg, ta forskjellige former eller snarere opptre på forskjellige måter. I Java skjer polymorfisme vanligvis når en overordnet klassereferanse brukes til å referere til et underordnet klasseobjekt.

Hva det betyr og hvordan det fungerer i Java:

Hva er polymorfisme i Java? Generelt betyr det at du kan bruke samme metodenavn til forskjellige formål. Det er to typer polymorfisme i Java: metodeoverstyring (dynamisk polymorfisme) og metodeoverbelastning (statisk polymorfisme).

Metode overstyrer

Du kan overstyre en overordnet klasses metode i en barneklasse, og tvinge den til å fungere på en annen måte. La oss lage en Musician- foreldreklasse med en play()- metode.

Eksempel på polymorfisme i Java-kode


   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...");
    }
}
Ulike musikere bruker forskjellige instrumenter. La oss lage to barneklasser: Pianist og fiolinist . Takket være polymorfisme vil hver utføre sin egen versjon av play()- metoden. Når du overstyrer, kan du bruke @Override- kommentaren, men det er ikke nødvendig.

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...");
    }
}
Fiolinisten kan være solist eller medlem av et orkester. La oss ta det i betraktning når vi overstyrer play() -metoden vår.

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...");

    }
}
La oss lage en Demo- klasse, der vi skal lage tre objekter, en forekomst av hver av de tidligere opprettede klassene. Vi får se hvilke resultater 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();
    }
}
Her er hva 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...
Hver fiolinist og pianist er musiker, men ikke alle musikere er fiolinist eller pianist. Det betyr at du kan bruke musikerens spillemetode hvis du ikke trenger å lage en ny. Eller du kan ringe foreldrenes metode fra barnet ved å bruke supernøkkelordet . La oss gjøre det i Pianists kode:

public class Pianist extends Musician {

    String favoritePianoType;
    
    @Override
    void play(){
        super.play();
        System.out.println("I am playing the piano...");
    }
}
La oss nå kalle vår main() -metode i Demo- klassen. Her er 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...

Metode overbelastning

Metodeoverbelastning betyr å bruke ulike metoder med samme navn i samme klasse. De må være forskjellige når det gjelder antall, rekkefølge eller type parametere. Anta at en pianist kan spille et akustisk piano og et elektrisk piano. For å spille en elektrisk, trenger musikeren strøm. La oss lage to forskjellige play()- metoder. Den første uten parametere, for et akustisk piano, og den andre med en parameter som indikerer om strøm er tilgjengelig.

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.");
    }
}
Forresten, du kan bruke den første play() -metoden i den andre play(boolean) -metoden på denne måten:

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.");
    }
La oss legge til noen linjer i demoklassen vår for å demonstrere vår overbelastning:

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);
    }
}
Her er 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 hvilken metode som skal brukes basert på parameterne og objekttypen. Det er polymorfisme.

Abstraksjon

Når vi definerer en klasse, prøver vi å bygge en modell av noe. Anta for eksempel at vi skriver et videospill kalt MyRacer med forskjellige racerbiler. En spiller kan velge en av dem og deretter oppdatere den senere eller kjøpe en annen. Så... Hva er en bil? En bil er en ganske komplisert ting, men hvis vi prøver å lage et racingvideospill (i motsetning til en kjøresimulator), trenger vi ikke å beskrive alle tusenvis av gir og pakninger den inneholder. Vi trenger modellen, topphastigheten, manøvreringsegenskapene, prisen, fargen... Og kanskje det er nok. Det er modellen av en bil for spillet vårt. Senere i MyRacer 2, anta at vi bestemmer oss for å legge til dekk som påvirker kjøreegenskapene på veien. Her er modellen annerledes, fordi vi har lagt til flere detaljer. La' s definerer dataabstraksjon som prosessen med å identifisere bare de viktige (eller nødvendige) egenskapene til et objekt og ignorere eventuelle irrelevante detaljer. Det er forskjellige nivåer av abstraksjon. Hvis du for eksempel er passasjer på en buss, må du vite hvordan bussen din ser ut og hvor den skal, men du trenger ikke vite hvordan du kjører den. Hvis du er bussjåfør, trenger du ikke å vite hvordan du lager en ny buss – du trenger bare å vite hvordan du kjører den. Men hvis du er en bussprodusent, må du gå til et lavere abstraksjonsnivå, fordi detaljene i bussdesignet er veldig viktige for deg. Jeg håper du forstår hva jeg mener. du trenger å vite hvordan bussen din ser ut og hvor den skal, men du trenger ikke vite hvordan du kjører den. Hvis du er bussjåfør, trenger du ikke å vite hvordan du lager en ny buss – du trenger bare å vite hvordan du kjører den. Men hvis du er en bussprodusent, må du gå til et lavere abstraksjonsnivå, fordi detaljene i bussdesignet er veldig viktige for deg. Jeg håper du forstår hva jeg mener. du trenger å vite hvordan bussen din ser ut og hvor den skal, men du trenger ikke vite hvordan du kjører den. Hvis du er bussjåfør, trenger du ikke å vite hvordan du lager en ny buss – du trenger bare å vite hvordan du kjører den. Men hvis du er en bussprodusent, må du gå til et lavere abstraksjonsnivå, fordi detaljene i bussdesignet er veldig viktige for deg. Jeg håper du forstår hva jeg mener.

Slik fungerer det i Java:

La oss bygge fire abstraksjonsnivåer i Java, eller rettere sagt i OOP — fra det laveste (det mest spesifikke) til det høyeste (det mest abstrakte).
  1. Det laveste abstraksjonsnivået er et spesifikt objekt. Det er en enhet med et sett med egenskaper som tilhører en bestemt klasse. Den har spesifikke feltverdier

  2. En mal for å lage objekter er en klasse. Det er en beskrivelse av et sett med objekter med lignende egenskaper og indre struktur.

  3. En abstrakt klasse er en abstrakt beskrivelse av egenskapene til et sett med klasser (den fungerer som en mal for arv fra andre klasser). Den har et høyt abstraksjonsnivå, så det er umulig å lage objekter direkte fra en abstrakt klasse. Bare underordnede klasser av abstrakte klasser kan brukes til å lage objekter. En abstrakt klasse kan inkludere metoder med en implementering, men dette er ikke et krav.

  4. Et grensesnitt er en konstruksjon av Java-programmeringsspråkkonstruksjonen som bare inneholder abstrakte offentlige metoder og statiske konstantfelt (endelig statisk). Med andre ord kan verken abstrakte klasser eller grensesnitt brukes til å generere objekter.

BTW, i Java 8 eller nyere kan grensesnitt ikke bare ha abstrakte metoder og konstanter, men også standard og statiske metoder. I Java definerer et grensesnitt en atferd, mens en abstrakt klasse brukes til å lage et hierarki. Ett grensesnitt kan implementeres av flere klasser.

Eksempel på grensesnitt i Java-kode


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

interface Vulcan {
	int angleOfPointyEars; 
	public void turnOffEmotions(boolean isOn);
	public void telepathy();
}
Du kan implementere mer enn ett grensesnitt

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...");
}

}
For nybegynnere dekker det alle hovedkonseptene innen objektorientert programmering i Java. I tillegg til de fire hovedprinsippene for OOP, har Java også assosiasjon, aggregering og komposisjon. Du kan kalle dem "ekstra OOP-prinsipper". De fortjener sin egen artikkel.
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION