JAVA ΕΝΟΤΗΤΑ 24 –STATIC FIELDS, STATIC METHODS, STATIC INITIALIZERS

 


ΕΙΣΑΓΩΓΗ

Στο σημερινό δωρεάν μάθημα Java, θα αναλύσουμε τους τρόπους με τους οποίους η Java χρησιμοποιεί την λέξη κλειδί static. Θα ξεκινήσουμε πρώτα με την ανάλυση της θεωρίας και μετά θα δούμε παραδείγματα στα οποία χρησιμοποιούνται static fields, static methods και static initializers.

Όπως ήδη έχουμε αναφέρει σε προηγούμενα δωρεάν μαθήματα Java, όταν δημιουργούμε μεταβλητές ή μεθόδους σε μια κλάση, αυτά τα στοιχεία δεν ανήκουν στην κλάση αλλά στο αντικείμενο και σε κάθε αντικείμενο που πρόκειται να δημιουργηθεί από αυτή την κλάση. Για αυτό άλλωστε και δεν έχει νόημα να θέτουμε αρχικές τιμές σε αυτές τις μεταβλητές, γιατί όλα τα αντικείμενα που θα δημιουργηθούν θα έχουν λάβει την ίδια τιμή για την συγκεκριμένη μεταβλητή ή μεταβλητές. Μερικές φορές όμως μας είναι επιθυμητό μια μεταβλητή ή μια μέθοδο να μην ανήκει στο αντικείμενο αλλά να ανήκει στην ίδια την κλάση. Δηλαδή να έχει έναν global ρόλο μεταβλητής ή μεθόδου. Αυτό σημαίνει ότι δεν θα χρειάζεται να δημιουργήσουμε αντικείμενο (αφού ανήκουν στην κλάση) αλλά θα μπορούμε άμεσα να τα καλέσουμε και να τα χρησιμοποιήσουμε. Εδώ βασικά φαίνεται τώρα και ο διαχωρισμός που γίνεται όταν αναφερόμαστε σε static και non-static. Όταν έχουμε non-static μεταβλητές ή μεθόδους, επίσημα αυτά στην γενική τους μορφή ονομάζονται instance members. Ενώ οι μεταβλητές και οι μέθοδοι που είναι static (δηλαδή ανήκουν στην κλάση) ονομάζονται class members.

Τώρα αν απορείτε γιατί μαθαίνουμε μια τέτοια θεωρία ενώ υπάρχουν άλλα πιο σημαντικά θέματα να μάθουμε, η απάντηση είναι απλή – γιατί την χρησιμοποιεί η ίδια η java ακόμα και στις πιο απλές βιβλιοθήκες. Μάλιστα την λογική αυτή την έχετε χρησιμοποιήσει πολλαπλές φορές μέχρι τώρα χωρίς να το έχετε καταλάβετε.

Ας πάρουμε για παράδειγμα την κλάση EmployeeDemo που είχαμε δημιουργήσει σε προηγούμενο δωρεάν μάθημα Java.

EmployeeDemo.java

package com.example;

public class EmployeeDemo{
    public static void main(String[]args){
   
    Employee michail= new Employee("Michail",20000,"Development");
     System.out.println("Thank you for registering the employee "+michail.getName());
   
    }
}


Στο παραπάνω πρόγραμμα, συναντάμε την λογική του static στον ορισμό της main( ) μεθόδου. Ο συγκεκριμένος ορισμός δίνει τη δυνατότητα στο JVM περιβάλλον να καλέσει την μέθοδο main( ) και να ξεκινήσει η εκτέλεση του προγράμματος χωρίς να είναι απαραίτητο να δημιουργηθεί πρώτα ένα αντικείμενο από την κλάση EmployeeDemo.

Στο ίδιο πρόγραμμα, χρησιμοποιούμε την λογική static, χωρίς να είναι άμεσα ορατή όταν καλούμε την println( ) μέθοδο αφού την καλούμε χωρίς πρώτα να έχουμε δημιουργήσει κάποιο αντικείμενο. Καλούμε απλά μια static κλάση που ονομάζεται System η οποία εσωτερικά δημιουργεί ένα αντικείμενο out είδος PrintStream στο οποίο ανήκει η μέθοδος println( ).

