Filosofie cea mai recentă ediție java. Bruce Eckel - Filosofia Java3. Programare pe partea serverului

  • 12.04.2020

Pentru a schimba documentul implicit, editați manual fișierul „blank.fb2”.

Prefața 13

Java SE5 și SE6 14

Multumesc 14

Capitolul 1 Introducere în obiecte 17

Dezvoltarea abstractizării 18

Obiectul are interfața 20

Unitatea oferă servicii 22

Implementare ascunsă 23

Implementarea reutilizarii 24

Moștenirea 25

Obiecte interschimbabile și polimorfism 29

Ierarhia rădăcină unică 33

Containere 33

Tipuri parametrizate 35

Crearea, utilizarea obiectelor și durata lor de viață 36

Gestionarea excepțiilor: tratarea erorilor 38

Execuție paralelă 38

Java și Internetul 39

Capitolul 2. Totul este un obiect 48

Toate obiectele trebuie create în mod explicit 49

Obiectele nu trebuie niciodată șterse 53

Crearea de noi tipuri de date 54

Metode, argumente și valori returnate 56

Crearea unui program Java 58

Cuvânt cheie static 60

Primul nostru program Java 61

Comentarii și documentație încorporată 64

Stilul de proiectare a programului 70

Capitolul 3 Operatori 71

Comenzi simple de imprimare 71

Instrucțiuni Java 72

Literale 82

Java nu are dimensiunea () 92

Reluați 100

Capitolul 4 Structuri de control 101

Sintaxă foreach 105

rupe și continuă 108

Comanda greșită a ajuns la 109

Reluați 115

Capitolul 5 Inițializare și terminare 116

Constructorul garantează inițializarea 116

Supraîncărcarea metodei 118

Curățare: finalizare și colectarea gunoiului 130

Inițializarea membrilor clasei 137

Inițializarea constructorului 140

Inițializarea matricei 146

Reluarea 151

Capitolul 6 Controlul accesului 152

Pachet ca modul de bibliotecă 153

Specificatori de acces Java 159

Interfață și implementare 163

Acces la clasele 164

Reluarea 167

Capitolul 7 Reutilizarea claselor 169

Sintaxa compoziției 170

Sintaxa moștenirii 172

Delegația 176

Combinarea compoziției și a moștenirii 178

Compoziție versus moștenire 184

Conversie de tip ascendent 186

Cuvânt cheie final 188

Reluarea 197

Capitolul 8. Polimorfismul 198

Din nou despre transformarea ascendentă. . . > 199

Caracteristici 201

Constructori și polimorfism 208

Covarianța tipului returnat 216

Dezvoltare cu moștenire 217

Reluați 220

Capitolul 9 Interfețe 221

Clase și metode abstracte 221

Interfețe 224

Separarea interfeței de implementare 227

Extinderea unei interfețe prin moștenire 233

Interfețele ca mijloc de adaptare 236

Interfețe imbricate 239

Interfețe și fabrici 242

Reluați 244

Capitolul 10 Clasele interioare 245

Crearea claselor interioare 245

Comunicare cu o clasă externă 246

Construcțiile .this și .new 248

Clase interioare și upcasting 249

Clase interne fără nume 253

Clasele interioare: de ce? 261

Moștenirea din clasele interioare 272

Este posibil să trecem peste o clasă interioară? 272

Clasele interne locale 274

Reluarea 276

Capitolul 11 ​​Colecții de obiecte 277

Containere parametrizate și tipizate 277

Concepte de bază 280

Adăugarea grupurilor de elemente 281

Iteratoare 288

Setul 294

Coada 298

PriorityQueue 299

Colecție și Iterator 301

Expresia „metodă-adaptor” 306

Reluați 309

Capitolul 12 Gestionarea erorilor și excepțiilor 310

Excepții majore 310

Prinderea a 312 de excepții

Crearea propriilor excepții 314

Specificații de excepție 319

Prinderea excepțiilor arbitrare 320

Java 328 Excepții standard

Terminare cu 330 în sfârșit

Folosind în final cu returnare 334

Limitări la utilizarea excepțiilor 336

Constructori 339

Identificarea excepției 343

Soluții alternative 344

Reluați 351

Capitolul 13 Informații despre tip 352

Nevoia de inferență de tip dinamic (RTTI) 352

Înregistrarea fabricilor 372

Reflecție: informații dinamice ale clasei 376

Mediatori dinamici 380

Obiecte cu o stare nedeterminată 384

Interfețe și informații despre tip 390

CV 394

Capitolul 14 Parametrizare 397

Parametrizare simplă 398

Interfețe parametrizate 404

Metode parametrizate 407

Construirea modelelor complexe 419

Restricții 437

Metacaracterele 440

Reluați 452

Capitolul 15 Matrice 454

Caracteristicile matricelor 454

Matrice ca obiect 456

Returnare matrice 458

Rețele multidimensionale 460

Matrice și parametrizare 463

Crearea datelor de testare 465

Crearea de matrice folosind generatoare 470

Arrays 474 Helper Toolkit

Reluarea 482

Capitolul 16 Sistemul Java I/O 483

Dosar clasa 484

Intrare și ieșire 489

Adăugarea de atribute și interfețe 491

Clasele de cititor și scriitor 494

RandomAccessFile: singur 497

Utilizarea tipică a fluxurilor I/O 498

Cititoare și scriitoare de fișiere 505

I/O standard 507

I/O noi (nio) 510

Comprimarea datelor 531

Serializarea obiectelor 536

Preferințe 553

Reluați 555

Capitolul 17 Execuție paralelă 557

Clasa de fire 559

Artiștii 561

Partajarea resurselor 578

Comunicarea între fire 598

Blocare reciprocă 602

Componente noi de bibliotecă 607

CountdownLatch 607

CyclicBarrier 609

PriorityBlockingQueue 614

Semafoare 619

Modelare 624

Reluați 629

Index alfabetic 631

Introducere în obiecte

Disectăm natura, o transformăm în concepte și le atribuim un sens, așa cum facem în multe feluri, pentru că toți suntem părți la un acord care este valabil într-o societate legată de vorbire și care este fixat în structura limbajului. .. Nu putem comunica deloc, decât prin acordul cu organizarea și clasificarea datelor stabilite prin prezentul acord.

Benjamin Lee Worf (1897-1941)

Apariția revoluției computerului îi datorăm mașinii. Prin urmare, limbajele noastre de programare încearcă să fie mai aproape de această mașină.

Dar, în același timp, computerele nu sunt atât mecanisme, ci mijloace de amplificare a gândirii („biciclete pentru minte”, cum îi place să spună lui Steve Jobs) și un alt mijloc de auto-exprimare. Ca urmare, instrumentele de programare se înclină mai puțin spre mașini și mai mult spre mintea noastră, precum și către alte forme de exprimare a aspirațiilor umane, cum ar fi literatura, pictura, sculptura, animația și cinematografia. Programarea orientată pe obiecte (OOP) face parte din transformarea computerului într-un vehicul pentru auto-exprimare.

Acest capitol vă va prezenta elementele de bază ale POO, inclusiv o privire asupra principalelor metode de dezvoltare software. Ea, și cartea în general, presupune că aveți experiență de programare într-un limbaj procedural, nu neapărat C. Dacă simțiți că înainte de a citi această carte vă lipsesc cunoștințele despre programare și sintaxa C, folosiți seminarul multimedia Thinking in C. care poate fi descărcat de pe site

În luna martie a anului trecut, a aplicat la o ramură a unui mare companie internationalaîn Samara (da. Am avut multă aroganță și ambiție încă din copilărie). La acea vreme știam html, css, java, javascript (de bază), pascal, visualbasic6, interogări mysql, php, ideea generala:c++. Nu cunoșteam Java deloc. Mi-au oferit un loc de muncă ca layout designer, dar am refuzat-o. Doar ca programator! Apoi mi-au dat o listă:

Bruce Eckel Thinking in Java (traducere în rusă a ediției a 2-a sau originalul celei de-a 4-a - citiți ambele)
-Steve McConnell - cod perfect.
- Gang of four - Design model. (acesta este aproape ABC-ul OOP)
-fii cât mai clar cu privire la diferența dintre j2se și j2ee.

în decembrie a apărut nevoia. am găsit un loc de muncă într-un mic studio web din Samara. a fost imediat clar că aceștia sunt escroci, dar era nevoie de orice muncă pe care o puteam arăta viitorilor angajatori. nu au plătit să plătească (chiar dacă toate urechile au fost mâncate cu promisiuni), dar codul a fost adus la standardele de proiectare și, cel mai important, au învățat unde să caute și ce să caute în caz de erori, ce puțin lucrurile sunt ușor de ratat.

Pe lângă literatura de mai sus, am urmat cursul intuit (acum înțeleg că este ridicol în domeniul său de aplicare, dar în principiu există elemente de bază acolo)

La sfârșitul lunii februarie, mi-am retrimis CV-ul și am primit o invitație pentru un interviu. Au fost 6 interviuri în total și au durat 1,5 luni. Două dintre ele au fost reținute prin intermediul unei legături video cu Moscova. Întreaga imagine amintea de filmul „Vino mâine”. Dar până la urmă am primit o ofertă de muncă. Contractul a fost întocmit pentru angajare cu jumătate de normă, deoarece. Nu aveam o diplomă la momentul respectiv. Luna trecută am primit diplomă și contractul a fost reînnoit pentru unul full-time.

Poziția actuală de Soft-Engineer. Salariul este mai mult decât satisfăcător. Ieri, în legătură cu trecerea la un loc de muncă cu normă întreagă, l-au majorat cu 30%.

Chiar și în biroul acela de escroc au cerut exemple de muncă. Am prezentat munca depusă de mine freelance. Chiar și lucrările în alte limbi sunt întotdeauna mult mai bune decât niciuna.

Ps: Diploma albastra la FIZICA. Sunt complet autodidact, așa că totul este în mâinile tale. Am doar engleza de la scoala Gratuit (7 ore pe saptamana). deşi americanul care a venit la noi în călătoria lui în jurul lumii nu îl cunoaşte bine. Abia am înțeles jumătate din cauza accentului lui. dar acest lucru nu este atât de critic în departamentul meu. toată documentația în engleză - vei învăța chiar dacă nu știai)))))

Mulțumiri speciale acestui forum. De fapt, am studiat aici- a predat zilnic toate subiectele care apar)

BIBLIOTECA PROGRAMATORULUI

Bruce Eckel

editia a 4-a

