Ce este static. Modificatori în Java: statici, finali, abstracti, sincronizați, tranzitori, volatili

Ce este static

În unele cazuri, este de dorit să se definească un membru al unei clase care va fi folosit independent de orice obiect al acelei clase. De obicei, un membru al clasei ar trebui să fie accesat numai împreună cu un obiect din clasa sa. Cu toate acestea, puteți crea un membru al clasei care poate fi folosit singur, fără referire la o anumită instanță. Pentru a crea un astfel de membru, trebuie să plasați la începutul declarației sale cuvânt cheie static. Când un membru al clasei este declarat static, acesta este accesibil înainte ca orice obiect din clasa sa să fie creat și fără referire la niciun obiect. Atât metodele, cât și variabilele pot fi declarate statice. Cel mai comun exemplu de membru static este metoda main(). Această metodă este declarată statică deoarece trebuie declarată înainte de a crea orice obiect.

Variabilele de instanță declarate ca statice sunt, în esență, variabile globale. Când declară obiecte din clasa lor, programul nu creează nicio copie a variabilei statice. În schimb, toate instanțele unei clase au aceeași variabilă statică.

Metodele declarate ca fiind statice sunt supuse unui număr de restricții.

  • Ei pot apela doar alte metode statice.
  • Ar trebui să acceseze numai variabile statice.
  • Ei nu se pot referi în niciun fel la membrii de tip this sau super. (Cuvântul cheie super este legat de moștenire și este tratat în capitolul următor.)

Dacă trebuie să efectuați calcule pentru a inițializa variabilele statice, puteți declara un bloc static care va fi executat o singură dată când clasa este prima încărcată. Următorul exemplu arată o clasă care conține metoda statica, mai multe variabile statice și un bloc de inițializare statică:

// Demonstrarea variabilelor, metodelor și blocurilor statice.
clasa UseStatic(
static int a = 3;
static int b;
static void met(int x) (
System.out.println("x = " + x) ;
System.out.println("a = " + a);
System.out.println("b = " + b) ;
}
static (
System.out.println("Blocul static inițializat.");
b = a * 4;
}

met(42);
}
}

Imediat după încărcarea clasei UseStatic, programul execută toate instrucțiunile statice. Mai întâi, valoarea lui a este setată la 3, apoi programul execută un bloc static care tipărește un mesaj și apoi inițializează variabila b cu valoarea a*4, sau 12. Programul apelează apoi metoda main(), care apelează metoda meth(), trecând valoarea x 42. Trei instrucțiuni println() referă două variabile statice a și b la o variabilă locală x.

Ieșirea acestui program arată astfel:

Bloc static initializat, x = 42 a = 3 b = 12

În afara clasei în care sunt definite, metodele și variabilele statice pot fi utilizate independent de orice obiect. Pentru a face acest lucru, pur și simplu specificați numele clasei lor, urmat de operația cu puncte. De exemplu, dacă o metodă de tip static trebuie apelată din afara clasei sale, acest lucru se poate face folosind următoarea formă generală:

class_name.method()

Aici class_name este numele clasei în care este declarată metoda de tip static. După cum puteți vedea, acest format este similar cu cel folosit pentru a apela metode non-statice prin variabile de referință la obiect. O variabilă statică este accesată într-un mod similar - prin operatorul punct care urmează numele clasei. Acesta este modul în care Java implementează versiuni gestionate ale metodelor și variabilelor globale.

Să dăm un exemplu. În cadrul metodei main(), metoda statică callme() și variabila statică b sunt accesate prin numele lor de clasă StaticDemo.

clasa StaticDemo (
static int a = 42;
static int b = 99;
static void callme() (
System.out.println("a = " + a);
}
}
clasa StaticByName(
public static void main(Argumente șir) (
StaticDemo.callme();
System.out.println("b = " + StaticDemo.b);
}
}

Ieșirea acestui program arată astfel.

Vom vorbi despre modificatori: ce sunt modificatorii, domeniile, modificatorii pentru clase, câmpuri, metode. Cred că nu va fi plictisitor.

Modificatori în Java sunt cuvinte cheie care conferă anumite proprietăți unei clase, unui câmp de clasă sau unei metode.

Pentru a indica vizibilitatea unei clase a metodelor și câmpurilor sale, există 4 modificatori de acces:

  • privat membrii clasei sunt accesibili numai în cadrul clasei;
  • pachet-privat sau implicit (implicit) membrii clasei sunt vizibili în interiorul pachetului;
  • protejat membrii clasei sunt disponibili în interiorul pachetului și în clasele descendente;
  • public membrii clasei sunt disponibile pentru toată lumea.

Dacă vă amintiți, la final, când importasem deja clasa Cat, mai aveam o eroare de compilare.

Chestia este că nu am specificat niciun modificator de acces la câmpurile și metodele noastre și au o proprietate implicită (membrii clasei sunt vizibili în interiorul pachetului). Pentru a remedia eroarea de compilare pentru codul nostru și pentru a o rula în cele din urmă, trebuie să facem publice constructorul și metodele noastre. Apoi pot fi apelate din alte pachete.

Poate începeți să vă întrebați: pentru ce sunt toate acestea? De ce să nu faceți codul vizibil din orice pachet sau clasă, dar trebuie să restricționați accesul? Aceste întrebări vor dispărea de la sine atunci când va veni timpul să scriem proiecte complexe și greoaie. Acum, când scriem aplicații a căror funcționalitate este limitată la una sau două clase, se pare că nu are rost să limităm ceva.

