Sintaxa limbajului Java; Structura lexicală a unui program

De la WikiLabs
Jump to navigationJump to search

O aplicație Java este formată din două tipuri de elemente: clase și interfețe.

Regulă: Un fișier .java conține exact o interfață SAU o clasă principală și zero sau mai multe clase interne (inner-class).


Sfat: Evitați utilizarea claselor interne. Acestea se pot implementa sub forma unei clase principale, cu modificatorii de acces potriviți.

Înainte să prezentăm structura lexicală a claselor și interfețelor, trebuie să discutăm despre tipurile de date.

Tipuri de date

În Java, există două categorii de tipuri de date: primitive și referințe. Tipurile primitive se referă la cele care au valori imediate (valori numerice sau logice), iar referințele sunt nume care identifică obiecte.

Tipuri primitive

În Java, tipurile primitive sunt:

Nume Categorie Număr de biți Valori posibile
byte întreg 8 -128 : 127
short întreg 16 -32768 : 32767
int întreg 32 -2147483648 : 2147483647
long întreg 64 -9223372036854775808 : 9223372036854775807
char caracter 16 Orice caracter Unicode
boolean logic 8 true, false
float virgulă mobilă 32 ±1.18 x 10-38 : ±3.4 x 1038
double virgulă mobilă 64 ±2.23 x 10-308 : ±1.80 x 10308

Variabilele de tip primitiv se declară în același fel ca cele din C. Ca exemplu:

int someInteger;
int someOtherInteger = 10;
float someFloat = 10.5f;
double someDouble = 5.3 + someFloat;
boolean condition = true;
char oneChar = 'g';
char newLineChar = '\n';

Atenție: În Java nu există tipuri de date fără semn.

Similar cu C, există și în Java operatori de cast pentru tipuri de date primitive. Unele operații de conversie sunt implicite (de la întreg la virgulă mobilă), dar unele trebuie specificate (de la virgulă mobilă la întreg, de la char la întreg), iar altele nu sunt posibile deloc (de la boolean la orice alt tip sau invers).

În Java, tipurile primitive numerice se inițializează implicit cu valoarea 0, iar cele logice cu valoarea false.

Tipuri referință

Variabilele de tip referință sunt identificatori ale unor obiecte. Două referințe pot identifica același obiect, dar o referință nu poate identifica două obiecte în același timp.

Regulă: Obiectele (inclusiv vectorii) se creează folosind operatorul new

Referințele se inițializează implicit cu valoarea null.

Referință la obiecte

Obiectele de orice tip (clasă), se creează folosind operatorul new conform următorului exemplu:

1Object obj;
2new Object();
3obj = new Object();

Se observă linia 2, care instanțiază un obiect nou, de tipul clasei Object, dar nu există nici o referință la acest obiect nou creat. Astfel, imediat după instanțiere, memoria alocată este eliberată de către garbage collector, aceasta ne mai putând fi accesibilă. Linia 3 instanțiază un obiect nou, și variabila obj va fi referință la obiectul nou creat. Mai multe despre instanțierea obiectelor și constructori în capitolele care urmează.

Exemplu de program Schema bloc
//...
Object _obj1 = new Object(); //notam acest obiect cu ID1
Object _obj2 = new Object(); //notam acest obiect cu ID2
Object _obj3 = _obj1;        //referinta la acelasi obiect ID1
Object _obj4 = null;         //nu refera nici un obiect
Object _obj5;                //initializat implicit cu null
//...
Exemplu de variabile tip referință

Vectori multi-dimensionali

Și vectorii, în Java, se tratează ca niște obiecte, adică există variabile de tip referință care identifică zona de memorie alocată pentru vector.

Atenție: În cazul instanțierii vectorilor de referințe (unde fiecare locație este o referință, și nu un tip primitiv de date), operatorul new alocă memorie doar pentru vector, nu și pentru fiecare locație în parte.

Atenție: Ca și în C, numărătoarea elementelor dintr-un vector începe de la 0. Astfel, pentru un vector de 5 elemente, există pozițiile 0, 1, 2, 3 și 4.

