Προγραμματισμός

Το απόλυτο superclass, Μέρος 1

Οι έμπειροι προγραμματιστές Java συχνά θεωρούν δεδομένες δυνατότητες Java που οι νεοεισερχόμενοι βρίσκουν σύγχυση. Για παράδειγμα, ένας αρχάριος μπορεί να μπερδευτεί για το Αντικείμενο τάξη. Αυτή η ανάρτηση ξεκινά μια σειρά τριών μερών στην οποία παρουσιάζω και απαντώ σε ερωτήσεις σχετικά Αντικείμενο και τις μεθόδους του.

Αντικείμενο Βασιλιά

Ε: Τι είναι το Αντικείμενο τάξη?

ΕΝΑ: ο Αντικείμενο τάξη, η οποία αποθηκεύεται στο java.lang πακέτο, είναι το απόλυτο superclass όλων των κατηγοριών Java (εκτός από το Αντικείμενο). Επίσης, οι συστοιχίες εκτείνονται Αντικείμενο. Ωστόσο, οι διεπαφές δεν επεκτείνονται Αντικείμενο, που επισημαίνεται στην Ενότητα 9.6.3.4 της προδιαγραφής γλώσσας Java: ... σκεφτείτε ότι ενώ δεν υπάρχει διεπαφή Αντικείμενο ως supertype ....

Αντικείμενο δηλώνει τις ακόλουθες μεθόδους, τις οποίες θα συζητήσω πλήρως αργότερα σε αυτήν την ανάρτηση και στην υπόλοιπη σειρά:

  • προστατευμένος κλώνος αντικειμένου ()
  • boolean ισούται με (αντικείμενο obj)
  • προστατευμένο κενό οριστικοποιείται ()
  • Κατηγορία getClass ()
  • int hashCode ()
  • άκυρη ειδοποίηση ()
  • άκυρη ειδοποίησηΌλα ()
  • String toString ()
  • άκυρη αναμονή ()
  • άκυρη αναμονή (μεγάλο χρονικό όριο)
  • άκυρη αναμονή (μεγάλο χρονικό όριο, int nanos)

Μια κλάση Java κληρονομεί αυτές τις μεθόδους και μπορεί να παρακάμψει οποιαδήποτε μέθοδο που δεν έχει δηλωθεί τελικός. Για παράδειγμα, οι μητελικόςtoString () μέθοδος μπορεί να παρακαμφθεί, ενώ το τελικόςΠερίμενε() οι μέθοδοι δεν μπορούν να παρακαμφθούν.

Ε: Μπορώ να επεκτείνω ρητά το Αντικείμενο τάξη?

ΕΝΑ: Ναι, μπορείτε να επεκτείνετε ρητά Αντικείμενο. Για παράδειγμα, δείτε την Καταχώριση 1.

Λίστα 1. Εκτείνεται ρητά Αντικείμενο

εισαγωγή java.lang.Object; δημόσια τάξη Ο υπάλληλος επεκτείνει το αντικείμενο {private String name; δημόσιος υπάλληλος (όνομα συμβολοσειράς) {this.name = name; } δημόσια συμβολοσειρά getName () {return name; } public static void main (String [] args) {Employee emp = new Employee ("John Doe"); System.out.println (emp.getName ()); }}

Μπορείτε να μεταγλωττίσετε την καταχώριση 1 (javac Employee.java) και εκτελέστε το αποτέλεσμα Employee.class αρχείο (java Υπάλληλος), και θα παρατηρήσετε Τζον Ντο ως έξοδος.

Επειδή ο μεταγλωττιστής εισάγει αυτόματα τύπους από το java.lang πακέτο, το εισαγωγή java.lang.Object; η δήλωση δεν είναι απαραίτητη. Επίσης, η Java δεν σας αναγκάζει να επεκτείνετε ρητά Αντικείμενο. Αν το έκανε, δεν θα μπορούσατε να επεκτείνετε άλλες τάξεις εκτός από Αντικείμενο επειδή η Java περιορίζει την επέκταση κλάσης σε μία κλάση. Επομένως, συνήθως θα επεκτείνατε Αντικείμενο σιωπηρά, όπως καταδεικνύεται στη Λίστα 2.