(^PPTER

Moscova - Sankt Petersburg - Nijni Novgorod - Voronezh Rostov-pe-Don - Ekaterinburg - Samara - Novosibirsk Kiev - Harkov - Minsk

BBK 32.973 2-018.1

Eckel B.

E38 Filosofia Java. Biblioteca programatorului. a 4-a ed. - Sankt Petersburg: Peter, 2009. - 640 e.: ill. - (Seria „Biblioteca programatorului”).

ISBN 978-5-388-00003-3

Java nu poate fi înțeles privindu-l doar ca o colecție a unor harakureshki - este necesar să înțelegem sarcinile acestui limbaj ca probleme speciale de programare în general. r3ia este o carte despre probleme de programare: de ce au devenit probleme și ce abordare folosește Java pentru a le rezolva. Prin urmare, trăsăturile limbajului discutat în fiecare capitol sunt indisolubil legate de modul în care sunt folosite pentru a rezolva anumite probleme.

Această carte, care a rezistat multor retipăriri în original, datorită prezentării sale profunde și cu adevărat filozofice a complexităților limbajului, este considerată unul dintre cele mai bune manuale pentru programatorii Java.

BBK 32.973.2-018.1 UDC 004.3

Drepturi de publicare obținute prin acord cu Prentice Hall PTR.

Toate drepturile rezervate. Nicio parte a acestei cărți nu poate fi reprodusă sub nicio formă fără permisiunea scrisă a deținătorilor drepturilor de autor.

Informațiile conținute în această carte au fost obținute din surse considerate de către editor a fi de încredere. Cu toate acestea, având în vedere eventualele erori umane sau tehnice, editorul nu poate garanta acuratețea și caracterul complet al informațiilor furnizate și nu este responsabil pentru posibile greșeli legate de utilizarea cărții.

ISBN 978-0131872486 © Prentice Hall PTR, 2006

ISBN 978-5-388-00003-3 © Traducere în rusă de Peter Press LLC, 2009

© Publicație în limba rusă, proiectat de Piter Press LLC, 2009

Cuvânt înainte.................................13

Java SE5 și SE6..............................14

Mulțumiri..................................14

Capitolul 1 Introducere în obiecte..............................17

Dezvoltarea abstracţiei .........................18

Un obiect are o interfață..............................20

Unitatea furnizează servicii..................................22

Implementare ascunsă.................................23

Reutilizarea implementării.................................24

Moștenirea.................................25

Obiecte interschimbabile și polimorfism..................................29

Ierarhie cu rădăcină unică.............................33

Containere..............................33

Tipuri parametrizate.................................35

Crearea, folosirea obiectelor și timpul vieții acestora.......................36

Gestionarea excepțiilor: tratarea erorilor..............................38

Execuție paralelă.................................38

Java și Internetul..................................39

Rezumat...................................47

Capitolul 2 Totul este un obiect...................................48

Toate obiectele trebuie create în mod explicit..............................49

Obiectele nu trebuie niciodată șterse..................................53

Crearea de noi tipuri de date..................................54

Metode, argumente și valori returnate...............................56

Crearea unui program Java..................................58

Cuvânt cheie static..............................60

Primul nostru program Java.................................61

Comentarii și documentație încorporată...................................64

Stilul de programare ..................................70

Rezumat...................................70

Capitolul 3. Operatori.................................71

Comenzi simple de imprimare.................................71

Declaraţii Java.................................72

Literale..................................82

Java nu are sizeof() ................................................92

Reluare.................................100

Capitolul 4 Structuri de control ..................101

Sintaxă foreach.................................105

întoarcere, .................................107

rupe și continuă.............................................108

Comandă de acces proastă ..........................109

Rezumat...................................115

Capitolul 5. Iniţializare şi terminare...........................116

Inițializarea garanțiilor constructorului..................................116

Supraîncărcarea metodei.................................118

Curățare: finalizare și colectare a gunoiului..................................130

Inițializarea membrilor clasei..............................137

Inițializarea constructorului ...............................140

Inițializarea tablourilor.................................146

Rezumat..............................151

Capitolul 6 Controlul accesului ..........................152

Pachetul ca modul de bibliotecă..............................153

Specificatori de acces Java.................................159

Interfaţă şi implementare.................................163

Accesarea claselor.................................164

Rezumat...................................167

Capitolul 7 Reutilizarea claselor.................................169

Sintaxa compoziţiei..............................170

Sintaxa moștenirii.................................172

Delegare.................................176

Combinarea compoziției și moștenirii..................................178

Compoziţie versus moştenire..................................184

protejat.................................185

Conversie ascendentă a tipului......................................186

Cuvânt cheie final..............................188

Rezumat...................................197

Capitolul 8 Polimorfismul

Din nou despre transformarea ascendentă. . . >>................199

Caracteristici..................................201

Constructori şi polimorfism..............................208

Covarianța tipului de returnare.................................216

Proiectarea cu moștenire..................................217

Reluare.................................220

Capitolul 9. Interfeţe.......................221

Clase și metode abstracte.................................................221

Interfeţe..............................224

Separarea interfeţei de implementare..............................227

Extinderea unei interfeţe prin moştenire...........................233

Interfeţele ca mijloc de adaptare.................................................236

Interfeţe imbricate.................................239

Interfeţe şi fabrici..................................242

Reluare..............................244

Capitolul 10 Clasele interioare.................................245

Crearea claselor interioare.................................245

Comunicarea cu o clasă externă.................................246

Construcțiile .this și .new..................................248

Clasele interioare și conversia ascendentă.......................249

Clase interioare nenumite......................253

Clasele interioare: de ce? ................................. 261

Moștenirea din clasele interioare.................................272

Este posibil să trecem peste o clasă interioară?......................272

Clasele interioare locale..............................274

Rezumat.................................276

Capitolul 11 ​​Colecţii de obiecte..............................277

Containere parametrizate și tipizate.......................277

Concepte de bază.................................280

Adăugarea de grupuri de elemente.................................281

Lista.................................285

Iteratoare..............................288

LinkedList..............................291

Stiva.................................292

Mulți................................................. 294

Harta.................................296

Coadă..................................298

PriorityQueue.................................299

Colecție și Iterator..................................301

Expresia „metodă-adaptor” .........................306

Reluare..............................309

Capitolul 12 Gestionarea erorilor și excepțiilor..................310

Excepții majore..................................310

Captarea excepțiilor..................................312

Crearea propriilor excepții..............................314

Specificații de excepție.................................319

Prinderea excepțiilor arbitrare..................................320

Excepții Java standard..................................328

Terminând cu în cele din urmă.......................330

Folosind in sfarsit cu returnare.................334

Restricții privind utilizarea excepțiilor..................................336

Constructori..............................339

Identificarea excepţiilor..............................343

Soluții alternative..................................344

Rezumat...................................351

Capitolul 13 Informații de tip..................................352

Nevoia de inferență de tip dinamic (RTTI) .........352

Înregistrarea fabricii ................................372

Reflecție: informații dinamice despre o clasă..................................376

Intermediari dinamici..............................380

Obiecte cu stare nedeterminată..................................384

Interfețe și informații despre tip..............................390

Reluare..............................394

Capitolul 14

Parametrizare simplă.................................398

Interfeţe parametrizate.................................404

Metode parametrizate...................407

Construire modele complexe..............................419

Restricţii.................................437

Metacaracterele..............................440

Reluare.................................452

Capitolul 15. Matrice.................................454

Caracteristici ale matricei.................................454

Matrice ca obiect..............................456

Returnarea unei matrice.................................458

Matrice multidimensionale.................................460

Matrice și parametrizare..............................463

Crearea datelor de testare.................................465

Crearea matricei utilizând generatoare......................470

Setul de instrumente Arrays Helper..............................474

CV.................................482

Capitolul 16 Sistemul Java I/O..............................483

Clasa de fișiere.................................484

Intrare și ieșire.................................489

Adăugarea de atribute și interfețe..................................491

Orele de cititor și scriitor.................................494

RandomAccessFile: de la sine.......................497

Utilizarea tipică a fluxurilor I/O.................................498

Cititori și scriitori de fișiere...................................505

Probabil că nu mă înșel presupunând că majoritatea cursanților de Java au început să o facă cu ajutorul celebrei cărți a lui Bruce Eckel: „Gândirea în Java”, cunoscut în ediția rusă ca „Filosofia Java”. Din pacate in în format electronic(în limba rusă) ediția a II-a a acestei cărți este cea mai răspândită, bazată pe versiunea Java 1.1, care și-a pierdut de multă relevanță. Inovațiile apărute în următoarele versiuni de Java (și mai ales în Java SE5) au fost foarte semnificative, ceea ce a dus la o revizuire serioasă a cărții la a patra ediție (a cărei traducere a fost publicată în limba rusă). Cu toate acestea, într-un format electronic ușor de citit (și cel mai important - pentru căutare rapidă), versiunea în limba rusă a acestei publicații nu a existat. Prin urmare, am decis să umplu acest gol și să produc o versiune completă a acestei cărți populare în formatul „wikibook”. Cred că aceste informații vor fi interesante și utile nu numai pentru cei care învață limbi străine, ci și pentru toți cei care lucrează în Java, datorită numărului mare de exemple excelente care ilustrează aproape toate aspectele programării în acest limbaj. Mai ales când vine vorba de caracteristicile Java utilizate rar.

wikibook „Filosofia Java” postat la:

„Primăvara în acțiune”

Cărți din serie „.....în acțiune”(de obicei în format PDF și, de obicei, în engleză) sunt pe măsură de populare în anumite cercuri :) Printre ele se numără și Talmudele încăpătoare, precum „JSTL în acțiune”(ușor de citit și cu cunoștințe moderate de engleză, dar potrivit pentru rolul de referință bună pe subiect) și meșteșuguri mai modeste, cum ar fi Struts în acțiune("Nu totul este aur..."). Carte „Primăvara în acțiune”în această listă este încă din categoria „greilor”, și în toate sensurile cuvântului. Să o citești fără limba engleză fluentă, probabil, nu este ușor. Iar ideea nu este mai degrabă complexitatea materialului prezentat (nu este complicat), ci faptul că s-a dovedit - excesiv de „englez-artistic”, sau ceva.... Plin de digresiuni lirice, expresii înaripate, jocuri de cuvinte și alți bla bla bla, autori de limbi străine, transformă rapid citirea acestui ghid (în limba originală) într-un proces obositor. Dar, pe de altă parte, vă permite să știți că cuvântul "a desena"(de obicei - „tragere”) poate fi folosit în sensul „extras din” (lit. - „trage, trage”). Ca urmare (ținând cont de stilul general de prezentare adoptat în carte) să înțelegem sens exact fraze precum: "... trageți de primăvară aceste date...", se întâmplă în același timp - și nu este ușor, și este extrem de necesar. Prin urmare, cititorii capitolelor pe care nu le-am tradus vor trebui să decidă singuri ce au vrut autorii în astfel de cazuri: să se exprime poetic despre crearea (înregistrarea) unui fișier sau să povestească în mod jucăuș despre citirea lui.

Această carte a fost convertită de mine dintr-un PDF într-un wikibook ca referință expresă pentru uzul meu personal. Prin urmare, traducerea nu este totală, ci doar pe alocuri - pentru care a existat suficient entuziasm. Restul capitolelor au fost pur și simplu oferite într-o formă convenabilă pentru căutare rapidă. Este publicat, TOTUL în formă - „așa cum este”, și nu ar trebui să dai vina pe calitatea textului rus... Nu sunt un traducător profesionist și nu am avut un editor literar. Poate voi dezamăgi pe cineva cu faptul că nu am tradus unele locuri și capitole ale cărții (și nici nu am de gând să le traduc), dar a fost necesar să las o rezervă pentru generațiile viitoare.

wikibook "Primavara in actiune " postat la:

INTRODUCERE ÎN OBIECTE

Apariția revoluției computerului îi datorăm mașinii. Prin urmare, limbajele noastre de programare încearcă să fie mai aproape de această mașină.

Dar, în același timp, computerele nu sunt atât mecanisme, ci mijloace de amplificare a gândirii („biciclete pentru minte”, cum îi plăcea lui Steve Jobs să spună) și un alt mijloc de auto-exprimare. Ca urmare, instrumentele de programare se înclină mai puțin spre mașini și mai mult spre mintea noastră, precum și către alte forme de exprimare a aspirațiilor umane, cum ar fi literatura, pictura, sculptura, animația și cinematografia. Programarea orientată pe obiecte (OOP) face parte din transformarea computerului într-un mediu de auto-exprimare.

Acest capitol vă va prezenta elementele de bază ale POO, inclusiv o privire asupra tehnicilor de programare de bază. Ea, și cartea în general, presupune că aveți experiență de programare într-un limbaj procedural, nu neapărat C.

Acest capitol conține pregătitoare și materiale suplimentare. Mulți cititori preferă să-și imagineze mai întâi imaginea de ansamblu și abia apoi să înțeleagă complexitățile OOP. Prin urmare, multe dintre ideile din acest capitol servesc pentru a vă oferi o înțelegere solidă a OOP. Cu toate acestea, mulți oameni nu înțeleg ideea generală până când nu văd în mod specific cum funcționează lucrurile; astfel de oameni se blochează adesea în termeni generali, fără să aibă exemple în fața lor. Dacă sunteți unul dintre cei din urmă și sunteți dornic să începeți cu elementele de bază ale limbii, puteți trece la următorul capitol - săriți peste acesta nu va fi un obstacol în scrierea programelor sau în învățarea limbii. Cu toate acestea, ar trebui să reveniți la acest capitol mai târziu pentru a vă lărgi orizonturile și pentru a înțelege de ce obiectele sunt atât de importante și unde se încadrează în proiectarea programului.

Dezvoltarea abstracției

Toate limbajele de programare sunt construite pe abstractizare. Poate că dificultatea sarcinilor de rezolvat depinde direct de tipul și calitatea abstracției. Prin „tip” vreau să spun: „Ce anume facem abstracție?” Limbajul de asamblare este o mică abstractizare din computerul pe care rulează. Multe așa-numite limbi „de comandă” au fost create după el (cum ar fi Fortran, BASICși C) au fost abstracții de nivelul următor. Aceste limbaje aveau un avantaj semnificativ față de limbajul de asamblare, dar abstracția lor de bază încă te face să te gândești la structura computerului, mai degrabă decât la problema rezolvată. Programatorul trebuie să stabilească o relație între modelul mașinii (în „spațiul soluției”, care reprezintă locul în care este implementată soluția – de exemplu, un computer) și modelul problemei de rezolvat (în „spațiul problemei” , care este locul unde există problema - de exemplu, zona aplicată). Stabilirea unei conexiuni presupune un efort desprins de limbajul de programare propriu-zis; rezultatul sunt programe greu de scris și greu de întreținut. Nu numai atât, a creat și o întreagă industrie de „metodologii de programare”.

O alternativă la modelarea unei mașini este modelarea problemei care se rezolvă. Limbi timpurii precum LISPși APL, a ales o abordare specială a modelării lumii din jur („Toate problemele sunt rezolvate prin liste” sau, respectiv, „Algoritmii rezolvă totul”). PROLOG tratează toate problemele ca lanțuri de soluții. Au fost create limbaje pentru programare pe baza unui sistem de constrângeri și limbaje speciale în care programarea a fost efectuată prin manipularea structurilor grafice (sfera de aplicare a acestora din urmă s-a dovedit a fi prea îngustă). Fiecare dintre aceste abordări este bună într-o anumită zonă a problemelor de rezolvat, dar odată ce părăsești această zonă, devine dificil să le folosești.

Abordarea obiect merge un pas mai departe, oferind programatorului un mijloc de a reprezenta o sarcină în spațiul său. Această abordare este destul de generală și nu impune restricții asupra tipului de problemă care se rezolvă. Elementele spațiului problemei și reprezentările lor în spațiul soluției se numesc „obiecte”. (Probabil veți avea nevoie de alte obiecte care nu au analogi în spațiul problemei.) Ideea este că programul se poate adapta la specificul problemei prin crearea de noi tipuri de obiecte, astfel încât, în timp ce citiți codul care rezolvă problema, puteți simultan vezi cuvintele, descriindu-o. Aceasta este o abstractizare mai flexibilă și mai puternică, depășind tot ceea ce a existat înainte în capacitățile sale. Unii designeri de limbaje cred că programarea orientată pe obiecte în sine nu este suficientă pentru a rezolva toate problemele de programare și susțin o combinație de paradigme de programare diferite într-un singur limbaj. Se numesc astfel de limbi multi-paradigma(multiparadigme). Vezi cartea lui Timothy Budd Multiparadigm Programming in Leda (Addison-Wesley, 1995).. Astfel, OOP vă permite să descrieți sarcina în contextul sarcinii în sine, și nu în contextul computerului pe care va fi executată soluția. Cu toate acestea, conexiunea cu computerul este încă păstrată. Fiecare obiect este ca un mic computer; are o stare si operatii pe care le permite. O astfel de analogie se potrivește bine cu lumea exterioară, care este „realitatea dată nouă în obiecte” care au caracteristici și comportament.

