Declararea claselor

Până în prezent, s-a arătat modul în care putem utiliza în programul nostru clase existente în biblioteci (în pachetele de clase). Vom studia în continuare cum putem crea propriile noastre clase.

Cea mai simplă formă a unei declarații de clasă este următoarea:

    class NumeClasa {
       declaratii_de_membri
    }

Observăm că declarația începe cu cuvântul-cheie class, urmat de numele clasei și de corpul clasei, cuprins între acolade.

Numele clasei este un identificator. Se obisnuiește ca numele clasei sa înceapă cu literă majusculă. Dacă numele este compus din mai multe cuvinte, fiecare din acestea începe cu majusculă.

Corpul clasei cuprinde declarații de membri ai clasei respective. Acestea pot fi:
    - declarații de câmpuri;
    - declarații de constructori;
    - declarații de metode.
Nu este obligatoriu ca într-o clasă să existe toate aceste categorii de declarații. Pot exista, de exemplu, clase în care apar numai declarații de metode. În principiu, pot exista și clase care conțin numai câmpuri și nu conțin metode, deși astfel de situații apar foarte rar în practică.

Declararea câmpurilor

Declarațiile de câmpuri servesc pentru a descrie structura de date specifică clasei respective. Câmpurile se mai numesc și variabile membre și pot fi ale clasei sau ale instanței (ale obiectului). Se preferă denumirea de câmpuri, pentru a le deosebi de variabilele locale ale metodelor.

Câmpurile instanței se declară la fel ca variabilele locale ale metodelor, numai că declarația respectivă nu apare în blocul unei metode, ci în corpul clasei.  De exemplu:
    int m=172, n=2*m-4, r;
Aceste câmpuri pot avea valori diferite pentru fiecare instanță a clasei respective. În consecință, câmpurile instanței sunt plasate în zona de memorie rezervată instanței respective, astfel că ele sunt distincte pentru fiecare instanță.

Câmpurile clasei se numesc și câmpuri statice. Declararea unor astfel de câmpuri se face asemănător cu cea a câmpurilor de instanță, dar declarația are în față, în acest caz, modificatorul static.De exemplu:
    static double u=3.65, v=2.87*u-3.1, x;
La inițializarea câmpurilor de instanță se pot folosi atât valori ale câmpurilor statice, cât și ale altor câmpuri de instanță. În schimb, la inițializarea câmpurilor statice se pot folosi numai valori ale altor câmpuri statice.
 
Câmpurile statice (ale clasei) sunt plasate în memorie în zona rezervată clasei căreia îi aparțin și nu în cea rezervata instanțelor. În consecință, câmpurile clasei există în memorie într-un singur exemplar, care este accesibil fiecărei instanțe.

Valorile inițiale implicite ale câmpurilor: dacă nu sunt inițializate explicit, câmpurile statice și cele nestatice primesc valori implicite astfel:
  - câmpurile booleene primesc valoarea false;
  - câmpurile numerice primesc valoarea 0 (chiar și cele de tip char, care este tot tip numeric!);
  - câmpurile referința primesc valoarea null.

Remarcăm, deci, că există o deosebire între crearea câmpurilor (variabilelor membre) și crearea variabilelor locale. La crearea câmpurilor, acestora li se atribuie implicit o valoare inițială, în timp ce la crearea variabilelor locale acestora trebuie sa li se atribuie valori în mod explicit. Dacă o variabilă locală apare într-o expresie fără să aibă o valoare atribuită anterior, compilatorul Java semnaleaza această situație ca o eroare de programare.


 

Exemplu de clasă care nu conține constructori expliciți sau metode:
 
Acest exemplu este dat în scop didactic, pentru a ilustra declararea, inițializarea și utilizarea câmpurilor. Crearea unei clase fără metode nu corespunde principiului de baza al programării orientate pe obiecte, conform căruia o clasă conține atât date, cât și metodele prin care acestea sunt tratate. În consecință, declararea unor clase fără metode se evită în practica programării orientate pe obiecte, chiar dacă o astfel de declarație este permisă. De altfel, chiar daca declarația de clasă nu conține constructori sau metode, ea are un constructor implicit (fără parametri) și moștenește metodele superclasei, în particular metodele clasei Object.

