Προγραμματισμός με αντικείμενα
Διομήδης Σπινέλλης
Τμήμα Διοικητικής Επιστήμης και Τεχνολογίας
Οικονομικό Πανεπιστήμιο Αθηνών
dds@aueb.gr
Ορισμός κλάσεων
- Η δήλωση μιας κλάσης (class)
αποτελείται από το όνομα, τα πεδία (fields)
(ή ιδιότητες (properties))
και τις μεθόδους (methods).
class Point {
};
- Οι κλάσεις χρησιμοποιούνται για την υλοποίηση
αφηρημένων τύπων δεδομένων (abstract data types).
Για το σκοπό αυτό μπορούμε να ορίσουμε μέλη (members)
της κλάσης:
μεταβλητές και συναρτήσεις
που είναι ορατά μόνο από συναρτήσεις που αναφέρονται στον τύπο αυτό
(private) καθώς και μεταβλητές και συναρτήσεις που είναι καθολικά
ορατά (public).
Οι μεταβλητές ορίζουν πεδία και οι συναρτήσεις ορίζουν
μεθόδους των αντικειμένων της κλάσης.
Παράδειγμα:
import gr.aueb.dds.BIO;
class Point {
private int x, y;
public int getx() { return x; }
public int gety() { return y; }
public void setpos(int sx, int sy) {
x = sx;
y = sy;
}
public void display() {
BIO.println("x=" + x);
BIO.println("y=" + y);
}
};
Χρήση αντικειμένων
- Αφού δηλωθεί μια κλάση (σε ένα αρχείο κλάση.java)
μπορούν να οριστούν μεταβλητές (αντικείμενα) με το όνομα (τύπο) της κλάσης.
Πρόσβαση στις μεθόδους της κλάσης έχουν οι μεταβλητές αυτές με τη
σύνταξη μεταβλητή.συνάρτηση.
- Κάθε αντικείμενο της κλάσης έχει ένα δικό του αντίγραφο των πεδίων
που έχουμε ορίσει.
- Σε αντίθεση με τους βασικούς τύπους της Java (int, boolean, double)
που ο ορισμός τους καθορίζει και την περιοχή της μνήμης που φυλάσσονται
τα αντίστοιχα στοιχεία, οι μεταβλητές που ορίζονται ως αντικείμενα περιέχουν
απλώς έναν δείκτη σε περιοχή της μνήμης που πιθανώς να περιέχει τα στοιχεία
για το συγκεκριμένο αντικείμενο.
- Αφού ορίσουμε μια μεταβλητή ως αντικείμενο, πρέπει να καθορίσουμε
και την περιοχή της μνήμης στην οποία θα φυλάσσονται τα πεδία του.
Αυτή μπορεί να είναι:
- Νέα περιοχή μνήμης οριζόμενη με τη σύνταξη
objectVariable = new ObjectType();
Παράδειγμα:
Point a;
a = new Point();
- Αντιγράφοντας στο συγκεκριμένο αντικείμενο κάποιο άλλο
το οποίο έχει ήδη οριστεί:
Παράδειγμα:
Point a, b;
a = new Point();
b = a;
Στην περίπτωση αυτή, τα δύο αντικείμενα μοιράζονται (share)
την ίδια περιοχή της μνήμης.
Παράδειγμα:
import gr.aueb.dds.BIO;
class Point {
private int x, y;
// Return x coordinate
public int getx() { return x; }
// Return y coordinate
public int gety() { return y; }
// Set coordinate to x, y
public void setpos(int sx, int sy) {
x = sx;
y = sy;
}
// Display point coordinates
public void display() {
BIO.print("(x=" + x);
BIO.println(", y=" + y + ")");
}
// Test point functionality
public static void main(String args[])
{
Point a, b;
Point c;
a = new Point();
a.setpos(2, 3);
BIO.println("a.getx()=" + a.getx());
b = new Point();
b.setpos(8, 7);
c = b;
BIO.print("b=");
b.display();
BIO.print("c=");
c.display();
c.setpos(66, 100);
b.display();
}
}
- Οι συναρτήσεις της κλάσης μπορούν να έχουν πρόσβαση στα στοιχεία της
κλάσης με τους εξής τρόπους:
- με το όνομά τους (π.χ. x),
- μέσω της ορισμένης από τη γλώσσα
μεταβλητής this που δείχνει στην κλάση (π.χ. this.x),
Παράδειγμα:
public void setpos(int sx, int sy) {
this.x = sx;
y = sy;
}
Χωρισμός κλάσεων σε αρχεία
- Έχοντας ορίσει μια κλάση, μπορούμε να χηρισμοποιήσουμε αντικείμενα
της κλάσης αυτής σε άλλες κλάσεις.
Τις άλλες κλάσεις μπορούμε να τις ορίσουμε σε ξεχωριστά αρχεία.
Όταν γράψουμε όλα τα αρχεία, μεταγλωττίζουμε το αρχείο .java που
περιέχει τη μέθοδο main.
- Παράδειγμα:
Αρχείο Point.java
import gr.aueb.dds.BIO;
class Point {
private int x, y;
public void setpos(int sx, int sy) {
x = sx;
y = sy;
}
public void display() {
BIO.print("(x=" + x);
BIO.println(", y=" + y + ")");
}
}
Αρχείο TestPoint.java
import gr.aueb.dds.BIO;
class TestPoint {
public static void main(String args[])
{
Point a;
a = new Point();
a.setpos(2, 3);
a.display();
}
}
Ορατότητα
- Όταν χρησιμοποιούμε μεταβλητές μιας κλάσης που έχουμε ορίσει μέσα
σε μια άλλη, τότε μπορούμε να χρησιμοποιήσουμε όλα τα πεδία και όλες
τις μεθόδους που έχουν οριστεί με τον προσδιοριστή public.
- Αντίθετα, δεν μπορούμε να χρησιμοποιήσουμε σε άλλες κλάσεις
πεδία και μεθόδους που έχουμε ορίσει ως private.
Αυτές μπορούν να χρησιμοποιηθούν μόνο μέσα από μεθόδους της
κλάσης στην οποία ορίζονται.
- Στο παρακάτω παράδειγμα στην κλάση Point τα στοιχεία public είναι τα
x, y, και moveToCenter().
Αντίθετα τα μέλη (ιδιότητες) της κλάσης visible και serialNumber και η
μέθοδος setpos
δεν είναι ορατά και προσβάσιμα παρά μόνο από τις συναρτήσεις (μεθόδους)
της κλάσης.
Αρχείο Point.java
import gr.aueb.dds.BIO;
class Point {
// Public fields
public int x, y;
private boolean visible;
private int serialNumber;
// Private method
private void setpos(int sx, int sy) {
x = sx;
y = sy;
}
// Public methd
public void moveToCenter() {
setpos(0, 0);
}
}
Αρχείο TestPoint.java
import gr.aueb.dds.BIO;
class TestPoint {
public static void main(String args[])
{
Point a;
a = new Point();
a.moveToCenter();
// Use public field
a.x = 10;
}
}
Μέθοδοι κατασκευής και καταστροφής
- Σε κάθε κλάση μπορούν να οριστούν
μέθοδοι κατασκευής (constructors) με όνομα ίδιο
με αυτό της κλάσης και μια
μέθοδος καταστροφής (destructor) με όνομα finalize.
-
Η μέθοδος κατασκευής καλείται κάθε φορά που δημιουργείται
ένα νέο αντικείμενο (με τη χρήση new ObjectType).
-
Η μέθοδος καταστροφής καλείται κάθε φορά που παύει να υπάρχει
ένα αντικείμενο δηλαδή αντίστοιχα όταν τελειώνει το πρόγραμμα, όταν
η ροή βγαίνει από το τοπικό τμήμα, ή όταν το σύστημα αποκομιδής αχρήστων
της Java καταστρέφει το συγκεκριμένο αντικείμενο.
-
Το όρισμα που δηλώνουμε στη μέθοδο κατασκευής επιτρέπει προσδιορισμό
ιδιοτήτων του αντικειμένου που δημιουργούμε (π.χ. τον αριθμό στοιχείων
σε έναν πίνακα) ή αρχικών τιμών.
- Για το ίδιο αντικείμενο μπορούμε να ορίσουμε πολλές μεθόδους κατασκευής
με διαφορετικά ορίσματα.
-
Η μέθοδος καταστροφής δε δέχεται κάποιο όρισμα.
- Η μέθοδος κατασκευής και η μέθοδος καταστροφής δε χρειάζονται κάποιον
προσδιοριστή (static, public, void).
- Η κλήση της συνάρτησης κατασκευής μπορεί να γίνει κατά τον
ορισμό μιας μεταβλητής με τη μορφή "κλάση μεταβλητή = new κλάση(όρισμα)".
- Παράδειγμα:
import gr.aueb.dds.BIO;
class Point {
private int x, y;
// Point constructor
Point(int ix, int iy) {
x = ix;
y = iy;
}
// Point destructor
public void finalize() {
BIO.println("Another point bites the dust");
BIO.print("The point was at ");
display();
}
public void display() {
BIO.print("(x=" + x);
BIO.println(", y=" + y + ")");
}
static public void main(String args[]) {
Point a = new Point(1, 2);
Point b = new Point(5, 8);
a = b;
// Force the garbage collector to run
System.gc();
}
}
Ιδιότητες και μέθοδοι της κλάσης
Σε μια κλάση μπορούν να δηλωθούν (με τον προσδιορισμό static)
μεταβλητές οι οποίες υπάρχουν μόνο μια φορά για όλη την κλάση,
καθώς και συναρτήσεις που μπορούν
να κληθούν με τη σύνταξη κλάση.συνάρτηση.
Οι μεταβλητές αυτές χρησιμοποιούνται για την επεξεργασία στοιχείων
που αφορούν ολόκληρη την κλάση και όχι τα αντικείμενά της.
Οι συναρτήσεις που έχουν οριστεί static δεν έχουν πρόσβαση σε μη
static μεταβλητές ούτε στη μεταβλητή this.
Το παρακάτω παράδειγμα ορίζει έναν μετρητή numPoints που μετρά πόσα
σημεία είναι ενεργά καθώς και την αντίστοιχη συνάρτηση πρόσβασης getNumPoints:
import gr.aueb.dds.BIO;
class Point {
private int x, y;
// Count number of points used
private static int numPoints;
// Point constructor
Point(int ix, int iy) {
x = ix;
y = iy;
numPoints++; // Adjust points counter
}
// Point constructor
Point(int xy) {
x = y = xy;
numPoints++; // Adjust points counter
}
// Point destructor
public void finalize() {
BIO.println("Another point bites the dust");
BIO.print("The point was at ");
display();
numPoints--; // Adjust points counter
}
// Return number of points currently used
public static int getNumPoints() {
return numPoints;
}
public void display() {
BIO.print("(x=" + x);
BIO.println(", y=" + y + ")");
}
static public void main(String args[]) {
Point a = new Point(1, 2);
Point b = new Point(5, 8);
Point c = new Point(7);
c.display();
a = b;
BIO.println(getNumPoints() + " points are alive now");
BIO.println("Garbage collecting");
// Force the garbage collector to run
System.gc();
BIO.println(Point.getNumPoints() + " points are alive now");
}
}
Φροντιστηριακή άσκηση
Να υλοποιηθεί σε Java μια κλάση που να παριστάνει αντικείμενα
σε κίνηση με ιδιότητες την αρχική τους θέση και
την ταχύτητά τους στους άξονες x και y και τις παρακάτω μεθόδους:
- void setInitialPosition(double x, double y)
-
Θέτει την αρχική θέση του αντικειμένου.
- void setVelocity(double x, double y)
-
Θέτει την ταχύτητα x, y του αντικειμένου.
- double GetXPosition(int t)
- Επιστρέφει τη θέση x του αντικειμένου κατά τη χρονική στιγμή t.
- double GetYPosition(int t)
- Επιστρέφει τη θέση y του αντικειμένου κατά τη χρονική στιγμή t.
Με βάση το παραπάνω να υλοποιηθεί πρόγραμμα που
- Ορίζει δύο αντικείμενα.
- Για κάθε ένα από τα αντικείμενα ζητάει από το χρήστη την αρχική του θέση,
την ταχύτητά του και ένα χρόνο t και εμφανίζει στην οθόνη τη θέση του κατά το
χρόνο t.
Σημείωση: η θέση κάθε αντικειμένου υπολογίζεται μόνο με βάση την αρχική του
θέση και την ταχύτητά του.
Παράδειγμα:
A1x=6
A1y=12
A1 vx=1
A1 vy=1
t=2
A1x=8 A1y=14
A2x=6
A2y=67
A2 vx=10
A2 vy=10
t=1
A2x=16 A2y=77
Ασκήσεις
- Να υλοποιηθούν σε Java οι κλάσεις andgate, orgate, notgate.
Οι κλάσεις αυτές θα προσομοιάζουν τις αντίστοιχες λογικές πύλες.
Κάθε κλάση να έχει μεθόδους που να θέτουν τις εισόδους (π.χ. setA, setB)
και μέθοδο που να επιστρέφει την τιμή της εξόδου (π.χ. getOutput).
- Με τη χρήση των παραπάνω κλάσεων (και μόνο) να υλοποιήσετε
μια κλάση που να προσομοιάζει έναν ημιαθροιστή.
Η κλάση αυτή να έχει μεθόδους που να θέτουν τις δύο εισόδους και
μεθόδους που να επιστρέφουν το άθροισμα και το κρατούμενο.
- Να γράψετε ένα πρόγραμμα σε Java που να τυπώνει τους πίνακες
αλήθειας για τις παραπάνω κλάσεις.
- (Προαιρετικά) Με τη χρήση των παραπάνω κλάσεων να υλοποιήσετε
μια κλάση που να προσομοιάζει έναν πλήρη αθροιστή.
Η κλάση αυτή πρέπει να έχει μεθόδους που να θέτουν τις δύο εισόδους και το
κρατούμενο εισόδου καθώς και μεθόδους που να επιστρέφουν το άθροισμα και
το κρατούμενο εξόδου.
Βιβλιογραφία
- Γιώργος Λιακέας
Εισαγωγή στην Java. σ. 141-152
Εκδόσεις Κλειδάριθμος 2001.