Alan Kay a rezumat și a dedus cele cinci trăsături principale ale limbajului Convorbire scurtă- primul limbaj de succes orientat pe obiecte, unul dintre predecesori Java. Aceste caracteristici reprezintă o abordare „pură” academică a programării orientate pe obiecte:

  • Totul este un obiect. Gândiți-vă la un obiect ca la o variabilă rafinată; stochează date, dar poți „interoga” un obiect, cerându-i să efectueze operații asupra lui însuși. Teoretic, absolut orice componentă a problemei care se rezolvă (un câine, o clădire, un serviciu etc.) poate fi reprezentată ca obiect.
  • Un program este un grup de obiecte care își spun reciproc ce să facă prin mesaje. Pentru a face o cerere unui obiect, îi „trimiteți un mesaj”. Mai mult vizual, un mesaj poate fi reprezentat ca un apel la o metodă aparținând unui anumit obiect.
  • Fiecare obiect are propria „memorie” formată din alte obiecte. Cu alte cuvinte, tu creezi obiect nou prin încorporarea în el a obiectelor existente. Astfel, este posibil să se construiască un program arbitrar complex, ascunzând complexitatea generală în spatele simplității obiectelor individuale.
  • Fiecare obiect are un tip. Cu alți termeni, fiecare obiect este o instanță a unei clase, unde „clasa” este echivalentul cuvântului „tip”. Cea mai importantă diferență dintre clase constă tocmai în răspunsul la întrebarea: „Ce mesaje pot fi trimise unui obiect?”
  • Toate obiectele de un anumit tip pot primi aceleași mesaje. După cum vom vedea în curând, aceasta este o circumstanță foarte importantă. Întrucât un obiect de tip „cerc” este și un obiect de tip „formă”, este adevărat că un „cerc” este cu siguranță capabil să primească mesaje pentru o „formă”. Și asta înseamnă că poți scrie cod pentru forme și să fii sigur că va funcționa pentru tot ceea ce se încadrează sub conceptul de formă. Interschimbabilitatea este unul dintre cele mai puternice concepte din OOP.

Booch a oferit o descriere și mai concisă a obiectului:

Un obiect are stare, comportament și identitate.

Concluzia este că un obiect poate avea date interne (care este starea obiectului), metode (care definesc comportamentul) și fiecare obiect poate fi distins în mod unic de orice alt obiect - mai precis, fiecare obiect are o adresă unică în minte Acest lucru este valabil cu unele limitări, deoarece obiectele pot exista de fapt pe alte mașini și în spații de adrese diferite și pot fi, de asemenea, stocate pe disc. În aceste cazuri, identitatea unui obiect trebuie să fie determinată de altceva decât de o adresă de memorie..

Obiectul are o interfață

Este probabil că Aristotel a fost primul care a studiat cu atenție conceptul de tip; a vorbit despre „o clasă de pești și o clasă de păsări”. Conceptul că toate obiectele, deși unice, fac în același timp parte dintr-o clasă de obiecte cu caracteristici și comportament similare, a fost folosit în primul limbaj orientat pe obiecte, Simula-67, odată cu introducerea cuvântului cheie fundamental. clasă , care a introdus un nou tip în program.

Limba Simulare, după cum sugerează și numele, a fost creat pentru a dezvolta și simula situații similare cu problema clasică a „casătorului bancar”. Aveți grupuri de casieri, clienți, conturi, plăți și valute - o mulțime de „obiecte”. Obiectele care sunt identice în toate, cu excepția stării interne în timp ce programul rulează, sunt grupate în „clase de obiecte”. De aici a venit cuvânt cheie clasă . Crearea unor tipuri de date abstracte este un concept fundamental în orice programare orientată pe obiecte. Tipurile de date abstracte acționează în același mod ca și tipurile încorporate: puteți crea variabile de tip (numite obiecte sau instanțe în termeni OOP) și le puteți manipula (numite mesagerie sau interogare; faceți o cerere și obiectul decide ce să facă cu ea.). Membrii (elementele) fiecărei clase au asemănări: fiecare cont are un sold, fiecare casier acceptă depozite etc. În același timp, toți membrii diferă prin starea lor internă: fiecare cont are un sold individual, fiecare casier are un nume uman. Prin urmare, toate casieriile, clienții, conturile, transferurile etc. pot fi reprezentate de entități unice în interiorul program de calculator. Aceasta este esența unui obiect și fiecare obiect aparține unei anumite clase care îi definește caracteristicile și comportamentul.

Deci, deși creăm noi tipuri de date în limbaje obiect, practic toate aceste limbi folosesc cuvântul cheie „clasă”. Când vezi cuvântul „tip”, gândește-te la „clasă” și invers Unii oameni fac distincția între cele două subliniind că un tip definește o interfață, în timp ce o clasă este o implementare concretă a unei interfețe..

Deoarece o clasă definește un set de obiecte cu caracteristici identice (membri de date) și comportament (funcționalitate), clasa este de fapt un tip de date, deoarece, de exemplu, un număr în virgulă mobilă are și un număr de caracteristici și comportamente. Diferența este că programatorul definește o clasă pentru a reprezenta un aspect al sarcinii, în loc să folosească un tip deja existent care reprezintă o unitate de stocare a datelor în mașină. Extindeți limbajul de programare adăugând noi tipuri de date pentru a se potrivi nevoilor dumneavoastră. Sistemul de programare favorizează noile clase și le acordă exact aceeași atenție ca și tipurile încorporate.

Abordarea orientată pe obiect nu se limitează la construirea de modele. Indiferent dacă sunteți sau nu de acord că orice program este un model al sistemului pe care îl dezvoltați, utilizarea tehnologiei OOP reduce cu ușurință un set mare de probleme la o soluție simplă.

Odată ce o nouă clasă a fost definită, puteți crea orice număr de obiecte din acea clasă și apoi le puteți manipula ca și cum ar fi parte din problema în cauză. De fapt, una dintre principalele dificultăți în OOP este stabilirea unei corespondențe unu-la-unu între obiectele din spațiul problemei și obiectele din spațiul soluției.

Dar cum faci ca un obiect să facă ceea ce vrei tu să facă? Trebuie să existe un mecanism de trimitere a unei cereri către un obiect pentru a efectua o acțiune - finalizarea unei tranzacții, desenarea pe ecran etc. Fiecare obiect poate efectua doar o anumită gamă de solicitări. Interogările pe care le puteți trimite unui obiect sunt determinate de interfața acestuia, iar interfața obiectului este determinată de tipul acestuia. Cel mai simplu exemplu ar fi un bec electric:

Light lt = new Light() ;
lt.on();

O interfață definește ce solicitări puteți face unui anumit obiect. Totuși, codul care execută interogările trebuie să existe și el undeva. Acest cod, împreună cu datele ascunse, formează implementarea. Din punct de vedere al programării procedurale, ceea ce se întâmplă nu este atât de dificil. Tipul conține o metodă pentru fiecare cerere posibilă, iar atunci când este primită o anumită cerere, este apelată metoda corespunzătoare. Procesul este de obicei combinat într-unul singur: „trimiterea unui mesaj” (transmiterea unei cereri) către un obiect și procesarea acestuia de către un obiect (executarea codului).

În acest exemplu, există un tip (clasă) numit ușoară (lampa), obiect de beton de tip ușoară Cu nume Aceasta , iar clasa acceptă diverse interogări obiect ușoară : Opriți becul, porniți-l, faceți-l mai luminos sau diminuați-l. Tu creezi un obiect ușoară , definind o „referință” la acesta ( Aceasta ) și apelând operatorul nou pentru a crea o nouă instanță de acest tip. Pentru a trimite un mesaj unui obiect, trebuie să specificați numele obiectului și să îl asociați cu cererea dorită cu un punct. Din punctul de vedere al utilizatorului unei clase predefinite, acest lucru este suficient pentru a opera asupra obiectelor sale.

Graficul prezentat mai sus urmează formatul UML (Limbaj de modelare unificat). Fiecare clasă este reprezentată printr-un dreptunghi, toate câmpurile de date descrise sunt plasate în partea de mijloc a acesteia, iar metodele (funcțiile obiectului căruia îi trimiteți mesaje) sunt enumerate în partea de jos a dreptunghiului.

Adesea pe grafice UML sunt afișate doar numele clasei și metodele publice, iar partea din mijloc lipsește. Dacă sunteți interesat doar de numele clasei, puteți sări peste și partea de jos.

Facilitatea oferă servicii

Când încercați să proiectați sau să înțelegeți structura unui program, este adesea util să vă gândiți la obiecte ca „furnizori de servicii”. Programul dumneavoastră oferă servicii utilizatorului și face acest lucru prin intermediul serviciilor oferite de alte obiecte. Scopul tău este să produci (sau mai bine să găsești în bibliotecile de clasă) setul de obiecte care vor fi optime pentru rezolvarea problemei tale.

Pentru început, întreabă-te: „Dacă aș putea elimina prin magie obiecte dintr-o pălărie, care mi-ar putea rezolva problema chiar acum?” Să presupunem că dezvoltați un program de contabilitate. Se poate imagina un set de obiecte care oferă ferestre standard pentru introducerea informațiilor contabile, un alt set de obiecte care efectuează calcule contabile, un obiect care imprimă cecuri și facturi pe diverse imprimante. Poate că unele dintre aceste obiecte există deja, iar pentru alte obiecte, merită să ne dăm seama cum ar putea arăta. Ce servicii ar putea oferi aceste facilități și ce facilități ar avea nevoie pentru a-și face treaba? Dacă continuați așa, mai devreme sau mai târziu veți spune: „Acest obiect este suficient de simplu încât să putem sta și să-l notăm” sau „Cu siguranță un astfel de obiect există deja”. Aceasta este o modalitate rezonabilă de a distribui soluția problemei la obiecte separate.

Reprezentarea unui obiect așa cum are un furnizor de servicii beneficiu suplimentar: ajută la îmbunătățirea conectivității ( coeziunea) obiect. Conectivitate bună - calitate esentiala produs software: înseamnă că diverse aspecte ale unei componente software (cum ar fi un obiect, deși se poate referi și la o metodă sau la o bibliotecă de obiecte) se potrivesc bine. Unul dintre greșeli comune permis atunci când proiectați un obiect este suprasaturarea acestuia cu un număr mare de proprietăți și capabilități. De exemplu, atunci când dezvoltați un modul care imprimă cecuri, ați putea dori ca acesta să „știe” totul despre formatare și imprimare.

Dacă te gândești bine, cel mai probabil vei ajunge la concluzia că acest lucru este prea mult pentru un singur obiect și vei merge la trei sau mai multe obiecte. Un obiect va fi un catalog cu toate formele posibile de cecuri și poate fi întrebat despre cum trebuie tipărit cecul. Un alt obiect sau set de obiecte va fi responsabil pentru o interfață de imprimare generalizată care „știe” totul despre diverse tipuri de imprimante (dar nu „înțelege” nimic în contabilitate – este mai bine să cumperi un astfel de obiect decât să-l dezvolți singur). În cele din urmă, al treilea obiect va folosi pur și simplu serviciile obiectelor descrise pentru a finaliza sarcina. Astfel, fiecare obiect este un set conectat de servicii pe care le oferă. Într-un proiect orientat pe obiecte bine planificat, fiecare obiect face o treabă bună de a face o anumită sarcină fără a încerca să facă mai mult decât trebuie. După cum se arată, acest lucru nu numai că vă permite să determinați ce obiecte să cumpărați (obiect cu o interfață de imprimare), dar vă permite și să ajungeți cu un obiect care poate fi apoi folosit în altă parte (catalog de chitanțe).

Reprezentarea obiectelor ca furnizori de servicii simplifică foarte mult sarcina. Este util nu numai în timpul dezvoltării, ci și atunci când cineva încearcă să vă înțeleagă codul sau să refolosească obiectul - atunci va putea evalua în mod adecvat obiectul pentru nivelul de serviciu oferit, iar acest lucru va simplifica foarte mult integrarea acestuia din urmă în alt proiect.

Implementare ascunsă

Este util să împărțiți programatorii în creatori de clasă(cei care creează noi tipuri de date) și programatori clientSunt recunoscător pentru acest termen prietenului meu Scott Meyers.(consumatorii claselor care folosesc tipuri de date în aplicațiile lor). Scopul celui de-al doilea este de a colecta cât mai multe clase posibil pentru a se angaja în dezvoltarea rapidă a programelor. Scopul unui creator de clasă este de a construi o clasă care expune doar ceea ce are nevoie programatorul client și ascunde orice altceva. De ce? Programatorul client nu va putea accesa părțile ascunse, ceea ce înseamnă că creatorul claselor își rezervă posibilitatea de a le schimba arbitrar, fără teama că va răni pe cineva. Partea „ascunsă” este de obicei cea mai „fragilă” parte a obiectului, care poate fi ușor coruptă de un programator client neglijent sau ignorant, astfel încât ascunderea implementării reduce numărul de erori în programe.