În exemplul din fișierul TestClasa1.java se declară și se utilizează o clasă care nu conține constructori si metode, ci numai câmpuri de date. O astfel de clasă este folosită, deci, ca o structură din limbajele neorientate pe obiecte (cum este struct în C sau record în Pascal). În acest fișier sunt declarate două clase: clasa Proba1, care este o simplă structură de date fără metode proprii, și clasa TestClasa1, care conține metoda main si servește ca aplicație în care se testează clasa Proba1.
Clasa Proba1 este declarată astfel:
 
/* Declararea clasei Proba1
   Clasa contine numai campuri de date
*/

class Proba1 {
  static int m=9, n=m-3;
  int a=7, b=m*a+1, c, d;
  char c1='@', c2;
  String s1="un sir", s2=s1+" extins", s3;
}

În această clasă, s-au definit câmpurile statice (ale clasei) m și n și câmpurile nestatice (de instanță) a, b, c și d - toate de tip int. S-au declarat, de asemenea, câmpurile c1 și c2 de tip char și câmpurile s1, s2 și s3 care contin referințe la instanțe ale clasei String. Unele din aceste câmpuri sunt inițializate, altele au valori inițiale implicite. La inițializarea câmpului de instanță b s-a folosit și valoarea câmpului static a. Câmpurile c și d sunt inițializate implicit cu valoarea 0.
 
Amintim o deosebire importantă între modul cum sunt plasate în memorie valorile primitive și obiectele (instanțele claselor): valorile primitive sunt plasate chiar în zona de memorie rezervată variabilelor respective, în timp ce instanțele claselor sunt plasate în memoria dinamică. În consecință:
  - valorile câmpurilor primitive statice m și n sunt plasate în memorie o singură dată, în spațiul rezervat clasei Proba1;
  - valorile câmpurilor primitive nestatice a, b, c, d, c1 și c2 sunt plasate în câmpurile corespunzătoare ale instanțelor clasei Proba1, deci acestea vor avea câte o valoare pentru fiecare instanță;
  - în campurile s1, s2 si s3 (care, în cazul nostru, sunt tot nestatice, deci se plaseaza în fiecare instanță), ca valori se pun numai referințe la instanțe ale clasei String. În consecință, șirurile "un șir" și "un șir extins" se vor creea în memoria dinamică, sub forma de obiecte ale clasei String, iar în câmpurile s1 și s2 ale fiecărei instanțe a clasei Proba1 se vor pune numai referințe la aceste șiruri. Câmpul s3 va fi inițializat cu referința null.

Clasa Proba1 poate fi utilizată în alte clase Java la fel ca o structură de date (înregistrare) "tradițională" cum ar fi struct în limbajul C, sau record în limbajul Pascal. Un exemplu de aplicație, în care se utilizează clasa Proba1, este clasa TestClasa1 din fișierul TestClasa1.java, pe care o reproducem în continuare.
 
/* Programul in care se utilizeaza clasa Proba1 ca o structura
   obisnuita
*/