Exemplu de program Schema bloc
int[] _array1;
_array1 = new int[5];
_array1[3] = 10;
Object[] _array2 = new Object[10];
_array2[2] = new Object();
int[][] _array3 = new int[5][3];
_array3[1][1] = 5;
char[][][] _array4;
Exemplu de variabile tip referință - vectori

Clasa Java

Clasa este tipul de bază în Java, ca și în orice alt limbaj bazat pe obiecte.

Regulă: Nimic nu poate exista în afara unei clase sau unei interfețe.


Regulă: Orice fișier conține o singură clasă principală.

Cuvântul cheie care definește o clasă este class, acesta fiind urmat pe numele clasei și de implementarea acesteia între acolade.

Convenție: Orice nume de clasă sau interfață începe cu literă mare.


class SomeClass{
    // here comes the implementation of the class
}

Implementarea unei clase este formată din definiții de câmpuri și definiții și implementări de metode. Câmpurile reprezintă datele conținute de clasă și metodele reprezintă funcționalitatea clasei, adică funcțiile care acționează asupra câmpurilor.

Regulă: Operatorul de acces la membrii unei clase (câmpuri sau metode) este ".".

Pachetele Java

Clasele unei aplicații Java sunt structurate într-o ierarhie similară cu cea a fișierelor și directorelor (folder-elor) de pe o partiție, unde fișierele sunt similare claselor și directorele sunt similare pachetelor. Separatorul folosit între numele de pachete și clasă este caracterul ".".

Regulă: Un pachet Java poate conține clase și alte pachete.

Cuvântul cheie care determină pachetul în care este plasată o clasă este package. Dacă această directivă lipsește, clasa este plasată implicit în "rădăcina" ierarhiei de pachete.

Regulă: Pentru a putea folosi o clasă într-o aplicație, clasa respectivă trebuie importată în orice altă clasă care o folosește. Excepție face pachetul java.lang care este importat implicit în orice clasă, ca și clasele definite în rădăcină (fără directivă de pachet).


package myPackage;

import java.util.ArrayList;
import java.io.*;

class TestClass{

public static void main(String[] _args){
    //ArrayList class is in package java.util so it needs to be imported
    ArrayList _arrayList = new ArrayList(); 

    //both FileOutputStream and IOException are in package java.io so they
    //are both available.
    try{
        FileOutputStream _fileOutputStream = new FileOutputStream("test");
        _fileOutputStream.close();
    }catch(IOException _ioe){
        _ioe.printStackTrace();
    }

    //Class System is in package java.lang so it is automatically imported
    System.out.println("Done!");
}

}

Definiția câmpurilor

Câmpurile, ca orice variabilă, pot fi de tip primitiv sau referință. Ele se declară în interiorul clasei, dar în afara oricărei metode:

Convenție: Numele de câmpuri încep cu literă mică.


class SomeClass{
    
    //fields here
    int somePrimitiveField;
    boolean someOtherPrimitiveField;
    Object aReferenceField;
    int[][] anotherReferenceFieldToAnArray;

}

Atenție: Câmpurile au valori diferite pentru fiecare instanță, adică pentru fiecare obiect.

Exemplu de program Schema bloc
class MainClass{

public static void main(String[] _args){
    SomeClass _obj1 = new SomeClass(); //notam acest obiect cu ID1
    SomeClass _obj2 = new SomeClass(); //notam acest obiect cu ID2
    _obj1.somePrimitiveField = 5;
    _obj2.somePrimitiveField = 10;
    System.out.println("Field somePrimitiveField from _obj1 is = " + 
        _obj1.somePrimitiveField + " and from _obj2 is = " +
        _obj2.somePrimitiveField);
    if(_obj1.somePrimitiveField == _obj2.somePrimitiveField){
        System.out.println("They are equal!");
    }else{
        System.out.println("They are NOT equal!");
    }
}

}
Exemplu de câmpuri în obiecte

Definiția metodelor