În orice relație, este important să existe niște granițe care nu sunt depășite de niciunul dintre participanți. Prin crearea unei biblioteci, stabilești o relație cu programatorul client. El este un programator la fel ca tine, dar va folosi biblioteca ta pentru a crea o aplicație (și poate o bibliotecă de nivel superior). Dacă dai acces oricui tuturor membrilor clasei, programatorul client poate face orice dorește cu clasa și nu ai cum să o faci să „joace după reguli”. Chiar dacă mai târziu trebuie să restricționați accesul anumitor membri ai clasei dvs., acest lucru nu se poate face fără un mecanism de control al accesului. Întreaga structură a clasei este deschisă tuturor participanților.

Astfel, primul motiv pentru restricționarea accesului este necesitatea de a proteja detaliile „fragile” de la programatorul client – ​​părți ale „bucătăriei” interne care nu fac parte din interfața cu care utilizatorii își rezolvă problemele. De fapt, acest lucru este util și pentru utilizatori - aceștia vor vedea imediat ce este important pentru ei și ce pot ignora.

Al doilea motiv pentru apariția restricției de acces este dorința de a permite dezvoltatorului de bibliotecă să schimbe mecanismele interne ale clasei fără a-și face griji cu privire la modul în care acest lucru va afecta programatorul client. De exemplu, puteți implementa o anumită clasă în grabă pentru a accelera dezvoltarea unui program și apoi o puteți rescrie pentru a îmbunătăți viteza. Dacă ați separat și protejat corespunzător interfața și implementarea, acest lucru nu ar trebui să fie deloc dificil.

Java folosește trei cuvinte cheie explicite care caracterizează nivelul de acces: public privat și protejat . Scopul și utilizarea lor este foarte simplă. Acești specificatori de acces determină cine are dreptul de a folosi definițiile care le urmează. Cuvânt public înseamnă că următoarele definiții sunt disponibile pentru toată lumea. Dimpotrivă, cuvântul privat înseamnă că clauzele care îl urmează sunt disponibile numai creatorului tipului, în interiorul metodelor sale. Termen privat - „zidul de fortăreață” între tine și programator-client. Dacă cineva încearcă să folosească privat -membri, va fi oprit de o eroare de compilare. specificatorul protejat acţionează similar cu privat , cu o singură excepție - clasele derivate au acces la membrii marcați protejat dar nu au acces la privat -membri (vom acoperi moștenirea în curând).

LA Java există și un acces „implicit” folosit în absența oricăruia dintre specificatorii enumerați. Este uneori numit și acces în interiorul pachetului ( acces la pachet) deoarece clasele pot folosi membrii prieteni ai altor clase în pachetul lor, dar în afara pachetului, aceiași membri prieteni dobândesc statutul privat .

Implementare Reutilizare

Clasa generată și testată ar trebui (în mod ideal) să fie un bloc de cod util. Cu toate acestea, se dovedește că atingerea acestui obiectiv este mult mai dificilă decât cred mulți oameni; dezvoltarea obiectelor reutilizabile necesită experiență și înțelegere a esenței materiei. Dar odată ce ai înțeles bine constructie buna, se va cere pur și simplu implementarea în alte programe. Reutilizarea codului este unul dintre cele mai impresionante beneficii ale limbajelor orientate pe obiecte.

Cel mai simplu mod de a reutiliza o clasă este prin crearea directă a obiectului acesteia, dar puteți plasa și un obiect din acea clasă într-o clasă nouă. Numim această injecție de obiect (crearea obiectului membru). Noua clasă poate conține orice număr de obiecte de alte tipuri, în orice combinație care este necesară pentru a obține funcționalitatea dorită. Din moment ce compilăm noua clasa din clasele deja existente, această metodă este numită compoziţie(atunci când compoziția este realizată dinamic, este de obicei numită agregare). Compoziția este adesea denumită o relație „are” ( are o), ca, de exemplu, în propoziția „O mașină are un motor”.

(În diagramele UML, compoziția este indicată printr-un diamant umplut, arătând, de exemplu, că există o singură mașină. De obicei folosesc o formă mai generală de relație: numai linii, fără diamant, ceea ce înseamnă asociere (link). Acest lucru este de obicei suficient pentru majoritatea graficelor în care diferența dintre compoziție și agregare nu este semnificativă pentru dvs.)

Compoziția este un instrument foarte flexibil. Obiectele membre ale noii tale clase sunt de obicei declarate private ( privat ), ceea ce le face inaccesibile programatorilor clienți care folosesc clasa. Acest lucru permite modificarea acestor obiecte membre fără modificarea codului client existent. De asemenea, puteți modifica acești membri în timpul execuției pentru a controla dinamic comportamentul programului dvs. Moștenirea descrisă mai jos nu are această flexibilitate deoarece compilatorul impune anumite restricții asupra claselor create folosind moștenirea.

Moștenirea joacă un rol important în programarea orientată pe obiecte, așa că i se acordă adesea multă atenție, iar un începător ar putea crede că moștenirea ar trebui aplicată peste tot. Și acest lucru este plin de crearea de soluții stângace și inutil de complexe. În schimb, atunci când creați noi clase, ar trebui să evaluați mai întâi posibilitatea compoziției, deoarece este mai simplă și mai flexibilă. Dacă adoptați abordarea recomandată, constructele dvs. de programare vor deveni mult mai clare. Și pe măsură ce câștigați experiență practică, nu va fi greu de înțeles unde ar trebui folosită moștenirea.

Moştenire

În sine, ideea unui obiect este extrem de convenabilă. Obiectul vă permite să combinați datele și funcționalitatea la nivel conceptual, adică puteți reprezenta conceptul dorit din spațiul sarcinii în loc să îl concretizați folosind un dialect al mașinii. Aceste concepte formează unitățile fundamentale ale unui limbaj de programare, descrise cu cuvântul cheie class.

Dar trebuie să recunoașteți, ar fi păcat să creați un fel de clasă și apoi să faceți toată treaba din nou pentru o clasă similară. Este mult mai rațional să luați o clasă gata făcută, să o „clonați” și apoi să faceți completări și actualizări la clona rezultată. Acesta este exact ceea ce obțineți ca urmare a moștenirii, cu o singură excepție - dacă clasa inițială (numită și clasa de bază *, superclasa sau clasa părinte) se schimbă, atunci toate modificările sunt reflectate în „clona” sa (numită clasă derivată). , clasă moștenită, subclasă sau clasă copil).

(Săgeata (triunghiul gol) din diagrama UML indică de la clasa derivată la clasa de bază. După cum veți vedea în curând, pot exista mai multe clase derivate.)

Un tip definește mai mult decât proprietățile unui grup de obiecte; este asociat și cu alte tipuri. Cele două tipuri pot împărtăși asemănări și comportamente, dar diferă în ceea ce privește numărul de caracteristici, precum și capacitatea de a procesa mai multe mesaje (sau de a le procesa diferit). Pentru a exprima această comunalitate de tipuri, moștenirea folosește conceptul de tipuri de bază și derivate. Tipul de bază conține toate caracteristicile și acțiunile comune tuturor tipurilor derivate din acesta. Creați un tip de bază pentru a reprezenta baza înțelegerii dumneavoastră a unora dintre obiectele din sistemul dumneavoastră. Alte tipuri sunt derivate din tipul de bază, exprimând alte implementări ale acestei entități.

De exemplu, o mașină de reciclare sortează deșeurile. Tipul de bază va fi „gunoi”, iar fiecare gunoi are o greutate, un cost etc. și poate fi zdrobită, topită sau descompusă. Pe baza acesteia, se moștenesc tipuri mai specifice de gunoi, având caracteristici suplimentare (sticla are culoare) sau trăsături comportamentale ( cutie de aluminiu poate fi zdrobită, cutia de oțel este atrasă de un magnet). În plus, unele comportamente pot varia (costul hârtiei depinde de tipul și starea acesteia). Moștenirea vă permite să creați o ierarhie de tipuri care descrie problema rezolvată în contextul tipurilor sale.

Al doilea exemplu este un exemplu clasic cu forme geometrice. Tipul de bază aici este „formă”, iar fiecare formă are o dimensiune, culoare, locație etc. Fiecare formă poate fi desenată, ștearsă, mutată, pictată etc. În continuare, sunt produse (moștenite) anumite tipuri de forme: cerc, pătrat, triunghi etc., fiecare dintre ele având propriile caracteristici și comportamente suplimentare. De exemplu, unele forme suportă o operație de oglindire. Caracteristicile individuale ale comportamentului pot diferi, ca în cazul calculării ariei unei figuri. Ierarhia tipurilor cuprinde atât proprietăți similare, cât și diferite ale formelor.

Reducerea soluției la conceptele folosite în exemplu este extrem de convenabilă, deoarece nu aveți nevoie de multe modele intermediare care să lege descrierea soluției cu descrierea problemei. Când lucrați cu obiecte, ierarhia tipurilor devine modelul principal, așa că treceți de la descrierea directă a sistemului în lumea reală la descrierea sistemului în cod. De fapt, una dintre dificultățile în planificarea orientată pe obiect este că îți este foarte ușor să mergi de la începutul unei probleme până la sfârșitul unei soluții. O minte pregătită pentru soluții complexe rămâne adesea blocată atunci când folosește abordări simple.

Prin moștenirea de la un tip existent, creați un tip nou. Acest tip nou nu conține numai toți membrii tipului existent (deși membrii marcați ca privat , ascuns și inaccesibil), dar, mai important, repetă interfața clasei de bază. Aceasta înseamnă că toate mesajele pe care le-ați putea trimite la clasa de bază, le puteți trimite și către clasa derivată. Și din moment ce distingem tipurile de clase după setul de mesaje pe care le putem trimite, aceasta înseamnă că clasa derivată este un caz special al clasei de bază. În exemplul anterior, „un cerc este o figură”. Echivalența tipurilor obținută prin moștenire este una dintre condițiile fundamentale pentru înțelegerea sensului programării orientate pe obiecte.

Deoarece atât clasele de bază, cât și cele derivate au aceeași interfață principală, trebuie să existe o implementare pentru acea interfață. Cu alte cuvinte, undeva trebuie să existe cod care este executat atunci când un obiect primește un anumit mesaj. Dacă pur și simplu ați moștenit o clasă și nu ați făcut nimic altceva, metodele din interfața clasei de bază vor trece neschimbate în clasa derivată. Aceasta înseamnă că obiectele unei clase derivate nu sunt doar de același tip, ci au și același comportament și, în același timp, moștenirea în sine își pierde sensul.

Există două moduri de a schimba noua clasă din clasa de bază. Prima este destul de evidentă: metode complet noi sunt adăugate la clasa derivată. Ele nu mai fac parte din interfața clasei de bază. Se pare că clasa de bază nu a făcut tot ce a cerut sarcina, așa că ați adăugat câteva metode. Cu toate acestea, o abordare atât de simplă și primitivă a moștenirii se dovedește uneori a fi soluția ideală la problemă. Cu toate acestea, un lucru de luat în considerare cu atenție este că clasa de bază poate avea nevoie și de aceste metode adăugate. Procesul de identificare a modelelor și de revizuire a arhitecturii este o rutină zilnică în programarea orientată pe obiecte.

În timp ce moștenirea sugerează uneori că o interfață va fi îmbunătățită cu noi metode (în special în Java, unde moștenirea este indicată de cuvântul cheie se extinde , adică „extinde”), acest lucru nu este deloc necesar. Al doilea mod, mai important, de a modifica o clasă este să Schimbare comportamentul deja metode existente clasa de bază. Aceasta se numește o metodă de suprascriere (sau suprascriere).

Pentru a suprascrie o metodă, pur și simplu creați o nouă definiție a acelei metode într-o clasă derivată. Spui cumva „Folosesc aceeași metodă de interfață, dar vreau să facă lucruri diferite pentru noul meu tip”.

Când folosiți moștenirea, apare întrebarea evidentă: ar trebui ca moștenirea să înlocuiască metodele numai clasa de bază (și nu adăugați metode noi care nu există în clasa de bază)? Acest lucru ar însemna că clasa derivată ar fi exact același tip ca și clasa de bază, deoarece au aceeași interfață. Ca rezultat, puteți înlocui liber obiectele clasei de bază cu obiecte ale clasei derivate. Puteți vorbi despre o înlocuire completă, iar aceasta este adesea numită principiul substituției. Într-un fel, acest mod de moștenire este ideal. Acest tip de relație între o clasă de bază și o clasă derivată este adesea denumită relație. "este ceva", deoarece se poate spune „un cerc este o figură”. Pentru a determina cât de adecvată va fi moștenirea, este suficient să verificăm dacă există o relație „este” între clase și cât de justificată este.

În alte cazuri, interfața clasei derivate este completată cu elemente noi, ceea ce duce la extinderea acesteia. Noul tip poate fi folosit în continuare în locul tipului de bază, dar acum această înlocuire nu este ideală deoarece noile metode nu sunt disponibile din tipul de bază. O astfel de conexiune este descrisă de expresia „similar cu” (acesta este termenul meu); noul tip conține interfața vechiului tip, dar include și noile metode și nu se poate spune că aceste tipuri sunt exact aceleași. Să luăm ca exemplu un aparat de aer condiționat.

Să presupunem că casa ta este echipată cu totul echipamentul necesar pentru a controla procesul de răcire. Imaginați-vă acum că aparatul de aer condiționat s-a defectat și l-ați înlocuit cu un încălzitor capabil atât de încălzire, cât și de răcire. Un încălzitor este „ca” un aparat de aer condiționat, dar poate face mai mult. Deoarece sistemul de control al casei dvs. poate controla doar răcirea, este limitat în comunicarea cu partea de răcire a noii unități. Interfața noului obiect a fost extinsă, iar sistemul existent nu recunoaște altceva decât interfața originală.

