Ardıcıllıq:
- Superclass (əgər varsa);
- Static variable declarations and static initializers (faylda yerləşdiyi ardıcıllıqla);
- Instance variable declarations and instance initializers (faylda yerləşdiyi ardıcıllıqla);
- Constructor.
public class LoadTest { public static void main(String[] args) { System.out.println("START"); new Child(); System.out.println("END"); } } class Grandparent { static { System.out.println("static - grandparent"); } { System.out.println("instance - grandparent"); } public Grandparent() { System.out.println("constructor - grandparent"); } } class Parent extends Grandparent { { System.out.println("instance - parent"); } public Parent() { System.out.println("constructor - parent"); } static { System.out.println("static - parent"); } } class Child extends Parent { public Child() { System.out.println("constructor - child"); } static { System.out.println("static - child"); } { System.out.println("instance - child"); } }
Output:
START static - grandparent static - parent static - child instance - grandparent constructor - grandparent instance - parent constructor - parent instance - child constructor - child END
Bu 4 qaydanın hamısı eyni zamanda ancaq o vaxt işləyir ki, classın obyekti yaradılmış olsun. Əgər new
açar sözündən istifadə edərək classa müraciət edilmirsə, ancaq 1-ci və 2-ci bənd icra edilir. 3-cü və 4-cü bənd classın obyekti yaradılana qədər gözləməyə məcburdular, əks halda icra edilmirlər. Çünki classın obyekti yaradılanda constructor çağırılır və yalnız constructor çağırıldıqdan sonra instance dəyişən və bloklar initialize olunur (yaxud başladılır).
public class InitalizationOrder { private String name = "Orxan"; { System.out.println(name); } private static int count = 2; static { System.out.println(count); } { count++; System.out.println(count); } public InitalizationOrder(){ System.out.println("Constructor"); } public static void main(String[] args) { System.out.println("Start main method"); new InitalizationOrder(); } }
Output:
2 Start main method Orxan 3 Constructor
Main metod static üzvlər (members) initialize olunduqdan sonra run olmağa başlayır. Aşağıdakı nümunə nisbətən daha qəlizdir:
public class YetMoreInitializationOrder { static { new YetMoreInitializationOrder(); } static { add(4); } static void add(int num){ System.out.print(num + " "); } YetMoreInitializationOrder(){ add(3); } static { add(5); } { add(1); } { add(2); } public static void main(String[] args) { } }
Output:
1 2 3 4 5
Bu mövzuda çaşdırıcı məqamlar çoxdur, ona görə də sizi çaşdıra biləcək daha bir neçə kod nümunəsinə baxacağıq. Aşağıdakı kod nümunəsi ilə başlayaq:
class B { static int i = 10; static { System.out.println("B Loaded"); } } public class A { static { System.out.println("A Loaded"); } public static void main(String[] args) { System.out.println("A should have been loaded"); B b = null; //line1 System.out.println("B should not have been loaded"); System.out.println(b.i); } }
Output:
A Loaded A should have been loaded B should not have been loaded B Loaded 10
Bu nümunədə bizim diqqətimizi çəkən line1 sətridir. Bu sətirdə B
classı tipində bir dəyişən elan edilsə də B
classı yüklənmir (not load). Çünki JVM görür ki, B
classının hər hansı bir üzvünə müraciət yoxdur, ona görə də B
classını hələ yükləməyə (load) ehtiyac olmadığını başa düşür. Amma sonuncu sətirdə B
classına məxsus i
dəyişəni çağırılır. Bu dəfə artıq B
classı yüklənilir, çünki istifadə edilmədən öncə class mütləq yüklənilməlidir.
İndi qeyd edəcəyimiz nümunə bir növü yuxarıda qeyd etdiyimiz nümunənin məzmun olaraq davamıdır, yəni yuxarıdakı nümunəni əbəs yerə qeyd etməmişik. Amma bu nümunə daha çətindir, ilk baxışdan sadə görünməsinə baxmayaraq:
class Super { static String ID = "QBANK"; } class Sub extends Super{ static { System.out.print("In Sub"); } } class Test { public static void main(String[] args){ System.out.println(Sub.ID); } }
Nümunə Enthuware sual bankındandır və kodu run etdikdə nə çap ediləcəyi soruşulur. Yəqin ki, çoxunuz fikirləşirsiniz ki, "In Sub"
və "QBANK"
hər ikisi çap ediləcək. Mən də belə fikirləşmişdim, amma bu düzgün cavab deyil 🙂 Kodu run etdikdə sadəcə "QBANK"
çap edilir. "In Sub"
çap edilmir, səbəb isə budur: biz Sub
classına məxsus hər hansı bir üzvə müraciət etməyənə qədər Sub
classının static bloku icra edilmir. Bu nümunədə ID
dəyişəni Super
classına məxsusdur, onun Sub
classı üzərindən çağırılmasına baxmayaraq Sub
classının yüklənilməsinə ehtiyac yoxdur.
İndi isə Coderanch forumundan daha 2 sual nümunəsinə baxaq. Bu nümunələr də həm çox maraqlıdır, həm də ki bir az qəribə. Birinci nümunəmizdən başlayaq:
class Egg { public static void main(String[] args) { Egg egg = new Egg(); System.out.println("number: " + egg.number); } { number = 4; } // line1 private int number = 3; // line2 }
Bu sualda da diqqətiniz çəkən yəqin line1 sətri olacaq. İlk baxışdan elə fikirləşirsən ki, bu sətir compile xətası verəcək, çünki number
dəyişəninə elan olunduğu sətirdən bir öncəki sətirdə instance initializer blokda yeni dəyər mənimsədilib. Amma bu sətir xəta vermir, normal compile olunur. Əgər line2 sətirini commentə atsaq, o zaman line1 xəta verəcək.
Bəs belə olan halda maraqlıdır kodu run etdikdə 3
çap olunacaq yoxsa 4
? Cavab 3
-dür.
O zaman fikirləşə bilərik ki, yəqin line1 icra edilmir. Amma elə deyil, icra edilir. Çox qəribə olsa da number
dəyişəninə əvvəlcə 4
dəyəri mənimsədilir, sonra isə 3
dəyəri ilə əvəz edilir (overridden with 3). Suala ətraflı aşağıdakı linkdən baxa bilərsiniz:
https://coderanch.com/t/658852/certification/Initialisation-order
İndi isə digər nümunəyə baxaq:
class A { private int iA = 1; public A() { print(); } public void print() { System.out.println("iA = " + iA); } } class B extends A { private int iB = 2; public B() {} public void print() { System.out.println("iB = " + iB); } } class TestAB { public static void main(String[] args) { new A(); new B(); } }
Output:
iA = 1
iB = 0
Bu nümunədə də sizə qəribə gələn çox güman output-da iB
dəyərinin 2
deyil 0
olmasıdır. main
metodda A
classının constructoru çağırıldıqda hər şey aydındır. B
classının constructoru çağırıldıqda isə varislik özəlliyindən dolayı ilk öncə A
classının constructoru çağırılır və print()
metodu çağırılır. B
classı print()
metodunu override etdiyindən A
classının deyil, B
classının print()
metodu icra edilir. Bu zaman isə iB
dəyişəninin dəyəri hələ 0
olur. Niyə? Çünki B
classının instance dəyişənlərinə dəyərlər super classın constructoru icra edildikdən sonra mənimsədilir. Bu halda A
classının constructorunun icrasi hələ bitmədiyindən iB
dəyişəninə hələ dəyər mənimsədilməyib, ona görə də onun default dəyəri, yəni 0
çap edilir. Suala ətraflı aşağıdakı linkdən baxa bilərsiniz:
https://coderanch.com/t/653694/certification/instance-variable-creation-prior-object
[topics lang=az]
what happens if
class A{
B b = new B();
}
class B{
A a = new B();
}
which constructor runs first?
Correction to class B in question.
class B{
A a = new A();
}
Bu nümunədə əgər birinci
A
classı çağırılarsa, o zaman ilk öncəB
classının constructoru işləyəcək,B
classı çağırılarsa isə birinciA
classının constructoru işləyəcək. Amma nəticəStackOverflowError
verəcək. Çünki constructor çağırılarkən ilk öncə instance dəyişənlərə dəyərlər mənimsədilir və sonra constructorun gövdəsi (body) işləyir. Bu nümunədəA
classı çağırıldıqda,A
-nın constructoru işləməmişdən öncəb
instance dəyişəninə dəyər mənimsədilməyə cəhd edilir vəB
classının constructoru çağırılır.B
classının constructoru da çağırılarkəna
instance dəyişəninə dəyər mənimsədilməyə çalışılır vəA
classının constructoru çağırılır.A
classında baş verənlər eyniləB
classında da baş verir və ona görə də burada rekursiya yaranır. Və nəticədəStackOverflowError
baş verir.Nisbətən buna bənzər məntiqdə olan bir nümunəni rekursiya ilə bağlı yazdığım məqalənin də sonununda qeyd etmişəm.