Когато се създава манипулатор искаме да го свържем с някакъв обект. Изобщо това се прави с ключовата дума new. new казва, “Направи ми нов обект от този вид.” Така че горния пример може да бъде и:
String s = new String("asdf");
Това не само значи “Направи ми String,” но също дава информация как да се направи String чрез даването на началния стринг.
Разбира се String не е единственият съществуващ тип. Java идва преситен с готови типове. По-важното е, че може да създавате и свои типове. В това се състои основната активност в Java и за това ще учим в останалата част от книгата.
Къде живее паметта
Полезно е да се онагледи как стават някои неща докато се изпълнява програмата и в частност как се аранжира паметта. Има шест различни места за запомняне на данни:
-
Регистри. Това е най-бързата памет понеже е на различно място от обикновената: вътре в процесора. Регистрите са обаче жестоко ограничен ресурс и затова се запазват от компилатора за неговите нужди. Нямате директен контрол и програмите не виждат, че има регистри.
-
Стекът. В общата RAM (памет с произволен достъп) е, но директно се поддържа от процесора чрез неговия стеков указател. Указателят се увеличава за да освободи памет и намалява, за да я създаде. Това е извънредно бърз и ефективен начин да се създаде памет, отстъпващ само на регистрите. Java компилаторът трябва да знае докато компилира програмата точната дължина и време на живот на всичко, което е на стека, понеже трябва да създаде код, който да мърда указателя нагоре и надолу. Тези ограничения слагат граници за вашата програма така че макар и да има Java памет на стека – специално, манипулатори на обекти – Java обектите не се слагат на стека.
-
Хийп. Това е памет за обща употреба (също в RAM паметта) където всички Java обекти живеят. Хубавото е, че за разлика от случая със стека, компилаторът няма нужда да знае дължината на обектите и времето им на живот. Така има голяма гъвкавост при използването на хийпа. Щом ви потрябва обект, пишете код да го създаде чрез new и памет се алокира щом кодът се изпълни. Разбира се има цена за тази гъвкавост: повече време трябва за да се алокира памет в хийпа.
-
Статична памет. “Статична” е използвано тук в смисъл на “в определено място” (макар че е също в RAM). Статичната памет съдържа данни които са налични през цялото време на изпълнение на програмата. Може да използвате ключовата дума static за да посочите че даден елемент на обект ще е в тази памет, но самите Java никога не се слагат в статичната памет.
-
Памет за константи. Константите често се слагат в програмния код, където е по-сигурно, че никога няма да се променят. Понякога може да се сложат и в памет само за четене (ROM).
-
Не-RAM памет. Ако едни данни живеят напълно извън програмата, те могат да съществуват и когато програмата не е стартирана, без нейното управление. Два начални примера са streamed objects, където обектите са превърнати в поток от байтове, основно за да се изпратят към друга машина, и упоритите обекти (или устойчиви - б.пр.), в който случай обектите се запомнят на диска и остават и след като програмата е спряна. Трикът е и в двата случая да се използва енергонезависима памет, а после отново всичко да се прехвърли в обекновени RAM-базирани обекти когато е необходимо. Java 1.1 поддържа лека упоритост, а бъдещите версии на Java може би ще дават по-завършени решения на този въпрос.
Специален случай: първични типове
Има една група типове, които се третират специално; може да ги мислим като “първични” типове, които твърде често се използват при работа. Причината за специалното третиране е, че да се създаде нов тип с new, особено ако е малка, проста променлива, не е много ефективно, понеже new слага обектите на хийпа. За тези типове Java се връща на подхода използван в C и C++. Тоест, наместо да се създаде променлива чрез new, “автоматична” променлива се създава която не е манипулатор. Променливата съдържа стойността и се слага на стека, така че е много по-ефективна.
Java определя дължината на всеки първичен тип. Тези дължини не се променят при минаване от една машина на друга както в много други езици става. Тази инвариантност е една от причините Java програмите да са толкова преносими.
-
Първичен тип
|
Дължина
|
Минимум
|
Максимум
|
Обхващащ тип
|
boolean
|
1-bit
|
–
|
–
|
Boolean
|
char
|
16-bit
|
Unicode 0
|
Unicode 216- 1
|
Character
|
byte
|
8-bit
|
-128
|
+127
|
Byte1
|
short
|
16-bit
|
-215
|
+215 – 1
|
Short1
|
int
|
32-bit
|
-231
|
+231 – 1
|
Integer
|
long
|
64-bit
|
-263
|
+263 – 1
|
Long
|
float
|
32-bit
|
IEEE754
|
IEEE754
|
Float
|
double
|
64-bit
|
IEEE754
|
IEEE754
|
Double
|
void
|
–
|
–
|
–
|
Void1
|
Всички числови типове са със знак, така че не търсете беззнакови типове.
Примитивните типове данни също имат “обхващащи” ги класове. Това значи, че ако искате да създадете някой от примитивните типове в хийпа ще използвате асоциирания обхващащ тип. Например:
char c = 'x';
Character C = new Character(c);
или също може:
Character C = new Character('x');
Причините да се прави така ще се изяснят в друга глава.
Java 1.1 добави два класа за аритметика с висока точност: BigInteger и BigDecimal. Макар и приблизително да подхождат за категорията “обхващащи” класове, никой от тях няма аналог-примитив.
И двата класа имат методи, които дават аналози на операциите с примитивни типове. Тоест може да направите всичко с BigInteger or BigDecimal каквото можете с int или float, само дето трябва да използвате методи наместо оператори. Също, понеже повече неща се правят, операциите са по-бавни. Обменяте точност за скорост.
BigInteger поддържа цели [числа] с произволна точност. Това значи че може да представяте произволни числа и да пресмятате без загуба на значещи цифри.
BigDecimal е за числа с фиксирана запетая с произволна точност; може например да го използвате за финансови изчисления които са напълно акуратни.
За детайли относно конструкторите и методите на тези класове вижте документацията.
Масиви в Java
Практически всички програмни езици поддържат масиви. Използването им в C и C++ е опасно, понеже повечето масиви са просто блокове памет. Ако програмата използва памет извън блока или го използва неинициализиран (чести програмни грешки) ще има непредсказуеми резултати.2
Една от основните цели на Java е сигурността, така че много от проблемите които вадят душата на програмистите в C и C++ липсват в Java. Масивът в Java се гарантира да бъде инициализиран и не може да бъде достъпен извън границите си. Проверката на индексите и т.н. по време на изпълнение довеждат до малко повече разходи, но презумпцията е, че си струва заради сигурността.
Когато създавате масив от обекти фактически създавате масив от манипулатори и всеки от тях се инициализира със специална ключова думар означаваща една специална стойност: null. Когато Java вижда null той разбира, че съответния манипулатор няма асоцииран обект. Трябва да асоциирате обект с всеки манипулатор преди да го използвате и ако се опитате да го направите докато стойността му е още null проблемът ще изскочи по време на изпълнение. По този начин типичните грешки са пресечени в Java.
Може също да се създаде масив от примитиви. Отново компилаторът гарантира същите неща, както и по-горе.
Масивите ще се разгледат в детайли в следващите глави.
Сподели с приятели: |