Desigur, privind această ierarhie, devine clar că clasa de bază „sistem de răcire” nu este suficient de flexibilă; ar trebui redenumit „sistem de control al temperaturii”, astfel încât să includă și încălzirea – iar după aceea va funcționa principiul înlocuirii. Cu toate acestea, această diagramă oferă un exemplu a ceea ce se poate întâmpla în realitate.

După ce te-ai familiarizat cu principiul substituției, s-ar putea avea impresia că această abordare (înlocuirea completă) este singura modalitate de dezvoltare. În general, dacă ierarhiile dvs. de tip funcționează în acest fel, este foarte bine. Dar în unele situații este absolut necesar să adăugați metode noi la interfața de clasă derivată. La o examinare mai atentă, ambele cazuri par destul de evidente.

Obiecte interschimbabile și polimorfism

Când se utilizează ierarhii de tip, este adesea necesar să se trateze un obiect de un anumit tip ca tip de bază. Acest lucru vă permite să scrieți cod care nu depinde de tipurile concrete. Astfel, în exemplul de forme, metodele manipulează pur și simplu forme, indiferent dacă sunt cercuri, dreptunghiuri, triunghiuri sau unele forme nedefinite încă. Toate formele pot fi desenate, șterse și mutate, iar metodele pur și simplu trimit mesaje obiectului formă; nu le pasă de modul în care obiectul gestionează mesajul.

Un astfel de cod nu depinde de adăugarea de noi tipuri, iar adăugarea de noi tipuri este cea mai comună modalitate de a extinde programele orientate pe obiecte pentru a gestiona situații noi. De exemplu, puteți crea o nouă subclasă de formă (pentagon) fără a schimba metodele care funcționează numai cu forme generice. Capacitatea de a extinde cu ușurință un program prin introducerea de noi tipuri derivate este foarte importantă deoarece îmbunătățește foarte mult arhitectura programului, reducând în același timp costul de întreținere a software-ului.

Cu toate acestea, atunci când încercăm să ne referim la obiecte de tipuri derivate ca tipuri de bază (cercuri ca figură, o bicicletă ca vehicul, un cormoran ca pasăre etc.), apare o problemă. Dacă o metodă va spune unei figuri generice să se deseneze, sau unui vehicul să urmeze un anumit curs, sau unei păsări să zboare, compilatorul nu poate ști exact care parte a codului va fi executată. Acesta este scopul - atunci când un mesaj este trimis, programatorul nu vrea să știe ce cod este executat; metoda de desen poate fi aplicată unui cerc, unui dreptunghi și unui triunghi cu succes egal, iar obiectul va executa codul corect, în funcție de tipul său caracteristic.

Dacă nu aveți nevoie să știți ce bucată de cod este executată, atunci când adăugați un nou subtip, codul de implementare al acestuia poate fi diferit fără a necesita modificări ale metodei de la care a fost apelat. Dacă compilatorul nu știe exact ce cod să execute, ce face?

LA exemplul următor un obiect BirdController (controlul păsărilor) poate funcționa numai cu obiecte generice pasăre (pasăre), neștiind tipul lor exact. Din punct de vedere BirdController acest lucru este convenabil deoarece nu trebuie să scrieți cod special pentru a verifica tipul obiectului folosit pentru acesta pasăre , pentru a gestiona un comportament special. Cum se întâmplă ca atunci când apelați metoda mutare (), fără a specifica tipul exact pasăre , se execută acțiunea corectă - obiectul gâscă (gâsca) aleargă, zboară sau înoată și obiectul Pinguin (pinguin) alergare sau înot?

Răspunsul este explicat caracteristica principală programare orientată pe obiecte: compilatorul nu poate apela astfel de funcții în mod tradițional. Când apelați funcții create de un compilator non-OOP, utilizați legarea timpurie- multi nu cunosc acest termen pur si simplu pentru ca nu isi imagineaza alta optiune. Cu legarea timpurie, compilatorul generează un apel de funcție cu nume dat, iar linkerul leagă acest apel la adresa absolută a codului care urmează să fie executat. În OOP, programul nu este capabil să determine adresa codului până în timpul execuției, așa că atunci când trimiteți un mesaj către un obiect, trebuie să funcționeze un mecanism diferit.

Pentru a rezolva această problemă, limbajele de programare orientate pe obiecte folosesc conceptul legarea tardivă. Când trimiteți un mesaj către un obiect, codul apelat nu este cunoscut până în timpul execuției. Compilatorul se asigură doar că metoda există, verifică tipurile pentru parametrii ei și valoarea returnată, dar habar nu are ce fel de cod va fi executat.

Pentru a implementa legarea tardivă, Java folosește fragmente de cod speciale în loc de un apel absolut. Acest cod calculează adresa corpului metodei pe baza informațiilor stocate în obiect (procesul este tratat în detaliu în Capitolul 7 despre polimorfism). Astfel, fiecare obiect se poate comporta diferit, în funcție de conținutul acestei bucăți speciale de cod. Când trimiteți un mesaj, obiectul decide de fapt ce să facă cu el.

Unele limbi vă cer să specificați în mod explicit că o metodă ar trebui să utilizeze un mecanism flexibil de legare tardivă (în C++ există un cuvânt cheie pentru asta virtual). În aceste limbi, metodele nu sunt legate dinamic în mod implicit. LA Java legarea tardivă se face în mod implicit și nu trebuie să vă amintiți să adăugați cuvinte cheie pentru a oferi polimorfism.

Amintiți-vă exemplul cu forme. O familie de clase (bazată pe aceeași interfață) a fost prezentată într-o diagramă mai devreme în acest capitol. Pentru a demonstra polimorfismul, vom scrie un fragment de cod care ignoră specificul tipului și funcționează numai pe clasa de bază. Acest cod este separat de specificul tipului, astfel încât este mai ușor de scris și de înțeles. Și dacă un nou tip (de exemplu, un hexagon) este adăugat prin moștenire, atunci codul pe care îl scrieți pentru noul tip de figură va funcționa la fel de bine ca și codul pentru tipurile existente. Astfel, programul devine extensibil.

Să zicem că ai scris Java următoarea metodă (veți învăța cum să o faceți în scurt timp):

void do Something (Forma de formă) (
shape.erase(); // sterge
//...
shape.draw(); // a desena
}

Metoda funcționează cu o cifră generalizată ( formă ), adică nu depinde de tipul particular de obiect care este desenat sau șters. Acum folosim apelul de metodă Fă ceva() într-o altă parte a programului:

Cerc cerc = new Circle() ; // cerc
Triunghi triunghi = new Triunghi() ; // triunghi
Line line = linie nouă(); // linie
face ceva (cerc);
fă Ceva (triunghi) ;
face Something(line);

Apeluri de metodă Fă ceva() funcţionează automat corect, indiferent de tipul real al obiectului.

Acesta este de fapt un fapt destul de important. Luați în considerare linia:

face ceva (cerc);

Aici se întâmplă următoarele: unei metode care așteaptă un obiect formă , obiectul „cerc” este trecut ( Cerc ). Din moment ce cercul ( Cerc ) este simultan o cifră ( formă ), apoi metoda Fă ceva() și o tratează ca pe o siluetă. Cu alte cuvinte, orice mesaj pe care metoda îl poate trimite formă , este de asemenea acceptat Cerc . Această acțiune este complet sigură și la fel de logică.

Ne referim la acest proces de a trata un tip derivat ca și cum ar fi un tip de bază. upcasting. Cuvânt transformareînseamnă că obiectul este tratat ca aparținând unui tip diferit și ascendent se datorează faptului că în diagramele de moștenire, clasele de bază sunt de obicei în partea de sus, iar clasele derivate sunt distribuite în evantai în partea de jos. Aceasta înseamnă că conversia la tipul de bază este o mișcare în sus de-a lungul diagramei și, prin urmare, este „în sus”.

Un program orientat pe obiecte conține aproape întotdeauna un upcast, pentru că așa scapi de nevoia de a cunoaște exact tipul de obiect cu care lucrezi. Uită-te la corpul metodei Fă ceva() :

shape.erase();
// ...
shape.draw();

Rețineți că nu scrie „dacă ești un obiect Cerc , fă-o, iar dacă ești un obiect Pătrat fă asta și asta”. Un astfel de cod cu acțiuni separate pentru fiecare tip posibil formă va fi confuz și va trebui schimbat de fiecare dată când se adaugă un nou subtip formă . Și așa, spui doar: „Ești o figură și știu că ești capabil să te desenezi și să te ștergi, ei bine, fă-o și ai grijă singur de detalii.”

În codul metodei Fă ceva() Ceea ce este interesant este că totul merge bine. Când chemat a desena() pentru obiect Cerc alt cod este executat, și nu cel care funcționează atunci când este apelat a desena() pentru obiecte Pătrat sau linia , și atunci când a desena() aplicat unei figuri necunoscute formă , comportamentul corect este furnizat prin utilizarea tipului real formă . Este înăuntru cel mai înalt grad interesant, pentru că, după cum am menționat puțin mai devreme, atunci când compilatorul generează cod Fă ceva() , nu știe exact cu ce tipuri lucrează. În consecință, ar fi de așteptat ca versiunile metodei să fie apelate a desena() și şterge() din clasa de bază formă , nu variantele lor din clase concrete Cerc , Pătrat sau linia . Și totuși totul funcționează corect datorită polimorfismului. Compilatorul și sistemul runtime au grijă de toate detaliile; tot ce trebuie să știi este că se va întâmpla... și, mai important, cum să construiești programe folosind această abordare. Când trimiteți un mesaj unui obiect, acesta va alege comportamentul corect folosind un upcast.

Ierarhie cu o singură rădăcină

La scurt timp după apariție C++întrebarea de la OOP a început să fie discutată activ - toate clasele ar trebui în mod necesar să fie moștenite de la o singură clasă de bază? LA Java(ca în aproape toate celelalte limbi OOP, cu excepția C++) La această întrebare s-a răspuns afirmativ. Întreaga ierarhie a tipurilor se bazează pe o singură clasă de bază Obiect . S-a dovedit că ierarhia cu rădăcină unică are multe avantaje.

Toate obiectele dintr-o ierarhie cu o singură rădăcină au o interfață comună, astfel încât, în general, toate pot fi considerate ca un singur tip de bază. LA C++ a fost aleasă o altă opțiune - un strămoș comun nu există în această limbă. În ceea ce privește compatibilitatea cu codul vechi, acest model este mai în concordanță cu tradiția. C, și ați putea crede că este mai puțin limitat. Dar de îndată ce apare necesitatea unei programari cu drepturi depline orientate pe obiecte, va trebui să vă creați propria ierarhie de clasă pentru a obține aceleași beneficii care sunt încorporate în alte limbaje OOP. Și în orice bibliotecă de clasă nouă, este posibil să întâlniți o interfață incompatibilă. Încorporarea acestor noi interfețe în arhitectura programului dvs. va necesita efort suplimentar (și posibil moștenire multiplă). Merită „flexibilitatea” suplimentară? C++ costuri similare? Dacă aveți nevoie de el (ex. investitii mari la dezvoltarea codului C), atunci nu vei fi un învins. Dacă dezvoltarea începe de la zero, abordarea Java pare mai productiv.

Toate obiectele dintr-o singură ierarhie rădăcină sunt garantate că au o anumită funcționalitate comună. Știți că anumite operații de bază pot fi efectuate pe orice obiect din sistem. Toate obiectele sunt create cu ușurință pe heap-ul dinamic, iar transmiterea argumentelor este mult simplificată.

Ierarhia unică rădăcină face mult mai ușoară implementarea colectării gunoiului - una dintre cele mai importante îmbunătățiri Java comparat cu C++. Deoarece informațiile de tip sunt garantate a fi prezente în oricare dintre obiecte în timpul execuției, nu va exista niciodată un obiect în sistem al cărui tip nu poate fi determinat. Acest lucru este deosebit de important atunci când se efectuează operațiuni de sistem, cum ar fi gestionarea excepțiilor și pentru o mai mare flexibilitate de programare.

Containere

De multe ori nu se știe dinainte de câte obiecte vor fi necesare pentru a rezolva o anumită problemă și cât timp vor exista. De asemenea, nu este clar cum să depozitați astfel de obiecte. Câtă memorie ar trebui să fie alocată pentru a stoca aceste obiecte? Nu se știe, deoarece aceste informații vor deveni disponibile numai în timpul rulării programului.

Multe probleme din programarea orientată pe obiecte sunt rezolvate acțiune simplă: Creați un alt tip de obiect. Noul tip de obiect care rezolvă această problemă specifică conține referințe la alte obiecte. Desigur, și matricele, care sunt acceptate în majoritatea limbilor, pot juca acest rol. Cu toate acestea, noul obiect, numit de obicei recipient(sau o colecție, dar în Java termenul este folosit într-un sens diferit) se va extinde în mod necesar pentru a găzdui orice ai pune în el. Prin urmare, nu va trebui să știți dinainte pentru câte obiecte este proiectată capacitatea containerului. Doar creați un container și acesta va avea grijă de detalii.