Θα ξεκινήσουμε την θεωρία μας πρώτα από τα static fields. Θα χρησιμοποιήσουμε το παράδειγμα του Employee, που έχουμε ήδη αναλύσει πολλαπλές φορές σε προηγούμενα δωρεάν μαθήματα Java. Ο καινούργιος κώδικας, στον οποίο έχει προστεθεί η static μεταβλητή, είναι ο εξής:

Employee.java

package com.example;

class Employee {
    private String name;
    private int AFM;
    private double salary;
    private String department;
    private int remainingDays;
    private int employeeId;
    public static int counter;

    public Employee(String name, double salary, String department) {
        System.out.println("Constructing an Employee");
        this.name = name;
        this.salary = salary;
        this.department = department;
        this.employeeId = ++counter;
    }

    public int getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(int employeeId) {
        this.employeeId = employeeId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAFM() {
        return AFM;
    }

    public void setAFM(int AFM) {
        this.AFM = AFM;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public int getRemainingDays() {
        return remainingDays;
    }

    public void setRemainingDays(int remainingDays) {
        this.remainingDays = remainingDays;
    }

    public void mailCheck() {
        System.out.println("Mailing a check to " + this.name);
    }

    public double payment() {
        return this.salary / 52.0;
    }

    public void vacationCalc(int daysTotal, int daysRequested) {
        this.remainingDays = daysTotal - daysRequested;
        System.out.println("You have requested " + daysRequested + " days of vacation");
    }
}


Στην κλάση Employee έχουμε προσθέσει μια static μεταβλητή (static field) που ονομάζεται counter. Κάθε φορά που δημιουργούμε ένα αντικείμενο είδος Employee ,μέσα από τον constructor που έχουμε εμείς δημιουργήσει, αυξάνεται η τιμή του counter κατά ένα και αναθέτουμε την τιμή στην employeeId του αντικειμένου που δημιουργήσαμε. Κάθε αντικείμενο που δημιουργείται θα καλεί την ίδια static μεταβλητή και για όσο τρέχει το πρόγραμμα μας, η μεταβλητή αυτή θα διατηρεί την τιμή της. Το υπόλοιπο πρόγραμμα αποτελείται από getter και setter μεθόδους οι οποίες μας είναι απαραίτητες για να έχουμε πρόσβαση στις private instance μεταβλητές. Υπάρχει επίσης η vacationCalc( ) μέθοδος την οποία χρησιμοποιούμε για να μάθουμε πόσες μέρες διακοπών μας έχουν απομείνει.

Το ενδιαφέρον σημείο του κώδικα είναι στην EmployeeDemo μέσα από την οποία δημιουργούμε τα αντικείμενα είδος Employee. Ας δούμε τον κώδικα, και θα αναλύσουμε την θεωρία αμέσως μετά.

EmployeeDemo.java

package com.example;

public class EmployeeDemo {
    public static void main(String[] args) {
        Employee.counter = 100;
        Employee michail = new Employee("Michail", 20000, "Development");
        System.out.println(michail.getName() + ", your new employee id is: " + Employee.counter);
        Employee john = new Employee("John", 30000, "HR");
        System.out.println(john.getName() + ", your new employee id is: " + Employee.counter);
    }
}


Output

Constructing an Employee

Michail, your new employee id is: 101

Constructing an Employee

John, your new employee id is: 102

Όπως ήδη έχουμε αναφέρει, μια static μεταβλητή δεν ανήκει σε κάποιο αντικείμενο αλλά στην ίδια την κλάση. Για να αποκτήσουμε πρόσβαση στην μεταβλητή λοιπόν και να τις αναθέσουμε μια αρχική τιμή δεν έχουμε παρά να καλέσουμε το όνομα της κλάσης, τελεία, και το όνομα της μεταβλητής.

Employee.counter = 100;

Η μεταβλητή αυτή, λόγω της static ιδιότητας της, θεωρείστε την σαν ένα είδος global μεταβλητής. Κάθε φορά που δημιουργούμε ένα καινούργιο αντικείμενο, επηρεάζουμε, δια μέσω του constructor, και την τιμή του counter. Αρχικοποιήσαμε λοιπόν την μεταβλητή counter στην τιμή 100, και αφού δημιουργήσαμε δύο αντικείμενα, η τιμή του counter αυξήθηκε κατά δύο.

Έχουμε επίσης την δυνατότητα να ορίσουμε και μια static μέθοδο (static method). Η λογική κάτω από την οποία λειτουργεί είναι ακριβώς η ίδια όπως με την μεταβλητή. Όμως υπάρχει ένας πολύ αυστηρός περιορισμός – μια static μέθοδο μπορεί να καλέσει μόνο static μεταβλητές. Αν προσπαθήσετε να καλέσετε μια instance μεταβλητή μέσα από μια static μέθοδο o compiler της java θα σας επιστρέψει λάθος. Ας προσθέσουμε λοιπόν μια static μέθοδο στην Employee κλάση, η οποία μέθοδο θα επιστρέφει την τιμή του static counter.

Employee.Java

package com.example;

class Employee {
    private String name;
    private int AFM;
    private double salary;
    private String department;
    private int remainingDays;
    private int employeeId;
    public static int counter;

    public Employee(String name, double salary, String department) {
        System.out.println("Constructing an Employee");
        this.name = name;
        this.salary = salary;
        this.department = department;
        this.employeeId = ++counter;
    }

    public static int getCounter() {
        System.out.println("Inside getCounter");
        return counter;
    }

    public int getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(int employeeId) {
        this.employeeId = employeeId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAFM() {
        return AFM;
    }

    public void setAFM(int AFM) {
        this.AFM = AFM;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public int getRemainingDays() {
        return remainingDays;
    }

    public void setRemainingDays(int remainingDays) {
        this.remainingDays = remainingDays;
    }

    public void mailCheck() {
        System.out.println("Mailing a check to " + this.name);
    }

    public double payment() {
        return this.salary / 52.0;
    }

    public void vacationCalc(int daysTotal, int daysRequested) {
        this.remainingDays = daysTotal - daysRequested;
        System.out.println("You have requested " + daysRequested + " days of vacation");
    }
}


Τώρα στην EmployeeDemo κλάση μπορούμε να δούμε την τιμή της counter καλώντας την static μέθοδο getCounter( ).

EmployeeDemo.java

package com.example;

public class EmployeeDemo {
    public static void main(String[] args) {
        Employee.counter = 100;
        Employee michail = new Employee("Michail", 20000, "Development");
        System.out.println(michail.getName() + ", your new employee id is: " + Employee.getCounter());
        Employee john = new Employee("John", 30000, "HR");
        System.out.println(john.getName() + ", your new employee id is: " + Employee.getCounter());
    }
}


Output

Constructing an Employee

Inside getCounter

Michail, your new employee id is: 101

Constructing an Employee

Inside getCounter

John, your new employee id is: 102

Η χρήση της λέξης static δεν σταματάει όμως εδώ. Μπορούμε να χρησιμοποιήσουμε την λέξη static σε συνδυασμό με τα αντικείμενα έτσι ώστε να τρέξουμε κάποια tasks πριν δημιουργηθεί το αντικείμενο. Αυτό το είδος χρήσης του static ονομάζεται static initializer και ο τρόπος που το γράφουμε είναι να γράψουμε την λέξη static και μέσα σε άγκιστρα ( { } ) να γράψουμε τον κώδικα που θέλουμε να εκτελεστεί πριν δημιουργηθεί το αντικείμενο. Ας προσθέσουμε στην κλάση Employee και έναν static initializer. Ο κώδικας του είναι πολύ απλός απλά για να δείξουμε ότι εκτελείται πριν την δημιουργία του αντικειμένου.

Employee.java

package com.example;

class Employee {
    private String name;
    private int AFM;
    private double salary;
    private String department;
    private int remainingDays;
    private int employeeId;
    public static int counter;

    public Employee(String name, double salary, String department) {
        System.out.println("Constructing an Employee");
        this.name = name;
        this.salary = salary;
        this.department = department;
        this.employeeId = ++counter;
    }

    static {
        System.out.println("Static initializer running before object creation");
    }

    public static int getCounter() {
        System.out.println("Inside getCounter");
        return counter;
    }

    public int getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(int employeeId) {
        this.employeeId = employeeId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAFM() {
        return AFM;
    }

    public void setAFM(int AFM) {
        this.AFM = AFM;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public int getRemainingDays() {
        return remainingDays;
    }

    public void setRemainingDays(int remainingDays) {
        this.remainingDays = remainingDays;
    }

    public void mailCheck() {
        System.out.println("Mailing a check to " + this.name);
    }

    public double payment() {
        return this.salary / 52.0;
    }

    public void vacationCalc(int daysTotal, int daysRequested) {
        this.remainingDays = daysTotal - daysRequested;
        System.out.println("You have requested " + daysRequested + " days of vacation");
    }
}


EmployeeDemo.java

package com.example;

public class EmployeeDemo {
    public static void main(String[] args) {
        Employee.counter = 100;
        Employee michail = new Employee("Michail", 20000, "Development");
        System.out.println(michail.getName() + ", your new employee id is: " + Employee.getCounter());
        Employee john = new Employee("John", 30000, "HR");
        System.out.println(john.getName() + ", your new employee id is: " + Employee.getCounter());
    }
}


Output

Static initializer running before object creation

Constructing an Employee

Inside getCounter

Michail, your new employee id is: 101

Constructing an Employee

Inside getCounter

John, your new employee id is: 102

Όπως παρατηρείτε στο output, πριν δημιουργηθεί το πρώτο αντικείμενο, έτρεξε πρώτα ο static initializer. Όμως έτρεξε μια φορά, για το πρώτο αντικείμενο. Αν και είναι πολύ χρήσιμη μια τέτοια ιδιότητα, θα ήταν ακόμα πιο χρησιμότερη αν έτρεχε για κάθε αντικείμενο που δημιουργούσαμε. Αυτό είναι πολύ εύκολο να γίνει – αφαιρούμε απλά την λέξη static και αφήνουμε μόνο τα άγκιστρα. Ας δούμε τώρα πως αλλάζει ο κώδικας στο πρόγραμμα μας και το τελικό αποτέλεσμα.

Employee.java

package com.example;

class Employee {
    private String name;
    private int AFM;
    private double salary;
    private String department;
    private int remainingDays;
    private int employeeId;
    public static int counter;

    public Employee(String name, double salary, String department) {
        System.out.println("Constructing an Employee");
        this.name = name;
        this.salary = salary;
        this.department = department;
        this.employeeId = ++counter;
    }

    {
        System.out.println("Static initializer running before object creation");
    }

    public static int getCounter() {
        System.out.println("Inside getCounter");
        return counter;
    }

    public int getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(int employeeId) {
        this.employeeId = employeeId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAFM() {
        return AFM;
    }

    public void setAFM(int AFM) {
        this.AFM = AFM;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public int getRemainingDays() {
        return remainingDays;
    }

    public void setRemainingDays(int remainingDays) {
        this.remainingDays = remainingDays;
    }

    public void mailCheck() {
        System.out.println("Mailing a check to " + this.name);
    }

    public double payment() {
        return this.salary / 52.0;
    }

    public void vacationCalc(int daysTotal, int daysRequested) {
        this.remainingDays = daysTotal - daysRequested;
        System.out.println("You have requested " + daysRequested + " days of vacation");
    }
}


EmployeeDemo.java

package com.example;

public class EmployeeDemo {
    public static void main(String[] args) {
        Employee.counter = 100;
        Employee michail = new Employee("Michail", 20000, "Development");
        System.out.println(michail.getName() + ", your new employee id is: " + Employee.getCounter());
        Employee john = new Employee("John", 30000, "HR");
        System.out.println(john.getName() + ", your new employee id is: " + Employee.getCounter());
    }
}


Output

Static initializer running before object creation

Constructing an Employee

Inside getCounter

Michail, your new employee id is: 101

Static initializer running before object creation

Constructing an Employee

Inside getCounter

John, your new employee id is: 102

Μην ξεχάσετε να κάνετε ένα μικρό donation έτσι ώστε αυτό το blog να μεγαλώσει ακόμα πιο πολύ και να έχει περισσότερες δυνατότητες στην online παράδοση δωρεάν μαθημάτων.

full-width

Post a Comment

0 Comments