Metodele se pot defini și implementa exclusiv în interiorul unei clase. Metodele acționează asupra câmpurilor clasei. Ca și în C, metodele se declară folosind următorul tipar:

  • zero sau mai mulți modificatori;
  • tipul returnat (sau void dacă metoda nu întoarce nimic) - atenție, constructorii nu au tip returnat, nici măcar void;
  • numele metodei;
  • între paranteze rotunde, lista de argumente, definite prin tip și nume și separate prin virgulă;
  • între acolade, corpul metodei.
Convenție: Cu excepția constructorilor, toate numele de metode încep cu literă mică.


class SomeClass{
    
    //fields here
    int somePrimitiveField;
    boolean someOtherPrimitiveField;
    Object aReferenceField;
    int[][] anotherReferenceFieldToAnArray;

//methods here
//---------------------------------------------------------------------
int getSomePrimitiveField(){
    return somePrimitiveField;
}

//---------------------------------------------------------------------
void setSomePrimitiveField(int _somePrimitiveField){
    somePrimitiveField = _somePrimitiveField;
}

//---------------------------------------------------------------------
boolean aMoreComplexMethod(Object _aReferenceField){
    if(_aReferenceField != null){
        aReference = _aReferenceField;
    }

    return aReference != null;
}

}

Variabilele primitive sau de tip referință declarate în interiorul unei metode, sau ca argumente ale unei metode, se numesc variabile locale și nu sunt câmpuri ale clasei.

Convenție: Pentru a deosebi ușor câmpurile de variabilele locale, numele acestora din urmă vor începe cu caracterul "_".

Constructorii claselor

Constructorii sunt metode speciale ale unei clase care se apelează obligatoriu la instanțierea unui obiect. Acestea au următoarele proprietăți:

Regulă: Constructorii au același nume cu numele clasei din care fac parte.


Regulă: Constructorii nu au tip returnat (nici măcar void).

Exemplu de constructori pentru clasa SomeClass:

class SomeClass{
    
    //fields here
    int somePrimitiveField;
    boolean someOtherPrimitiveField;
    Object aReferenceField;
    int[][] anotherReferenceFieldToAnArray;

//constructors here
//---------------------------------------------------------------------
SomeClass(){                                    //this is a constructor
    somePrimitiveField = 0;
    someOtherPrimitiveField = false;
    aReferenceField = new Object();
    anotherReferenceFieldToAnArray = null;
}

//---------------------------------------------------------------------
SomeClass(int _somePrimitiveField){       //this is another constructor
    somePrimitiveField = _somePrimitiveField;
    someOtherPrimitiveField = false;
    aReferenceField = new Object();
    anotherReferenceFieldToAnArray = null;
}


//methods here
//---------------------------------------------------------------------
int getSomePrimitiveField(){
    return somePrimitiveField;
}

//---------------------------------------------------------------------
void setSomePrimitiveField(int _somePrimitiveField){
    somePrimitiveField = _somePrimitiveField;
}

//---------------------------------------------------------------------
boolean aMoreComplexMethod(Object _aReferenceField){
    if(_aReferenceField != null){
        aReference = _aReferenceField;
    }

    return aReference != null;
}

}


Regulă: Dacă într-o clasă nu s-a definit un constructor, atunci compilatorul Java introduce implicit un constructor fără argumente și fără conținut. Dacă însă în clasă s-a definit un alt constructor, atunci cel implicit dispare.


Regulă: Pentru instanțierea obiectelor (și nu a vectorilor), după operatorul new urmează întotdeauna apelul unui constructor!

Exemplu de apelare a constructorilor:

class MainClass{

public static void main(String[] _args){
    SomeClass _obj1 = new SomeClass();    //this calls the first constructor
    SomeClass _obj2 = new SomeClass(1234);  //this calls the second constructor

    System.out.println(_obj1.getSomePrimitiveField()); //this will print 0
    System.out.println(_obj2.getSomePrimitiveField()); //this will print 1234
}
    
}

Cuvintele cheie this și super

Cuvântul cheie this poate fi folosit în două situații:

  • ca apel de constructor din alt constructor;
  • ca referință la obiectul curent ca instanță a clasei curente.