Din fericire, un limbaj OOP bun vine cu un set de containere gata făcute. LA C++ face parte din biblioteca standard C++, numită uneori bibliotecă șabloane standard (Biblioteca de șabloane standard, STL). Convorbire scurtă vine cu o gamă foarte largă de containere. Java conține, de asemenea, containere în biblioteca sa standard. Pentru unele biblioteci, se consideră suficient să existe un singur container pentru toate nevoile, dar în altele (de exemplu, în Java) există diverse containere pentru toate ocaziile: mai multe tipuri diferite de liste Listă (pentru stocarea secvențelor de elemente), hărți Hartă (cunoscute și sub numele de tablouri asociative, vă permit să asociați obiecte cu alte obiecte), precum și seturi a stabilit (furnizarea de valori unice pentru fiecare tip). Bibliotecile de containere pot conține, de asemenea, cozi, copaci, stive și așa mai departe.

Din punct de vedere al designului, tot ce aveți nevoie este un container care vă poate rezolva problema. Dacă un tip de container satisface toate nevoile, nu există niciun motiv pentru a utiliza alte tipuri. Există două motive pentru care trebuie să alegi dintre containerele disponibile. În primul rând, containerele oferă o varietate de interfețe și interacțiuni. Comportamentul și interfața unei stive este diferită de cea a unei cozi, care se comportă diferit față de un set sau o listă. Unul dintre aceste containere este capabil să ofere mai mult de solutie eficienta sarcina ta în comparație cu restul. În al doilea rând, containere diferite efectuează aceleași operațiuni în moduri diferite. cel mai bun exemplu- aceasta este ArrayList și LinkedList . Ambele sunt secvențe simple care pot avea interfețe și comportamente identice. Dar unele operațiuni diferă semnificativ în timpul de execuție. Să presupunem timpul de eșantionare al unui element arbitrar în ArrayList rămâne întotdeauna același, indiferent de elementul selectat. Cu toate acestea, în LinkedList este dezavantajos să lucrați cu acces aleatoriu - cu cât un element este mai jos în listă, cu atât întârzierea provoacă căutarea acestuia. Pe de altă parte, dacă trebuie să inserați un element în mijlocul listei, LinkedList fă-o mai repede decât ArrayList . Acestea și alte operațiuni sunt eficienta diferitaîn funcție de structura interna recipient. În etapa de planificare a programului, puteți alege o listă LinkedList , iar apoi, în timpul procesului de optimizare, treceți la ArrayList . Datorită naturii abstracte a interfeței Listă o astfel de tranziție va necesita modificări minime ale codului.

Tipuri parametrizate (generice)

Înainte de eliberare Java SE5 containerele puteau stoca doar date Obiect - singurul tip universal Java. Ierarhia cu o singură rădăcină înseamnă că orice obiect poate fi considerat ca Obiect , deci containerul cu elementele Obiect potrivit pentru depozitarea oricăror obiecte Tipurile primitive nu pot fi depozitate în containere, ci datorită mecanismului ambalare automată(autoboxing) Java SE5 această limitare nu este esențială. Acest subiect va fi discutat mai detaliat mai târziu în carte..

Când lucrați cu un astfel de container, puneți pur și simplu referințe la obiect în el și, ulterior, le recuperați. Dar dacă containerul este capabil doar să depoziteze Obiect , apoi atunci când o referire la un obiect de alt tip este plasată în el, o conversie la Obiect , adică pierderea „individualității” obiectului. Când îl aduci înapoi, primești o referință la Obiect , nu o referire la tipul care a fost plasat în container. Cum se transformă într-un anumit tip de obiect plasat într-un container?

Problema este rezolvată prin aceeași conversie de tip, dar de data aceasta nu utilizați un upcast (în sus din ierarhia moștenirii până la tipul de bază). Acum utilizați metoda de conversie în jos a ierarhiei de moștenire (la un tip copil). Aceasta metoda numit conversie descendentă. În cazul unei turnări în sus, se știe că cercul este o figură, deci se știe că conversia este sigură, dar în cazul unei turnări inverse (la un tip de copil), este imposibil să spunem în avans dacă un instanța reprezintă Obiect un obiect Cerc sau formă , așa că downcasting-ul este sigur doar dacă cunoașteți exact tipul obiectului.

Cu toate acestea, pericolul nu este atât de mare - reducerea la tipul greșit va duce la o eroare de rulare numită excepție(vezi mai jos). Dar atunci când preluați referințe de obiect dintr-un container, trebuie să vă amintiți cumva tipul real al obiectelor lor pentru a efectua deplasarea corectă.

Reducerea și verificarea tipului de rulare necesită timp suplimentar și efort suplimentar din partea programatorului. Sau poate puteți crea cumva un container care cunoaște tipul de obiecte stocate și, astfel, elimină nevoia de conversie a tipului și erori potențiale? Soluția se numește mecanism parametrizarea tipului. Tipurile parametrizate sunt clase pe care compilatorul le poate adapta automat pentru a lucra cu anumite tipuri. De exemplu, compilatorul poate configura un container parametrizat pentru a stoca și a prelua numai forme ( formă ).

Una dintre cele mai importante schimbări Java SE5 este suport pentru tipurile parametrizate ( generice). Tipurile parametrizate sunt ușor de recunoscut după parantezele unghiulare care includ numele tipurilor de parametri; de exemplu, container ArrayList , conceput pentru depozitarea obiectelor formă , este creat astfel:

ArrayList< Shape >forme = nou ArrayList< Shape > () ;

Multe componente standard ale bibliotecii au fost, de asemenea, modificate pentru a utiliza tipuri generice. După cum veți vedea în curând, tipurile generice apar în multe dintre exemplele de programe din această carte.

Crearea, utilizarea obiectelor și durata lor de viață

Unul dintre aspecte critice lucrul cu obiecte - organizarea creării și distrugerii lor. Pentru existența fiecărui obiect sunt necesare unele resurse, în primul rând memoria. Atunci când un obiect nu mai este necesar, acesta trebuie distrus pentru ca resursele pe care le ocupă să poată fi puse la dispoziția altora. În situații simple, sarcina nu pare dificilă: creezi un obiect, îl folosești atâta timp cât este nevoie și apoi îl distrugi. Cu toate acestea, situații mai complexe apar adesea în practică.

Să presupunem, de exemplu, că dezvoltați un sistem de gestionare a traficului aerian. (Același model este valabil și pentru controlul tarii într-un depozit, sau un sistem de închiriere video, sau o canisa pentru animale fără stăpân.) La început, totul pare simplu: se creează un container de avion, apoi se construiește o nouă aeronavă, care este plasată în un container al unei anumite zone de control al traficului aerian . În ceea ce privește eliberarea de resurse, obiectul corespunzător este pur și simplu distrus atunci când aeronava părăsește zona de urmărire.

Dar poate că există un alt sistem de înregistrare a aeronavelor, iar aceste date nu necesită o atenție atât de mare ca functie principala management. Poate că sunt înregistrările planului de zbor ale tuturor avioanelor mici care părăsesc aeroportul. Așa apare al doilea container pentru avioane mici; de fiecare dată când un nou obiect de avion este creat în sistem, acesta este inclus și în al doilea container dacă aeronava este mică. În continuare, un proces de fundal funcționează cu obiectele din acest container în momentele de ocupare minimă.

Acum sarcina devine mai complicată: de unde știi când să ștergi obiecte? Chiar dacă ați terminat cu obiectul, este posibil ca un alt sistem să interacționeze în continuare cu el. Aceeași întrebare apare într-o serie de alte situații și în sistemele software în care este necesară ștergerea explicit a obiectelor după finalizarea lucrului cu acestea (de exemplu, în C++), devine destul de complex.

Unde sunt stocate datele obiectului și cum este determinată durata lor de viață? LA C++ Eficiența este pe primul loc, așa că programatorului i se oferă posibilitatea de a alege. Pentru realizare viteza maxima locația de execuție și durata de viață pot fi determinate în momentul scrierii programului. În acest caz, obiectele sunt împinse în stivă (astfel de variabile sunt numite automat) sau în zona de depozitare statică. Deci, factorul principal este viteza de creare și distrugere a obiectelor, iar acest lucru poate fi de neprețuit în unele situații. Cu toate acestea, acest lucru vine cu prețul flexibilității, deoarece numărul de obiecte, durata de viață și tipurile acestora trebuie cunoscute exact în etapa de proiectare a programului. La rezolvarea problemelor de profil mai larg - dezvoltarea sistemelor de proiectare asistată de calculator
(CAD), controlul stocurilor sau controlul traficului aerian - această abordare poate fi prea limitată.

A doua modalitate este de a crea în mod dinamic obiecte într-o zonă de memorie numită „heap” ( morman). În acest caz, numărul de obiecte, tipurile lor exacte și durata de viață rămân necunoscute până la pornirea programului. Toate acestea sunt determinate „din mers” în timp ce programul rulează. Dacă aveți nevoie de un obiect nou, pur și simplu îl creați pe grămada atunci când este necesar. Deoarece heap-ul este gestionat dinamic, este nevoie de mult mai mult pentru a aloca memorie din heap în timpul execuției programului decât atunci când alocați memorie pe stivă. (Este nevoie de o singură instrucțiune de mașină pentru a aloca memorie pe stivă, mutând indicatorul stivei în jos, iar eliberarea acestuia se face prin mutarea acestui indicator în sus. Timpul necesar pentru a aloca memorie pe heap depinde de structura depozitului.)

Abordarea dinamică presupune că obiectele sunt mari și complexe, astfel încât timpul suplimentar necesar pentru alocarea și dealocarea memoriei nu va avea un impact semnificativ asupra procesului de creare a acestora. Apoi, flexibilitatea suplimentară este foarte importantă pentru rezolvarea problemelor de bază de programare.

LA Java este folosită doar a doua abordare Tipurile primitive, care vor fi discutate în continuare, sunt un caz special.. Cuvântul cheie este folosit de fiecare dată când este creat un obiect. nou pentru a construi o instanță dinamică.

Cu toate acestea, există un alt factor, și anume durata de viață a obiectului. În limbile care acceptă crearea de obiecte pe stivă, compilatorul determină cât timp este utilizat un obiect și îl poate distruge automat. Cu toate acestea, atunci când un obiect este creat pe heap, compilatorul nu are idee despre durata de viață a obiectului. În limbi precum C++, distrugerea obiectului trebuie să fie încadrată explicit în program; dacă acest lucru nu se face, apare o scurgere de memorie (o problemă comună în programe C++). LA Java există un mecanism numit colectarea gunoiului; detectează automat când un obiect nu mai este folosit și îl distruge. Colectorul de gunoi este foarte la îndemână pentru că scutește programatorul de multe bătăi de cap. Mai important, colectorul de gunoi vă oferă mult mai multă încredere că problema insidioasă a pierderilor de memorie nu s-a strecurat în programul dvs. (care a adus mai mult de un proiect în C++).

LA Java colectorul de gunoi este proiectat astfel încât să poată face față problemei eliberării memoriei în sine (acest lucru nu afectează alte aspecte ale sfârșitului vieții unui obiect). Colectorul de gunoi „știe” când un obiect nu mai este utilizat și își folosește cunoștințele pentru a dealoca automat memoria. Datorită acestui fapt (împreună cu faptul că toate obiectele moștenesc de la o singură clasă de bază Obiect și sunt create numai pe grămada) Java mult mai usor decat programarea C++. Dezvoltatorul trebuie să ia mai putine deciziiși depășiți mai puține obstacole.

Gestionarea excepțiilor: tratarea erorilor

Încă din primele zile ale limbajelor de programare, gestionarea erorilor a fost unul dintre cele mai dificile subiecte. Este foarte dificil să dezvoltați un mecanism bun de gestionare a erorilor, așa că multe limbi pur și simplu ignoră această problemă, lăsând-o în seama dezvoltatorilor de biblioteci de software. Acestea din urmă oferă soluții la jumătatea drumului care funcționează în multe situații, dar pot fi deseori pur și simplu ocolite (de obicei pur și simplu prin faptul că nu le acordăm atenție). Principala problemă cu multe mecanisme de gestionare a excepțiilor este că se bazează pe aderarea conștiincioasă a programatorului la reguli care nu sunt impuse de limbaj. Dacă programatorul este neglijent - și acest lucru se întâmplă adesea atunci când munca este grăbită - el poate uita cu ușurință de aceste mecanisme.

Mecanismul de gestionare a excepțiilor construiește gestionarea erorilor chiar în limbajul de programare sau chiar în sistem de operare. O excepție este un obiect care este aruncat la locul unei erori, care poate fi apoi „prins” de un handler de excepții adecvat conceput pentru anumite tipuri de erori. Gestionarea excepțiilor pare să definească o cale paralelă de execuție a programului care intră în vigoare atunci când ceva nu merge conform planului. Și pentru că definește o cale de execuție separată, codul de gestionare a erorilor nu se amestecă cu codul normal. Acest lucru simplifică scrierea programelor, deoarece nu trebuie să verificați în mod constant eventualele erori. În plus, excepția nu este ca un cod de eroare numeric returnat de o metodă, sau un flag setat în cazul unei situații de problemă, acesta din urmă putând fi ignorat. Excepția nu poate fi ignorată, se garantează că va fi tratată undeva. În cele din urmă, excepțiile fac posibilă restabilirea funcționării normale a programului după o operație incorectă. În loc să încheiați doar programul, puteți corecta situația și continua să îl rulați; crescând astfel fiabilitatea programului.

Mecanismul de gestionare a excepțiilor în Java iese în evidență față de restul limbilor pentru că a fost construit în limbaj de la bun început și este responsabilitatea dezvoltatorului să o folosească. Acesta este singurul mod acceptabil de a raporta erori. Dacă nu scrieți cod pentru gestionarea corectă a excepțiilor, veți primi o eroare în timpul compilării. O astfel de abordare consecventă simplifică uneori foarte mult gestionarea erorilor.

