Hvilke konstruktører er det i Java? Java-konstruktører

Som du vet, er et objekt en forekomst av en bestemt klasse. For å opprette det, bruk det nye søkeordet, for eksempel:
Personstudent = ny person(“Mike”)
Denne koden lager et nytt objekt Person og navnet hans er angitt - Mike. Linje " Mike" sendes som et argument til den tilsvarende konstruktøren Person:

Person(strengnavn) ( dette.navn = navn; )

Denne konstruktøren lar deg spesifisere en persons navn når du oppretter et objekt.

Konstruktøren kalles alltid når en ny forekomst av klassen opprettes. Med andre ord, konstruktører brukes til å initialisere tilstanden til et objekt når et objekt opprettes. Konstruktører ser ut som vanlige metoder, men de er ikke:

  • Konstruktører har samme navn som klassenavnet.
  • Konstruktører, som metoder, har en liste over parametere de godtar, men har ikke en returtype (ikke engang ugyldig).

Nedenfor er et sett med 7 grunnleggende regler for arbeid med konstruktører , slik at du fullt ut kan forstå arbeidet deres.

  1. Konstruktører kan bli overbelastet:

Dette betyr at en klasse kan ha mange forskjellige konstruktører hvis parameterlistene deres er forskjellige. For eksempel:

Klasse Rektangel ( int width; int høyde; Rectangle() ( width = 1; høyde = 1; ) Rectangle(int width) ( this. width = width; this. height = width; ) Rectangle(int width, int height) ( this.width = width; this.height = høyde; ) )

Har tre forskjellige konstruktører Rektangel Du kan opprette et nytt objekt på tre forskjellige måter:

Rektangel rektangel1 = ny Rektangel(); Rektangel rektangel2 = nytt rektangel(10); Rektangel rektangel3 = nytt rektangel(10,20);

  1. Standard konstruktør:

Det er ikke nødvendig å erklære konstruktører i det hele tatt. Hvis ingen av konstruktørene er definert, så kompilatoren Java genererer automatisk en standardkonstruktør som er tom og har ingen parametere.

For eksempel, hvis vi skriver rektangelklassen slik:

Klasse Rektangel ( int bredde, høyde; int area() ( ) int perimeter() ( ) )

Deretter setter kompilatoren automatisk inn en standardkonstruktør: ectangle() ( )

  1. Kompilatoren vil ikke generere en standardkonstruktør hvis klassen allerede har en konstruktør:

Tenk på følgende eksempel:

Klasse Rektangel ( int width; int høyde; Rectangle(int width) ( this. width = width; this. height = width; ) Rectangle(int width, int height) ( this. width = width; this. height = height; ) )

Når du prøver å lage et nytt objekt: Rectangle rect1 = new Rectangle(); Kompilatoren vil gi en feil fordi den ikke kan finne en konstruktør uten argumenter.

  1. Konstruktører er ikke arvet:

I motsetning til metoder, er konstruktører ikke arvet. Eksempel:

Klasse rektangel ( rektangel(int width, int høyde) ( ) ) klasse Square forlenger rektangel ( )

Du kan ikke gjøre noe sånt som dette: Square box = new Square(10, 10);

  1. Konstruktører kan være private!

Du kan gjøre konstruktøren privat for å hindre ekstern kode i å opprette en ny forekomst av klassen. Hva kan være fordelen med en privat konstruktør?

I et designmønster kalt Singleton, brukes den private konstruktøren for å sikre at det alltid bare er én forekomst av klassen. Klasse Singleton gir en statisk metode for å oppnå denne unike forekomsten.

  1. Standardkonstruktøren har samme tilgangsmodifikator som klassen:

Når du skriver følgende klasse: offentlig klasse Person ( )

Når du setter inn en standardkonstruktør, vil kompilatoren også indikere den nødvendige tilgangsmodifikatoren: public Preson();

  1. Den første linjen i en konstruktør må kalle enten en overbelastet konstruktør av samme klasse eller en konstruktør av superklassen:

Hvis dette ikke er tilfelle, vil kompilatoren automatisk legge til et kall til superklassekonstruktøren uten argumenter super(); Dette kan resultere i en feil fordi en slik konstruktør kanskje ikke eksisterer i superklassen. Eksempel:

Klasse Foreldre ( Foreldre(int nummer) ( ) ) klasse Barn utvider Foreldre ( Barn() ( ) )

Vil resultere i en feil fordi kompilatoren setter inn et kall til super() i klassekonstruktøren Barn:

Child() (super();// lagt til av kompilatoren)

Imidlertid vil ikke kompilatoren sette inn en standardkonstruktør for klassen Foreldre, fordi Det er allerede andre designere.

For å oppnå formål utover behovene til enkel initialisering, gir klassen spesielle medlemmer - konstruktører(konstruktører). En konstruktør er en blokk med uttrykk som brukes til å initialisere et opprettet objekt. Initialisering skjer inntil den nye operatøren returnerer en objektreferanse til anropsblokken. Konstruktører har samme navn som klassen de er deklarert i. Som vanlige klassemetoder kan konstruktører ta et hvilket som helst antall argumenter (inkludert null), men i motsetning til metoder kan de ikke returnere verdier av noen type. Når du oppretter et objekt av en klasse som inneholder en konstruktør som er deklarert med parametere, blir den nye operatoren etterfulgt av navnet på klassen og en liste over tilsvarende argumenter, omsluttet av parenteser. Konstruktører kalles etter tilordne standardverdier til feltene til det nyopprettede objektet og utføre eksplisitte instruksjoner for initialisering av feltene.

I den utvidede versjonen av Body-klassen, teksten som du ser nedenfor, gjenopprettes objektet til sin opprinnelige tilstand ved å bruke både initialiseringsuttrykk og konstruktøren.

offentlig lang idNUm;

offentlig streng Navn= "<Без имени>";

offentlige kroppsbaner = null;

privat statisk lang nesteID = 0;

idNUM = nesteID++;

En konstruktørerklæring består av klassenavnet, etterfulgt av en (muligens tom) liste med parametere, avgrenset med parenteser, og konstruktørens kropp, en blokk med uttrykk omsluttet av krøllete klammeparenteser. Konstruktører, i likhet med vanlige klassemedlemmer, kan utstyres med de samme tilgangsmodifikatorene, men konstruktører er strengt tatt medlemmer av en spesiell type; imidlertid slike subtile semantiske forskjeller, som regel, bortsett fra i situasjoner der arv spiller inn.

Konstruktøren av Body-klassen er deklarert uten parametere. Formålet er ganske viktig - det sikrer unikheten til idNum-feltverdien til et nyopprettet klasseobjekt. I den opprinnelige versjonen av klassen kan en liten feil fra brukerprogrammereren, for eksempel assosiert med en uforsiktig utført operasjon med å tilordne en verdi til idNum-feltet eller med fraværet av instruksjoner for å øke innholdet i nextID-feltet. føre til at flere kroppsobjekter får samme ordensnummer. Dette resultatet er helt klart feil fordi det bryter med den delen av klassekontrakten som sier:

"IdNum-verdiene til forskjellige klasseobjekter må være unike."

Ved å overføre ansvaret for å velge riktige idNum-verdier til klassen selv, vil vi bli kvitt slike feil en gang for alle. Nå er konstruktøren av Body-klassen den eneste enheten som endrer innholdet i nextID-feltet og trenger tilgang til det. Derfor kan og bør vi utpeke nextID-variabelen som privat for å forhindre at den får tilgang utenfor klassen. Ved å gjøre dette vil vi eliminere en av de potensielle feilkildene som truer fremtidige brukere av klassen vår.

Har en designer Vi står fritt til å velge verdiene som er egnet for å tilordne til idNUm-variabelen. Fremtidige versjoner av klassen kan gi, for eksempel, operasjonen med å bla gjennom en database som inneholder informasjon om himmellegemer kjent for vitenskapen, søke etter det tilsvarende astronomiske objektet ved navn og beregne et nytt serienummer for objektet bare hvis søket mislykkes. En slik endring i klasseteksten vil ikke ha noen innvirkning på den eksisterende applikasjonskoden, siden implementeringsdetaljene er skjult i selve klassens innvoller.

Initialiseringsuttrykkene for variablene navn og baner i kroppen til klasseerklæringen tildeler sistnevnte noen gyldige og passende verdier. Nå, når et objekt er opprettet, får det automatisk et første sett med kontraktstilfredsstillende egenskaper som beskriver dets tilstand. Videre står vi fritt til å endre noen av dem etter eget skjønn:

Kroppssol = new Body(); // idNUM = 0

sol. name = "Sol";

Kroppsjord = ny Kropp(); // idNum = 1

earth.name = "jord";

jord.baner = sol;

Under prosessen med å lage et objekt ved å bruke den nye operatoren, kalles konstruktøren av Body-klassen etter tilordne feltnavnet og ogbitene til initialiseringsuttrykkene gitt av oss.

Saken som er vurdert ovenfor - når vi på forhånd, når programmet skrives, er klar over navnet på det astronomiske objektet og om hvilket himmellegeme dens bane går rundt - er relativt sjelden. Fra dette synspunktet ville det være ganske rimelig å lage en annen konstruktør som tar verdiene til objektnavnet og en referanse til det sentrale objektet som argumenter:

Body(String bodyName, Body orbitsAround) (

navn = bodyName; baner = orbitsAround;

Som du lett kan se, refererer en klassekonstruktør til en annen gjennom dette uttrykket - den første kjørbare instruksjonen i kroppen til den initierende konstruktøren. Et slikt forslag kalles et eksplisitt kall til konstruktøren. Hvis konstruktøren du har tenkt å kalle eksplisitt krever argumenter, må de sendes når de kalles. Hvilken konstruktør som skal kalles, bestemmes av antall argumenter og settet med deres typer. I dette tilfellet betyr dette uttrykket å kalle en parameterløs konstruktør som lar deg angi idNum-verdien til objektet og øke gjeldende innhold i det statiske feltet nextID med én. Å ringe dette gjør det mulig å unngå å gjenta koden for initialisering av idNum og endring av nextID. Nå blir koden for å lage objekter mye enklere:

Kroppssol = new Kropp("sol", null);

Kroppsjord = ny Kropp("jord", sol);

Versjonen av konstruktøren som kalles under kjøringen av den nye operatøren, bestemmes av strukturen til listen over beståtte argumenter.

Hvis et eksplisitt konstruktøranropsuttrykk brukes, må det være den første kjørbare setningen i kroppen til den initierende konstruktøren. Uttrykk som brukes som anropsargumenter må ikke inneholde referanser til felt og metoder for det aktuelle objektet - i alle tilfeller antas det at konstruksjonen av objektet på dette stadiet ennå ikke er fullført.

Av hensyn til logisk fullstendighet, kan vi introdusere en annen enkeltargumentkonstruktør i Body-klassen som brukes til å lage objekter som representerer himmellegemer som ikke er satellitter til større kropper. Denne konstruktøren er praktisk å bruke når du på forhånd vet at verdien av det andre argumentet er null:

Body(String bodyName) (

dette(kroppsnavn, null);

Her bruker vi igjen teknikken med å kalle konstruktøren eksplisitt.

Det er ikke uvanlig at en klassekontrakt krever at koden som er ansvarlig for å lage objekter i den klassen, gir tilleggsinformasjon til klassekonstruktørene. For eksempel kan du, som forfatter av Body-klassen, angi følgende betingelse: "Alle objekter i Body-klassen må ha et navn." For å garantere kjøringen kan du inkludere en navneparameter i parameterlisten til hver klassekonstruktør - nå trenger du ikke bekymre deg for å initialisere navnefeltet.

Nedenfor er flere grunner til bruk av spesialiserte konstruktører.

· Uten hjelp fra konstruktører med parametere, er noen klasser ikke i stand til å gi objektene sine akseptable startverdier.

· Når du bruker ekstra konstruktører, forenkles oppgaven med å bestemme de opprinnelige egenskapene til objekter (et godt eksempel er konstruktøren av Body-klassen med to parametere).

· Objektskaping er ofte forbundet med høye kostnader på grunn av feil valg av initialisatorer. For eksempel, hvis et klasseobjekt inneholder en tabell og de beregningsmessige kostnadene ved å konstruere den er store, ville det være helt uklokt å gi en standard initialisering, vel vitende om at du senere på en eller annen måte må forkaste resultatet av arbeidet og gjenta. initialiseringen for å gi tabellen egenskapene som trengs i en bestemt situasjon. Det er mer tilrådelig å umiddelbart bruke en passende konstruktør som kan lage en tabell med de nødvendige egenskapene.

· Hvis konstruktøren ikke er merket med public-attributtet, er kretsen av fag som kan bruke den til å lage forekomster av klassen begrenset. Du har for eksempel rett til å forhindre programmerere som bruker en pakke fra å lage objekter av klassen ved å gi alle klassekonstruktører tilgang på pakkenivå.

Konstruktører uten parametere er også mye brukt. Hvis du, når du deklarerte en klasse, ikke opprettet en enkelt konstruktør av noe slag, vil kompilatoren automatisk inkludere en tom konstruktør uten parametere i klassen. En lignende konstruktør (kalt standard konstruktør) opprettes bare hvis ingen andre konstruktører eksisterer, siden det er eksempler på klasser (som f.eks. Attr, diskutert i neste kapittel) der bruk av en parameterløs konstruktør er upraktisk eller umulig. Hvis både en parameterløs konstruktør og en eller flere ekstra konstruktører er nødvendig, må den første opprettes eksplisitt. Standardkonstruktøren mottar samme tilgangsattributt som klassen den er opprettet for; hvis klasseerklæringen er utstyrt med den offentlige modifikatoren, så er konstruktøren

vil bli merket som offentlig.

Det er en annen spesiell form for konstruktør - kopi konstruktør, som tar et objekt av gjeldende type som et argument og lager en kopi av det. Vanligvis lagrer en slik konstruktør verdiene til det opprinnelige objektet i feltene til det nye objektet, men noen ganger tvinger klassens semantikk forfatteren til å gi mer sofistikerte operasjoner i kroppen til kopikonstruktøren. Nedenfor er en enkel kopikonstruktør for objekter av typen Body.

Kropp (andre kropp) (

idNum = annet.idNum;

Navn= annet.navn;

baner = andre.baner;

Tilnærmingen til å løse problemet med kopiering av objekter som involverer bruk av spesielle konstruktører er ikke mye brukt i klasser av standard Java-pakker, siden bruk av Clone-metoden anses som mer å foretrekke for å oppnå et slikt mål (se avsnitt 3.9 på side 111). Imidlertid er en enkel kopikonstruktør gitt, for eksempel som en del av String-klassen, og i samlingsklasser (de er omtalt i kapittel 16)

Generiske versjoner av kopikonstruktører støttes, som lar deg initialisere en samling med innholdet i en annen samling (ikke nødvendigvis av samme type). Faktisk krever det samme innsats å lage kopikonstruktører som å skrive en korrekt klonemetode.

Det er tillatt å nevne erklærte unntak i teksten til konstruktører.

Throws-leddet plasseres etter parameterlisten, rett før den åpne krøllete klammeren som markerer begynnelsen av konstruktørkroppen. Hvis en konstruktørerklæring inneholder en throws-klausul, må enhver metode som indirekte får tilgang til konstruktøren når en ny setning kjøres, enten fange opp unntak av disse typene ved å bruke passende catch-klausuler eller liste disse typene i throws-delen av sin egen erklæring.

Hallo! I dag skal vi se på et veldig viktig tema som angår objektene våre. Her kan vi uten å overdrive si at du vil bruke denne kunnskapen hver dag i virkelig arbeid! Vi skal snakke om designere.

Du hører kanskje dette begrepet for første gang, men faktisk har du sannsynligvis brukt konstruktører, men du la bare ikke merke til det selv :) Vi får se dette senere.

Hva er konstruktører og hvorfor trengs de?

La oss se på to eksempler. offentlig klasse bil ( String model; int maxSpeed; public static void main (String args) ( Car bugatti = new Car () ; bugatti. model = "Bugatti Veyron" ; bugatti. maxSpeed ​​​​= 407 ; ) ) Vi har laget bilen vår og installerte for ham modellen og maksimal hastighet. Men i et reelt prosjekt vil Bil-objektet helt klart ha mer enn 2 felt. Og for eksempel 16 felt! offentlig klasse bil ( strengmodell; //modell int maxSpeed; //topphastighet//motorkapasitet//eierens etternavn//antall seter i kabinen StrengesalongMaterial; //interiørmateriale boolsk forsikring; //er hun forsikret?//produsentens land int trunkVolum; //trunkvolum int akselerasjonTil100km; public static void main (String args) ( Car bugatti = new Car () ; bugatti. color = "blue" ; bugatti. accelerationTo100km = 3 ; bugatti. engineVolume = 6.3 ; bugatti. manufacturerCountry = "Italy" ; bugatti. ownerFirstName = " Amigo" ; bugatti. yearOfIssue = 2016 ; bugatti. insurance = true ; bugatti. pris = 2000000 ; bugatti. isNew = false ; bugatti. placesInTheSalon = 2 ; bugatti. maxSpeed ​​​​= 407 =" bugatti; Veyron model " Bugatti. ) ) Vi har opprettet et nytt bilobjekt. Ett problem: Vi har 16 felt, men vi initialiserte bare 12! Prøv nå å bruke koden for å finne de vi har glemt! Ikke så lett, ikke sant? I en slik situasjon kan programmereren lett gjøre en feil og hoppe over initialiseringen av et felt. Som et resultat vil programmets oppførsel bli feil: offentlig klasse bil ( String model; //model int maxSpeed; //topphastighet int hjul; //diskbredde dobbel motorVolum; //motorkapasitet streng farge; //farge int yearOfIssue; //år for utgivelse String ownerFirstName; //eiernavn String eierEtternavn; //eierens etternavn lang pris; //pris boolsk erNy; //ny eller ikke int stederInTheSalon; //antall seter i kabinen StrengesalongMaterial; //interiørmateriale boolsk forsikring; //er hun forsikret? StrengeprodusentLand; //produsentens land int trunkVolum; //trunkvolum int akselerasjonTil100km; //akselerasjon til 100 km/t på sekunder public static void main (String args) ( Car bugatti = new Car () ; bugatti. color = "blue" ; bugatti. accelerationTo100km = 3 ; bugatti. engineVolume = 6.3 ; bugatti. manufacturerCountry = "Italy" ; bugatti. ownerFirstName = " Amigo" ; bugatti. yearOfIssue = 2016 ; bugatti. insurance = true ; bugatti. pris = 2000000 ; bugatti. isNew = false ; bugatti. placesInTheSalon = 2 ; bugatti. maxSpeed ​​​​= 407 =" bugatti; Veyron model " Bugatti. System. out.println( "Modell Bugatti Veyron. Motorstørrelse - "+ bugatti. motorVolum + ", bagasjerom - " + bugatti. trunkVolum + ", interiøret er laget av"+ bugatti. salongMaterial + ", hjulbredde -"+ bugatti. hjul + ". Ble kjøpt i 2018 av Mr. "+ bugatti. eierEtternavn); ) ) Konsollutgang: Bugatti Veyron modell. Motorvolum - 6,3, bagasjerom - 0, interiør laget av null, felgbredde - 0. Ble kjøpt i 2018 av Mr. null Kjøperen din, som betalte 2 millioner dollar for en bil, vil åpenbart ikke like å bli kalt " Mr. null"! Men seriøst, til slutt endte programmet vårt opp med et feilskapt objekt - en bil med felgbredde 0 (det vil si ingen felger i det hele tatt), en manglende bagasjerom, et interiør laget av ukjent materiale, og til og med som tilhører en ukjent . Man kan bare forestille seg hvordan en slik feil kan oppstå mens programmet kjører! Vi må på en eller annen måte unngå slike situasjoner. Vi trenger at programmet vårt har en begrensning: når vi lager et nytt maskinobjekt for det Alltid Modell og maksimal hastighet skal spesifiseres f.eks. Ellers må du ikke tillate objektoppretting. Denne oppgaven er lett å utføre konstruktørfunksjoner. De har fått navnet sitt av en grunn. Konstruktøren lager et slags "skjelett" av klassen, som hvert nytt objekt i klassen må tilsvare. For enkelhets skyld, la oss gå tilbake til en enklere versjon av bilklassen med to felt. Gitt våre krav, vil konstruktøren for bilklassen se slik ut: offentlig bil (strengmodell, int maxSpeed) ( denne . model = modell; denne . maxSpeed ​​​​= maxSpeed; ) Og å lage et objekt ser nå slik ut: offentlig static void main (String args ) ( Car bugatti = new Car ("Bugatti Veyron", 407) ; ) Vennligst merk, hvordan en konstruktør lages. Den ligner på en vanlig metode, men den har ikke en returtype. I dette tilfellet er klassenavnet angitt i konstruktøren, også med stor bokstav. I vårt tilfelle - Bil. I tillegg bruker konstruktøren et nytt nøkkelord dette. "dette" på engelsk betyr "dette, dette". Dette ordet refererer til et bestemt objekt. Koden i konstruktøren: offentlig bil (String model, int maxSpeed) ( denne . model = model; this . maxSpeed ​​​​= maxSpeed; ) kan oversettes nesten bokstavelig: " modell for denne bilen (som vi nå lager) = modellargumentet, som er spesifisert i konstruktør. maxSpeed ​​​​for denne maskinen (som vi lager) = maxSpeed ​​​​argumentet som er spesifisert i konstruktøren." Og så skjedde det: offentlig klasse bil ( String model; int maxSpeed; offentlig bil (String model, int maxSpeed) ( denne . modellen = modell; denne . maxSpeed ​​​​= maxSpeed; ) offentlig statisk tomrom hoved (String args) ( Bil bugatti = ny bil ("Bugatti Veyron", 407 ); System. out. println (bugatti. modell) ; System. out. println (bugatti. maxSpeed) ; ) ) Konsollutgang: Bugatti Veyron 407 Konstruktøren tildelte de nødvendige verdiene. Du har kanskje lagt merke til at en konstruktør er veldig lik en vanlig metode! Sånn er det: en konstruktør er en metode, bare litt spesifikk :) Akkurat som i en metode sendte vi parametere til konstruktøren vår. Og akkurat som et metodekall, vil et konstruktørkall mislykkes hvis de ikke er spesifisert: offentlig klasse Car ( String model; int maxSpeed; public Car (String model, int maxSpeed) ( denne . model = model; this . maxSpeed ​​​​= maxSpeed; ) public static void main (String args) ( Car bugatti = new Car () ; //error! ) ) Se, konstruktøren gjorde det vi prøvde å oppnå. Nå kan du ikke lage en bil uten fart eller uten modell! Likhetene mellom konstruktører og metoder slutter ikke der. Akkurat som metoder, kan konstruktører være det overbelastning. Tenk deg at du har 2 katter hjemme. Du tok en av dem som kattunge, og du tok med den andre hjem fra gaten som voksen, og du vet ikke nøyaktig hvor gammel han er. Dette betyr at programmet vårt skal kunne lage katter av to typer - med navn og alder for den første katten, og kun med navn - for den andre katten. For å gjøre dette vil vi overbelaste konstruktøren: public class Cat ( String name; int age; //for den første katten //for den andre katten public Cat (String name) ( this . name = name; ) public static void main (String args) ( Cat barsik = new Cat ( "Barsik" , 5 ) ; Cat streetCatNamedBob = new Cat ( "Bob" ) ; ) ) K Til den opprinnelige konstruktøren med parameterne "navn" og "alder", la vi til en annen, bare med et navn. Vi overbelastet metoder på samme måte i tidligere leksjoner. Nå kan vi lage begge versjoner av katter :)

Husker du at vi i begynnelsen av forelesningen sa at du allerede hadde brukt konstruktører, men at du bare ikke la merke til det? Dette er sant. Faktum er at hver klasse i Java har en såkalt standard konstruktør. Den har ingen argumenter, men den avfyres hver gang et objekt av en klasse opprettes. public class Cat ( public static void main (String args) ( Cat barsik = new Cat () ; ) ) Ved første øyekast er dette ikke merkbart. Vel, vi laget et objekt og laget det, hvor er designerens arbeid? For å se dette, la oss skrive en tom konstruktør for Cat-klassen med egne hender, og inne i den vil vi skrive ut en setning til konsollen. Hvis det vises, har konstruktøren fungert. public class Cat ( public Cat () ( System. out. println ( "Opprettet en katt!" ); ) public static void main (String args) ( Cat barsik = new Cat () ; //det er her standardkonstruktøren fungerte } } Konsollutgang: De skapte en katt! Her er bekreftelsen! Standardkonstruktøren er alltid usynlig til stede i klassene dine. Men du trenger å vite en funksjon til. Standardkonstruktøren forsvinner fra klassen når du lager en konstruktør med argumenter. Beviset på dette har vi faktisk allerede sett ovenfor. Her i denne koden: public class Cat ( String name; int age; public Cat (String name, int age) ( this . name = name; this . age = age; ) public static void main (String args) ( Cat barsik = new Cat () ; //error! ) ) Vi kunne ikke lage en katt uten navn og alder, fordi vi definerte en konstruktør for Cat: streng + tall. Standardkonstruktøren umiddelbart etter dette forsvant fra klassen. Så husk: hvis du trenger flere konstruktører i klassen din, inkludert tomme, må den opprettes separat. For eksempel lager vi et program for en veterinærklinikk. Klinikken vår ønsker å gjøre gode gjerninger og hjelpe hjemløse katter, som vi ikke vet navn eller alder om. Da skal koden vår se slik ut: public class Cat ( String name; int age; //for huskatter offentlig katt (strengnavn, int alder) ( dette . navn = navn; dette . alder = alder; ) //for gatekatter public Cat () ( ) public static void main (String args) ( Cat barsik = new Cat ("Barsik" , 5 ); Cat streetCat = new Cat () ; ) ) Nå som vi har gjort standardkonstruktøren eksplisitt, har vi kan lage katter av begge typer :) For en konstruktør (som for enhver metode) er rekkefølgen på argumentene veldig viktig. La oss bytte navn og aldersargumenter i konstruktøren vår. offentlig klasse Katt ( Strengnavn; int alder; offentlig Katt (int alder, strengnavn) ( dette . navn = navn; denne . alder = alder; ) offentlig statisk tomrom hoved (String args) ( Katt barsik = ny katt ("Barsik") " , 10 ); //feil! ) ) Feil! Konstruktøren sier tydelig: når du oppretter et Cat-objekt, må det passeres nummer og streng, i den rekkefølgen. Det er derfor koden vår ikke fungerer. Husk å huske dette og ha dette i bakhodet når du lager dine egne klasser: offentlig katt (strengnavn, int alder) ( dette . navn = navn; dette . alder = alder; ) offentlig katt (int alder, strengnavn) ( dette .alder = alder; dette .navn = navn; ) Dette er to helt forskjellige konstruktører! Hvis du uttrykker svaret på spørsmålet i én setning "Hvorfor trenger du en konstruktør?", vi kan si: for å sikre at objekter alltid er i riktig tilstand. Når du bruker konstruktører vil alle variablene dine bli korrekt initialisert, og det vil ikke være biler med hastighet 0 eller andre "feil" objekter i programmet. Bruken av dem er veldig fordelaktig, først og fremst for programmereren selv. Hvis du initialiserer feltene selv, er det stor risiko for å gå glipp av noe og gjøre en feil. Men dette vil ikke skje med en konstruktør: hvis du ikke sendte alle de nødvendige argumentene til den eller blandet typene deres, vil kompilatoren umiddelbart gi en feil. Det er verdt å nevne det separat Du bør ikke legge logikken til programmet ditt inne i konstruktøren. For dette har du til din disposisjon metoder, der du kan beskrive all funksjonaliteten du trenger. La oss se på hvorfor konstruktørlogikk er en dårlig idé: offentlig klasse CarFactory ( String name; int age; int carsCount; public CarFactory (String name, int age, int carsCount) ( dette . navn = navn; dette . alder = alder; dette . carsCount = carsCount; System. out. println ( "Det ble grunnlagt" "I gjennomsnitt produserer hun"+ (this . carsCount/ this . age) + "cars per year" ); ) public static void main (String args) ( CarFactory ford = new CarFactory ( "Ford" , 115 , 50000000 ) ; ) ) Vi har en klasse Bilfabrikk, som beskriver en bilfabrikk. Inne i konstruktøren initialiserer vi alle feltene og plasserer logikken her: vi viser litt informasjon om fabrikken til konsollen. Det ser ut til at det ikke er noe galt med dette, programmet fungerte perfekt. Konsoll utgang: Bilfabrikken vår heter Ford. Den ble grunnlagt for 115 år siden. I løpet av denne tiden har den produsert 50.000.000 biler. I gjennomsnitt produserer den 434.782 biler per år. Men faktisk har vi plantet en tidsinnstilt bombe. Og slik kode kan veldig lett føre til feil. La oss forestille oss at nå snakker vi ikke om Ford, men om den nye fabrikken «Amigo Motors», som har eksistert i mindre enn ett år og har produsert 1000 biler: offentlig klasse CarFactory ( String name; int age; int carsCount; public CarFactory (String name, int age, int carsCount) ( this . name = name; this . age = age; this . carsCount = carsCount; System. out. println ( "Vår bilfabrikk heter"+ dette. Navn); System. ute. println( "Det ble grunnlagt"+ dette. alder + "år siden"); System. ute. println( "I løpet av denne tiden produserte den"+ dette. carsCount + "cars"); System. ute. println( "I gjennomsnitt produserer hun"+ (this . carsCount/ this . age) + "cars per year" ); ) public static void main (String args) ( CarFactory ford = new CarFactory ( "Amigo Motors" , 0 , 1000 ) ; ) ) Konsollutgang: Vår bilfabrikk heter Amigo Motors Exception i tråd "hoved" java.lang.ArithmeticException: / by zero Den ble grunnlagt for 0 år siden I løpet av denne tiden produserte den 1000 biler på CarFactory. (CarFactory.java:15) på CarFactory.main(CarFactory.java:23) Prosessen fullført med utgangskode 1 Vi har kommet! Programmet endte med en merkelig feil. Vil du prøve å gjette hva årsaken er? Årsaken er logikken vi legger inn i konstruktøren. Nærmere bestemt på denne linjen: System. ute. println( "I gjennomsnitt produserer hun"+ (this . carsCount/ this . age) + "cars per year" ); Her gjør vi regnestykket og deler antall produserte biler på alderen på fabrikken. Og siden vår fabrikk er ny (det vil si at den er 0 år gammel), er resultatet divisjon med 0, noe som er forbudt i matematikk. Som et resultat avsluttes programmet med en feil. Hva burde vi ha gjort? Flytt all logikken til en egen metode og kall den for eksempel printFactoryInfo() . Du kan sende det et CarFactory-objekt som en parameter. Du kan også legge all logikken der, og samtidig - behandle mulige feil, som vår med null år. Hver sin smak. Konstruktører er nødvendig for å stille inn tilstanden til et objekt riktig. For forretningslogikk har vi metoder. Du bør ikke blande det ene med det andre. Her er noen nyttige linker der du kan lese mer om konstruktører:

En konstruktør er en struktur som ligner på en metode, hvis formål er å lage en forekomst av en klasse. Egenskaper til designeren:
  • Navnet på konstruktøren må samsvare med navnet på klassen (ved konvensjon er den første bokstaven stor, vanligvis et substantiv);
  • Det er en konstruktør i enhver klasse. Selv om du ikke skriver en, vil Java-kompilatoren lage en standardkonstruktør, som vil være tom og ikke gjøre noe annet enn å kalle superklassekonstruktøren.
  • En konstruktør ligner på en metode, men den er ikke en metode, den regnes ikke engang som et medlem av klassen. Derfor kan den ikke arves eller overstyres i en underklasse;
  • Konstruktører er ikke arvet;
  • Det kan være flere konstruktører i en klasse. I dette tilfellet sies konstruktørene å være overbelastet;
  • Hvis en klasse ikke definerer en konstruktør, legger kompilatoren automatisk til en parameterløs konstruktør til koden;
  • En konstruktør har ikke en returtype, den kan ikke engang være en void-type; hvis en void-type returneres, er den ikke lenger en konstruktør, men en metode, til tross for sammenfallet med klassenavnet.
  • En retursetning er tillatt i en konstruktør, men bare tom, uten noen returverdi;
  • Konstruktøren tillater bruk av tilgangsmodifikatorer; du kan angi en av modifikatorene: offentlig, beskyttet, privat eller uten modifikator.
  • En konstruktør kan ikke ha abstrakte, endelige, native, statiske eller synkroniserte modifikatorer;
  • Dette nøkkelordet refererer til en annen konstruktør i samme klasse. Hvis det brukes, må kallet til det være den første linjen til konstruktøren;
  • Supernøkkelordet kaller konstruktøren til overordnet klasse. Hvis den brukes, må referansen til den være den første linjen i konstruktøren;
  • Hvis konstruktøren ikke foretar et kall til forfaderklassens superkonstruktør (med eller uten argumenter), legger kompilatoren automatisk til kode for å kalle stamfarklassens konstruktør uten argumenter;

Standard konstruktør

Det er en konstruktør i enhver klasse. Selv om du ikke skriver en, vil Java-kompilatoren lage en standardkonstruktør. Denne konstruktøren er tom og gjør ingenting annet enn å kalle superklassekonstruktøren. De. hvis du skriver: public class Eksempel ( ) så tilsvarer dette å skrive: public class Eksempel ( Eksempel () ( super ; ) ) I dette tilfellet er forfedreklassen ikke spesifisert, og som standard arver alle Java-klasser Objektet klasse, så konstruktøren av Object-klassen kalles. Hvis en klasse definerer en konstruktør med parametere, men det er ingen overbelastet konstruktør uten parametere, er det en feil å kalle konstruktøren uten parametere. I Java siden versjon 1.5 er det imidlertid mulig å bruke konstruktører med argumenter med variabel lengde. Og hvis det er en konstruktør som har et argument med variabel lengde, vil det ikke være en feil å kalle standardkonstruktøren. Det vil ikke fordi argumentet for variabel lengde kan være tomt. Følgende eksempel vil for eksempel ikke kompilere, men hvis du fjerner kommentering av konstruktøren med et variabellengde-argument, vil kompileringen og utførelsen være vellykket og resultere i kodelinjen DefaultDemo dd = new DefaultDemo() ; DefaultDemo(int ... v)-konstruktøren vil bli kalt. Naturligvis er det i dette tilfellet nødvendig å bruke JSDK 1.5. Fil DefaultDemo.java class DefaultDemo ( DefaultDemo (String s) ( System. out. print ( "DefaultDemo(String)" ) ; ) /* DefaultDemo(int ... v) ( System.out.println("DefaultDemo(int ...)"); ) */ public static void main (String args) ( DefaultDemo dd = new DefaultDemo () ; ) ) Resultatet av programutdata når konstruktøren er ukommentert: DefaultDemo (int . . . ) Men i vanlig tilfelle når ingen konstruktør er definert i klassen i det hele tatt, vil kallet en standard konstruktør (uten parametere) være nødvendig fordi standard konstruktørerstatning skjer automatisk.

Når du oppretter et objekt, utføres følgende handlinger sekvensielt:
  • Objektklassen søkes blant klassene som allerede er brukt i programmet. Hvis den ikke er der, søkes den i alle kataloger og biblioteker som er tilgjengelige for programmet. Når en klasse er oppdaget i en katalog eller et bibliotek, blir klassens statiske felt opprettet og initialisert. De. For hver klasse initialiseres statiske felt bare én gang.
  • Minne er allokert for objektet.
  • Klassefeltene initialiseres.
  • Klassekonstruktøren kjører.
  • En kobling til det opprettede og initialiserte objektet dannes. Denne referansen er verdien av uttrykket som skaper objektet. Et objekt kan også opprettes ved å kalle newInstance()-metoden til klassen java.lang.Class. I dette tilfellet brukes en konstruktør uten parameterliste.

Overbelastende konstruktører

Konstruktører av samme klasse kan ha samme navn og annen signatur. Denne egenskapen kalles kombinasjon eller overbelastning. Hvis en klasse har flere konstruktører, er konstruktøroverbelastning tilstede.

Parameteriserte konstruktører

Signaturen til en konstruktør er antallet og typene av parametere, samt sekvensen av deres typer i listen over konstruktørparametere. Returtypen er ikke tatt i betraktning. Konstruktøren returnerer ingen parametere. Denne uttalelsen forklarer på en måte hvordan Java skiller mellom overbelastede konstruktører eller metoder. Java skiller overbelastede metoder ikke ved deres returtype, men ved antall, typer og sekvens av typer inndataparametere. En konstruktør kan ikke engang returnere en void-type, ellers vil den bli til en vanlig metode, selv om den ligner på klassenavnet. Følgende eksempel viser dette. Fil VoidDemo.java class VoidDemo ( /** * Dette er en konstruktør */ VoidDemo () ( System. ut. println ( "Konstruktør") ; ) void VoidDemo () ( System. ut. println ( "Metode") ; ) public static void main (String s ) ( VoidDemo m = new VoidDemo () ; ) ) Som et resultat vil programmet gi ut: Konstruktør Dette beviser nok en gang at en konstruktør er en metode uten returparametere. Du kan imidlertid spesifisere en av tre modifikatorer for en konstruktør: offentlig, privat eller beskyttet. Og eksemplet vil nå se slik ut: File VoidDemo2.java class VoidDemo2 ( /** * Dette er en konstruktør */ public VoidDemo2 () ( System. out. println ( "Konstruktør" ) ; ) /** * Og dette er allerede en vanlig metode, selv til tross for likheten med * klassenavnet, siden den har en void-returtype */ private void VoidDemo2 () ( System. out. println ( "Method" ) ; ) public static void main (String s ) ( VoidDemo2 m = new VoidDemo2 () ; ) ) Det er lov å skrive en retursetning i konstruktøren, men bare tom, uten noen returverdi. Fil ReturnDemo.java class ReturnDemo ( /** * Konstruktøren tillater bruk av * return-operatoren uten parametere. */ public ReturnDemo () ( System. out. println ( "Konstruktør" ) ; return ; ) public static void main (String s ) ( ReturnDemo r = new ReturnDemo () ; ) )

Konstruktører parameterisert med argumenter med variabel lengde

Java SDK 1.5 introduserte et etterlengtet verktøy - argumenter med variabel lengde for konstruktører og metoder. Tidligere ble et variabelt antall dokumenter behandlet på to upraktiske måter. Den første av dem ble designet for å sikre at det maksimale antallet argumenter er begrenset til et lite antall og er kjent på forhånd. I dette tilfellet var det mulig å lage overbelastede versjoner av metoden, en for hver versjon av listen over argumenter som ble sendt til metoden. Den andre metoden er designet for noe ukjent på forhånd og et stort antall argumenter. I dette tilfellet ble argumentene plassert i en matrise, og denne matrisen ble sendt til metoden. Argumenter med variabel lengde er oftest involvert i påfølgende manipulasjoner med variable initialiseringer. Det er praktisk å erstatte fraværet av noen av de forventede konstruktør- eller metodeargumentene med standardverdier. Argumentet med variabel lengde er en matrise og behandles som en matrise. For eksempel vil konstruktøren for en klasse Checking med et variabelt antall argumenter se slik ut: klasse Checking ( public Checking (int . . . n) ( ) ) Tegnkombinasjonen... forteller kompilatoren at et variabelt antall argumenter vil bli brukt, og at disse argumentene vil bli lagret i en matrise, hvis referanseverdi er inneholdt i variabelen n. Konstruktøren kan kalles med et annet antall argumenter, inkludert ingen argumenter i det hele tatt. Argumentene plasseres automatisk i en matrise og sendes gjennom n. Hvis det ikke er noen argumenter, er lengden på matrisen 0. Listen over parametere, sammen med variabel lengde-argumenter, kan også inneholde obligatoriske parametere. I dette tilfellet må en parameter som inneholder et variabelt antall argumenter være den siste i listen over parametere. For eksempel: klassekontroll ( offentlig kontroll (String s, int . . . n) ( ) ) En veldig åpenbar begrensning gjelder antall parametere med variabel lengde. Det må bare være én parameter med variabel lengde i parameterlisten. Gitt to parametere med variabel lengde, er det umulig for kompilatoren å bestemme hvor den ene parameteren slutter og den andre begynner. For eksempel: klasse Kontroll ( offentlig kontroll (String s, int . . n, double . . . d) //FEIL! ( ) ) File Checking.java Det finnes for eksempel utstyr som er i stand til å gjenkjenne bilskilt og huske tallene av terrengplasser der besøkte hver av bilene på en dag. Det er nødvendig å velge fra den totale massen av registrerte biler de som i løpet av dagen besøkte to gitte ruter, for eksempel 22 og 15, ifølge områdekartet. Det er ganske naturlig at en bil kan besøke mange torg i løpet av dagen, eller kanskje bare ett. Åpenbart er antall besøkte ruter begrenset av bilens fysiske hastighet. La oss lage et lite program der klassekonstruktøren vil ta som argumenter bilnummeret som en obligatorisk parameter og antall besøkte kvadrater i området, hvor antallet kan være variabelt. Konstruktøren vil sjekke om en bil har dukket opp i to ruter; hvis den har det, så vis nummeret på skjermen.

Sende parametere til konstruktøren

Det er hovedsakelig to typer parametere i programmeringsspråk:
  • grunnleggende typer (primitiver);
  • referanser til objekter.
Begrepet call by value betyr at konstruktøren mottar verdien som sendes til den av den kallende modulen. I motsetning betyr call by reference at konstruktøren mottar adressen til variabelen fra den som ringer. Java bruker kun call by value. Etter parameterverdi og etter parameterkoblingsverdi. Java bruker ikke call by reference for objekter (selv om mange programmerere og forfattere av noen bøker hevder dette). Parametere ved overføring av objekter i Java implementeres ikke fra lenken, A etter objektreferanseverdi! I begge tilfeller mottar konstruktøren kopier av verdiene til alle parametere. Konstruktøren kan ikke gjøre med sine inngangsparametere:
  • konstruktøren kan ikke endre verdiene til inngangsparametrene til hovedtypene (primitive);
  • konstruktøren kan ikke endre inndataparameterreferanser;
  • Konstruktøren kan ikke tilordne inndataparameterreferanser til nye objekter.
Konstruktøren kan gjøre med sine inngangsparametere:
  • endre tilstanden til objektet som sendes som en inngangsparameter.
Følgende eksempel viser at i Java sendes inndataparametere til en konstruktør av objektreferanseverdi. Dette eksemplet reflekterer også at konstruktøren ikke kan endre referanser til inngangsparametere, men faktisk endrer referanser til kopier av inngangsparametere. Fil Empoyee.java class Employee ( Employee (String x, String y) ( String temp = x; x = y; y = temp; ) public static void main ( String args ) ( String name1 = new String ( "Alice" ) ; Streng navn2 = ny streng ("Mary") ; Ansatt a = ny ansatt (navn1, navn2); System. ut. println ("navn1=" + navn1); System. ut. println ("navn2=" + navn2) ; ) ) Utgangen av programmet er: navn1= Alice navn2= Mary Hvis Java brukte kall ved referanse for å sende objekter som parametere, ville konstruktøren byttet navn1 og navn2 i dette eksemplet. Konstruktøren vil faktisk ikke bytte objektreferansene som er lagret i variablene name1 og name2. Dette antyder at konstruktørparameterne initialiseres med kopier av disse referansene. Deretter bytter konstruktøren kopiene. Når konstruktøren er fullført, blir variablene x og y ødelagt, og de opprinnelige variablene navn1 og navn2 fortsetter å referere til de tidligere objektene.

Endring av parametere som sendes til konstruktøren.

Konstruktøren kan ikke endre de beståtte parameterne for grunnleggende typer. Konstruktøren kan imidlertid endre tilstanden til objektet som sendes som en parameter. Tenk for eksempel på følgende program: Fil Lønn1.java klasse Lønn1 ( Lønn1 (int x) ( x = x * 3 ; System. out. println ( "x=" + x) ; ) public static void main ( String args ) ( int verdi = 1000 ; Lønn1 s1 = ny Lønn1 (verdi) ; System. ut. println ("value=" + verdi) ; ) ) Utgangen av programmet er: x= 3000 verdi= 1000 Denne metoden vil selvsagt ikke endre hovedtypeparameteren. Derfor, etter å ha kalt konstruktøren, forblir verdien av variabelverdien lik 1000. Tre ting skjer i hovedsak:
  1. X-variabelen initialiseres med en kopi av verdiparameteren (dvs. tallet 1000).
  2. Verdien av variabelen x er tredoblet - den er nå lik 3000. Verdivariabelen forblir imidlertid satt til 1000 .
  3. Konstruktøren avsluttes og variabelen x brukes ikke lenger.
I det følgende eksempelet tredobles den ansattes lønn vellykket fordi verdien av en objektreferanse overføres som en parameter til metoden. Fil Lønn2.java klasse Lønn2 ( int verdi = 1000 ; Lønn2 () ( ) Lønn2 (Lønn2 x) ( x. verdi = x. verdi * 3 ; ) public static void main (String args) ( Lønn2 s1 = ny Lønn2 () ; Lønn2 s2 = ny Lønn2 (s1) ; System. ut. println ("s1.value="+ s1. verdi); System. ute. println("s2.value="+ s2. verdi); ) ) Utgangen av programmet er: s1. verdi = 3000 s2. verdi= 1000 Verdien til objektreferansen brukes som parameter. Ved utføring av linjen Lønn2 s2 = ny Lønn2(s1) ; konstruktøren av Lønn2(Lønn x) vil bli gitt verdien til objektreferansen til variabelen s1 , og konstruktøren vil faktisk tredoble lønnen for s1.value , siden til og med kopien (Lønn x) opprettet inne i konstruktøren peker til objektet til variabelen s1 .

Konstruktører parametrisert av primitiver.

Hvis parametrene til den overbelastede konstruktøren bruker en primitiv som kan innsnevres (for eksempel int<- double), то вызов метода со суженным значением возможен, несмотря на то, что метода, перегруженного с таким параметром нет. Например: Файл Primitive.java class Primitive { Primitive (double d) { d = d + 10 ; System. out. println (!}"d=" + d) ; } public static void main (String args ) { int i = 20 ; Primitive s1 = new Primitive (i) ; } } Utgangen av programmet er: d= 30,0 Til tross for at Primitive-klassen ikke har en konstruktør som har en parameter av typen int , vil en konstruktør med en inngangsparameter på double fungere. Før konstruktøren kalles, vil variabelen i utvides fra type int til type double . Det motsatte alternativet, når variabelen i ville være av typen double og konstruktøren bare ville ha en int-parameter, ville i denne situasjonen føre til en kompileringsfeil.

Ringer konstruktøren og den nye operatøren

Konstruktøren blir alltid tilkalt med den nye operatøren. Når en konstruktør kalles med den nye operatøren, genererer konstruktøren alltid en referanse til et nytt objekt. Det er umulig å tvinge konstruktøren til å danne en referanse til et allerede eksisterende objekt i stedet for en referanse til et nytt objekt, bortsett fra ved å erstatte objektet som deserialiseres. Og med den nye operatøren, i stedet for en referanse til et nytt objekt, er det umulig å danne en referanse til et allerede eksisterende objekt. For eksempel: Fil Lønn3.java klasse Lønn3 ( int verdi = 1000 ; Lønn3 () ( ) Lønn3 (Lønn3 x) ( x. verdi = x. verdi * 3 ; ) offentlig statisk tomrom main (String args) ( Lønn3 s1 = ny Lønn3 () ; System. ut. println ("Første objektopprettelse: " + s1. verdi) ; Lønn3 s2 = ny Lønn3 (s1) ; System. ut. println ( "Andre objektopprettelse: " + s2. verdi) ; System . out. println (+ s1. verdi) ; Lønn3 s3 = ny Lønn3 (s1) ; System. out. println ("Tredje objektopprettelse: " + s3. verdi) ; System. out. println ( "Hva skjedde med det første objektet?:"+ s1. verdi); ) ) Utgangen av programmet er: Opprettelse av første objekt: 1000 Opprettelse av andre objekt: 1000 Hva skjedde med det første objektet? : 3000 Opprettelse av tredje objekt: 1000 Hva skjedde med det første objektet? : 9000 Først bruker du linjen Lønn3 s1 = ny Lønn3() ; et nytt objekt opprettes. Deretter, hvis du bruker linjen Lønn3 s2 = ny Lønn3(s1) ; eller linjene Lønn3 s3 = ny Lønn3(s1) ; det ville være mulig å lage en referanse til et allerede eksisterende objekt, da vil s1.value s2.value og s3.value lagre samme verdi 1000 . Faktisk, i linjen Lønn3 s2 = ny Lønn3(s1) ; et nytt objekt vil bli opprettet for variabelen s2 og tilstanden til objektet for variabelen s1 vil endres ved å sende referanseverdien til objektet i konstruktørparameteren. Dette kan verifiseres av utdataresultatene. Og når du kjører linjen Lønn3 s3 = ny Lønn3(s1) ; et NYTT objekt vil bli opprettet for variabel s3 og tilstanden til objektet for variabel s1 vil endres igjen.

Konstruktører og initialiseringsblokker, sekvens av handlinger når du kaller en konstruktør

Avsnittet Opprette et objekt og konstruktører viser de generelle handlingene som utføres når du oppretter et objekt. Blant dem er prosessene med å initialisere klassefelt og utarbeide klassekonstruktøren, som igjen har en intern rekkefølge:
  1. Alle datafeltene initialiseres til standardverdiene (0, usann eller null).
  2. Alle feltinitialiserere og initialiseringsblokker utføres i den rekkefølgen de er oppført i klasseerklæringen.
  3. Hvis en annen konstruktør kalles på den første linjen i en konstruktør, blir den kalte konstruktøren utført.
  4. Kroppen til konstruktøren utføres.
Konstruktøren er relatert til initialisering fordi det i Java er tre måter å initialisere et felt i en klasse på:
  • tilordne en verdi i erklæringen;
  • tilordne verdier i initialiseringsblokken;
  • angi verdien i konstruktøren.
Naturligvis må du organisere initialiseringskoden slik at den er lett å forstå. Følgende klasse er gitt som eksempel: klasse Initialisering ( int i; kort z = 10 ; statisk int x; statisk flytende y; statisk ( x = 2000 ; y = 3.141 ; ) Initialisering () ( System. ut. println ( " i= " + i) ; System. ut. println ("z=" + z) ; z = 20 ; System. ut. println ("z=" + z) ; ) ) I eksemplet ovenfor initialiseres variablene i følgende rekkefølge: først De statiske variablene x og y initialiseres til standardverdier. Deretter utføres den statiske initialiseringsblokken. Deretter initialiseres variabelen i til standardverdien og variabelen z initialiseres. Deretter begynner designeren å jobbe. Å kalle klassekonstruktører bør ikke avhenge av rekkefølgen felt er deklarert i. Dette kan føre til feil.

Konstruktører og arv

Konstruktører er ikke arvet. For eksempel: public class Eksempel ( Eksempel () ( ) public void sayHi () ( system. out. println ( "Hi") ; ) ) public class SubClass utvider Eksempel ( ) SubClass-klassen arver automatisk sayHi()-metoden definert i foreldreklassen. Samtidig arves ikke Eksempel()-konstruktøren til den overordnede klassen av dens underklasse.

Dette nøkkelordet i konstruktører

Konstruktører bruker dette for å referere til en annen konstruktør i samme klasse, men med en annen parameterliste. Hvis konstruktøren bruker dette nøkkelordet, må det være på den første linjen; ignorering av denne regelen vil resultere i en kompilatorfeil. For eksempel: File ThisDemo.java public class ThisDemo ( String name; ThisDemo (String s) ( name = s; System. out. println (name) ; ) ThisDemo () ( this ("John") ; ) public static void main ( String args ) ( ThisDemo td1 = new ThisDemo ("Mary") ; ThisDemo td2 = new ThisDemo () ; ) ) Utgangen av programmet er: Mary John Dette eksemplet har to konstruktører. Den første mottar et strengargument. Den andre mottar ingen argumenter, den kaller bare den første konstruktøren ved å bruke standardnavnet "John". Dermed kan du bruke konstruktører til å initialisere feltverdier eksplisitt og som standard, noe som ofte er nødvendig i programmer.

Supernøkkelordet i konstruktører

Konstruktører bruker super for å kalle superklassens konstruktør. Hvis konstruktøren bruker super , må dette kallet være på den første linjen, ellers vil kompilatoren gi en feil. Nedenfor er et eksempel: Fil SuperClassDemo.java public class SuperClassDemo ( SuperClassDemo () ( ) ) class Child utvider SuperClassDemo ( Child () ( super () ; ) ) I dette enkle eksemplet inneholder Child()-konstruktøren et kall til super( ) som oppretter en forekomstklasse SuperClassDemo, i tillegg til klasse Child. Fordi super må være den første setningen som kjøres i en underklasses konstruktør, er denne rekkefølgen alltid den samme og avhenger ikke av om super() brukes. Hvis den ikke brukes, vil standardkonstruktøren (ingen parametere) for hver superklasse, som starter med basisklassen, kjøres først. Følgende program viser når konstruktører kjøres. Call.java-filen //Lag superklasse A klasse A ( A () ( System. ut. println ( "Inne i en konstruktør." ) ; ) ) //Lag en underklasse B som utvider klasse A klasse B utvider A ( B () ( System. out. println ( "Inside B-konstruktør." ) ; ) ) //Lag en klasse (C) som utvider klasse B klasse C utvider B ( C () ( System. ut. println ( "Inside C constructor." ) ; ) ) klasse Call ( public static void main (String args ) ( C c = new C () ; ) ) Utgang fra dette programmet: Inne i en konstruktør. Innvendig B-konstruktør. Inne i C-konstruktør. Konstruktører kalles opp i rekkefølge etter klasseunderordning. Dette gir en viss mening. Siden superklassen ikke har kunnskap om noen underklasse, er enhver initialisering den må utføre separat. Hvis mulig, bør den gå foran enhver initialisering utført av underklassen. Derfor bør det gjøres først.

Tilpassbare konstruktører

Kjøreter et av de kraftige kjerneprinsippene i Java-språket som implementerer polymorfisme. En slik mekanisme beskytter imidlertid ikke utvikleren mot støping av inkompatibel type i noen tilfeller. Det vanligste tilfellet er manipulering av en gruppe objekter, hvis ulike typer er ukjente på forhånd og bestemmes under kjøring. Siden feil knyttet til typeinkompatibilitet bare kan vises på kjøretidsstadiet, gjør dette dem vanskelige å finne og eliminere. Innføringen av tilpassede typer i Java 2 5.0 flytter noen av disse feilene fra kjøretid til kompileringstid og gir noe av den manglende typen sikkerhet. Det er ikke behov for eksplisitt typestøping når du flytter fra en objekttype til en konkret type. Det bør huskes at typetilpasningsverktøy kun fungerer med objekter og ikke gjelder for primitive datatyper som ligger utenfor klassearvetreet. Med tilpassede typer utføres alle rollebesetninger automatisk og bak kulissene. Dette lar deg beskytte mot typefeil og gjenbruke kode mye oftere. Egendefinerte typer kan brukes i konstruktører. Konstruktører kan tilpasses selv om klassen deres ikke er en tilpasset type. For eksempel: klasse GenConstructor ( privat dobbel val;< T extends Number >GenConstructor (T arg) ( val = arg. doubleValue () ; ) void printValue () ( System. out. println ("val: " + val) ; ) ) klasse GenConstructorDemo ( public static void main (String args) ( GenConstructor gc1 = new GenConstructor(100); GenConstructor gc2 = new GenConstructor(123.5F); gc1. printValue(); gc2. printValue(); )) Fordi GenConstructor-konstruktøren spesifiserer en egendefinert typeparameter som må være en avledet klasse fra Number-klassen , den kan ringe fra hvilken som helst

Faktum er at:

1. Hvis du oppretter en klasse og definerer en konstruktør med argumenter i (klassen AClass, som kun har en konstruktør som aksepterer int i), vil ikke lenger kompilatoren lage en standard konstruktør. Fordi det ville bryte klassekontrakten til AClass, som ikke kan initialiseres uten argumenter. Hvis du også vil ha en standardkonstruktør, sett den nå eksplisitt.

Ellers ville det ikke vært noen måte å forhindre opprettelsen av en standardkonstruktør, noe som ville være dårlig.

2. Når du oppretter konstruktører for en BC-klasse som arver fra en annen klasse, krever kompilatoren at den første linjen i konstruktøren er et kall til en annen konstruktør (arvet eller i den klassen).

Hvorfor? Fordi når du arver fra en klasse, vil du gjenbruke logikken. Konstruktøren bringer klasseforekomsten til en initial integraltilstand. I ditt tilfelle krever AClass et argument for å initialisere, uten hvilket JVM ikke vet hvordan man initialiserer en forekomst av klassen.

Hvis en klasse ikke har noen konstruktører definert, så prøver den å lage en standardkonstruktør, dvs. uten argumenter:

Offentlig klasse ACklasse1 ( )

Siden det ikke er definert noen eksplisitte konstruktører og klassen ikke arver fra andre klasser, oppretter kompilatoren en standardkonstruktør.

Dette tilsvarer denne definisjonen:

Offentlig klasse AClass1 ( offentlig AClass1() ( ) )

La oss nå se på BClass1:

Offentlig klasse BClass1 utvider AClass1 ( )

Også her er ingen konstruktører eksplisitt definert, og kompilatoren prøver å lage en standard konstruktør. Siden AClass1 har en standardkonstruktør, vil den lage en standardkonstruktør som kaller AClass1s konstruktør. Denne koden tilsvarer dette:

Offentlig klasse BClass1 utvider AClass1 ( offentlig BClass1() ( super(); ) )

I ditt tilfelle opprettes en klasse UTEN en standardkonstruktør:

Offentlig AKlasse ( offentlig AKlasse(int i) ( ) )

Siden (minst én) konstruktør er beskrevet her, opprettes ikke lenger standardkonstruktøren. Det vil si at denne koden ikke lenger vil kompilere:

AClasse a = new AClass(); // virker ikke

trenger noe sånt

AClasse a = ny AClasse(1);

Følgelig vil enhver BClass-konstruktør kreve et kall til en AClass- eller BClass-konstruktør. Med denne beskrivelsen vil kompilatoren klage:

Offentlig BClass utvider AKlasse ( )

Fordi det vil være et forsøk på å kalle standardkonstruktøren til AClass, som ikke er definert:

Offentlig BClass utvider AClass ( offentlig BClass() ( super(); // feil; klasse AClass har ingen slik konstruktør) )

Det er imidlertid mulig å lage en BClass med en standardkonstruktør ved å sette AClass-konstruktøren til en verdi:

Offentlig klasse BClass utvider AClass ( offentlig BClass() ( super(1); ) )

Dette vil kompilere.