Programmering

Den ultimative superklasse, del 1

Erfarne Java-udviklere tager ofte for givet Java-funktioner, som nybegyndere finder forvirrende. For eksempel kan en nybegynder være forvirret over Objekt klasse. Dette indlæg lancerer en tredelt serie, hvor jeg præsenterer og besvarer spørgsmål om Objekt og dens metoder.

King Object

Spørgsmål: Hvad er Objekt klasse?

EN: Det Objekt klasse, der er gemt i java.lang pakke, er den ultimative superklasse for alle Java-klasser (undtagen Objekt). Arrays strækker sig også Objekt. Grænseflader udvides dog ikke Objekt, som er påpeget i afsnit 9.6.3.4 i Java Language Specification: ... overvej det, mens en grænseflade ikke har Objekt som en supertype ....

Objekt erklærer følgende metoder, som jeg vil diskutere fuldt ud senere i dette indlæg og i resten af ​​denne serie:

  • beskyttet objektklon ()
  • boolske lig (Objekt obj)
  • beskyttet tomrum afsluttes ()
  • Klasse getClass ()
  • int hashCode ()
  • ugyldig meddelelse ()
  • ugyldig notifyAll ()
  • String toString ()
  • ugyldig ventetid ()
  • ugyldig ventetid (lang timeout)
  • ugyldig ventetid (lang timeout, int nanoer)

En Java-klasse arver disse metoder og kan tilsidesætte enhver metode, der ikke er deklareret endelig. For eksempel er den ikke-endeligtoString () Metoden kan tilsidesættes, mens endeligvente() metoder kan ikke tilsidesættes.

Spørgsmål: Kan jeg eksplicit udvide Objekt klasse?

EN: Ja, du kan eksplicit udvide Objekt. Se f.eks. Listing 1.

Notering 1. Eksplicit udvides Objekt

import java.lang.Object; offentlig klasse Medarbejder udvider objekt {privat strengnavn; offentlig ansat (strengnavn) {this.name = navn; } public String getName () {return name; } offentlig statisk ugyldig hoved (String [] args) {Medarbejder emp = ny medarbejder ("John Doe"); System.out.println (emp.getName ()); }}

Du kan sammensætte liste 1 (javac Medarbejder.java) og kør det resulterende Medarbejderklasse fil (java Medarbejder), og du vil observere John Doe som output.

Fordi kompilatoren automatisk importerer typer fra java.lang pakke, den import java.lang.Object; Erklæring er unødvendig. Java tvinger dig heller ikke til eksplicit at udvide Objekt. Hvis det gjorde det, ville du ikke være i stand til at udvide andre klasser end Objekt fordi Java begrænser klasseudvidelse til en enkelt klasse. Derfor vil du typisk udvide Objekt implicit som vist i liste 2.

Notering 2. Implicit udvides Objekt

offentlig klassemedarbejder {privat strengnavn; offentlig ansat (strengnavn) {this.name = navn; } public String getName () {return name; } offentlig statisk ugyldig hoved (String [] args) {Medarbejder emp = ny medarbejder ("John Doe"); System.out.println (emp.getName ()); }}

Som i Listing 1, Listing 2's Medarbejder klasse udvides Objekt og arver dens metoder.

Kloning af genstande

Spørgsmål: Hvad gør det klon () metode udrette?

EN: Det klon () metoden opretter og returnerer en kopi af det objekt, som denne metode kaldes på.

Spørgsmål: Hvordan fungerer det klon () metode arbejde?

EN:Objekt redskaber klon () som en indfødt metode, hvilket betyder, at dens kode er gemt i et oprindeligt bibliotek. Når denne kode udføres, kontrollerer den klassen (eller en superklasse) af det påkaldende objekt for at se, om den implementerer java.lang.Cloneable grænseflade - Objekt implementerer ikke Klonabel. Hvis denne grænseflade ikke er implementeret, klon () kaster java.lang.CloneNotSupportedException, som er en afkrydset undtagelse (den skal håndteres eller videresendes til metodeopkaldsstakken ved at tilføje et kasteklausul til overskriften på den metode, klon () blev påberåbt). Hvis denne grænseflade er implementeret, klon () tildeler et nyt objekt og kopierer det kaldende objekts feltværdier til det nye objekts ækvivalente felter og returnerer en reference til det nye objekt.

Spørgsmål: Hvordan påberåber jeg mig klon () metode til at klone et objekt?

EN: Givet en objektreference, påberåbe sig klon () på denne reference og kast det returnerede objekt fra Objekt til den type genstand, der klones. Liste 3 viser et eksempel.

Liste 3. Kloning af et objekt

offentlig klasse CloneDemo implementerer Cloneable {int x; offentlig statisk ugyldig hoved (String [] args) kaster CloneNotSupportedException {CloneDemo cd = new CloneDemo (); cd.x = 5; System.out.printf ("cd.x =% d% n", cd.x); CloneDemo cd2 = (CloneDemo) cd.clone (); System.out.printf ("cd2.x =% d% n", cd2.x); }}