Este de remarcat faptul că gestionarea excepțiilor nu este o caracteristică a unui limbaj orientat pe obiecte, deși în aceste limbi o excepție este de obicei reprezentată de un obiect. Un astfel de mecanism exista chiar înainte de apariția limbajelor orientate pe obiecte.

Programare în paralel

Unul dintre conceptele fundamentale ale programării este ideea de a face mai multe lucruri în același timp. Multe sarcini necesită ca programul să-și întrerupă activitatea curentă, să rezolve o altă sarcină și apoi să revină la procesul principal. Problema a fost rezolvată în moduri diferite.
La început, programatorii care cunoșteau arhitectura mașinii au scris proceduri de gestionare a întreruperilor, adică suspendarea procesului principal a fost efectuată la nivel hardware. Această soluție a funcționat bine, dar a fost complexă și non-mobilă, ceea ce a făcut mult mai dificilă portarea unor astfel de programe pe noi tipuri de computere.

Uneori, întreruperile sunt cu adevărat necesare pentru a procesa operațiunile sarcinilor critice în timp, dar există o întreagă clasă de sarcini în care trebuie doar să împărțiți sarcina în mai multe părți executate separat, astfel încât programul să reacționeze mai rapid la influențele externe. Aceste părți ale programului executate separat sunt numite cursuri, și întregul principiu se numește concurență(concurență), sau calcul paralel. Un exemplu comun de concurență este interfața cu utilizatorul. Într-un program cu fire, utilizatorul poate apăsa un buton și poate obține un răspuns rapid fără a aștepta ca programul să finalizeze operația curentă.

De obicei, firele definesc doar modul în care este alocat timpul unui procesor. Dar dacă sistemul de operare acceptă mai multe procesoare, fiecare fir poate fi atribuit unui procesor separat; așa se realizează paralelismul adevărat. Una dintre caracteristicile frumoase ale paralelismului, la nivel de limbaj, este că programatorul nu trebuie să știe dacă există unul sau mai multe procesoare în sistem. Programul este împărțit în mod logic în fire, iar dacă mașina are mai multe procesoare, rulează mai repede fără setări speciale.

Toate acestea dau impresia că fluxurile sunt foarte ușor de utilizat. Dar există o captură: resursele partajate. Dacă mai multe fire de execuție încearcă să acceseze aceeași resursă în același timp, apar probleme. De exemplu, două procese nu pot trimite simultan informații către o imprimantă. Pentru a preveni conflictele, resursele partajate (cum ar fi o imprimantă) ar trebui să fie blocate în timpul utilizării. Firul blochează resursa, își finalizează operarea și apoi eliberează blocarea, astfel încât altcineva să poată accesa resursa.

Suport de concurență încorporat în limbaj Java, și cu ieșirea Java SE5 a adăugat un sprijin semnificativ la nivel de bibliotecă.

Javași Internet

În cazul în care un Java este un alt limbaj de programare, se pune întrebarea: de ce este atât de important și de ce este prezentat ca un pas revoluționar în dezvoltarea software-ului? Din punctul de vedere al sarcinilor tradiționale de programare, răspunsul nu este imediat evident. Deşi limba Java utilă la construirea de aplicații de sine stătătoare, cea mai importantă aplicație a sa a fost și rămâne programarea pentru rețea world wide web.

Ce este Web-ul?

La prima vedere, Web-ul pare destul de misterios datorită abundenței de termeni noi precum „surfing”, „prezență” și „pagini de pornire”. Pentru a înțelege ce este aceasta, este util să înțelegeți imaginea de ansamblu - dar mai întâi trebuie să înțelegeți interacțiunea sistemelor client/server, care sunt unul dintre cele mai sarcini provocatoare computer de calcul.

Client/Server Computing

Ideea de bază din spatele sistemelor client/server este că aveți un depozit centralizat de informații - de obicei sub forma unei baze de date - și că informațiile sunt furnizate la cererea unui grup de oameni sau computere. Într-un sistem client/server, un depozit centralizat de informații joacă un rol cheie, care permite de obicei modificarea datelor în așa fel încât aceste modificări să fie propagate către utilizatorii informațiilor. Toate împreună: un depozit de informații, software, care distribuie informații, iar computerul (calculatoarele) pe care sunt stocate software-ul și datele se numesc server. Software-ul de pe mașina utilizatorului care comunică cu serverul, primește informațiile, o procesează și apoi le afișează în mod corespunzător se numește client.

Deci, conceptul de bază al calculului client/server nu este chiar atât de complicat. Problemele apar deoarece un server încearcă să deservească mai mulți clienți în același timp. În mod obișnuit, în soluție este implicat un sistem de management al bazei de date, iar dezvoltatorul încearcă să „optimizeze” structura datelor prin distribuția acesteia în tabele. În plus, sistemul permite adesea clientului să adauge noi informații la server. Și asta înseamnă că noile informații ale clientului trebuie protejate de pierderi în timpul stocării în baza de date, precum și de posibilitatea de a le suprascrie cu date de la alt client. (Aceasta se numește procesarea tranzacțiilor.) Când schimbați software-ul client, nu trebuie doar să îl compilați și să îl testați, ci și să îl instalați pe mașinile client, ceea ce poate fi mult mai dificil și mai costisitor decât ați putea crede. Este deosebit de dificil să organizezi suport pentru multe sisteme de operare și arhitecturi computerizate diferite. În cele din urmă, este necesar să se țină cont cel mai important factor performanță: serverul poate primi sute de solicitări în același timp, iar cea mai mică întârziere amenință cu consecințe grave. Pentru a reduce latența, programatorii încearcă să distribuie calculele, adesea chiar și pe mașina client, și uneori să le transfere pe mașini server suplimentare, folosind așa-numitul middleware (middleware). (Programele proxy facilitează, de asemenea, întreținerea programelor.)

Ideea simplă de a disemina informații între oameni are atât de multe niveluri de complexitate în implementarea ei încât, în general, soluția ei pare de neatins. Și totuși este vital: aproximativ jumătate din toate sarcinile de programare se bazează pe acesta. Este implicată în rezolvarea problemelor de la procesarea comenzilor și tranzacțiile cu cardul de credit până la diseminarea de tot felul de date - științifice, guvernamentale, cotații bursiere... lista este nesfârșită. În trecut, trebuia să creați o soluție separată pentru fiecare sarcină nouă. Aceste soluții nu sunt ușor de creat, cu atât mai greu de utilizat, iar utilizatorul a trebuit să învețe o nouă interfață cu fiecare program nou. Sarcina de calcul client/server necesită o abordare mai largă.

Web-ul este ca un server gigant

De fapt, web-ul este un sistem uriaș client/server. Cu toate acestea, asta nu este tot: o singură rețea toate serverele și clienții coexistă simultan. Cu toate acestea, acest fapt nu ar trebui să vă intereseze, deoarece de obicei vă conectați și interacționați cu un singur server (chiar dacă trebuie să îl căutați în toată lumea).

La început, a fost folosit un simplu schimb unidirecțional de informații. Ai făcut o cerere către server, acesta ți-a trimis un fișier pe care programul tău de vizualizare (adică clientul) l-a procesat pentru tine. Dar, în curând, pur și simplu obținerea de pagini statice de pe server nu a fost suficientă. Utilizatorii doreau să profite din plin de sistemul client/server, să trimită informații de la client la server pentru, de exemplu, să răsfoiască baza de date a serverului, să adauge noi informații pe server sau să plaseze comenzi (care necesita măsuri speciale de securitate) . Observăm în mod constant aceste schimbări în procesul de dezvoltare web.

Instrumentele de navigare pe web (browsere) au reprezentat un mare pas înainte: au introdus conceptul de informație care este afișată în același mod pe orice tip de computer. Cu toate acestea, primele browsere erau încă primitive și au încetat rapid să îndeplinească cerințele. S-au dovedit a nu fi deosebit de interactive și au încetinit activitatea atât a serverelor, cât și a internetului în ansamblu - pentru orice acțiune care necesita programare, trebuia să trimiți informații către server și să aștepți ca acesta să o proceseze. Uneori trebuia să așteptați câteva minute doar pentru a afla că ați omis o scrisoare din cerere. Deoarece browserul era doar un vizualizator, nu putea efectua nici măcar cele mai simple sarcini de programare. (Pe de altă parte, această securitate garantată - utilizatorul era protejat de rularea programelor care conțineau viruși sau bug-uri.)

Au fost luate diferite abordări pentru a rezolva aceste probleme. Pentru început, standardele de afișare grafică au fost îmbunătățite, astfel încât browserele să poată afișa animații și videoclipuri. Sarcinile rămase au necesitat capacitatea de a rula programe pe mașina clientului, în interiorul browserului. Aceasta a fost numită programare pe partea clientului.

Programare pe partea clientului

Inițial, sistemul de interacțiune server-browser a fost conceput pentru conținut interactiv, dar suportul pentru această interactivitate a fost încredințat în totalitate serverului. Serverul a generat pagini statice pentru browserul clientului, care pur și simplu le procesa și le afișa. Standard HTML acceptă cele mai simple intrări: câmpuri de text, butoane radio, casete de selectare, liste și meniuri derulante, împreună cu butoane care pot face doar două lucruri: resetați datele formularului și trimiteți-le la server. Informațiile trimise sunt procesate de interfață CGI (Common Gateway Interface), acceptat de toate serverele web. Textul solicitării indică CGI cum să tratezi cu datele. Cel mai adesea, la cerere, un program este lansat din directorul cgi-bin de pe server. (În linia cu adresa paginii din browser, după trimiterea datelor din formular, uneori puteți vedea subșirul în mizeria de caractere cgi-bin.) Astfel de programe pot fi scrise în aproape toate limbile. Deseori folosit Perl, deoarece este orientat pe text și, de asemenea, un limbaj interpretat, prin urmare, poate fi folosit pe orice server, indiferent de tipul de procesor sau sistem de operare. Cu toate acestea, limbajul Piton(limba mea preferată - du-te la www.Python.org) recâștigă treptat „teritoriu” de la el datorită puterii și simplității sale.

Multe servere web puternice funcționează astăzi în întregime pe baza CGI; în principiu, această tehnologie vă permite să rezolvați aproape orice problemă. Cu toate acestea, serverele web au fost construite pe CGI programele sunt greu de întreținut și au probleme de reacție. Timp de raspuns CGI-programul depinde de cantitatea de informații transmise, precum și de încărcarea serverului și a rețelei. (Din cauza tuturor lansării menționate CGI programul poate dura mult timp). Primii designeri ai web-ului nu au prevăzut cât de repede vor fi epuizate resursele sistemului, deoarece era folosit în diferite aplicații. De exemplu, este aproape imposibil să afișați grafice în timp real în acesta, deoarece, cu orice schimbare a situației, este necesar să construiți un nou fișier GIF și să îl transferați către client. Fără îndoială că ați avut propriile experiențe amare - de exemplu, prin simpla trimitere a datelor din formular. Apăsați un buton pentru a trimite informații; serverul pornește CGI- un program care detectează o eroare, generează HTML- o pagină care îți spune despre asta și apoi trimite această pagină în direcția ta; trebuie să tastați din nou datele și să încercați din nou. Nu numai că este lent, ci și pur și simplu inelegant.

Problema este rezolvată prin programare pe partea clientului. De regulă, browserele rulează pe computere puternice capabile să rezolve o gamă largă de sarcini și cu o abordare standard bazată pe HTML computerul așteaptă pur și simplu să fie servit pe pagina următoare. Cu programarea pe partea clientului, browserului i se oferă toată munca pe care o poate face, iar pentru utilizator, acest lucru se traduce printr-o navigare pe web mai rapidă și o interactivitate mai bună.

Cu toate acestea, discutarea despre programarea clientului nu este mult diferită de discuțiile despre programare în general. Condițiile sunt aceleași, dar platformele sunt diferite: browserul seamănă cu un sistem de operare puternic trunchiat. Trebuie să programați oricum, așa că programarea pe partea client creează o serie amețitoare de probleme și soluții. Această secțiune se încheie cu o privire de ansamblu asupra unora dintre problemele și abordările inerente programării pe partea clientului.

Module de expansiune

Una dintre cele mai importante domenii în programarea clientului a devenit dezvoltarea modulelor de extensie ( plug-in-uri). Această abordare permite programatorului să adauge noi funcționalități browserului prin descărcarea unui program mic care este încorporat în browser. De fapt, din acest moment, browserul capătă noi funcționalități. (Un modul de extensie se încarcă o singură dată.) Pluginurile au oferit browserelor o serie de inovații rapide și puternice, dar scrierea unui astfel de modul nu este o sarcină ușoară și este puțin probabil să doriți să creați extensii de fiecare dată când creați un site nou. Valoarea plug-in-urilor pentru programarea clientului este că permit unui programator experimentat să adauge noi funcții în browser fără a cere permisiunea creatorului său. Astfel, modulele de extensie oferă o „uşă din spate” pentru integrarea noilor limbaje de programare pe partea clientului (deși nu toate limbajele sunt implementate în astfel de module).

Limbaje de scripting

Dezvoltarea plug-in-urilor a dat naștere multor limbaje de scripting. Folosind un limbaj de scripting, încorporați programul client direct în HTML-pagina, iar modulul care procesează această limbă este activat automat la vizualizarea acesteia. Limbile de script sunt de obicei destul de ușor de învățat; în esență, codul de script este textul care face parte din HTML-pages, astfel încât se încarcă foarte repede ca parte a unei singure solicitări către server în timpul preluării paginii. Prețul pentru aceasta este că oricine vă poate vedea (și fura) codul. Cu toate acestea, este puțin probabil să scrieți ceva emulat și sofisticat în limbaje de scripting, așa că problema copierii codului nu este atât de rea.