Exemplu de utilizare a cuvântului cheie this:

import java.util.ArrayList;

class SomeClass{
    
    //fields here
    int somePrimitiveField;
    boolean someOtherPrimitiveField;
    Object aReferenceField;
    int[][] anotherReferenceFieldToAnArray;

//constructors here
//---------------------------------------------------------------------
SomeClass(){                                    //this is a constructor
    this(10);    //this is a call to the other constructor
                 // (see method overloading)
}

//---------------------------------------------------------------------
SomeClass(int _somePrimitiveField){       //this is another constructor
    somePrimitiveField = _somePrimitiveField;
    someOtherPrimitiveField = false;
    aReferenceField = new Object();
    anotherReferenceFieldToAnArray = null;
}


//methods here
//---------------------------------------------------------------------
int getSomePrimitiveField(){
    return somePrimitiveField;
}

//---------------------------------------------------------------------
void setSomePrimitiveField(int somePrimitiveField){
    this.somePrimitiveField = somePrimitiveField;
    /*here, "this" is a reference to the 
     *current object. It is used to make the difference 
     *between the field somePrimitiveField and the local
     *variable with the same name. Writing "somePrimitiveField" 
     *with no object specifier ("this") will direct the compiler
     *to the closest defined variable with that name: the local
     *variable:
     */
     somePrimitiveField = 10; //this is the local variable
     this.somePrimitiveField = 10; // this is the field

     /*"this" can also be used for passing the current object
      *as argument to other methods:
      */
     ArrayList _list = new ArrayList();
     _list.add(this);
}

//---------------------------------------------------------------------
boolean aMoreComplexMethod(Object _aReferenceField){
    if(_aReferenceField != null){
        aReference = _aReferenceField;
    }

    return aReference != null;
}

}


Cuvântul cheie super este folosit în două situații (vezi ierarhii de clase):

  • ca apel al unui constructor al superclasei dintr-un constructor al clasei curente;
  • ca referință la obiectul curent ca instanță a superclasei.
import java.util.ArrayList;

class SomeClass{
    
    //fields here
    int somePrimitiveField;
    boolean someOtherPrimitiveField;
    Object aReferenceField;
    int[][] anotherReferenceFieldToAnArray;

//constructors here
//---------------------------------------------------------------------
SomeClass(){                                    //this is a constructor
    this(10);    //this is a call to the other constructor
                 // (see method overloading)
}

//---------------------------------------------------------------------
SomeClass(int _somePrimitiveField){       //this is another constructor
    super(); 

    /* Class SomeClass is extended from Object class so
     * the super() call is the call to the Object() 
     * constructor.
     */

    somePrimitiveField = _somePrimitiveField;
    someOtherPrimitiveField = false;
    aReferenceField = new Object();
    anotherReferenceFieldToAnArray = null;
}


//methods here
//---------------------------------------------------------------------
int getSomePrimitiveField(){
    return somePrimitiveField;
}

//---------------------------------------------------------------------
void setSomePrimitiveField(int somePrimitiveField){
    this.somePrimitiveField = somePrimitiveField;
    /*here, "this" is a reference to the 
     *current object. It is used to make the difference 
     *between the field somePrimitiveField and the local
     *variable with the same name. Writing "somePrimitiveField" 
     *with no object specifier ("this") will direct the compiler
     *to the closest defined variable with that name: the local
     *variable:
     */
     somePrimitiveField = 10; //this is the local variable
     this.somePrimitiveField = 10; // this is the field

     /*"this" can also be used for passing the current object
      *as argument to other methods:
      */
     ArrayList _list = new ArrayList();
     _list.add(this);
}

//---------------------------------------------------------------------
boolean aMoreComplexMethod(Object _aReferenceField){
    if(_aReferenceField != null){
        aReference = _aReferenceField;
    }

    return aReference != null;
}

//---------------------------------------------------------------------
String toString(){
    return "SomeClass " + super.toString();

    /* Here, the "super" keyword is used as a reference to the current
     * object but it is calling the method "toString" defined in the
     * superclass (see method overwriting). If the "super" was missing,
     * it would recursively call the same method, crashing the program.
     * Instead, it is now calling an entirely different method belonging
     * to the superclass.
     */
}

}