Liste 3 erklærer en CloneDemo klasse, der implementerer Klonabel interface. Denne grænseflade skal implementeres eller en påkaldelse af Objekt's klon () metode vil resultere i et kast CloneNotSupportedException eksempel.

CloneDemo erklærer en singel int-baseret instansfelt navngivet x og en hoved () metode, der udøver denne klasse. hoved () erklæres med en kasteklausul, der passerer CloneNotSupportedException op metoden opkald stakken.

hoved () første øjeblikkelige CloneDemo og initialiserer den resulterende instans kopi af x til 5. Derefter outputter instansens x værdi og påberåber sig klon () i dette tilfælde, at kaste det returnerede objekt til CloneDemo inden du gemmer dens reference. Endelig udsender det klonens x feltværdi.

Kompilering Listing 3 (javac CloneDemo.java) og kør applikationen (java CloneDemo). Du skal overholde følgende output:

cd.x = 5 cd2.x = 5

Spørgsmål: Hvorfor skal jeg tilsidesætte klon () metode?

EN: Det forrige eksempel behøvede ikke at tilsidesætte klon () metode, fordi den kode, der påberåber sig klon () er placeret i klassen, der klones (dvs. CloneDemo klasse). Men hvis den klon () påkaldelse er placeret i en anden klasse, skal du tilsidesætte klon (). Ellers modtager du en "klon har beskyttet adgang i Objekt"besked fordi klon () erklæres beskyttet. Listing 4 præsenterer en refactored Listing 3 for at demonstrere tilsidesættelse klon ().

Fortegnelse 4. Kloning af et objekt fra en anden klasse

klasse Data implementerer Cloneable {int x; @Override public Object-klon () kaster CloneNotSupportedException {returner super.clone (); }} public class CloneDemo {public static void main (String [] args) throw CloneNotSupportedException {Data data = new Data (); data.x = 5; System.out.printf ("data.x =% d% n", data.x); Data data2 = (Data) data.clone (); System.out.printf ("data2.x =% d% n", data2.x); }}

Fortegnelse 4 erklærer en Data klasse, hvis forekomster skal klones. Denne klasse implementerer Klonabel interface for at forhindre CloneNotSupportedException fra at blive kastet, når klon () metode kaldes, erklærer int-baseret instansfelt xog tilsidesætter klon () metode. Denne metode udføres super.clone () at påberåbe sig sin superklasse (Objekti dette eksempel) klon () metode. Den altoverskyggende klon () metode identificerer CloneNotSupportedException i sit kast klausul.

Liste 4 erklærer også a CloneDemo klasse, der instantierer Data, initialiserer sit forekomsterfelt, udsender værdien af ​​denne forekomsts forekomsterfelt, kloner Data forekomst og outputter denne instanss eksempelfeltværdi.

Kompilere lister 4 (javac CloneDemo.java) og kør applikationen (java CloneDemo). Du skal overholde følgende output:

data.x = 5 data2.x = 5

Spørgsmål: Hvad er lav kloning?

EN:Lav kloning (også kendt som lav kopiering) er duplikering af et objekts felter uden at duplikere nogen objekter, der henvises til fra objektets referencefelter (hvis det har nogen). Liste 3 og 4 viser lav kloning. Hver af de cd-, cd2-, data- og data2-henviste felter identificerer et objekt, der har sin egen kopi af int-baseret x Mark.

Lav kloning fungerer godt, når alle felter er af primitiv type, og (i mange tilfælde), når der henvises til referencefelter uforanderlig (uforanderlige) objekter. Men hvis nogen refererede objekter er mutable, kan ændringer foretaget på et af disse objekter ses af det originale objekt og dets (e) klon (er). Listing 5 præsenterer en demonstration.

Fortegnelse 5. Demonstration af problemet med lav kloning i en referencefeltkontekst

klasse Medarbejder implementerer Klonbar {privat strengnavn; privat int alder privat adresse adresse Medarbejder (strengnavn, alder, adresseadresse) {dette.navn = navn; this.age = alder; denne.adresse = adresse; } @ Override public Object-klon () kaster CloneNotSupportedException {returner super.clone (); } Adresse getAddress () {returadresse; } String getName () {return name; } int getAge () {return age; }} klasse Adresse {privat strengby; Adresse (strengby) {this.city = by; } String getCity () {return city; } ugyldig setCity (strengby) {this.city = by; }} offentlig klasse CloneDemo {offentlig statisk ugyldig hoved (String [] args) kaster CloneNotSupportedException {Medarbejder e = ny medarbejder ("John Doe", 49, ny adresse ("Denver")); System.out.printf ("% s:% d:% s% n", e.getName (), e.getAge (), e.getAddress (). GetCity ()); Medarbejder e2 = (Medarbejder) e.clone (); System.out.printf ("% s:% d:% s% n", e2.getName (), e2.getAge (), e2.getAddress (). GetCity ()); e.getAddress (). setCity ("Chicago"); System.out.printf ("% s:% d:% s% n", e.getName (), e.getAge (), e.getAddress (). GetCity ()); System.out.printf ("% s:% d:% s% n", e2.getName (), e2.getAge (), e2.getAddress (). GetCity ()); }}