Un limbaj de scripting care este acceptat de aproape orice browser fără a instala module suplimentare este JavaScript(avand foarte putine in comun cu Java; numele a fost folosit pentru a „prinde” o bucată de succes Java la magazin). Din păcate, implementările originale JavaScriptîn browsere diferite erau destul de diferite unele de altele și chiar între versiuni diferite ale aceluiași browser. Standardizare JavaScript in forma ECMAScript a fost util, dar a durat timp pentru ca suportul său să apară în toate browserele (în plus, compania Microsoft au promovat activ propria limbă VBScript, care seamănă vag JavaScript). În cazul general, dezvoltatorul trebuie să se limiteze la un minim de posibilități JavaScript astfel încât codul este garantat să funcționeze în toate browserele. În ceea ce privește gestionarea erorilor și codul de depanare JavaScript, atunci aceasta este o sarcină dificilă în cel mai bun caz. Doar recent dezvoltatorii au reușit să creeze un adevărat sistem complex scris in JavaScript(companie Google, serviciu Gmail), și a necesitat cel mai mare entuziasm și experiență.

Acest lucru arată că limbajele de scripting utilizate în browsere au fost concepute pentru o serie de sarcini specifice, în principal pentru a crea o interfață grafică mai bogată și mai interactivă ( GUI). Cu toate acestea, limbajul de scripting poate fi folosit pentru 80% din sarcinile de programare ale clientului. Sarcina ta poate fi doar în acele 80%. Deoarece limbajele de scripting facilitează și rapid crearea cod de programare, ar trebui mai întâi să luați în considerare doar un astfel de limbaj înainte de a trece la soluții tehnologice mai complexe, cum ar fi Java.

Java

Dacă limbajele de scripting preiau 80% din sarcinile de programare client, atunci cine se poate ocupa de celelalte 20%? Pentru ei, cea mai populară soluție astăzi este Java. Nu numai că este un limbaj de programare puternic proiectat având în vedere securitatea, compatibilitatea cu platformele și internaționalizarea, dar este și un instrument în continuă evoluție, cu noi caracteristici și biblioteci care se potrivesc elegant sarcinilor de programare tradiționale complexe: multitasking, acces la baze de date, programare în rețea și calcul distribuit. Programarea clientului Java se reduce la dezvoltarea applet-urilor, precum și la utilizarea pachetului Java Web Start.

Un applet este un mini-program care poate rula numai în interiorul unui browser. Appleturile sunt încărcate automat ca parte a unei pagini web (în același mod în care, de exemplu, sunt încărcate graficele). Când un applet este activat, acesta execută programul. Acesta este unul dintre avantajele applet-ului - vă permite să distribuiți automat programe către clienți de pe server exact atunci când utilizatorul are nevoie de aceste programe și nu înainte. Utilizatorul primește cea mai recentă versiune a programului client, fără probleme și dificultăți asociate cu reinstalarea. Conform ideologiei Java, programatorul creează un singur program care rulează automat pe toate computerele care au browsere cu interpret încorporat Java. (Acest lucru este valabil pentru aproape toate computerele.) Din moment ce Java este un limbaj de programare complet, cât mai mult lucru posibil ar trebui făcut pe partea clientului înainte (sau după) apelarea serverului. De exemplu, nu trebuie să trimiteți o solicitare prin Internet pentru a afla că a existat o eroare în datele primite sau în anumiți parametri, iar computerul client poate desena rapid un fel de grafic fără a aștepta ca serverul să o facă și trimite înapoi fișierul imagine. Această schemă nu numai că oferă un beneficiu de viteză și receptivitate imediată, dar reduce și sarcina pe transportul și serverele de rețea principală, prevenind încetinirile experienței generale pe Internet.

Alternative

Sincer să fiu, applet-uri Java nu a justificat entuziasmul iniţial. La prima apariție Java toată lumea a fost foarte entuziasmată de applet-uri, deoarece acestea permiteau programare serioasă pe partea clientului, o capacitate de răspuns crescută și o lățime de bandă redusă pentru aplicațiile de Internet. Se prevedea că appletele vor avea un viitor grozav.

Într-adevăr, o serie de applet-uri foarte interesante pot fi găsite pe Web. Și totuși tranziția în masă la appleturi nu a avut loc. Probabil principala problemă a fost că descărcarea pachetului de 10 MB pentru a instala mediul Java Runtime Environment (JRE) prea înfricoșător pentru utilizatorul obișnuit. Faptul că firma Microsoft nu s-a pornit JREîn curs de livrare Internet Explorer, a decis în cele din urmă soarta applet-urilor. Oricum, applet-uri Java nu au fost utilizate pe scară largă.

Cu toate acestea, applet-uri și aplicații Java Web Start sunt foarte utile în unele situații. Dacă configurația computerelor utilizatorilor finali este sub control (de exemplu, în organizații), utilizarea acestor tehnologii pentru distribuirea și actualizarea aplicațiilor client este destul de justificată; economisește mult timp, forță de muncă și bani (mai ales cu actualizări frecvente).

.NET și C#

De ceva vreme, principalul concurent Java-appleturile erau considerate componente ActiveX de la companie Microsoft, deși au cerut pentru munca lor prezența pe mașina clientului Windows. Acum Microsoft opus Java concurenți cu drepturi depline: este o platformă .NET si limbaj de programare DIN#. Platformă .NET este aproximativ la fel cu o mașină virtuală Java(JVM) și biblioteci Java, și limba DIN# are o asemănare clară cu limbajul Java. Fără îndoială, acesta este cel mai bun lucru creat de Microsoft în domeniul limbajelor și mediilor de programare. Desigur, dezvoltatorii Microsoft avea un oarecare avantaj; au văzut că în Java a reușit și ce nu a avut, și a putut să se bazeze pe aceste fapte, dar rezultatul s-a dovedit a fi destul de demn. Pentru prima dată de la nașterea lui, Java a existat un adevărat concurent. Dezvoltatorii de la Soare trebuia să arunce o privire la DIN#, aflați de ce programatorii ar putea dori să treacă la acest limbaj și depune toate eforturile pentru a se îmbunătăți serios Javaîn Java SE5.

LA acest momentîntrebarea principală este dacă Microsoft va permite portarea completă a .NET către alte platforme. LA Microsoft susțin că nu este nicio problemă în asta și în proiect mono() oferă o implementare parțială .NET pentru linux. Cu toate acestea, deoarece această implementare este incompletă, deocamdată Microsoft nu decide să arunce nicio parte din ea, să parieze .NET ca tehnologie multiplatformă este încă devreme.

Internet și intranet

Web-ul oferă cea mai generală soluție pentru sarcinile client/server, așa că are sens să folosiți aceeași tehnologie pentru a rezolva probleme specifice; acest lucru este valabil mai ales pentru interacțiunea clasică client/server în cadrul companiei. Abordarea tradițională client/server are probleme cu diferențele dintre tipurile de computere client, adăugând la acestea dificultatea instalării de noi programe pentru clienți; ambele probleme sunt rezolvate de browsere și de programarea client-side. Când tehnologia web este utilizată pentru a forma o rețea de informații în cadrul unei companii, o astfel de rețea se numește intranet. Intranet-urile oferă mult mai multă securitate decât Internetul, deoarece puteți controla fizic accesul la serverele companiei dumneavoastră. În ceea ce privește învățarea, este mult mai ușor pentru cineva care înțelege conceptul de browser să înțeleagă diferite pagini și applet-uri, astfel încât timpul de a stăpâni sisteme noi este redus.

Problema securității ne aduce la una dintre direcțiile care apar automat în programarea clientului. Dacă programul tău rulează pe Internet, atunci nu știi pe ce platformă va rula. Trebuie acordată o atenție deosebită pentru a evita răspândirea codurilor incorecte. Aici avem nevoie de soluții multiplatforme și sigure, cum ar fi Java sau limbaj de scripting.

Intranet-urile au alte restricții. Destul de des, toate mașinile din rețea rulează pe platformă Intel/Windows. Pe intranet, sunteți responsabil pentru calitatea codului dvs. și puteți remedia erorile pe măsură ce sunt găsite. În plus, este posibil să aveți deja o colecție de soluții care s-au dovedit a funcționa în sisteme client/server mai tradiționale, în timp ce noul software trebuie instalat manual pe computerul client cu fiecare actualizare. Timpul petrecut cu actualizări este cel mai puternic argument în favoarea tehnologiilor de browser, unde actualizările se fac în mod invizibil și automat (la fel se poate face Java Web Start). Dacă sunteți implicat în întreținerea unui intranet, este mai înțelept să luați calea care vă permite să valorificați ceea ce aveți deja fără a fi nevoie să rescrieți programele în limbi noi.

Când se confruntă cu volumul imens de sarcini de programare a clientului, care poate dezamăgi orice proiectant, cel mai bine este să le evaluați în ceea ce privește raportul cost/beneficiu. Luați în considerare limitările problemei dvs. și încercați să vă imaginați cea mai scurtă cale de a o rezolva. Deoarece programarea clientului este încă programare, tehnologiile de dezvoltare care promit cea mai rapidă soluție sunt întotdeauna relevante. Această atitudine proactivă vă va oferi oportunitatea de a vă pregăti pentru problemele inevitabile ale programării.

Programare pe partea serverului

Discuția noastră a ocolit subiectul programării pe server, pe care mulți îl consideră a fi cel mai mult punct forte Java. Ce se întâmplă când trimiteți o solicitare către server? De cele mai multe ori, cererea se reduce la o simplă solicitare „trimite-mi acest fișier”. Browserul procesează apoi fișierul în mod corespunzător: as HTML-page like imagine like Java-applet, ca un script etc.

O solicitare mai complexă către server implică de obicei accesarea bazei de date. În cel mai frecvent caz, este solicitată o căutare complexă în baza de date, ale cărei rezultate sunt apoi convertite de server în HTML-pagina si iti trimite. (Desigur, dacă clientul este capabil să efectueze o acțiune folosind Java sau un limbaj de scripting, datele pot fi procesate de el, ceea ce va fi mai rapid și va reduce încărcarea serverului.) Sau poate fi necesar să vă înregistrați în baza de date atunci când vă alăturați unui grup sau să faceți checkout, ceea ce va necesita modificări ale bazei de date. Astfel de cereri trebuie procesate de un cod de pe server; în general, aceasta este ceea ce se numește programare pe server. În mod tradițional, programarea serverului se făcea pe Perl, Python, C++ sau alt limbaj care vă permite să creați programe CGI, dar există opțiuni mai interesante. Acestea includ cele bazate pe Java servere web care vă permit să faceți programare pe server Java folosind așa-numitele servlet-uri. Servlets și descendenții lor JSP-uri, sunt cele două motive principale pentru care companiile de conținut web se mută la Java, în principal pentru că rezolvă problemele de incompatibilitate între browsere.

În ciuda tuturor discuțiilor despre Java ca limbaj de programare pe Internet, Java de fapt, este un limbaj de programare cu drepturi depline, capabil să rezolve aproape toate problemele rezolvate în alte limbi. Avantaje Java nu se limitează la o portabilitate bună: este adecvarea pentru rezolvarea problemelor de programare și rezistența la erori și o bibliotecă standard mare și numeroase dezvoltări terțe - atât existente, cât și în curs de dezvoltare.

rezumat

Știți cum arată un program procedural: definiții de date și apeluri de funcții. Aflarea scopului unui astfel de program necesită efort prin analizarea funcțiilor și crearea unei imagini de ansamblu în mintea ta. Din acest motiv, crearea unor astfel de programe necesită utilizarea unor instrumente intermediare - în sine ele sunt mai orientate spre computer și nu spre sarcina care urmează să fie rezolvată.

Deoarece OOP adaugă multe concepte noi celor deja disponibile în limbaje procedurale, este firesc să presupunem că codul Java va fi mult mai complicată decât o metodă similară într-un limbaj procedural. Dar aici te așteaptă o surpriză plăcută: un program bine scris în Java de obicei mult mai ușor de înțeles decât omologul său procedural. Tot ce vedeți sunt definiții de obiecte care reprezintă concepte de spațiu de decizie (nu concepte de implementare pe computer) și mesaje trimise acelor obiecte care reprezintă acțiuni în acel spațiu. Unul dintre avantajele OOP este că un program bine conceput poate fi înțeles pur și simplu uitându-se la codul sursă. În plus, de obicei trebuie să scrieți mult mai puțin cod, deoarece multe sarcini pot fi deja rezolvate cu ușurință. bibliotecile existente clase.

Programare și limbaj orientat pe obiecte Java nu sunt potrivite pentru toată lumea. Este foarte important să vă aflați mai întâi nevoile pentru a decide dacă treceți la Java sau este mai bine sa optezi pentru un alt sistem de programare (inclusiv cel pe care il folosesti in prezent). Dacă știi că în viitorul apropiat te vei confrunta cu nevoi foarte specifice sau munca ta va fi supusă restricțiilor care Java nu face față, este mai bine să luați în considerare alte posibilități (în special, vă recomand să aruncați o privire mai atentă asupra limbii Piton,). Alegerea Java, trebuie să înțelegeți ce alte opțiuni sunt disponibile și de ce ați ales această cale.

Schimb de mesaje:

Trimiteți direct pe această parte: http://website/javabooks/Thinking_in_Java_4th_edition.html Trimiteți coduri BB: Thinking_in_Java_4th_edition
mesaj html: