Книга е още в много ранна фаза на написване



страница26/73
Дата25.07.2016
Размер13.53 Mb.
#6732
1   ...   22   23   24   25   26   27   28   29   ...   73

Инициализация на членове


Java излиза от пътя си за да гарантира че променливите са инициализирани пре­ди да бъдат използвани. В случая на променливи които са определени локално в ме­тод тази гаранция идва във формата на грешка при компилация. Така ако на­пи­шем:

void f() {

int i;

i++;


}

Ще получим съобщение за грешка в което се казва че i може да не е било ини­циа­лизирано. Разбира се, компилаторът би могъл да даде на i стойност по под­раз­биране, но е по-вероятно това да е програмистка грешка, която ще се открие по тази начин. Заставянето на програмиста да даде стойността най-вероятно ще хва­не бъг.

Ако примитивът е член-данни на клас, обаче, нещата са по-различни. Тъй като все­ки метод би могъл да инициализира данните, не е практично да се кара про­гра­мистът да го прави. Не е безопасно и да се оставя каквато се случи стойност, та­ка че всеки член се гарантира да има определена начална стойност. Тези стой­но­сти могат да се видят тук:

//: c04:InitialValues.java

// Shows default initial values
class Measurement {

boolean t;

char c;

byte b;


short s;

int i;


long l;

float f;


double d;

void print() {

System.out.println(

"Data type Inital value\n" +

"boolean " + t + "\n" +

"char " + c + "\n" +

"byte " + b + "\n" +

"short " + s + "\n" +

"int " + i + "\n" +

"long " + l + "\n" +

"float " + f + "\n" +

"double " + d);

}

}
public class InitialValues {



public static void main(String[] args) {

Measurement d = new Measurement();

d.print();

/* In this case you could also say:

new Measurement().print();

*/

}



} ///:~

Програмата извежда следното:

Data type Inital value

boolean false

char

byte 0


short 0

int 0


long 0

float 0.0

double 0.0

char стойността е нула (нулева стойност, не цифрата нула - б.пр.) и затова не се вижда.

Ще видите по-нататък че при декларация на манипулатор в метод той се ини­циа­лизира с нула по начало.

Може да се види, че макар и да не се дават стойности, променливите се ини­циа­ли­зират. Така че най-малкото го няма наказанието работа с неинициализирани про­менливи.

Задаване на инициализация


Какво става ако искаме да зададем начална стойност? Един пряк начин е просто да присвоим стойността в мястото, където се обявява променливата в класа. (За­бележете че това не може да се направи в C++, макар че C++ новаците ви­на­ги се опитват.) Ето дефинициите на полета за класа Measurement променени за да получават начални стойности:

class Measurement {

boolean b = true;

char c = 'x';

byte B = 47;

short s = 0xff;

int i = 999;

long l = 1;

float f = 3.14f;

double d = 3.14159;


//. . .

Също може да се инициализират и не-примитиви по този начин. Ако Depth е клас може да вмъкнете променлива и да я инициализирате така:

class Measurement {

Depth o = new Depth();

boolean b = true;
// . . .

Ако не сте дали на o начална стойност и продължите напред и се опитате така да го използвате, ще получите грешка по време на изпълнение от тип изклю­че­ние (разгледан в глава 9).

Може даже да извикате метод за даване на начална стойност:

class CInit {

int i = f();

//...


}

Този метод може да има аргументи, разбира се, но те не могат да бъдат неини­циа­лизирани още променливи. Това е допустимо:

class CInit {

int i = f();

int j = g(i);

//...


}

Но това не е:

class CInit {

int j = g(i);

int i = f();

//...


}

Тук компилаторът, съвсем на място, се оплаква от позоваване напред, понеже то се отнася за реда на инициализация, а не за компилацията.

Този подход към инициализацията е прост и праволинеен. Той има огра­ни­че­ние­то че всеки обект от тип Measurement ще получи същите начални стой­но­сти. Понякога това е точно което искаме, но в други случаи трябва повече гъв­ка­вост.

Инициализация в конструкторите


Конструкторът може да се използва за задаване на начални стойности и това е по-гъвкав начин, понеже по време на изпълнение може да се викат методи и да се определят стойностите. Трябва да се помни едно нещо, обаче: не се предотвратява автоматичната инициализация, която става преди викането на метода. Например ако напишем:

class Counter {


int i;
Counter() { i = 7; }
// . . .

i отначало ще стане нула, а после 7. Това се отнася за всички примитивни типове и манипулаторите на обекти, включително онези, които получават явно стойности при декларацията им. По тази причина компилаторът не се опитва да ви заставя да инициализирате елементи никъде в конструктора или преди да се използват – те са вече инициализирани.5

Ред на инициализация


Вътре в клас редът се определя от реда на деклариране на променливите. Ако и про­менливите да са размесени с дефиниции на методи, всички променливи се ини­циа­лизират преди да се извика кой да е метод – даже конструктора. На­при­мер:

//: c04:OrderOfInitialization.java

// Demonstrates initialization order.
// When the constructor is called, to create a

// Tag object, you'll see a message:

class Tag {

Tag(int marker) {

System.out.println("Tag(" + marker + ")");

}

}


class Card {

Tag t1 = new Tag(1); // Before constructor

Card() {

// Indicate we're in the constructor:

System.out.println("Card()");

t3 = new Tag(33); // Re-initialize t3

}

Tag t2 = new Tag(2); // After constructor



void f() {

System.out.println("f()");

}

Tag t3 = new Tag(3); // At end



}
public class OrderOfInitialization {

public static void main(String[] args) {

Card t = new Card();

t.f(); // Shows that construction is done

}

} ///:~


В Card декларациите на Tag нарочно са разхвърляни за да се види, че ини­циа­ли­зацията протича най-напред. В добавка t3 се преинициализира в кон­струк­то­ра. Изходът е:

Tag(1)


Tag(2)

Tag(3)


Card()

Tag(33)


f()

Така t3 манипулаторът бива инициализиран два пъти, един преди и един по вре­ме на извикването на конструктора. (Първият обект се бракува, така че може да бъ­де почистен по-късно.) На пръв поглед това може да изглежда неефективно, но то гарантира правилна инициализация – какво би станало ако би бил опре­де­лен претоварен конструктор който не инициализира t3 и нямаше “де­фолт­на”инициализация за t3 в декларацията му?


Инициализация на статични данни


Когато данните са static е същото; ако е примитив и не го инициализирате по­лу­ча­ва стойност по подразбиране. Ако е манипулатор остава инициализирано с ну­ла докато не създадете обект и свържете манипулатора с него.

Ако искате да сложите инициализацията в точката на декларацията, прави се съ­що като за не-статични. Тъй като обаче има само едно място в паметта за static не­зависимо от броя на създадените обекти възниква въпросът кога става ини­циа­лизацията. Примерът прави въпроса по-ясен:

//: c04:StaticInitialization.java

// Specifying initial values in a

// class definition.
class Bowl {

Bowl(int marker) {

System.out.println("Bowl(" + marker + ")");

}

void f(int marker) {



System.out.println("f(" + marker + ")");

}

}


class Table {

static Bowl b1 = new Bowl(1);

Table() {

System.out.println("Table()");

b2.f(1);

}

void f2(int marker) {



System.out.println("f2(" + marker + ")");

}

static Bowl b2 = new Bowl(2);



}
class Cupboard {

Bowl b3 = new Bowl(3);

static Bowl b4 = new Bowl(4);

Cupboard() {

System.out.println("Cupboard()");

b4.f(2);


}

void f3(int marker) {

System.out.println("f3(" + marker + ")");

}

static Bowl b5 = new Bowl(5);



}
public class StaticInitialization {

public static void main(String[] args) {

System.out.println(
"Creating new Cupboard() in main");

new Cupboard();

System.out.println(

"Creating new Cupboard() in main");

new Cupboard();

t2.f2(1);

t3.f3(1);

}

static Table t2 = new Table();



static Cupboard t3 = new Cupboard();

} ///:~


Bowl подволява да се види създаването на клас и Table и Cupboard създават static членове на Bowl разпръснати в техните декларации на класовете. За­бе­ле­же­те че Cupboard създава не-static Bowl b3 преди static декларациите. Изходът по­казва какво се е случило:

Bowl(1)


Bowl(2)

Table()


f(1)

Bowl(4)


Bowl(5)

Bowl(3)


Cupboard()

f(2)


Creating new Cupboard() in main

Bowl(3)


Cupboard()

f(2)


Creating new Cupboard() in main

Bowl(3)


Cupboard()

f(2)


f2(1)

f3(1)


Инициализацията на static се прави само ако е необходимо. Ако не съз­дад­е­теTable и никога не се отнесете към Table.b1 или Table.b2, static Bowl b1 и b2 ни­кога няма да се създадат. Обаче те се създават само когато първия Table обект се създава (или първия достъп до static се върши). След това static обект не се реинициализира.

Редът на инициализацията е вички static първо, ако не са били инициализирани по време на предишното създаване на обект и тогава не-static обектите. Може да видите доказателство за това в изхода.

От помощ ще е да резюмираме процеса на създаване на обект. Нека класът се каз­ва Dog:


  1. Първият път когато се създава обект от тип Dog или когато за пръв път до static метод или static поле от клас Dog се извършва достъп Java ин­тер­пре­та­торът трябва да намери Dog.class, което той прави чрез търсене по "пътя за класовете".

  2. Като е натоварен Dog.class (което създава Class обект, както ще научите по-нататък), всички негови staticинициализатори се пускат да работят. Така static инициализацията става само веднъж, когато Class обектът се на­то­вар­ва за пръв път.

  3. Когато се прави new Dog( ), процесът на конструиране на Dog обекта първо алокира достатъчно памет от хийпа за Dog обект.

  4. Тази памет се нулира, автоматично задавайки стойностите по подразбиране на всички примитиви в Dog (нула за числата и еквивалент за boolean и char).

  5. Всички инициализации които са при декларациите се изпълняват.

  6. Конструкторите се изпълняват. Както ще видите в глава 6, това би могло да пре­дизвика значителна активност, особено когато има наследяване.

Явна инициализация на static


Java позволява да се групират друг вид инициализации на static в специална “static конструкционна клауза” (понякога наричана static block) в класа. Това из­глежда така:

class Spoon {


static int i;
static {
i = 47;
}
// . . .

Изглежда като метод, но е точно ключовата дума static следвана от тялото на ме­тода. Този код, както другата static инициализация, се изпълнява само вед­нъж, първия път, когато се създава обект от този клас или се прави достъп до static член от него клас (даже и да не е създаден обект от този клас). Например:

//: c04:ExplicitStatic.java

// Explicit static initialization

// with the "static" clause.
class Cup {

Cup(int marker) {

System.out.println("Cup(" + marker + ")");

}

void f(int marker) {



System.out.println("f(" + marker + ")");

}

}


class Cups {

static Cup c1;

static Cup c2;

static {


c1 = new Cup(1);

c2 = new Cup(2);

}

Cups() {


System.out.println("Cups()");

}

}


public class ExplicitStatic {

public static void main(String[] args) {

System.out.println("Inside main()");

Cups.c1.f(99); // (1)

}

static Cups x = new Cups(); // (2)



static Cups y = new Cups(); // (2)
} ///:~

static инициализаторите за Cups ще се пуснат или като се прави достъп до static обекта c1 на реда отбелязан с (1), или редът (1) се постави на коментар а ко­ментарът на (2) се махне. Ако и (1) и (2) се поставят на коментар, static ини­циа­лизация за Cups не се прави въобще.

Инициализация на нестатични екземпляри


Java 1.1 има подобен синтаксис за инициализация на нестатични променливи за все­ки обект. Ето един пример:

//: c04:Mugs.java

// Java 1.1 "Instance Initialization"
class Mug {

Mug(int marker) {

System.out.println("Mug(" + marker + ")");

}

void f(int marker) {



System.out.println("f(" + marker + ")");

}

}


public class Mugs {

Mug c1;


Mug c2;

{

c1 = new Mug(1);



c2 = new Mug(2);

System.out.println("c1 & c2 initialized");

}

Mugs() {


System.out.println("Mugs()");

}

public static void main(String[] args) {



System.out.println("Inside main()");

Mugs x = new Mugs();

}

} ///:~


Може да видите че клаузата за инициализация на екземпляра:

{

c1 = new Mug(1);



c2 = new Mug(2);

System.out.println("c1 & c2 initialized");

}

изглежда точно като клаузата за статичните, освен че я няма ключовата дума stati. Този синтаксис е необходим за поддържане на инициализацията на анонимни вътрешни класове (виж глава 7).





Сподели с приятели:
1   ...   22   23   24   25   26   27   28   29   ...   73




©obuch.info 2024
отнасят до администрацията

    Начална страница