В повечето програмни езици въпросът за времето на живот на обектите (в широкия смисъл на думата - б.пр.) заема голяма част от общото програмистко усилие. Колко дълго ще трае дадена променлива? Ако трябва да се разруши, кога да се направи това? Трудностите с времетраенето на променливите довеждат до множество грешки и тази секция показва как Java съществено опростява въпроса, почиствайки всичко вместо вас.
Обхвати
Повечето процедурни езици имат концепцията за обхват. С нея се определят както видимостта, така и времето на живот на променливите (в дадения обхват - б.пр.). В C, C++ и Java обхватът се определя чрез поставяне на големи скоби {}. Така например:
{
int x = 12;
/* само x достъпно */
{
int q = 96;
/* x и q достъпни */
}
/* само x достъпно */
/* q “извън обхвата” */
}
Променлива определена в даден обхват е достъпна само до края на същия обхват.
Индентацията прави Java кода по-лесен за четене. Тъй като Java is е език със свободна форма, табулациите и краищата на редовете не променят програмата по същество.
Забележете че не може да се прави следното, макар и да би могло в C и C++:
{
int x = 12;
{
int x = 96; /* Неприемливо!! */
}
}
Компилаторът ще каже, че x вече е било дефинирано. Така C и C++ възможността да се “скрие” променлива в по-голям обхват не е позволено, понеже проектантите на Java са считали, че това ще доведе до проблемни програми.
Обхват на обекти
Java нямат същото време на живот както примитивите. Като създадете Java обект с new той остава и след края на обхвата. Така при:
{
String s = new String("a string");
} /* край на обхвата */
манипулаторът s изчезва на края на обхвата. String обектът към който сочеше s обаче още заема памет. В това парче код няма начин вече да се достигне обекта, понеже манипулаторът му е извън обхвата. В други глави ще научите как може да се управлява обектът през цялата програма.
Това извежда наяве че тъй като обектите създадени с new остават толкова, колкото ги искате, камарата програмистки проблеми просто изчезва в C++ и Java. Най-тежките проблеми изглежда да възникват в C++ понеже няма никаква помощ от езика да се определи дали обектите са още необходими. И по-важното, в C++ трябва да осигурите унищожаването на обектите когато сте си свършили работата с тях.
Това извиква интересен въпрос. Ако Java оставя обектите неразрушени, кое предпазва от препълването на паметта и спирането на програмата? Това е точно проблемът, който би възникнал в C++ (ько програмистът не се грижи достатъчно, вж. предния абзац - б.пр.). Малко магия се случва. Java има боклучар, който гледа всички обекти създадени с new и определя към кои от тях вече няма обръщения от никъде. Тогава освобождава паметта на тези обекти, така че да се използва от други обекти. Това значи, че никога не се грижите за самата памет. Просто създавате обекти и когато те не са нужни вече, от само себе си изчезват. Това елиминира определен клас от програмистки проблеми: т.н. “изтичане на памет,” където програмистът е забравил да освободи паметта.
Създаване на нови типове данни: class
Ако всичко е обект(и), как да определим поведението и изгледа на даден клас обекти? Другояче казано, как да определим типа на обект? Може би очаквате ключова дума “тип” и тя сигурно би имала смесъл. Исторически обаче повечето ООП езици са използвали думата class за да означат “Ще ви кажа на какво ще прилича новия обект.” Ключовата дума class (която толкова много се използва навсякъде, че даже не е с удебелени букви в книгата) се следва от името на новия тип. Например:
class ATypeName { /* тялото на класа ще е тук */ }
Това въвежда нов тип, така че може да създадете обект с new:
ATypeName a = new ATypeName();
В AtypeName тялото на класа е само един коментар (звездите и наклонените черти и това, което е между тях) така че не много може да се направи с него. Фактически не можете да му кажете да прави повече от нищо (тоест, не може да му изпратите никакво интересно съобщение) докато не му определите методи.
Полета и методи
Когато определяте клас (всичко което правите в Java е определяне на класове, правене на такива обекти и изпращане на съобщения до тях) може да сложите два типа елементи в класа: членове-данни (понякога наричани полета) и членове-функции (типично наричани методи). Членъ-данни е обект (с когото комуникирате чрез манипулатора му) от какъвто и да е тип. Може да бъде и някой примитив (и тогава няма да има манипулатор). Ако е манипулатор на обект, трябва правилно да го инициализирате да сочи към обект, преди да го използвате (с new, както видяхме по-рано)в специална функция наречена конструктор (описана напълно в глава 4). Ако е първичен тип може да го инициализирате направо в точката на дефинирането му. (Както ще видите по-късно, манипулаторите също могат да се инициализират в точката на определянето им (другояче - декларацията им, б.пр.).)
Всеки обект има собствена памет за своите членове-данни; Те не се споделят от различните обекти. Ето пример за клас с членове данни:
class DataOnly {
int i;
float f;
boolean b;
}
Този клас не прави нищо, но може да се създаде такъв обект:
DataOnly d = new DataOnly();
Може да се присвояват стойности на членовете-данни, но най-напред трябва да научите как се означава член на обект. Това се прави с точка между името на обекта и члена (objectHandle.member). Например:
d.i = 47;
d.f = 1.1f;
d.b = false;
Възможно е също обектът да съдържа други обекти, които искате да модифицирате. За целта продължавате с точките. Например:
myPlane.leftTank.capacity = 100;
Класът DataOnly не може да прави повече от това да съдържа данни, понеже няма член-функции (методи). За да се разбере как те работят първо трябва да се разберат аргументите и връщаните стойности, които ще опишем накратко.
Когато примитивен даннов тип е член на клас гарантира се, че ще получи стойност по подразбиране, ако не го инициализирате:
-
Първичен тип
|
Подр. стойност
|
Boolean
|
false
|
Char
|
‘\u0000’ (null)
|
byte
|
(byte)0
|
short
|
(short)0
|
int
|
0
|
long
|
0L
|
float
|
0.0f
|
double
|
0.0d
|
Внимателно отбележете, че стойностите по подразбиране се гарантират от Java когато променливата се използва като член на клас. Това осигурява членовете-примитиви да са винаги инициализирани (нещо което C++ не прави), намаляват се източниците на грешки.
Тази гаранция не се отнася за “локалните” променливи – онези, които не са полета в клас. Така е ако имате вътре в дефиниция на функция:
int x;
Тогава x ще приеме каквато се случи стойност (както в C и C++); няма да бъде автоматично инициализирана с нула. Вие сте си отговорни за присвояването на подходяща стойност на x преди използването му. Ако забравите, Java определено изпреварва C++: имате грешка при компилация с предупреждение, че променлива може да не е инициализирана. (Много C++ компилатори биха ви предупредили за това, но в Java това е грешка, а не предупреждение.)
Сподели с приятели: |