Λίστα 2. Επέκταση σιωπηρά Αντικείμενο

Δημόσιος υπάλληλος κατηγορίας {private String name; δημόσιος υπάλληλος (όνομα συμβολοσειράς) {this.name = name; } δημόσια συμβολοσειρά getName () {return name; } public static void main (String [] args) {Employee emp = new Employee ("John Doe"); System.out.println (emp.getName ()); }}

Όπως στην καταχώριση 1, στην καταχώριση 2's Υπάλληλος η τάξη επεκτείνεται Αντικείμενο και κληρονομεί τις μεθόδους του.

Αντικείμενα κλωνοποίησης

Ε: Τι κάνει το κλώνος () μέθοδος ολοκληρώσει;

ΕΝΑ: ο κλώνος () Η μέθοδος δημιουργεί και επιστρέφει ένα αντίγραφο του αντικειμένου στο οποίο καλείται αυτή η μέθοδος.

Ε: Πώς το κάνει κλώνος () μέθοδος εργασίας;

ΕΝΑ:Αντικείμενο υλοποιεί κλώνος () ως εγγενής μέθοδος, που σημαίνει ότι ο κώδικάς του αποθηκεύεται σε μια εγγενή βιβλιοθήκη. Όταν αυτός ο κώδικας εκτελείται, ελέγχει την κλάση (ή μια υπερκλάση) του αντικειμένου επίκλησης για να δει εάν εφαρμόζει το java.lang.Cloneable διεπαφή -- Αντικείμενο δεν εφαρμόζει Κλωνοποιήσιμο. Εάν αυτή η διεπαφή δεν εφαρμόζεται, κλώνος () ρίχνει java.lang.CloneNotSupportedException, η οποία είναι μια ελεγχόμενη εξαίρεση (πρέπει να αντιμετωπιστεί ή να περάσει τη στοίβα μεθόδου-κλήσης, προσαρτώντας μια ρήτρα ρίψης στην κεφαλίδα της μεθόδου στην οποία κλώνος () επικαλέστηκε). Εάν εφαρμοστεί αυτή η διεπαφή, κλώνος () εκχωρεί ένα νέο αντικείμενο και αντιγράφει τις τιμές πεδίου του καλώντας αντικειμένου στα αντίστοιχα πεδία του νέου αντικειμένου και επιστρέφει μια αναφορά στο νέο αντικείμενο.

Ε: Πώς μπορώ να επικαλεστώ το κλώνος () μέθοδος κλωνοποίησης ενός αντικειμένου;

ΕΝΑ: Με δεδομένη αναφορά αντικειμένου, επικαλέστε κλώνος () σε αυτήν την αναφορά και ρίξτε το επιστρεφόμενο αντικείμενο από Αντικείμενο στον τύπο του αντικειμένου που θα κλωνοποιηθεί. Η λίστα 3 παρουσιάζει ένα παράδειγμα.

Λίστα 3. Κλωνοποίηση ενός αντικειμένου

δημόσια τάξη CloneDemo υλοποιεί το Cloneable {int x; public static void main (String [] args) ρίχνει το CloneNotSupportedException {CloneDemo cd = νέο 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); }}

Η λίστα 3 δηλώνει α CloneDemo τάξη που εφαρμόζει το Κλωνοποιήσιμο διεπαφή. Αυτή η διεπαφή πρέπει να εφαρμοστεί ή να γίνει επίκληση του Αντικείμενο'μικρό κλώνος () μέθοδος θα οδηγήσει σε ρίψη CloneNotSupportedException παράδειγμα.

CloneDemo δηλώνει ένα intμε βάση το πεδίο παρουσίας με όνομα Χ και ένα κύριος() μέθοδος που ασκεί αυτήν την τάξη. κύριος() δηλώνεται με ρήτρα ρίψεων που περνά CloneNotSupportedException επάνω στη στοίβα μεθόδου-κλήσης.

κύριος() πρώτοι CloneDemo και αρχικοποιεί το αντίγραφο του στιγμιότυπου που προκύπτει Χ προς την 5. Στη συνέχεια εξάγει την παρουσία Χ αξία και επικαλείται κλώνος () σε αυτήν την περίπτωση, μεταδίδοντας το αντικείμενο που επιστράφηκε CloneDemo πριν αποθηκεύσετε την αναφορά του. Τέλος, εξάγει τον κλώνο Χ τιμή πεδίου.

