Java Hellenic User Group logo

Αρχική Σελίδα
Αγορά Συζήτησης
Κείμενα/Βοηθήματα

Valid CSS!

Pass by reference και Pass by Value

Εισαγωγή

Όπως είδαμε σε προηγούμενα κείμενα οι μεταβλητές αποτελούνε ένα αναπόσπαστο κομμάτι, όχι μόνο της Java αλλά όλων των γλωσσών προγραμματισμού. Αυτές οι μεταβλητές συνήθως περνάνε από μέθοδο σε μέθοδο και από αντικείμενο σε αντικείμενο ώστε να μπορέσει ένα πρόγραμμα να εκπληρώσει το σκοπό του. Υπάρχουνε δύο τρόποι που οι μεταβλητές μπορούνε να διέρχονται μεταξύ των μεθόδων και αντικειμένων. Μεσω της «τιμής τους» (by-value) ή «της αναφοράς τους» (by reference).

Ορισμός

Πρωτού αναλύσουμε αν οι μεταβλητές στη Java περνούνται by value ή by reference ας δούμε πρώτα τι σημαίνουνε οι προτάσεις «pass-by-value» και «pass-by-reference».Με τον όρο «pass-by-value» εννοούμε ότι περνιέται ένα αντίγραφο της μεταβλητής μεταξύ των μεθόδων. Δηλαδή η τιμή της μεταβλητής αντιγράφεται και το αντίγραφό της διέρχεται μεταξύ των καλεσμάτων των μεθόδων. Οποιαδήποτε αλλαγή στο αντίγραφο της μεταβλητής δεν έχει αντίκτυπο στην αρχική μεταβλητή.Με τον όρο «pass-by-reference» εννούμε ότι περνιέται η μεταβλητή αυτή καθεαυτή. Οποιαδήποτε μεταβολή στη μεταβλητή έχει αντίκτυπο σε οποιοδήποτε άλλο μέρος χρησιμοποιείται αυτή η μεταβλητή.

Pass by value

Στη Java όπως ξέρουμε υπάρχουνε δύο ειδών μεταβλητές, οι μεταβλητές πρωτογενή τύπου και οι μεταβλητές τύπου αντικειμένου (αναφορές). Και τα δύο είδη μεταβλητών περνούνται by-value (αντέγραψε την τιμή και πέρασε το αντίγραφο). Και με τους πρωτογενείς τύπους και με τις αναφορές παίρνουμε ένα αντίγραφο του περιεχομένου. Είναι ένα συνηθισμένο λάθος να νομίζεται ότι οι μεταβλητές πρωτογενή τύπου περνούνται by-value ενώ οι αναφορές περνούνται by-reference.

Για να το καταλάβουμε καλύτερα ας ρίξουμε μία ματιά στο παρακάτω παράδειγμα.

int number = 10;
System.out.println(number);
incrementNumber(number);
System.out.println(number);

public void incrementNumber(int anotherNumber){
     anotherNumber++;
     System.out.println(anotherNumber);
}

Στο παραπάνω παράδειγμα περνάμε στη μέθοδο incrementNumber ένα αντίγραφο της τιμής της μεταβλητής number. Οποιαδήποτε αλλαγή γίνει στη μεταβλητή anotherNumber μέσα στη μέθοδο δε θα έχει αντίκτυπο στη μεταβλητή number έξω από τη μέθοδο. Η εικονική μηχανή περνάει ένα αντίγραφο της τιμής και οποιαδήποτε αλλαγή γίνει στο αντίγραφο δε θα επηρεάσει την αρχική τιμή. Έτσι το αποτέλεσμα, πριν και μετά καλέσουμε τη μέθοδο incrementNumber θα είναι 10 και 10, ενώ μέσα στη μέθοδο η μεταβλητή anotherNumber παίρνει την τιμή 11. Βλέπουμε ότι αν και αυξήσαμε τη μεταβλητή anotherNumber δεν επηρρέασε τη μεταβλητή number διότι άλλαξε μόνο το αντίγραφο της number.

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

StringBuffer sb1 = new StringBuffer("hello");
System.out.println(sb1.toString());
sayHelloWorld(sb1);
System.out.println(sb1);

public void sayHelloWorld(StringBuffer buffer){
     buffer.append("world");
     System.out.println(buffer.toString());
}

Για να καταλάβουμε πλήρως το παραπάνω παράδειγμα ας κοιτάξουμε στην παρακάτω απεικόνιση όπου sb1 και buffer είναι αναφορές τύπου StringBuffer που και οι δύο αναφέρονται στο ίδιο αντικείμενο στη μνήμη το οποίο περιέχει την τιμή  'hello'. H sb1 είναι η αρχική αναφορά ενώ η buffer είναι το αντίγραφό της.

image1


Όπως είπαμε μέσα στη μέθοδο έχει περάσει ένα αντίγραφο της αναφοράς sb1. Λόγω του ότι και τα δύο αντίγραφα αναφέρονται στο ίδιο αντικείμενο, οποιαδήποτε αλλαγή κάνει η αναφορά buffer στο αντικείμενο αυτό θα έχει αντίκτυπο και στην αρχική μεταβληή (sb1). Η μεταβλητή buffer αλλάζει το αντικείμενο με το να προσθέσει την λέξη  ' world', άρα η παραπάνω απεικόνιση γίνεται:

image 2

Οπότε το αποτέλεσμα του προγράμματος θα είναι:

hello

hello world

hello world

αφού και η αρχική μεταβλητή και το αντίγραφό της αναφέρονται στο ίδιο αντικείμενο. Οποιαδήποτε αλλαγή κάνει το αντίγραφο της μεταβλητής θα έχει αντίκτυπο και στην αρχική μεταβλητή. Το αντίθετο βέβαια δε συμβαίνει. Μπορούμε να αλλάξουμε το αντικείενο στο οποίο οι μεταβλητές sb1 και buffer αναφέρονται, αλλά δεν κάνουμε τίποτα στις μεταβλητές τις ίδιες. Αν όμως χειριστούμε τη μεταβλητή buffer και κάνουμε κάτι σαν:


public void sayHelloWorld(StringBuffer buffer){

buffer =  new StringBuffer("hey");

     buffer.append("world");

     System.out.println(buffer.toString());

}


τότε το αποτέλεσμα θα είναι:

hello

hey world

hello

Αλλάξαμε τη μεταβλητή buffer αλλά αυτό δεν επηρρέασε τη μεταβλητή sb1 διότι η buffer είναι ένα αντίγραφο της sb1. Και το αντίθετο φυσικά ισχύει. Οποιαδήποτε αλλαγή στη μεταβλητή sb1 δεν έχει αντίκτυπο στη μεταβλητή buffer.



Aποστολόπουλος Πάρις
Κωνσταντινίδης Πάνος


  Get Java Now!