class TestClasa1 {
  public static void main(String args[]) {
    /* Se declara doua referinte catre instante ale clasei Proba1,
       iar prima din ele se si initializeaza
    */
    Proba1 p1=new Proba1(), p2;
    /* Se mai creaza o instanta si se atribuie lui p2
    */
    p2=new Proba1();
    /* Se afiseaza unele campuri ale instantelor p1 si p2; */
    System.out.println("Campuri din p1: n="+p1.n+" m="+p1.m+" a="+
      p1.a+" b="+p1.b+" c="+p1.c+" c1="+p1.c1+" c2="+p1.c2+
      "  (int)p1.c2="+(int)p1.c2);
    System.out.println("Campuri din p2: n="+p2.n+" m="+p2.m+" a="+
      p2.a+" b="+p2.b+" c="+p2.c+"\n s1="+p2.s1+" s2="+p2.s2+
      " s3="+p2.s3);
    /* Afisarea campurilor statice calificandu-le cu numele clasei */
    System.out.println("Afisarea campurilor statice: m="+Proba1.m+
      " n="+Proba1.n);
    /* Modificam atribuim p1.a si p2.a valori diferite, apoi reafisam
       toate campurile
    */
    p1.a=12; p2.a=-9;
    System.out.println("Dupa modificarea valorilor campurilor a:");
    System.out.println("Campuri din p1: n="+p1.n+" m="+p1.m+" a="+
      p1.a+" b="+p1.b+" c="+p1.c);
    System.out.println("Campuri din p2: n="+p2.n+" m="+p2.m+" a="+
      p2.a+" b="+p2.b+" c="+p2.c);
    /* Modificam campul static p1.m si reafisam p1.m, p2.m si Proba1.m
    */
    p1.m=-12;
    System.out.println("Dupa modificare: p1.m="+p1.m+"  p2.m="+p2.m+
      "  Proba1.m="+Proba1.m);
    /* Modificam campul static n folosind expresia Proba1.n si afisam
    */
    Proba1.n=-25;
    System.out.println("Dupa modificare: p1.n="+p1.n+"  p2.n="+p2.n+
      "  Proba1.n="+Proba1.n);
    /* Atribuim o valoare campului de instanta p1.c */
    p1.c=1234;
    System.out.println("Dupa atribuire: p1.c="+p1.c+"  p2.c="+p2.c);
  }
}

La compilarea fișierului sursă TestClasa1.java, vor fi create două fișiere bytecode, câte unul pentru fiecare clasă, numite în mod corespunzător Proba1.class și TestClasa1.class. Pentru executarea aplicației se va folosi comanda
    java TestClasa1
Clasa Proba1 nu poate fi pusă în execuție în acest mod, deoarece ea nu conține metoda main. Această clasă poate fi folosită numai în cadrul altei clase.

Rezultatele afișate pe ecran la executarea acestei aplicații sunt următoarele:
 
Campuri din p1: n=6 m=9 a=7 b=64 c=0 c1=@ c2=   (int)p1.c2=0
Campuri din p2: n=6 m=9 a=7 b=64 c=0
 s1=un sir s2=un sir extins s3=null
Afisarea campurilor statice: m=9 n=6
Dupa modificarea valorilor campurilor a:
Campuri din p1: n=6 m=9 a=12 b=64 c=0
Campuri din p2: n=6 m=9 a=-9 b=64 c=0
Dupa modificare: p1.m=-12 p2.m=-12 Proba1.m=-12
Dupa modificare: p1.n=-25 p2.n=-25 Proba1.n=-25
Dupa atribuire: p1.c=1234 p2.c=0

Urmărind aceste rezultate, putem constata că:
  - câmpurile numerice neinițializate explicit (inclusiv cele de tip char) sunt inițializate implicit la valoarea zero;
  - valorile câmpurilor statice pot fi utilizate calificând numele câmpului respectiv, atât cu numele unei instanțe (de exemplu, p1.m), cât și calificandu-le cu numele clasei (de exemplu, Proba1.m);
  - valorile câmpurilor nestatice pot fi utilizate numai calificând numele câmpului respectiv cu numele unei instanțe (de exemplu, p2.a);
  - dacă se atribuie o nouă valoare unui câmp nestatic al unei instanțe, valorile câmpurilor cu același nume ale celorlalte instanțe rămân nemodificate;
  - dacă se atribuie o nouă valoare unui câmp static, această modificare se constată în toate înstanțele, deoarece câmpul respectiv este unic (există numai în cadrul clasei).



© Copyright 2000 - Severin BUMBARU, Universitatea "Dunărea de Jos" din Galați