Java er et objektorienteret sprog. Det betyder, at du skal skrive Java-programmer ved hjælp af et objektorienteret paradigme. Og dette paradigme indebærer brug af objekter og klasser i dine programmer. Lad os prøve at bruge eksempler til at forstå, hvad klasser og objekter er, og hvordan man anvender grundlæggende OOP-principper (abstraktion, arv, polymorfi og indkapsling) i praksis.
Ved hjælp af abstraktion kan vi nu identificere den generelle information i dette objekthierarki: det generelle abstrakte objekt (telefon), telefonens fælles karakteristika (f.eks. år for dens oprettelse) og den fælles grænseflade (alle telefoner kan modtage og foretage opkald). Sådan ser det ud i Java:
Hvad er en genstand?
Den verden, vi lever i, består af objekter. Når vi ser os omkring, kan vi se, at vi er omgivet af huse, træer, biler, møbler, tallerkener og computere. Alle disse ting er objekter, og hver af dem har et sæt specifikke egenskaber, adfærd og formål. Vi er vant til genstande, og vi bruger dem altid til helt bestemte formål. Skal vi for eksempel på arbejde, bruger vi bil. Hvis vi vil spise, bruger vi retter. Og hvis vi vil hvile, finder vi en behagelig sofa. Mennesker er vant til at tænke i genstande for at løse problemer i hverdagen. Dette er en af grundene til, at objekter bruges i programmering. Denne tilgang kaldes objektorienteret programmering. Lad os give et eksempel. Forestil dig, at du har udviklet en ny telefon og ønsker at starte masseproduktion. Som telefonens udvikler ved du, hvad den er til, hvordan den fungerer, og hvad dens dele er (krop, mikrofon, højttaler, ledninger, knapper osv.). Hvad mere er, er det kun du, der ved, hvordan du forbinder disse dele. Men du planlægger ikke at lave telefonerne personligt - du har et helt team af arbejdere til at gøre dette. For at eliminere behovet for gentagne gange at forklare, hvordan man forbinder telefonens dele, og for at sikre, at alle telefoner er lavet på samme måde, skal man, inden man begynder at producere dem, lave en tegning, der beskriver, hvordan telefonen er organiseret. I OOP kalder vi sådan en beskrivelse, tegning, diagram eller skabelon for en klasse. Det danner grundlag for at skabe objekter, når programmet kører. En klasse er en beskrivelse af objekter af en bestemt type - som en almindelig skabelon bestående af felter, metoder og en konstruktør. Et objekt er en instans af en klasse.Abstraktion
Lad os nu tænke på, hvordan vi kan flytte fra et objekt i den virkelige verden til et objekt i et program. Vi bruger telefonen som eksempel. Dette kommunikationsmiddel har en historie, der strækker sig over mere end 100 år. Den moderne telefon er en meget mere kompleks enhed end dens forgænger fra det 19. århundrede. Når vi bruger telefonen, tænker vi ikke på dens organisation og de processer, der foregår inde i den. Vi bruger ganske enkelt de funktioner, som telefonens udviklere tilbyder: knapper eller en berøringsskærm til at indtaste et telefonnummer og foretage opkald. En af de første telefongrænseflader var et håndsving, der skulle drejes for at foretage et opkald. Det var selvfølgelig ikke særlig bekvemt. Men den opfyldte sin funktion fejlfrit. Hvis du sammenligner de mest moderne og de allerførste telefoner, du kan straks identificere de vigtigste funktioner for enheden fra det sene 19. århundrede og for den moderne smartphone. De er evnen til at foretage opkald og evnen til at modtage opkald. Det er faktisk det, der gør telefonen til en telefon, og ikke noget andet. Nu har du lige anvendt et princip om OOP: identificer et objekts vigtigste egenskaber og information. Dette princip kaldes abstraktion. I OOP kan abstraktion også defineres som en metode til at repræsentere elementer af en virkelig opgave som objekter i et program. Abstraktion er altid forbundet med generaliseringen af bestemte egenskaber ved et objekt, så det vigtigste er at adskille meningsfuld information fra det ubetydelige i forbindelse med den aktuelle opgave. Derudover kan der være flere abstraktionsniveauer. Lade' Prøv at anvende abstraktionsprincippet på vores telefoner. Til at begynde med vil vi identificere de mest almindelige typer telefoner - fra de allerførste telefoner til nutidens. For eksempel kunne vi repræsentere dem i form af diagrammet i figur 1.![Principper for OOP - 2](https://cdn.codegym.cc/images/article/d4f1cc74-77fa-46bf-bbd7-772ec2746147/800.jpeg)
public abstract class AbstractPhone {
private int year;
public AbstractPhone(int year) {
this.year = year;
}
public abstract void call(int outgoingNumber);
public abstract void ring(int incomingNumber);
}
I et program kan vi skabe nye typer telefoner ved at bruge denne abstrakte klasse og anvende andre grundlæggende principper for OOP, som vi vil udforske nedenfor.
Indkapsling
Med abstraktion identificerer vi, hvad der er fælles for alle objekter. Men hver type telefon er unik og adskiller sig på en eller anden måde fra de andre. Hvordan trækker vi grænser og identificerer denne individualitet i et program? Hvordan gør vi det, så ingen ved et uheld eller bevidst kan bryde vores telefon eller forsøge at konvertere en model til en anden? I den virkelige verden er svaret indlysende: du skal lægge alle delene i et telefoncover. Når alt kommer til alt, hvis du ikke gør det - i stedet for at efterlade alle telefonens interne dele og forbindelsesledninger på ydersiden - vil en eller anden nysgerrig eksperimentator helt sikkert ønske at "forbedre" vores telefon. For at forhindre sådan manipulation anvendes princippet om indkapsling i et objekts design og drift. Dette princip siger, at et objekts attributter og adfærd kombineres i en enkelt klasse, objektet' s interne implementering er skjult for brugeren, og der er en offentlig grænseflade til at arbejde med objektet. Programmørens opgave er at bestemme, hvilke af et objekts attributter og metoder, der skal være tilgængelige for offentligheden, og hvilke interne implementeringsdetaljer, der skal være utilgængelige.Indkapsling og adgangskontrol
Antag, at oplysninger om en telefon (dens produktionsår eller producentens logo) er indgraveret på dens bagside, når den laves. Informationen (dens tilstand) er specifik for denne særlige model. Vi kan sige, at producenten sørgede for, at denne information var uforanderlig - det er usandsynligt, at nogen ville tænke på at fjerne graveringen. I Java-verdenen beskriver en klasse tilstanden af fremtidige objekter ved hjælp af felter, og deres adfærd beskrives ved hjælp af metoder. Adgang til et objekts tilstand og adfærd styres ved hjælp af modifikatorer, der anvendes på felter og metoder: privat, beskyttet, offentlig og standard. For eksempel besluttede vi, at produktionsåret, producentens navn og en af metoderne er interne implementeringsdetaljer for klassen og ikke kan ændres af andre objekter i programmet. I kode,
public class SomePhone {
private int year;
private String company;
public SomePhone(int year, String company) {
this.year = year;
this.company = company;
}
private void openConnection(){
// findSwitch
// openNewConnection...
}
public void call() {
openConnection();
System.out.println("Calling");
}
public void ring() {
System.out.println("Ring-ring");
}
}
Den private modifikator tillader kun at få adgang til klassens felter og metoder inden for denne klasse. Det betyder, at det er umuligt at tilgå private felter udefra, fordi de private metoder ikke kan kaldes. Begrænsning af adgangen til openConnection- metoden giver os også mulighed for frit at ændre metodens interne implementering, da metoden med garanti ikke vil blive brugt af eller afbryde arbejdet med andre objekter. For at arbejde med vores objekt lader vi opkalds- og ringemetoderne være tilgængelige ved hjælp af den offentlige modifikator. At levere offentlige metoder til at arbejde med objekter er også en del af indkapslingen, da hvis adgang blev nægtet fuldstændigt, ville det blive ubrugeligt.
Arv
Lad os tage et nyt kig på diagrammet over telefoner. Du kan se, at det er et hierarki, hvor en model har alle funktionerne fra modellerne placeret højere langs sin gren og tilføjer nogle af sine egne. For eksempel bruger en smartphone et mobilnetværk til kommunikation (har en mobiltelefons egenskaber), er trådløs og bærbar (har en trådløs telefons egenskaber) og kan modtage og foretage opkald (har en telefons egenskaber). Det, vi har her, er arv af objektegenskaber. I programmering betyder arv at bruge eksisterende klasser til at definere nye. Lad os overveje et eksempel på brug af arv til at oprette en smartphone-klasse. Alle trådløse telefoner er drevet af genopladelige batterier, som har en vis batterilevetid. Derfor tilføjer vi denne egenskab til den trådløse telefonklasse:
public abstract class CordlessPhone extends AbstractPhone {
private int hour;
public CordlessPhone (int year, int hour) {
super(year);
this.hour = hour;
}
}
Mobiltelefoner arver egenskaberne fra en trådløs telefon, og vi implementerer opkalds- og ringemetoderne i denne klasse:
public class CellPhone extends CordlessPhone {
public CellPhone(int year, int hour) {
super(year, hour);
}
@Override
public void call(int outgoingNumber) {
System.out.println("Calling " + outgoingNumber);
}
@Override
public void ring(int incomingNumber) {
System.out.println("Incoming call from " + incomingNumber);
}
}
Og endelig har vi smartphone-klassen, der i modsætning til klassiske mobiltelefoner har et fuldgyldigt styresystem. Du kan udvide din smartphones funktionalitet ved at tilføje nye programmer, der kan køre på dens operativsystem. I kode kan klassen beskrives som følger:
public class Smartphone extends CellPhone {
private String operationSystem;
public Smartphone(int year, int hour, String operationSystem) {
super(year, hour);
this.operationSystem = operationSystem;
}
public void install(String program) {
System.out.println("Installing " + program + " for " + operationSystem);
}
}
Som du kan se, lavede vi en del ny kode til at beskrive Smartphone- klassen, men vi fik en ny klasse med ny funktionalitet. Dette princip i OOP gør det muligt betydeligt at reducere mængden af Java-kode, der kræves, og dermed gøre livet lettere for programmøren.
Polymorfi
På trods af forskelle i udseende og design af forskellige typer telefoner, kan vi identificere nogle almindelige adfærd: De kan alle modtage og foretage opkald, og alle har et ret klart og enkelt sæt kontrolelementer. Med hensyn til programmering lader abstraktionsprincippet (som vi allerede er bekendt med) os sige, at telefonobjekter har en fælles grænseflade. Det er derfor, folk nemt kan bruge forskellige modeller af telefoner, der har de samme kontroller (mekaniske knapper eller en berøringsskærm), uden at dykke ned i enhedens tekniske detaljer. Således bruger du en mobiltelefon konstant, og du kan nemt foretage et opkald fra din vens fastnet. Princippet i OOP, der siger, at et program kan bruge objekter med en fælles grænseflade uden nogen information om objektets interne struktur, kaldes polymorfi. Lade' s forestille mig, at vi har brug for vores program til at beskrive en bruger, der kan bruge enhver telefon til at ringe til en anden bruger. Sådan kan vi gøre det:
public class User {
private String name;
public User(String name) {
this.name = name;
}
public void callAnotherUser(int number, AbstractPhone phone){
// And here's polymorphism: using the AbstractPhone type in the code!
phone.call(number);
}
}
}
Nu vil vi beskrive flere slags telefoner. En af de første telefoner:
public class ThomasEdisonPhone extends AbstractPhone {
public ThomasEdisonPhone(int year) {
super(year);
}
@Override
public void call(int outgoingNumber) {
System.out.println("Crank the handle");
System.out.println("What number would you like to connect to?");
}
@Override
public void ring(int incomingNumber) {
System.out.println("The phone is ringing");
}
}
En almindelig fastnettelefon:
public class Phone extends AbstractPhone {
public Phone(int year) {
super(year);
}
@Override
public void call(int outgoingNumber) {
System.out.println("Calling " + outgoingNumber);
}
@Override
public void ring(int incomingNumber) {
System.out.println("The phone is ringing");
}
}
Og endelig en fed videotelefon:
public class VideoPhone extends AbstractPhone {
public VideoPhone(int year) {
super(year);
}
@Override
public void call(int outgoingNumber) {
System.out.println("Connecting video call to " + outgoingNumber);
}
@Override
public void ring(int incomingNumber) {
System.out.println("Incoming video call from " + incomingNumber);
}
}
Vi opretter objekter i main()- metoden og tester callAnotherUser()- metoden:
AbstractPhone firstPhone = new ThomasEdisonPhone(1879);
AbstractPhone phone = new Phone(1984);
AbstractPhone videoPhone=new VideoPhone(2018);
User user = new User("Jason");
user.callAnotherUser(224466, firstPhone);
// Crank the handle
// What number would you like to connect to?
user.callAnotherUser(224466, phone);
// Calling 224466
user.callAnotherUser(224466, videoPhone);
// Connecting video call to 224466
At kalde den samme metode på brugerobjektet giver forskellige resultater. En specifik implementering af opkaldsmetoden vælges dynamisk i metoden callAnotherUser() baseret på den specifikke type objekt, der sendes, når programmet kører. Dette er polymorfis største fordel – muligheden for at vælge en implementering under kørsel. I eksemplerne på telefonklasser givet ovenfor brugte vi metodetilsidesættelse - et trick, hvor vi ændrer implementeringen af en metode defineret i basisklassen uden at ændre metodesignaturen. Dette erstatter i det væsentlige metoden: den nye metode, der er defineret i underklassen, kaldes, når programmet køres. Normalt, når vi tilsidesætter en metode, vil @Overrideanmærkning bruges. Det fortæller compileren at kontrollere signaturerne for de tilsidesatte og tilsidesættende metoder. Til sidst, for at sikre, at dine Java-programmer er i overensstemmelse med principperne for OOP, skal du følge disse tips:
- identificere et objekts hovedkarakteristika;
- identificere fælles egenskaber og adfærd og bruge arv ved oprettelse af klasser;
- bruge abstrakte typer til at beskrive objekter;
- forsøg altid at skjule metoder og felter relateret til en klasses interne implementering.
GO TO FULL VERSION