Συλλογή καταχώρισης 3 (javac CloneDemo.java) και εκτελέστε την εφαρμογή (java CloneDemo). Πρέπει να παρατηρήσετε την ακόλουθη έξοδο:

cd.x = 5 cd2.x = 5

Ε: Γιατί θα πρέπει να παρακάμψω το κλώνος () μέθοδος?

ΕΝΑ: Το προηγούμενο παράδειγμα δεν χρειάστηκε να παρακάμψει το κλώνος () μέθοδος επειδή ο κώδικας που επικαλείται κλώνος () βρίσκεται στην κλάση που κλωνοποιείται (δηλαδή, το CloneDemo τάξη). Ωστόσο, εάν το κλώνος () επίκληση βρίσκεται σε διαφορετική τάξη, θα πρέπει να παρακάμψετε κλώνος (). Διαφορετικά, θα λάβετε ένα "Το clone έχει προστατευμένη πρόσβαση στο Object"μήνυμα επειδή κλώνος () δηλώνεται προστατευμένο. Η Λίστα 4 παρουσιάζει μια αναθεωρημένη Καταχώριση 3 για να αποδειχθεί υπερισχύουσα κλώνος ().

Λίστα 4. Κλωνοποίηση ενός αντικειμένου από άλλη τάξη

class class εφαρμόζει Cloneable {int x; Το @Override public Object clone () ρίχνει το CloneNotSupportedException {return super.clone (); }} δημόσια τάξη CloneDemo {public static void main (String [] args) ρίχνει το 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); }}

Η λίστα 4 δηλώνει α Δεδομένα τάξη των οποίων οι παρουσίες πρόκειται να κλωνοποιηθούν. Αυτή η τάξη εφαρμόζει το Κλωνοποιήσιμο διεπαφή για την πρόληψη CloneNotSupportedException από τη ρίψη όταν το κλώνος () καλείται μέθοδος, δηλώνει int- βασισμένο πεδίο παρουσίας Χ, και παρακάμπτει το κλώνος () μέθοδος. Αυτή η μέθοδος εκτελείται super.clone () να επικαλεστεί το superclass του (Αντικείμενοσε αυτό το παράδειγμα) κλώνος () μέθοδος. Το υπερισχύον κλώνος () μέθοδος προσδιορίζει CloneNotSupportedException στην ρήτρα του ρίχνει.

Η λίστα 4 δηλώνει επίσης α CloneDemo τάξη που δημιουργεί Δεδομένα, αρχικοποιεί το πεδίο εμφάνισης, εξάγει την τιμή του πεδίου εμφάνισης αυτής της παρουσίας, κλωνοποιεί το Δεδομένα instance και εξάγει την τιμή πεδίου παρουσίας αυτής της παρουσίας.

Συλλογή καταχώρισης 4 (javac CloneDemo.java) και εκτελέστε την εφαρμογή (java CloneDemo). Πρέπει να παρατηρήσετε την ακόλουθη έξοδο:

data.x = 5 data2.x = 5

Ε: Τι είναι η ρηχή κλωνοποίηση;

ΕΝΑ:Ρηχή κλωνοποίηση (επίσης γνωστός ως ρηχή αντιγραφή) είναι η επανάληψη των πεδίων ενός αντικειμένου χωρίς να αντιγράφετε αντικείμενα που αναφέρονται από τα πεδία αναφοράς του αντικειμένου (εάν έχει). Οι λίστες 3 και 4 δείχνουν ρηχή κλωνοποίηση. Κάθε ένα από CD-, cd2-, δεδομένα-, και δεδομένα2-αναφερόμενα πεδία προσδιορίζει ένα αντικείμενο που έχει το δικό του αντίγραφο του int-με βάση Χ πεδίο.

Η ρηχή κλωνοποίηση λειτουργεί καλά όταν όλα τα πεδία είναι πρωτόγονου τύπου και (σε ​​πολλές περιπτώσεις) όταν αναφέρονται τα πεδία αναφοράς αμετάβλητος (αμετάβλητα) αντικείμενα. Ωστόσο, εάν οποιαδήποτε αντικείμενα που αναφέρονται είναι μεταβλητά, οι αλλαγές που γίνονται σε οποιοδήποτε από αυτά τα αντικείμενα μπορούν να φανούν από το αρχικό αντικείμενο και τον κλώνο του. Η λίστα 5 παρουσιάζει μια επίδειξη.

