OCA Java imtahan mövzuları

Order of Initialization

Ardıcıllıq:

  1. Superclass (əgər varsa);
  2. Static variable declarations and static initializers (faylda yerləşdiyi ardıcıllıqla);
  3. Instance variable declarations and instance initializers (faylda yerləşdiyi ardıcıllıqla);
  4. 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-ci2-ci bənd icra edilir. 3-cü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""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]

About the author

Mushfiq Mammadov

3 Comments

      • 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ə birinci A 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ən a 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.

        class A {
            
            { System.out.println("Instance Initializer block of A"); }
            B b = new B();
            
            public A(){
                System.out.println("Constructor A");
            }
        }
        
        class B {
            
            { System.out.println("Instance Initializer block of B"); }
            A a = new A();
            
            public B(){
                System.out.println("Constructor B");
            }
        }
        
        class Test{
            public static void main(String[] args) {
                new A();
            //  new B();
            }
        }

        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.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.

 

This site uses Akismet to reduce spam. Learn how your comment data is processed.