Προγραµµατισµός ΙΙ Java 2 Προχωρηµένα Θέµατα Πακέτα Τις κλάσεις που κατασκευάζουµε µπορούµε να τις οργανώνουµε σε πακέτα εν «κουβαλάµε» µια-µια τις κλάσεις που επιθυµούµε αλλά ένα συµπαγές αρχείο, το πακέτο Σε ένα πακέτο βέβαια προτείνεται να οργανώνονται οµοειδείς κλάσεις π.χ. όλες τις κλάσεις που µπορεί να έχουµε κατασκευάσει για επεξεργασία εικόνας τις τοποθετούµε σε ένα πακέτο π.χ. image.processing 1
Πακέτα Έστω πως έχουµε 2 κλάσεις: class Hello static void printhello() System.out.println("Hello!"); class MyName static void printmyname() System.out.println("My name is Dimitris"); Πακέτα Μέχρι τώρα γνωρίζαµε πως για να τις χρησιµοποιήσουµε σε ένα εκτελέσιµο πρόγραµµα αρκεί να βρίσκονται στον κατάλογο που βρίσκεται και το εκτελέσιµο π.χ. class MyExe1 public static void main(string a[]) Hello.printHello(); MyName.printMyName(); 2
Πακέτα Πώς θα µπορούσαν να «πακεταριστούν» οι κλάσεις Hello και MyName σε ένα πακέτο? Αν ήταν πακεταρισµένες πως θα καλούνταν από µια άλλη κλάση όπως η MyExe1? Πακέτα Βήµα 1: Κατασκευάζω έναν νέο φάκελο στο δίσκο µε το όνοµα του πακέτου π.χ. φάκελος dimitris Βήµα 2: Αν επιθυµώ κατασκευάζω υποφακέλους µέσα στον πρώτο φάκελο για να κατατάξω τις κλάσεις σε οµάδες π.χ. υποφάκελος dimitris\proth και υποφάκελος dimitris\deyterh 3
Πακέτα Βήµα 3: Τοποθετώ κάθε κλάση στον υποφάκελο που ανήκει π.χ. η κλάση Hello γράφεται στον υποφάκελο dimitris/proth η κλάση MyName γράφεται στον υποφάκελο dimitris/deyterh Βήµα 4: Τροποποιώ το Hello.java και το MyName.java ώς εξής: Προσθέτω πάνω από τη γραµµή class Hello τη φράση package dimitris.proth; Προσθέτω πάνω από τη γραµµή class Myname τη φράση package dimitris.deyterh; Προσθέτω τη λέξη public πρίν από τη λέξη class και πρίν από τους ορισµούς των µεθόδων printhello και printmyname Πακέτα Έστω πως έχουµε 2 κλάσεις: package dimitris.proth; public class Hello public static void printhello() System.out.println("Hello!"); package dimitris.deyterh; public class MyName public static void printmyname() System.out.println("My name is Dimitris"); 4
Επίπεδα Ελέγχου Προσπέλασης Γιατί χρειάζεται το public Πακέτα Βήµα 5: Τροποποιώ το MyExe1 Προσθέτω πρίν από τη γραµµή class MyExe1 τις φράσεις: import dimitris.proth.hello; ή dimitris.proth.*; import dimitris.deyterh.myname; ή dimitris.deyterh.*; Έτσι έχουµε το MyExe1 γίνεται: import dimitris.proth.hello; import dimitris.deyterh.myname; class MyExe1 public static void main(string a[]) Hello.printHello(); MyName.printMyName(); 5
Πακέτα Ένα ή περισσότερα πακέτα µπορούν να οµαδοποιηθούν σε αρχεία.jar π.χ. jar cvf dimitris.jar dimitris Έτσι αντί για το φάκελο dimitris τώρα µπορεί ισοδύναµα να χρησιµοποιείται το dimitris.jar Παράδειγµα πολλών πακέτων σε ένα.jar αρχείο αποτελεί το γνωστό lib\tools.jar. Η αποσυµπίεση ενός.jar αρχείου γίνεται ως εξής: jar xvf dimitris.jar Εσωτερικές Κλάσεις Η Java δίδει τη δυνατότητα να οριστούν κλάσεις µέσα σε κλάσεις Οι κλάσεις που είναι ορισµένες στο εσωτερικό άλλων κλάσεων ονοµάζονται εσωτερικές (inner classes) Προσοχή! η εσωτερική δεν σηµαίνει υποκλάση Οι εσωτερικές κλάσεις µπορούν να έχουν πρόσβαση µόνο στις ιδιότητες και τις µεθόδους της εξωτερικής της κλάσης ή των εξωτερικών της κλάσεων Είναι αόρατες σε άλλες κλάσεις που ορίζονται εκτός της φιλοξενούσας κλάσης εν επιτρέπονται ορισµοί static εντός των εσωτερικών κλάσεων Χρησιµοποιούνται κυρίως για µικρές και συγκεκριµένες εργασίες 6
Εσωτερικές Κλάσεις import java.awt.*; public class Inner extends java.applet.applet Button b1 = new Button("One"); BlueButton b2 = new BlueButton("Two"); public void init() add(b1); add(b2); class BlueButton extends Button BlueButton(String label) super(label); this.setbackground(color.blue); // κατασκευάζουµε ένα µπλέ // κουµπί ειδικά για αυτό το // applet δηλ. την κλάση Inner Υποκλάσεις Mια κλάση µπορεί να έχει πολλές υποκλάσεις Μια υποκλάση κληρονοµεί τις ιδιότητες και τις µεθόδους της υπερκλάσης της Πώς κατασκευάζονται οι υποκλάσεις? Χρησιµοποιώντας τη λέξη extends π.χ. µια οποιαδήποτε µικροεφαρµογή κατασκευάζεται ως υποκλάση της κλάσης Applet του πακέτου java.applet public class MyApplet extends java.applet.applet 7
Υποκλάσεις class Human String name = "Unknown"; int age = 0; void sayname() System.out.println(name); class Programmer extends Human int languages = 0; // Γλώσσες που γνωρίζει void sayhowmanylanguages() System.out.println(languages); Υποκλάσεις Ο Programmer που είναι υποκλάση της κλάσης Human κληρονόµησε τελικά όλες τις ιδιότητες και τις µεθόδους του Human class Executable public static void main(string a[]) Programmer Babis = new Programmer(); Babis.name = "Babis"; Babis.age=20; Babis.languages=2; Babis.sayName(); Babis.sayHowManyLanguages(); 8
Υπερκλάσεις Η κλάση Programmer είναι υποκλάση της κλάσης Human Η κλάση Human όµως είναι υπερκλάση της κλάσης Programmer Με παρόµοιο τρόπο, µια κλάση που είναι υποκλάση κάποιας υπερκλάσης π.χ. η Programmer της Human, µπορεί και αυτή να έχει πολλές άλλες υποκλάσεις κ.ο.κ. Όµως µια κλάση π.χ. η Programmer µπορεί να έχει µόνο µια υπερκλάση π.χ. τη Human Εποµένως, µια κλάση µπορεί να κληρονοµήσει τις ιδιότητες και τις µεθόδους µόνο µιας υπερκλάσης. Αυτό είναι γνωστό ως πρόβληµα µονής κληρονοµικότητας Οι λέξεις this και super class Programmer extends Human int languages = 0; void sayhowmanylanguages() System.out.println(languages); void test_this(int languages, int age) System.out.println(languages); System.out.println(this.languages); System.out.println(age); System.out.println(super.age); super.sayname(); // τυπώνει το κοντινότερο languages // τυπώνει το languages του // τρέχοντος αντικειµένου // τυπώνει το κοντινότερο age // τυπώνει το age του // τρέχοντος αντικειµένου // καλεί τη µέθοδο sayname της // υπερκλάσης της Programmer 9
Αφηρηµένες Κλάσεις Αφηρηµένες κλάσεις (abstract classes) είναι µια κατηγορία κλάσεων που δεν επιτρέπουν τη δηµιουργία αντικειµένων τους Επιτρέπεται να έχουν υποκλάσεις Επιτρέπεται να δηµιουργούνται αντικείµενα των υποκλάσεών τους Οι υποκλάσεις τους κληρονοµούν κανονικά τις ιδιότητες και τις µεθόδους τους Οι µέθοδοί τους θεωρούνται ως προεπιλεγµένες για τις υποκλάσεις τους Οι µέθοδοί τους µπορούν να υποκατασταθούν από µεθόδους των υποκλάσεών τους που έχουν τον ίδιο ορισµό (δεν αρκεί να έχουν το ίδιο όνοµα) υπέρβαση Αφηρηµένες Μέθοδοι Αφηρηµένες µέθοδοι (abstract methods) είναι µια κατηγορία µεθόδων χωρίς όµως σώµα δηλ. χωρίς περιεχόµενα Το περιεχόµενο των αφηρηµένων µεθόδων ορίζεται στις υποκλάσεις της κλάσης που έχουν οριστεί: Άρα σε κάθε υποκλάση η αφηρηµένη µέθοδος µπορει να υλοποιειται µε διαφορετικό κώδικα!!! Οι αφηρηµένες κλάσεις και µέθοδοι χρησιµοποιούνται κυρίως ως πρότυπα (templates) τα οποία βέβαια υπερβαίνονται από κανονικές µεθόδους στις υποκλάσεις Παραδείγµατα µπορείτε να εξερευνήσετε στη βοήθεια αλλά και στον πηγαίο κώδικα των κλάσεων της ίδιας της Java (src.zip) 10
Αφηρηµένες Κλάσεις & Μέθοδοι public abstract class Animal private String name; public Animal(String nm) name=nm; public String getname() return (name); // κανονική µέθοδος public abstract void speak(); // αφαιρετική µέθοδος δεν έχει υναµική ιασύνδεση Μεθόδων public class AnimalReference public static void main(string[] args) Animal ref; Cow acow = new Cow("Bossy"); Dog adog = new Dog("Rover"); Snake asnake = new Snake("Earnie"); // υναµική διασύνδεση µεθόδων (Dynamic method binding) // Αυτόµατα ανιχνεύεται ποιά speak() από ποιά υποκλάση ζητείται ref = acow; ref.speak(); // καλείται η speak() της υποκλάσης Cow ref = adog; ref.speak(); // καλείται η speak() της υποκλάσης Dog ref = asnake; ref.speak(); // καλείται η speak() της υποκλάσης Snake 11
Πολυµορφισµός Η έννοια πολυµορφισµός αναφέρεται κυρίως στα εξής χαρακτηριστικά του αντικειµενοστραφούς προγραµµατισµού που έχουν µελετηθεί: Υπερφόρτωση (µεθόδων): Μια κλάση µπορεί να περιλαµβάνει πολλές µεθόδους µε το ίδιο όνοµα αλλά διαφορετικά ορίσµατα Υπέρβαση (µεθόδων): Μια υποκλάση µπορεί να φέρει µεθόδους που ορίζονται µε τον ίδιο τρόπο µε µεθόδους της υπερκλάσης της. Αφηρηµένες (µέθοδοι): ιαφορετικές υποκλάσεις µιας υπερκλάσης µπορεί να φέρουν µεθόδους που υλοποιούνται µε διαφορετικό τρόπο Τελικές Μέθοδοι & Κλάσεις Ηλέξη final µπορεί να χρησιµοποιηθεί όχι µόνο για σταθεροποιήσει µεταβλητές: Μια final µεταβλητή είναι σταθερά π.χ. final int MAX=10 Μια final µέθοδος δεν υποκαθίσταται από άλλη µέθοδο υποκλάσης, δηλ. δεν υπερβαίνεται π.χ. final void test(int x) Μια final κλάση δεν επιτρέπεται να έχει υποκλάσεις π.χ. final class test 12
Πίνακες Υποκλάσεων public class AnimalArray public static void main(string[] args) Animal[] ref = new Animal[3]; Cow acow = new Cow("Bossy"); Dog adog = new Dog("Rover"); Snake asnake = new Snake("Earnie"); ref[0] = acow; ref[1] = adog; ref[2] = asnake; for (int x=0; x<3; ++x) ref[x].speak(); ιασυνδέσεις Το πρόβληµα της µονής κληρονοµικότητας αντιµετωπίζεται µε τις διασυνδέσεις (interfaces) Οι διασυνδέσεις είναι παρόµοιες µε τις αφηρηµένες κλάσεις αλλά µε τις εξής διαφορές: Αντί για τη λέξη class χρησιµοποιείται η λέξη interface Όλες οι µέθοδοι που περιέχουν είναι οπωσδήποτε αφηρηµένες (abstract) Όλες οι ιδιότητες που περιέχουν είναι οπωσδήποτε στατικές και σταθερές (static final) Για να δείξουµε πως µια κλάση κληρονοµεί τις ιδιότητες και τις µεθόδους µιας ή περισσότερων διασυνδέσεων προσθέτουµε τη λέξη implements στον ορισµό της π.χ. class test implements interface1, interface2, interface3 13
ιασυνδέσεις public interface Working public void work(); // Ορίζεται η διασύνδεση // Η work είναι abstract αλλά δε χρειάζεται // να το γράψουµε αφού οι διασυνδέσεις // περιλαµβάνουν εξ ορισµού abstract κλάσεις public class WorkingDog extends Dog implements Working public WorkingDog(String nm) // απλά µια µέθοδος δηµιουργός super(nm); // δηµιουργεί αντικείµενο της κλάσης Dog public void work() // εδώ υλοποιείται η work παύει να είναι abstract speak(); System.out.println( ε µπορώ πια να βόσκω αγελάδες και πρόβατα!"); Αρχεία (επανάληψη) Στην ενότητα των αρχείων µελετήθηκαν: Αρχεία δεδοµένων τύπου byte FileInputStream rf = new FileInputStream( όνοµα αρχείου ); FileOutputStream wf = new FileOutputStream( όνοµα αρχείου ); rf.read()/wf.write() Αρχεία δεδοµένων τύπου όχι-byte DataInputStream drf = new DataInputStream(rf); DataOutputStream dwf = new DataOutputStream(wf); drf.readint(), drf.readdouble() / dwf.writeint(), dwf.writedouble() κλπ Αρχεία κειµένου FileReader rtf = new FileReader( όνοµα αρχείου ); FileWriter wtf = new FileWriter( όνοµα αρχείου ); rtf.read()/wtf.write(), rtf.readline() 14
Αρχεία (επανάληψη) Οι κλάσεις που αφορούν τα αρχεία βρίσκονται στο πακέτο java.io Η εγγραφή / ανάγνωση αρχείων πραγµατοποιείται σειριακά µέσω ροών δεδοµένων (streams) πρός και από το δίσκο Για την αποφυγή αλλεπάλληλων προσπελάσεων του δίσκου επιπρόσθετα των παραπάνω κλάσεων χρησιµοποιείται ενδιάµεση µνήµη (buffer) µέσω των BufferInputStream brf = new BufferInputStream(rf); BufferOutputStream brf = new BufferOutputStream(rf); BufferedReader brtf = new BufferedReader(rtf); BufferedWriter brtf = new BufferedWriter(rtf); Αρχεία Αντικειµένων Τί γίνεται όταν πρέπει να αποθηκευτούν ετερογενή δεδοµένα σε ένα αρχείο ; Παράδειγµα: καρτέλες ασθενών που περιλαµβάνουν προσωπικά στοιχεία (String) και τιµές εξετάσεων (double) Χρησιµοποιώντας τις παραπάνω κλάσεις κάτι τέτοιο θα µπορούσε να επιτευχθεί π.χ. χρησιµοποιώντας πολλά αρχεία αντί για ένα ΜΗ ΠΡΑΚΤΙΚΟ! Πρακτική λύση αποτελούν τα αρχεία αντικειµένων 15
Αρχεία Αντικειµένων Τα χειριζόµαστε µε παρόµοιο τρόπο, όπως και τα αρχεία τύπου όχιbyte π.χ. για ανάγνωση (input) Βήµα 1: import java.io.*; Βήµα 2: FileInputStream rf = new FileInputStream( όνοµα αρχείου ); Βήµα 3: ObjectInputStream orf = new ObjectInputStream(rf); Βήµα 4: orf.readobject() Βήµα 5: orf.close() Οµοίως για εγγραφή αρχείων ObjectOutputStream & writeobject() Μια σηµαντική λεπτοµέρεια: για να µπορεί ένα αντικείµενο µιας κλάσης να εγγραφεί/αναγνωσθεί µε τον τρόπο αυτό πρέπει η κλάση να υλοποιεί τη διασύνδεση Serializable ώστε τα δεδοµένα του αντικειµένου να µπορούν να σειριοποιθούν και να εγγραφούν/αναγνωσθούν σειριακά προς/από το δίσκο Αρχεία Αντικειµένων import java.io.*; public class ObjectToDisk // Εγγραφή αντικειµένου στο δίσκο public static void main(string[] arguments) Patient p = new Patient( Agglos Asthenis, 32.5); try FileOutputStream f = new FileOutputStream( patient.obj"); ObjectOutputStream o = new ObjectOutputStream(f); o.writeobject(p); o.close(); catch (IOException e) System.out.println("Error -- " + e.tostring()); 16
Αρχεία Αντικειµένων class Patient implements Serializable String name; double piesh; Patient(String name, double piesh) this.name = name; this.piesh = piesh; Αρχεία Αντικειµένων import java.io.*; public class ObjectFromDisk // Ανάγνωση αντικειµένου από το δίσκο public static void main(string[] arguments) try FileInputStream f = new FileInputStream("patient.obj"); ObjectInputStream o = new ObjectInputStream(f); Patient p = (Patient) o.readobject(); o.close(); catch (Exception e) System.out.println("Error -- " + e.tostring()); 17
Αρχεία Αντικειµένων Μέσω των κλάσεων ObjectInputStream / ObjectOutputStream µπορούν να αναγνωσθούν / εγγραφούν όχι µόνο αντικείµενα που κατασκευάζουµε, αλλά και Οι πρωταρχικοί τύποι δεδοµένων που υποστηρίζει η Java π.χ. int, float κλπ Αντικείµενα της Java που υλοποιούν εξ ορισµού τη διασύνδεση Serializable έστω και εµµέσως λόγω κληρονοµικότητας π.χ. String Όλα τα παραπάνω µαζί Αρχεία Αντικειµένων import java.io.*; class writediafora // Εγγραφή διαφόρων δεδοµένων public static void main(string a[]) FileOutputStream fos = new FileOutputStream("t.tmp"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeint(12345); oos.writeobject("today"); oos.writeobject(new Date()); oos.close(); 18
Αρχεία Αντικειµένων import java.io.*; class readdiafora // Ανάγνωση διαφόρων δεδοµένων public static void main(string a[]) FileInputStream fis = new FileInputStream("t.tmp"); ObjectInputStream ois = new ObjectInputStream(fis); int i = ois.readint(); String today = (String) ois.readobject(); Date date = (Date) ois.readobject(); ois.close(); 19