Λίστα 5. Επίδειξη του προβλήματος με ρηχή κλωνοποίηση σε ένα πεδίο αναφοράς

Ο υπάλληλος της τάξης εφαρμόζει το Cloneable {private String name; ιδιωτική ηλικία ιδιωτική διεύθυνση Διεύθυνση; Υπάλληλος (Όνομα συμβολοσειράς, int ηλικία, Διεύθυνση διεύθυνσης) {this.name = name; this.age = ηλικία; this.address = διεύθυνση; } Το @Override public Object clone () ρίχνει το CloneNotSupportedException {return super.clone (); } Διεύθυνση getAddress () {διεύθυνση επιστροφής; } String getName () {όνομα επιστροφής; } int getAge () {ηλικία επιστροφής; }} διεύθυνση τάξης {ιδιωτική πόλη String; Διεύθυνση (String city) {this.city = city; } String getCity () {επιστροφή πόλης; } void setCity (String city) {this.city = city; }} δημόσια τάξη CloneDemo {public static void main (String [] args) ρίχνει το CloneNotSupportedException {Employee e = new Employee ("John Doe", 49, νέα διεύθυνση ("Denver")); System.out.printf ("% s:% d:% s% n", e.getName (), e.getAge (), e.getAddress (). GetCity ()); Υπάλληλος e2 = (Υπάλληλος) e.clone (); System.out.printf ("% s:% d:% s% n", e2.getName (), e2.getAge (), e2.getAddress (). GetCity ()); e.getAddress (). setCity ("Σικάγο"); 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 ()); }}

Η λίστα 5 παρουσιάζει Υπάλληλος, Διεύθυνση, και CloneDemo τάξεις. Υπάλληλος δηλώνει όνομα, ηλικία, και διεύθυνση πεδία και είναι κλωνοποιήσιμο. Διεύθυνση δηλώνει μια διεύθυνση που αποτελείται από μια πόλη και οι παρουσίες της είναι μεταβλητές. CloneDemo οδηγεί την εφαρμογή.

CloneDemo'μικρό κύριος() η μέθοδος δημιουργεί ένα Υπάλληλος αντικείμενο και κλωνοποιεί αυτό το αντικείμενο. Στη συνέχεια αλλάζει το όνομα της πόλης στο πρωτότυπο Υπάλληλος αντικείμενο διεύθυνση πεδίο. Επειδή και τα δύο Υπάλληλος τα αντικείμενα αναφέρονται στο ίδιο Διεύθυνση αντικείμενο, η αλλαγμένη πόλη φαίνεται και από τα δύο αντικείμενα.

Συλλογή καταχώρισης 5 (javac CloneDemo.java) και εκτελέστε αυτήν την εφαρμογή (java CloneDemo). Πρέπει να παρατηρήσετε την ακόλουθη έξοδο:

John Doe: 49: Denver John Doe: 49: Denver John Doe: 49: Chicago John Doe: 49: Σικάγο

Ε: Τι είναι η βαθιά κλωνοποίηση;

ΕΝΑ:Βαθιά κλωνοποίηση (επίσης γνωστός ως βαθιά αντιγραφή) είναι η αναπαραγωγή των πεδίων ενός αντικειμένου έτσι ώστε να αντιγράφονται τυχόν αντικείμενα που αναφέρονται. Επιπλέον, τα αντικείμενα αναφοράς τους είναι διπλά - και ούτω καθεξής. Για παράδειγμα, η λίστα 6 των αντιδραστήρων της λίστας 5 για την αξιοποίηση της βαθιάς κλωνοποίησης. Δείχνει επίσης τύπους επιστροφής συνδιακύμανσης και έναν πιο ευέλικτο τρόπο κλωνοποίησης.

Λίστα 6. Βαθιά κλωνοποίηση του διεύθυνση πεδίο