Regulă: Orice constructor trebuie să înceapă cu un apel de alt constructor folosind super sau this. Dacă acesta nu este specificat explicit, atunci implicit este adăugat apelul super(); de către compilator. În această situație pot apărea erori de compilare în cazul în care superclasa nu are constructor fără argumente. În acest caz, apelul constructorului superclasei trebuie facut explicit.


Regulă: Orice apel de constructor folosind super sau this trebuie să fie prima expresie din constructorul curent.

Membrii statici

Membrii statici ai unei clase reprezintă membrii care nu-și schimbă valoarea sau comportamentul pentru obiecte distincte, adică țin de clasă, și nu de fiecare instanță în parte.

class SomeClass{
 
    //fields here
    static int someStaticField;

    int somePrimitiveField;
    boolean someOtherPrimitiveField;
    Object aReferenceField;
    int[][] anotherReferenceFieldToAnArray;
 
}


Exemplu de program Schema bloc
class MainClass{

public static void main(String[] _args){
    SomeClass _obj1 = new SomeClass(); //notam acest obiect cu ID1
    SomeClass _obj2 = new SomeClass(); //notam acest obiect cu ID2
    _obj1.someStaticField = 5;
    _obj2.someStaticField = 10;
    System.out.println("Field someStaticField from _obj1 is = " + 
        _obj1.someStaticField + " and from _obj2 is = " +
        _obj2.someStaticField);
    if(_obj1.someStaticField == _obj2.someStaticField){
        System.out.println("They are equal!");
    }else{
        System.out.println("They are NOT equal!");
    }

    System.out.println("Proper way of accessing static members: " +
        SomeClass.someStaticField);
}

}
Exemplu de câmpuri statice în obiecte


Sfat: Toate metodele care accesează doar câmpuri statice ale clasei curente, sau nici un câmp, se vor declara la rândul lor statice.


Sfat: Accesul la membrii statici ai unei clase se face utilizând numele clasei și nu nume de obiecte.


Regulă: Doar câmpurile, metodele și clasele interne pot fi declarate statice, nu și clasele principale.

Modificatori speciali pentru membrii

Modificatori de access

Modificator Clasa Curenta Clasa Derivată Pachet În afara pachetului
public DA DA DA DA
protected DA DA DA NU
implicit (fără specificator) DA DA NU NU
private DA NU NU NU
import java.util.ArrayList;

public class SomeClass{
    
    //fields here
    private int somePrimitiveField;
    private boolean someOtherPrimitiveField;
    private Object aReferenceField;
    private int[][] anotherReferenceFieldToAnArray;

//constructors here
//---------------------------------------------------------------------
public SomeClass(){                             //this is a constructor
    this(10);    //this is a call to the other constructor
                 // (see method overloading)
}

//---------------------------------------------------------------------
public SomeClass(int _somePrimitiveField){//this is another constructor
    super(); 

    /* Class SomeClass is extended from Object class so
     * the super() call is the call to the Object() 
     * constructor.
     */

    somePrimitiveField = _somePrimitiveField;
    someOtherPrimitiveField = false;
    aReferenceField = new Object();
    anotherReferenceFieldToAnArray = null;
}


//methods here
//---------------------------------------------------------------------
public int getSomePrimitiveField(){
    return somePrimitiveField;
}

//---------------------------------------------------------------------
protected void setSomePrimitiveField(int somePrimitiveField){
    this.somePrimitiveField = somePrimitiveField;
    /*here, "this" is a reference to the 
     *current object. It is used to make the difference 
     *between the field somePrimitiveField and the local
     *variable with the same name. Writing "somePrimitiveField" 
     *with no object specifier ("this") will direct the compiler
     *to the closest defined variable with that name: the local
     *variable:
     */
     somePrimitiveField = 10; //this is the local variable
     this.somePrimitiveField = 10; // this is the field

     /*"this" can also be used for passing the current object
      *as argument to other methods:
      */
     ArrayList _list = new ArrayList();
     _list.add(this);
}

//---------------------------------------------------------------------
private boolean aMoreComplexMethod(Object _aReferenceField){
    if(_aReferenceField != null){
        aReference = _aReferenceField;
    }

    return aReference != null;
}

//---------------------------------------------------------------------
public String toString(){
    return "SomeClass " + super.toString();

    /* Here, the "super" keyword is used as a reference to the current
     * object but it is calling the method "toString" defined in the
     * superclass (see method overwriting). If the "super" was missing,
     * it would recursively call the same method, crashing the program.
     * Instead, it is now calling an entirely different method belonging
     * to the superclass.
     */
}

}
Sfat: În general, câmpurile se declară private (vezi noțiunea de încapsulare) iar metodele getters și setters publice, constructorii publici. Există, desigur, și excepții.
public