Notering 5 gaver Medarbejder, Adresseog CloneDemo klasser. Medarbejder erklærer navn, alderog adresse felter; og er klonbar. Adresse erklærer en adresse bestående af en by og dens forekomster kan ændres. CloneDemo driver applikationen.

CloneDemo's hoved () metode skaber en Medarbejder objekt og kloner dette objekt. Derefter ændres byens navn i originalen Medarbejder objekt adresse Mark. Fordi begge Medarbejder objekter refererer til det samme Adresse objekt, den ændrede by ses af begge objekter.

Kompilere oversigt 5 (javac CloneDemo.java) og kør denne applikation (java CloneDemo). Du skal overholde følgende output:

John Doe: 49: Denver John Doe: 49: Denver John Doe: 49: Chicago John Doe: 49: Chicago

Spørgsmål: Hvad er dyb kloning?

EN:Dyb kloning (også kendt som dyb kopiering) er duplikering af et objekts felter, således at eventuelle refererede objekter duplikeres. Derudover duplikeres deres refererede objekter - og så videre. For eksempel, Listing 6 refactors Listing 5 for at udnytte dyb kloning. Det demonstrerer også kovariante returtyper og en mere fleksibel måde at klone på.

Liste 6. Dybt kloning af adresse Mark

klasse Medarbejder implementerer Klonbar {privat strengnavn; privat int alder privat adresse adresse Medarbejder (strengnavn, alder, adresseadresse) {dette.navn = navn; this.age = alder; denne.adresse = adresse; } @ Override offentlig medarbejderklon () kaster CloneNotSupportedException {Medarbejder e = (Medarbejder) super.clone (); e.adresse = adresse.klon (); returnere e; } Adresse getAddress () {returadresse; } String getName () {return name; } int getAge () {return age; }} klasse Adresse {privat strengby; Adresse (strengby) {this.city = by; } @ Override public Address klon () {returner ny adresse (ny streng (by)); } String getCity () {return city; } ugyldig setCity (strengby) {this.city = by; }} offentlig klasse CloneDemo {offentlig statisk ugyldig hoved (String [] args) kaster CloneNotSupportedException {Medarbejder e = ny medarbejder ("John Doe", 49, ny adresse ("Denver")); System.out.printf ("% s:% d:% s% n", e.getName (), e.getAge (), e.getAddress (). GetCity ()); Medarbejder e2 = (Medarbejder) e.clone (); System.out.printf ("% s:% d:% s% n", e2.getName (), e2.getAge (), e2.getAddress (). GetCity ()); e.getAddress (). setCity ("Chicago"); System.out.printf ("% s:% d:% s% n", e.getName (), e.getAge (), e.getAddress (). GetCity ()); System.out.printf ("% s:% d:% s% n", e2.getName (), e2.getAge (), e2.getAddress (). GetCity ()); }}

Fortegnelse 6 udnytter Java's understøttelse af covariante returtyper for at ændre returneringstypen af Medarbejderer overordnet klon () metode fra Objekt til Medarbejder. Fordelen er, at kode uden for Medarbejder kan klone en Medarbejder objekt uden at skulle kaste dette objekt til Medarbejder type.

Medarbejder's klon () metode påberåber sig først super.clone (), som lavt kopierer navn, alderog adresse felter. Det påberåber sig derefter klon () på den adresse felt for at lave en kopi af den refererede Adresse objekt.

Det Adresse klasse tilsidesætter klon () metode og afslører et par forskelle fra tidligere klasser, der tilsidesætter denne metode:

  • Adresse implementerer ikke Klonabel. Det er ikke nødvendigt, fordi kun Objekt's klon () metoden kræver, at en klasse implementerer denne grænseflade, og dette klon () metode kaldes ikke.
  • Den altoverskyggende klon () metoden kaster ikke CloneNotSupportedException. Denne afkrydsede undtagelse kastes kun fra Objekt's klon () metode, som ikke kaldes. Derfor behøver undtagelsen ikke at blive håndteret eller videregivet metoden-opkaldstakken via en kasteklausul.
  • Objekt's klon () metode kaldes ikke (der er ingen super.clone () opkald) fordi overfladisk kopiering ikke er påkrævet for Adresse klasse - der er kun et enkelt felt at kopiere.

At klone Adresse objekt, er det tilstrækkeligt at oprette et nyt Adresse objekt og initialiser det til en duplikat af det objekt, der henvises til fra by Mark. Den nye Adresse genstand returneres derefter.