Ο υπάλληλος της τάξης εφαρμόζει το Cloneable {private String name; ιδιωτική ηλικία ιδιωτική διεύθυνση Διεύθυνση; Υπάλληλος (Όνομα συμβολοσειράς, int ηλικία, Διεύθυνση διεύθυνσης) {this.name = name; this.age = ηλικία; this.address = διεύθυνση; } @Override δημόσιος υπάλληλος κλώνος () ρίχνει CloneNotSupportedException {Employee e = (Employee) super.clone (); e.address = address.clone (); επιστροφή e; } Διεύθυνση getAddress () {διεύθυνση επιστροφής; } String getName () {όνομα επιστροφής; } int getAge () {ηλικία επιστροφής; }} διεύθυνση τάξης {ιδιωτική πόλη String; Διεύθυνση (String city) {this.city = city; } @Override public Διεύθυνση κλώνος () {return new Address (new String (city)); } String getCity () {επιστροφή πόλης; } void setCity (String city) {this.city = city; }} δημόσια τάξη CloneDemo {public static void main (String [] args) ρίχνει το CloneNotSupportedException {Employee e = new Employee ("John Doe", 49, νέα διεύθυνση ("Denver")); System.out.printf ("% s:% d:% s% n", e.getName (), e.getAge (), e.getAddress (). GetCity ()); Υπάλληλος e2 = (Υπάλληλος) e.clone (); System.out.printf ("% s:% d:% s% n", e2.getName (), e2.getAge (), e2.getAddress (). GetCity ()); e.getAddress (). setCity ("Σικάγο"); 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 ()); }}

Η λίστα 6 αξιοποιεί την υποστήριξη της Java για συντελεστές επιστροφής για να αλλάξει τον τύπο επιστροφής του Υπάλληλοςυπερισχύει κλώνος () μέθοδο από Αντικείμενο προς την Υπάλληλος. Το πλεονέκτημα είναι ότι ο κωδικός είναι εξωτερικός Υπάλληλος μπορεί να κλωνοποιήσει ένα Υπάλληλος αντικείμενο χωρίς να πρέπει να ρίξετε αυτό το αντικείμενο στο Υπάλληλος τύπος.

Υπάλληλος'μικρό κλώνος () η μέθοδος επικαλείται πρώτα super.clone (), το οποίο αντιγράφει ρηχά το όνομα, ηλικία, και διεύθυνση πεδία. Στη συνέχεια επικαλείται κλώνος () στο διεύθυνση πεδίο για να δημιουργήσετε ένα αντίγραφο του αναφερόμενου Διεύθυνση αντικείμενο.

ο Διεύθυνση η τάξη παρακάμπτει το κλώνος () μέθοδος και αποκαλύπτει μερικές διαφορές από προηγούμενες τάξεις που παρακάμπτουν αυτήν τη μέθοδο:

  • Διεύθυνση δεν εφαρμόζει Κλωνοποιήσιμο. Δεν είναι απαραίτητο γιατί μόνο Αντικείμενο'μικρό κλώνος () μέθοδος απαιτεί μια κλάση να εφαρμόσει αυτήν τη διεπαφή, και αυτό κλώνος () δεν καλείται μέθοδος.
  • Το υπερισχύον κλώνος () η μέθοδος δεν ρίχνει CloneNotSupportedException. Αυτή η ελεγμένη εξαίρεση απορρίπτεται μόνο από Αντικείμενο'μικρό κλώνος () μέθοδος, η οποία δεν καλείται. Επομένως, η εξαίρεση δεν χρειάζεται να χειριστεί ή να μεταβιβάσει τη στοίβα κλήσης μεθόδου μέσω ενός όρου ρίψης.
  • Αντικείμενο'μικρό κλώνος () η μέθοδος δεν καλείται (δεν υπάρχει super.clone () κλήση) επειδή δεν απαιτείται ρηχή αντιγραφή για το Διεύθυνση τάξη - υπάρχει μόνο ένα πεδίο για αντιγραφή.

Για να κλωνοποιήσετε το Διεύθυνση αντικείμενο, αρκεί να δημιουργήσετε ένα νέο Διεύθυνση αντικείμενο και αρχικοποιήστε το σε ένα αντίγραφο του αντικειμένου που αναφέρεται από το πόλη πεδίο. Το νέο Διεύθυνση το αντικείμενο επιστρέφεται.