Un element public este accesibil oricărei alte clase.

protected

Un câmp sau metodă protejată este accesibilă claselor din același pachet și claselor derivate din alte pachete.

Regulă: O clasă principală nu poate avea specificatorul protected.
implicit (fără specificator)

Lipsa unui modificator de access implică faptul că elementul este accesibil clasei curente sau claselor din același pachet, dar nu și claselor din alte pachete.

private

Un câmp sau metodă privată este accesibilă doar clasei curente.

Regulă: O clasă principală nu poate avea specificatorul private.

Modificatorul final

Modificatorul final poate fi aplicat:

  • unei clase - clasa nu poate fi extinsă;
  • unei interfețe - interfața nu poate fi extinsă;
  • unei metode - metoda nu poate fi suprascrisă;
  • unui câmp - acestuia i se poate da o singură valoare care va rămâne constantă pe toată durata existenței obiectului.

Un câmp care este static și final este o constantă a clasei.

Modificatorul abstract

O metodă abstractă este o metodă care nu are implementare (doar prototipul).

Regulă: O clasă care conține cel puțin o metodă abstractă trebuie obligatoriu să fie declarată abstractă.


Regulă: O clasă abstractă nu poate fi instanțiată, ci doar extinsă. O clasă extinsă dintr-o clasă abstractă trebuie ori să implementeze toate metodele abstracte, ori să fie și ea declarată abstractă.

Exemplu:

abstract class Sorter{

    protected Object[] data;

public Sorter(Object[] _data){
    data = _data;
}

//this is an abstract method
protected abstract void sort();

public Object[] getResult(){
    sort();
    return data;
}

}


class BubbleSorter extends Sorter{

public BubbleSorter(Object[] _data){
    super(data);
}

//this is the implementation of the abstract method
protected void sort(){
    boolean _done;
    do{
        _done = true;
        for(int i=0; i<data.length - 1; i++){
            if(compare(data[i], data[i + 1]) < 0){
                Object _tmp = data[i];
                data[i] = data[i + 1];
                data[i + 1] = _tmp;
                _done = false;
            }
        }
    }while(!_done);
}

private int compare(Object _obj1, Object obj2){
    //...
} 

}

Modificatorul volatile

Un câmp este declarat volatil atunci când este scris de mai mult de un fir de execuție și în acest caz valoarea respectivă va fi încărcată de fiecare dată din memorie și nu se vor încărca în memoria cache.

Câmpurile volatile reprezintă o alternativă la mecanismul de sincronizare, dar poate fi folosit doar în anumite situații în care nu există race conditions. Vezi Programare concurentă - fire de execuție (Threads).

Modificatorul transient

Un câmp declarat transient nu va fi salvat împreună cu obiectul la serializarea acestuia (vezi serializarea obiectelor).

Modificatorul synchronized

Modificatorul native

Interfața Java

Construcții repetitive

Expresia for

Expresia while

Expresia do - while

Instrucțiunile break & continue

Construcții condiționale

Operatorul condițional (?)

Expresia if - else

Expresia switch

Operatori

Resurse

  1. Specificația limbajului Java