Imaginați-vă că aveți o clasă care afișează un obiect al unui anumit produs. De exemplu o mașină. Masina poate avea un pret. Ați creat un câmp de preț și multe alte câmpuri, o grămadă de metode care sunt responsabile pentru funcționalitate. Totul pare în regulă. Mașina dvs. de clasă face parte dintr-un proiect uriaș și toată lumea este fericită. Dar să presupunem că cineva a creat accidental sau intenționat o instanță a clasei de mașini și a stabilit un preț negativ. Poate un produs să aibă un preț negativ? Acesta este un exemplu foarte primitiv și este puțin probabil să se întâmple în viata reala, dar cred că ideea este clară. Uneori trebuie să acordați acces nu direct, ci prin anumite metode. Este posibil ca codul să fie responsabil pentru funcționalitatea altui cod și nu doriți ca cineva să schimbe și să editeze o parte din a dvs. În acest scop există o restricție de acces.

Modificatorul de acces pentru constructori, metode și câmpuri poate fi orice. O clasă poate fi doar publică sau implicită și poate exista doar o clasă publică într-un fișier.

Destul despre modificatorii de acces pentru moment. În articolul „Programare orientată pe obiecte” vom vorbi despre ele mai detaliat, dar acum să vorbim despre alți modificatori dintre care, apropo, sunt mulți.

Acum vine modificatorul static. Poate fi folosit înaintea unei metode, câmp și chiar a unei clase atunci când dorim să declarăm o clasă imbricată. În Java, puteți scrie clase în interiorul altor clase, iar dacă modificatorul dinaintea clasei din interiorul clasei este static, atunci o astfel de clasă se numește imbricată, dacă un alt modificator este sau implicit, atunci o astfel de clasă se numește intern. Va exista un articol separat despre clasele imbricate și interioare, deoarece totul nu este atât de simplu acolo.

Modificatorul static din fața unei metode sau câmp indică faptul că nu aparține unei instanțe a acelei clase. Ce înseamnă asta pentru noi? Când am declarat un câmp sau o metodă de clasă ca static, acesta poate fi apelat fără a utiliza o instanță a clasei. Adică, în loc de această construcție: Pisica pisica= pisica noua(); cat.method(), puteți scrie pur și simplu Cat.method(). Cu condiția ca metoda să fie declarată ca fiind statică. Variabilele statice sunt aceleași pentru toate obiectele de clasă. Au un singur link.

    Modificatori de clasă publică (

    static int anotherStaticField = 5 ;

    public static void myStaticMethod() (

    someField = "Câmpul meu" ;

    //nonStaticField = ""; eroare de compilare

    //nu puteți folosi câmpuri non-statice

    //în metode statice

    public void myNonStaticMethod() (

    anotherStaticField = 4; //se pot folosi câmpuri statice

    //în metode non-statice

    //Metoda principală are și un modificator static

    noi Modificatori() .myNonStaticMethod() ;

    Modificators.myStaticMethod(); //apelează metode și câmpuri statice

    //prin Classname.method

O alta notă importantă Un lucru de spus despre modificatorii statici: câmpurile statice sunt inițializate atunci când clasa este încărcată. Adesea, în diferite tipuri de teste Java, puteți găsi următorul cod:

Întrebare: ce va fi scos pe consolă? Trebuie să rețineți că blocul static va fi scos mai întâi în orice caz. Următorul va fi blocul implicit. Apoi, uită-te la ecranul consolei:

Următorul modificator la care ne vom uita va fi final.

Cred că cuvântul final vorbește de la sine. Folosind modificatorul final, spuneți că câmpurile nu pot fi modificate, metodele nu pot fi suprascrise și clasele nu pot fi moștenite (va exista un articol separat despre moștenire). Acest modificator se aplică numai claselor, metodelor și variabilelor (de asemenea, variabilelor locale).

CU modificator final Despre metode și clase vom vorbi în articolul OOP.

Urmează modificatorii care nu vor fi foarte clari pentru începători sau pentru cei care citesc această serie de articole de la zero. Și deși nu vă pot explica totul încă (din cauza faptului că nu știți material aferent), vă sfătuiesc în continuare să vă familiarizați cu ele. Când va veni timpul să folosiți acești modificatori, veți înțelege deja majoritatea termenilor folosiți mai jos.

Modificator sincronizate- indică faptul că metoda poate fi folosită doar de un fir la un moment dat. Deși acest lucru poate să nu vă spună nimic, utilitatea acestui modificator va fi văzută atunci când studiem multithreading.

Modificator tranzitoriu— indică faptul că un anumit câmp ar trebui ignorat în timpul serializării obiectului. De regulă, astfel de câmpuri stochează valori intermediare.

Modificator volatil- folosit pentru multithreading. Când un câmp cu modificatorul volatil va fi folosit și modificat de mai multe fire de execuție, acest modificator asigură că câmpul va fi schimbat la rândul său și nu va exista confuzie cu acesta.

Modificator nativînainte de a declara o metodă, indică faptul că metoda este scrisă într-un alt limbaj de programare. De obicei în limbaj C.

Modificator strictfp— Oferă operații pe numere tip plutitorși dublu (în virgulă flotantă) conform standardului IEEE 754 Sau, mai simplu, garantează că în cadrul metodei, rezultatele calculului vor fi aceleași pe toate platformele.

Nu am vorbit încă despre modificator abstract. O să vă povestesc pe scurt, de vreme ce fără a cunoaște elementele de bază programare orientată Nu văd niciun rost să vorbesc despre el.

O clasă care are modificatorul abstract nu poate crea o instanță. Singurul scop este să fie extins. Clasa abstractă poate conține atât metode abstracte, cât și metode obișnuite.

Vom vorbi mai multe despre modificatorul abstract în articolul OOP.

Aici putem termina articolul despre modificatori. Despre ei nu s-au spus multe. Dar acest lucru se datorează faptului că nu avem încă conceptele de POO. Pe parcursul mai multor articole, vom extinde cunoștințele despre modificatori și vom completa golurile.

Modificatorul static din Java este direct legat de clasă dacă câmpul este static, atunci aparține clasei, dacă metoda este statică, în mod similar, aparține clasei. Pe baza acestui lucru, puteți accesa o metodă statică sau un câmp folosind numele clasei. De exemplu, dacă câmpul count este static în clasa Counter, atunci puteți accesa variabila cu o interogare precum: Counter.count. Desigur, modificatorii de acces ar trebui să fie luați în considerare. De exemplu, câmpurile private sunt disponibile numai în cadrul clasei în care sunt declarate. Câmpurile protejate sunt disponibile pentru toate clasele din pachet ( pachet), precum și toate clasele descendente din afara pachetului. Pentru mai mult informatii detaliate citește articolul „privat vs protejat vs public”. Să presupunem că există o metodă statică increment() în clasa Counter a cărei sarcină este să crească numărul contorului. A apela aceasta metoda puteți folosi un apel precum Counter.increment() . Nu este nevoie să instanțiați clasa Counter pentru a accesa un câmp sau o metodă static. Aceasta este diferența fundamentală dintre obiectele statice și NON-statice (membrii clasei). Notă importantă. Amintiți-vă că membrii clasei statice aparțin direct clasei, nu instanței acesteia. Adică, valoarea numărului de variabile statice va fi aceeași pentru toate obiectele de tip Counter. În acest articol, vom analiza aspectele fundamentale ale utilizării modificatorului static în Java, precum și câteva caracteristici care vă vor ajuta să înțelegeți conceptele cheie de programare.

Ce ar trebui să știe fiecare programator despre modificatorul Static din Java.

În această secțiune, ne vom uita la elementele de bază ale utilizării metodelor, câmpurilor și claselor statice. Să începem cu variabilele.

    NU puteți accesa membrii nestatici ai unei clase într-un context static, cum ar fi o metodă sau un bloc. Compilarea codului de mai jos va duce la o eroare:

    clasă publică Counter (număr privat int; public static void main (String args) (System. out. println (număr) ; //eroare de timp de compilare) )

    Aceasta este una dintre cele mai frecvente greșeli făcute Programatori Java, mai ales pentru incepatori. Deoarece metoda principală este statică și variabila count nu, în acest caz metoda println din cadrul metodei principale va afișa „Eroare de timp de compilare”.

    Spre deosebire de variabilele locale, câmpurile și metodele statice NU sunt sigure pentru fire în Java. În practică, aceasta este una dintre cele mai frecvente cauze ale problemelor legate de securitatea programării multithreaded. Având în vedere că fiecare instanță a unei clase are aceeași copie a unei variabile statice, o astfel de variabilă are nevoie de protecție - „blocare” de către clasă. Prin urmare, atunci când utilizați variabile statice, asigurați-vă că acestea sunt sincronizate corect pentru a evita probleme precum condițiile de cursă.

    Metodele statice au un avantaj în utilizare deoarece... nu este nevoie să creați un obiect nou de fiecare dată pentru a accesa astfel de metode. O metodă statică poate fi apelată folosind tipul clasei în care sunt definite metodele. De aceea, astfel de metode sunt potrivite în mod ideal ca metode din fabrică și metode utilitare. Clasa java.lang.Math este un exemplu grozav în care aproape toate metodele sunt statice, acesta fiind același motiv pentru care clasele de utilitate din Java sunt finale.

    Pentru alții punct important este că NU puteți trece peste metodele statice. Dacă declarați aceeași metodă într-o subclasă, i.e. metoda cu același nume și semnătură, veți „ascunde” doar metoda superclasei în loc să o suprascrieți. Acest fenomen este cunoscut sub numele de metode de ascundere. Aceasta înseamnă că atunci când se apelează o metodă statică care este declarată atât într-o clasă părinte, cât și într-o clasă copil, metoda va fi apelată întotdeauna la momentul compilării, în funcție de tipul variabilei. Spre deosebire de suprascriere, astfel de metode nu vor fi executate în timp ce programul rulează. Să ne uităm la un exemplu:

    Clasa Vehicul ( public static void kmToMiles (int km) ( System. out. println ( „În interiorul clasei părinte/metoda statică”); ) ) clasa Mașina se extinde Vehicul ( public static void kmToMiles (int km) ( System. out. println ( „În interiorul unei clase copil/metodă statică”); ) ) public class Demo ( public static void main ( String args ) ( Vehicul v = mașină nouă ( ) ; v. kmToMiles ( 10 ) ; ) )

    Ieșire din consolă:

    În interiorul clasei părinte/metoda statică

    Codul demonstrează clar: în ciuda faptului că obiectul este de tip Car, metoda statică din clasa Vehicule este numită, deoarece o metodă a fost apelată în timpul compilării. Și rețineți că nu au existat erori în timpul compilării!

    De asemenea, puteți declara o clasă statică, cu excepția claselor nivel superior. Astfel de clase sunt cunoscute ca clase statice imbricate. Sunt utile pentru a reprezenta conexiuni îmbunătățite. Un prim exemplu de clasă statică imbricată este HashMap.Entry, care oferă o structură de date în interiorul unui HashMap. Este de remarcat faptul că, la fel ca orice altă clasă interioară, clasele imbricate sunt într-un fișier .class separat. Deci, dacă ați declarat cinci clase imbricate în clasa dvs. principală, ați avea 6 fișiere cu extensia .class. Un alt exemplu de utilizare este declararea propriului Comparator, de exemplu AgeComparator în clasa Employee.

    Modificatorul static poate fi declarat și într-un bloc static, mai bine cunoscut sub numele de bloc de inițializare static, care va fi executat atunci când clasa este încărcată. Dacă nu declarați un astfel de bloc, Java va colecta toate câmpurile statice într-o singură listă și o va executa când clasa este încărcată. Cu toate acestea, un bloc static nu poate arunca excepții prinse, dar le poate arunca pe cele neprinse. În acest caz, va apărea o „Eroare de inițializare excepție”. În practică, orice excepție aruncată în timpul execuției și inițializării câmpurilor statice va fi cuprinsă în această eroare de către Java. Acesta este și cel mai mult motiv comun erori „No Class Def Found Error”, deoarece clasa nu era în memorie când a fost accesată.

    Este util de știut că metodele statice sunt legate în timpul compilării, spre deosebire de metodele virtuale sau non-statice de legare, care sunt legate în timpul execuției pe obiectul real. Prin urmare, metodele statice nu pot fi suprascrise în Java deoarece polimorfismul runtime nu se aplică acestora. Aceasta este o limitare importantă de luat în considerare atunci când declarați o metodă statică. Acest lucru are sens numai atunci când nu există nicio posibilitate sau nevoie de a înlocui o astfel de metodă de către clasele moștenitoare. Metode din fabrică și metode de utilitate mostre bune folosind modificatorul static. Joshua Bloch a subliniat mai multe avantaje ale utilizării unei metode statice din fabrică față de un constructor în cartea sa Effective Java, care este citită obligatorie pentru fiecare programator al limbajului.

    O proprietate importantă a unui bloc static este inițializarea. Câmpurile sau variabilele statice sunt inițializate după ce clasa este încărcată în memorie. Ordinea de inițializare de sus în jos, în aceeași ordine în care sunt descrise în original Fișier Java clasă. Deoarece câmpurile statice sunt inițializate într-o manieră sigură pentru fire, această proprietate este folosită și pentru a implementa modelul Singleton. Dacă nu utilizați o listă Enum ca Singleton , dintr-un motiv sau altul, atunci există una pentru dvs alternativa buna. Dar în acest caz, este necesar să țineți cont de faptul că aceasta nu este o inițializare „leneșă”. Aceasta înseamnă că câmpul static va fi inițializat ÎNAINTE să-l „cere” cineva. Dacă obiectul consumă mult resurse sau este utilizat rar, atunci inițializarea lui într-un bloc static nu va funcționa în favoarea dvs.

    În timpul serializării, la fel ca variabilele tranzitorii, câmpurile statice nu sunt serializate. Într-adevăr, dacă salvați orice date într-un câmp static, atunci după deserializare noul obiect va conține valoarea sa primară (implicit), de exemplu, dacă câmpul static a fost o variabilă int, atunci valoarea sa după deserializare va fi zero, dacă tipul float este 0.0, dacă tipul obiect este nul. Sincer, aceasta este una dintre cele mai frecvente întrebări cu privire la serializarea în interviurile Java. Nu stocați cele mai importante date despre un obiect într-un câmp static!

    Și, în sfârșit, să vorbim despre importul static. Acest modificator are multe în comun cu operator standard import , dar spre deosebire de acesta vă permite să importați unul sau toți membrii statici ai unei clase. La importarea metodelor statice, acestea pot fi accesate ca și cum ar fi fost definite în aceeași clasă, la fel și la importul câmpurilor, le putem accesa fără a specifica numele clasei. Această ocazie aparut in versiuni Java 1.5, iar atunci când este utilizat corespunzător, îmbunătățește lizibilitatea codului. Acest design se găsește cel mai adesea în teste JUnit, deoarece Aproape toți dezvoltatorii de testare folosesc importuri statice pentru metode de afirmare, cum ar fi assertEquals() și duplicatele lor supraîncărcate. Dacă nimic nu este clar, bine ați venit pentru informații suplimentare.

    Asta e tot. Toate punctele de mai sus despre modificator static Fiecare programator trebuie să cunoască Java. Acest articol discutat informatii de baza despre variabile statice, câmpuri, metode, blocuri de inițializare și importuri. Inclusiv unele proprietăți importante, a cărui cunoaștere este esențială atunci când scrieți și înțelegeți programele Java. Sper că fiecare dezvoltator își va perfecționa abilitățile în utilizarea conceptelor statice, deoarece... Acest lucru este foarte important pentru o programare serioasă.”

Ultima actualizare: 19.04.2018

Cu exceptia metode convenționaleși câmpuri, o clasă poate avea câmpuri statice, metode, constante și inițializatori. De exemplu, clasa principală a programului are o metodă main, care este statică:

Public static void main(Argumente șir) ( )

Pentru a declara variabile statice, constante, metode și inițializatori, utilizați cuvântul cheie static înainte de a le declara.

Câmpuri statice

Când sunt create obiecte de clasă, fiecare obiect are propria copie a câmpurilor obișnuite non-statice. Și câmpurile statice sunt comune întregii clase. Prin urmare, ele pot fi utilizate fără a crea obiecte de clasă.

De exemplu, să creăm o variabilă statică:

Clasa publică Program( public static void main(String args) ( Persoană tom = Persoană nouă(); Persoană bob = Persoană nouă(); tom.displayId(); // Id = 1 bob.displayId(); // Id = 2 System.out.println(Person.counter); // 3 // modifică Person.counter Person.counter = 8 Person sam = new Person( // Id = 8 ) class Person ( private int id; static int counter=1; Person())( id = counter++; ) public void displayId())( System.out.printf("Id: %d \n", id); ) )

Clasa Person conține un numărător de variabile statice, care este incrementat în constructor și valoarea sa este atribuită variabilei id. Adică, la crearea fiecărui nou obiect Person, această variabilă va crește, astfel încât fiecare nou obiect Person va avea o valoare a câmpului id care este cu 1 mai mare decât cea precedentă.

Deoarece variabila contor este statică, ne putem referi la ea în program prin numele clasei:

System.out.println(Person.counter); // obținem valoarea Person.counter = 8; // schimba valoarea

Ieșirea din consolă a programului:

Id = 1 Id = 2 3 Id = 8

Constante statice

De asemenea, statice sunt constantele care sunt comune întregii clase.

Clasa publică Program( public static void main(String args) ( rază dublă = 60; System.out.printf("Radisu: %f \n", radius); // 60 System.out.printf("Zona: %f \n", Math.PI * rază); // 188.4 ) ) clasa Math( public final static PI dublu = 3,14; )

Este de remarcat faptul că în toate subiectele anterioare, constantele statice au fost deja utilizate în mod activ. În special, în expresia:

System.out.println("bună ziua");

out reprezintă doar o constantă statică a clasei System. Prin urmare, este accesat fără a crea un obiect din clasa System.

Inițializatoare statice

Inițializatoarele statice sunt concepute pentru a inițializa variabile statice sau pentru a efectua acțiuni care sunt efectuate atunci când este creat primul obiect. De exemplu, să definim un inițializator static:

Clasa publică Program( public static void main(String args) ( Persoană tom = Persoană nouă(); Persoană bob = Persoană nouă(); tom.displayId(); // Id = 105 bob.displayId(); // Id = 106 ) ) clasa Persoană( ID privat int; static int contor; static( counter = 105; System.out.println("Inițializator static"); ) Persoană())( id=counter++; System.out.println("Constructor" " ); ) public void displayId())( System.out.printf ("Id: %d \n", id); ) )

Un inițializator static este definit ca un inițializator obișnuit, doar că este precedat de cuvântul cheie static . În acest caz, în inițializatorul static am setat valoarea initiala contor câmp static și afișați un mesaj pe consolă.

În programul propriu-zis, sunt create două obiecte ale clasei Person. Prin urmare, ieșirea consolei va arăta astfel:

Inițializator static Constructor ID constructor: 105 Id: 106

Merită să luați în considerare faptul că inițializatorul static este apelat numai înainte ca obiectul de primă clasă să fie creat.

Metode statice

Metodele statice se aplică, de asemenea, întregii clase ca întreg. De exemplu, în exemplul de mai sus, contorul de variabile statice era accesibil din exterior și i-am putea schimba valoarea în afara clasei Person. Să-l facem indisponibil pentru schimbare din exterior, dar accesibil pentru citire. Pentru a face acest lucru, folosim o metodă statică:

Public class Program( public static void main(String args) ( Person.displayCounter(); // Counter: 1 Person tom = new Person(); Person bob = new Person(); Person.displayCounter(); // Counter: 3 ) ) clasă Person( private int id; private static int counter = 1; Person())( id = counter++; ) // metoda statică public static void displayCounter())( System.out.printf("Counter: %d \n ", contor); ) public void displayId())( System.out.printf ("Id: %d \n", id); ) )

Acum variabila statică nu este accesibilă din exterior, este privată. Și valoarea sa este afișată folosind metoda statică displayCounter. Pentru a apela o metodă statică, utilizați numele clasei: Person.displayCounter() .

Când folosim metode statice, trebuie să ținem cont de limitări: în metodele statice, putem apela doar alte metode statice și folosim doar variabile statice.

În general, metodele sunt definite ca fiind statice atunci când metodele nu vor afecta starea obiectului, adică câmpurile și constantele non-statice ale acestuia și nu are rost să creezi o instanță a clasei pentru a apela metoda. De exemplu:

Clasa publică Program( public static void main(String args) ( System.out.println(Operation.sum(45, 23)); // 68 System.out.println(Operation.subtract(45, 23)); // 22 System.out.println(Operation.multiply(4, 23)); // 92 ) class Operation( static int sum(int x, int y)( return x + y; ) static int subtract(int x, int y) )( return x - y; ) static int multiplicare(int x, int y)( return x * y; ) )

În acest caz, pentru metodele sum, scădere, înmulțire, nu contează ce instanță a clasei Operation este folosită. Aceste metode funcționează numai cu parametri, fără a afecta starea clasei. Prin urmare, ele pot fi definite ca statice.

O metodă statică este un fragment de program căruia i se atribuie unele nume unic, și care poate fi numit cu acest nume din alte părți ale programului. În momentul în care apare apelul, sunt efectuate acțiunile enumerate în interiorul metodei (în descrierea sau corpul acesteia).
În programarea orientată pe obiecte, sarcina principală a metodelor este de a schimba starea curentă a unui obiect, dar atâta timp cât obiectele nu sunt încă utilizate în program, metodele pot fi deja introduse. O metodă care este definită în cadrul unei clase, dar este apelată fără a fi aplicată unui obiect specific al acelei clase se numește static.

În plus față de numele și descrierea menționate mai sus, metoda are o serie de alte caracteristici:

  1. Un set de modificatori.
  2. Tipul valorii returnate.
  3. Un set de argumente (parametri).

Modificatori de metodă

Pentru a crea o metodă statică, trebuie să specificați modificatorul static înaintea numelui său. Dacă acest lucru nu se face, atunci metoda poate fi apelată într-o aplicație doar la un obiect specific din această clasă (va fi non-statică).

Modificatorul public este responsabil pentru nivelul de acces la metoda descrisă. În loc de public, pot fi specificate nivelurile de acces private sau protect sau nimic nu poate fi specificat, caz în care se va aplica nivelul de acces implicit.

Ne vom familiariza cu nivelurile de acces mai detaliat atunci când ne vom crea propriile clase, dar deocamdată observăm că accesul implicit permite accesul la o metodă din orice parte a pachetului în care este descrisă metoda. Iar nivelul public permite accesul la metodă de oriunde. Inclusiv din alte pachete și programe.

Metoda principală trebuie să aibă nivel de acces public tocmai pentru că este accesată mașină virtuală Java care nu face parte din niciun pachet.

În plus, există și alți modificatori care, de exemplu, vă permit să reglați funcționarea metodelor în timpul calculelor paralele (multi-threaded).

Tip de returnare

Metodele din Java pot fi împărțite în două grupe: funcții și proceduri. Primul grup include metode care sunt foarte asemănătoare cu funcțiile în sens matematic. Ca rezultat al muncii lor, astfel de metode returnează un anumit rezultat în locul din programul din care au fost chemate. tip existent, adică poate fi un număr întreg sau numar real sau valoare logică (int, double, boolean), matrice (referință la acesta), obiect (referință la acesta). Valoarea returnată trebuie să fie atribuită unei variabile tip potrivit sau trecut la o altă metodă ca argument.

Spre deosebire de funcții, metodele de tip procedural produc un fel de acțiuni utile, dar nu dați un rezultat complet care ar putea fi exprimat într-o anumită valoare sau obiect.

Double r = Math.random(); /* aleatoriu se referă la funcții */ System.out.println(r); /* println se referă la proceduri */

Dacă am crea o metodă care, ca println, ar imprima text pe ecran, dar în același timp numără numărul de spații din text și returnează acest rezultat, am obține o funcție. În acest caz, funcția va continua să efectueze acțiuni utile caracteristice procedurii println. În consecință, o funcție este mai universală decât o procedură, dar nu este întotdeauna necesară.

Când creați o metodă, primul pas este să determinați dacă va fi o funcție sau o procedură. Pentru calculele intermediare se folosesc de obicei funcții. De asemenea, procedurile pot fi utilizate pentru a scurta fragmente de cod similare.

După modificatori, dar și în stânga numelui metodei, este indicat tipul de valoare pe care o returnează (dacă metoda este o funcție, de exemplu: int sau double) sau cuvântul void (dacă metoda este o procedură).

Dacă o metodă este o funcție, atunci trebuie să conțină o comandă return, după care, separată de un spațiu, este indicată expresia a cărei valoare trebuie returnată ca rezultat al metodei.

Toate comenzile specificate în descrierea metodei după returnare nu vor mai fi executate return fără argument pot fi folosite în cadrul procedurilor. Pur și simplu va încheia procedura înainte de program (analog pentru întrerupere pentru o buclă).

Argumente (parametri)

La apelarea unei metode, un set de anumite valori i se poate transmite din programul principal. Pentru a învăța metoda să le accepte (și să le proceseze în cadrul metodei), în parantezele după numele metodei, perechile de formular ar trebui să fie listate: argument_type argument_name separate prin virgule.

Apoi, la apelarea unei metode, va fi posibil să specificați un set de valori corespunzătoare tipurilor descrise de argumente.

Valorile care sunt transmise metodei în momentul apelului se numesc parametri reali, iar numele argumentelor care apar în descrierea metodei se numesc parametri formali.

Fiecare parametru formal este o variabilă locală în interiorul metodei, adică nu este disponibil în afara metodei (în afara blocului de descriere). Când metoda este apelată, valoarea reală este copiată în parametrul formal.

În special, aceasta înseamnă că dacă trecem orice variabilă de tip de bază ca parametru unei metode atunci când este apelată, nu vom putea modifica valoarea acestei variabile în programul principal. Dacă orice obiect sau matrice este transmis unei metode printr-un argument, atunci numai o referință la obiect sau matrice (adică, adresa lor în memorie) este copiată în interiorul metodei. Acțiunile pe care le efectuăm asupra unui tablou sau obiect din interiorul unei metode vor afecta starea acestui tablou sau obiect în programul principal chiar și după ce metoda își finalizează activitatea. În cadrul metodei, am accesat aceeași adresă și am lucrat cu aceleași date în memorie care erau disponibile în programul principal.

Dacă numele parametrului actual se potrivește cu numele parametrului formal, atunci acest lucru nu implică nicio problemă: în interiorul metodei există o variabilă locală, în care valoarea variabilei globale cu același nume este copiată atunci când este apelată. Accesând acest nume în interiorul metodei, vom ajunge la o variabilă locală și nu vom putea ajunge la cea globală.

Descrierea metodei

Metoda trebuie descrisă în interiorul clasei, dar o metodă nu este descrisă în interiorul celeilalte, adică metoda trebuie să fie imbricată direct în blocul de clasă.

Schema generală de descriere a metodei:

Modificatori return_value_type method_name (argumente formale) ( // acțiuni efectuate de metodă // eventual, return )

În mod tradițional, numele metodei trebuie să înceapă cu o literă mică. Dacă este format din mai multe cuvinte, fiecare cuvânt ulterior începe cu majusculă. Numele metodei este ales astfel încât să fie clar ce face.

Să ne uităm la câteva exemple:

Public static double kvadk (dublu) ( dublu t; t = Math.pow(a, 0,5); return t; )

Acum, în cadrul metodei principale, putem folosi metoda noastră. De exemplu, așa:

Int a = 25; System.out.println(kvadk(a)); // 5.0 System.out.println(a) // 25

Când se transmit parametri reali unei metode, are loc autocasting. Dacă argumentul real nu se potrivește cu tipul formal, atunci Java încearcă să arunce argumentul real într-un tip mai generic (în acest caz, int a fost turnat la dublu).

Supraîncărcarea metodei

Semnătura unei metode este combinația dintre numele acesteia și un set de parametri formali.
Java vă permite să creați mai multe metode cu aceleasi nume, dar cu semnături diferite. Crearea unei metode cu același nume, dar cu un set diferit de parametri se numește supraîncărcare. Java determină ce metodă supraîncărcată ar trebui executată atunci când este apelată pe baza parametrilor actuali.

Void pr(double a) ( System.out.println(a); ) void pr (String a) ( System.out.println(a); ) void pr(int a) ( pentru (int i=0; i

Un exemplu de utilizare a metodei.

Int a = 5; int m = (1, 2, 8, 3) șir s = „Lumea”; pr (a) //metoda originală funcționează pr (a+s); // Lumea 5, prima suprasarcină rulează pr (m); // 1 2 8 3 pr (m+a); // eroare

Variabila a nu este de tip double, dar este procesată prin metoda originală, deoarece este posibilă transformarea automată de la int la double. Este imposibil în sens invers. Dacă metoda ar avea un argument de tip int, atunci nu ar putea scoate numere reale.

Supraîncărcarea metodei implementează o proprietate atât de importantă în programare precum polimorfismul. Polimorfic este un cod software care este asociat cu un nume comun, dar are implementări diferite. Ce implementare va funcționa este aleasă în funcție de contextul în care a fost menționat numele. În special pentru metode, supraîncărcările lor sunt polimorfe, iar selecția supraîncărcării executabile are loc în funcție de parametri.

Polimorfism: un nume, multe forme.

Exemple de utilizare a metodelor

Următorul program caută și afișează toți divizorii primi netriviali ai unui număr introdus de utilizator de la tastatură, începând cu cel mai mare divizor, sau raportează că numărul introdus este prim.

Import java.util.Scanner; clasă publică Main ( public static boolean isPrime(int n) ( for(int i = 2; i<= Math.sqrt(n) ; i++) { if(n%i == 0) { return false; } } return true; } public static void main(String args) { Scanner sc = new Scanner(System.in); System.out.print("Введите натуральное число: "); if(sc.hasNextInt()) { int u = sc.nextInt(); if(u >0) ( if(isPrime(u)) ( System.out.println ("Ați introdus un număr prim"); ) else ( System.out.print ("Factori primi ai numărului: "); for(int i = (int) Math.sqrt(u); i >= 2 ; i--) ( if(u%i == 0 && isPrime(i)) ( System.out.print(i+" "); ) ) Sistem. out.println (); ) ) else ( System.out.println(„Nu ați introdus un număr pozitiv”); ) ) else ( System.out.println(„Nu ați introdus un număr întreg”); ) ) )

În exemplul următor, mai multe metode cu același nume vor fi create din cauza supraîncărcării.

Prima versiune a metodei va traduce pur și simplu șirul, adică va fi de fapt un sinonim mai scurt pentru metoda încorporată System.out.println(). Această opțiune nu va avea parametri.

A doua variantă a metodei (prima sa supraîncărcare), verifică dacă argumentul numeric are fracțiune, dacă nu este acolo, atunci argumentul este convertit în numere întregi și afișat pe ecran fără o parte fracțională zero (3 în loc de 3,0). Această metodă va putea trece nu numai variabile de tip dublu, dar și variabile de orice alt tip pentru care este posibilă conversia automată în dublu (de exemplu, orice variabile întregi).

A treia metodă cu un parametru apelează pur și simplu a patra metodă, trecând matricea rezultată ca parametri, precum și un spațiu ca al doilea parametru. Vă rugăm să rețineți că apelăm la o metodă care va fi descrisă mai târziu în program, aceasta este complet acceptabilă.

A patra metodă produce o matrice numerică, procesând deja fiecare element metoda existenta. După fiecare element afișat, se adaugă separatorul trecut în parametru.

Clasa publică Main ( public static void pr() ( System.out.println(); ) public static void pr(double d) ( if((int)d == d) ( System.out.print((int)d) ) else ( System.out.print(d); ) ) public static void pr(double m) ( pr(m, " "); ) public static void pr(double m, String s) ( for(int i = 0;< m.length; i++) { pr(m[i]); System.out.print(s); } } public static void main(String args) { double arrn = {1, 2.71, 3.14, 15, -5, 92, 0.5}; double p = 3.0; int k = 13; pr(p); // вывод числа, без дробной части при возможности pr(); // переводит строку pr(arrn); // вывод числового массива в строку pr(); // переводит строку pr(arrn,", "); // вывод числового массива в строку через запятую pr(); // переводит строку pr(k); // вывод целого числа через автоприведение } }

Ca urmare a rulării programului, pe ecran vor fi afișate următoarele:

3 1 2.71 3.14 15 -5 92 0.5 1, 2.71, 3.14, 15, -5, 92, 0.5, 1

Sarcini

  1. Creați o metodă statică care va avea doi parametri întregi a și b și va returna un număr întreg aleator din segment ca valoare. Folosind această metodă, completați o matrice de 20 de numere întregi și afișați-o pe ecran.
  2. Creați o metodă care va afișa matricea specificată pe ecran ca șir. Folosind metoda creată și metoda din sarcina anterioară, umpleți 5 matrice a câte 10 elemente fiecare numere aleatoriiși afișați toate cele 5 matrice pe ecran, fiecare pe o linie separată.
  3. Creați o metodă care va sorta matricea specificată în ordine crescătoare în orice mod pe care îl cunoașteți.
  4. Matricea stochează 7 specificate în mod explicit șiruri de text. Scrieți un program care va sorta și afișa șiruri de caractere ordine alfabetică. De exemplu, dacă s-au dat următoarele rânduri:
Pușkin Lermontov Nekrasov Tolstoi L. N. Tolstoi A. N. Yesenin Paustovsky

Programul ar trebui să afișeze:

Yesenin Lermontov Nekrasov Paustovsky Pușkin Tolstoi A. N. Tolstoi L. N.

Sugestie: În primul rând, trebuie să creați o metodă care să stabilească relația de ordonare pentru cele două șiruri de caractere transmise metodei ca argumente.

Recursiune

Recursiunea este o metodă (funcție) care se numește în corpul său.

Să luăm în considerare un exemplu - calculul factorial. Pentru a calcula n!, este suficient să cunoaștem și să înmulțim (n-1)! si n.

Să creăm o metodă care implementează metoda descrisă.

Fapt int static (int n) ( if (n==1) ( return 1; ) else if (n==2) ( return 2; ) else ( return fact(n-1) * n; ) )

Metoda specificată calculează factorialul unui număr natural.

Să luăm în considerare un exemplu care calculează al n-lea număr Fibonacci prin recursivitate.

Să ne amintim cum arată primele elemente ale acestei serii: 1 1 2 3 5 8 13 ...

Static int fib (int n) ( dacă (n==1 || n == 2) ( return 1; ) return fib (n-2) + fib (n-1); )

Rețineți că în această metodă a doua returnare nu este plasată în blocul else pentru primul operator condițional. Acest lucru este permis, deoarece dacă condiția este îndeplinită și prima retur este declanșată, atunci execuția programului va ajunge la a doua retur dacă condiția nu este îndeplinită;

Calculele recursive duc adesea la necesitatea de a repeta aceleași acțiuni, ceea ce încetinește semnificativ programul.

Sarcini

  1. Aflați experimental, pornind de la ce element al șirului Fibonacci, calculul folosind recursiunea devine inacceptabil (durează mai mult de un minut).
  2. Creați o metodă hibridă care, pentru n mic, calculează al n-lea număr Fibonacci folosind recursivitate, iar pentru valori mai mari decât pragul n găsit în problema anterioară, calculează al n-lea număr Fibonacci folosind un algoritm iterativ (o buclă în care valorile a două elemente anterioare ale secvenței).
  3. Calculați de câte ori ar fi necesar să recalculați al patrulea element al șirului Fibonacci pentru a calcula al cincisprezecelea element.

Stack de apeluri

ÎN caz general numai unul poate fi executat la momentul curent singura metoda din întregul program. Aceasta înseamnă că dacă metoda A este conceput în așa fel încât în ​​corpul său numește o metodă b, Și tu A a sunat principal, apoi când programul începe, controlul va fi mai întâi transferat la metodă principal, apoi metoda A, apoi metoda b. Metodă b va returna rezultatul si controlul la A, A va returna rezultatul controlului la principal, și abia atunci vor fi executate comenzi de bază specificate în metodă principal pe liniile rămase după apel A.

Întreaga ierarhie (cine a sunat pe cine) este stocată într-o zonă specială de memorie numită stiva de apeluri. La acest fragment de memorie se adaugă elemente conform la următorul principiu: Ultimul element adăugat ar trebui să fie preluat mai întâi. Când funcționează metoda? b, se dovedește că sub el pe stivă există o metodă A si metoda principal.

Din acest motiv, există pericolul de depășire a stivei de apeluri în timpul procesului de recursivitate.

Există o așa-numită recursivitate complexă, în care metoda A apelează la o metodă b, b cauze Cu, A Cu cauze A.

2010, Alexey Nikolaevich Kostin. Departamentul TIDM, Facultatea de Matematică, Universitatea Pedagogică de Stat din Moscova.