Blok statis di Java tidak dijalankan

87
class Test {
    public static void main(String arg[]) {    
        System.out.println("**MAIN METHOD");
        System.out.println(Mno.VAL); // SOP(9090);
        System.out.println(Mno.VAL + 100); // SOP(9190);
    }

}

class Mno {
    final static int VAL = 9090;
    static {
        System.out.println("**STATIC BLOCK OF Mno\t: " + VAL);
    }
}

Saya tahu bahwa staticblok dieksekusi ketika kelas dimuat. Tetapi dalam hal ini variabel instan di dalam kelas Mnoadalah final, karena itu staticblok tidak dieksekusi.

Mengapa demikian? Dan jika saya akan menghapusnya final, apakah itu akan berfungsi dengan baik?

Memori mana yang akan dialokasikan terlebih dahulu, static finalvariabel atau staticblok?

Jika karena finalpengubah akses kelas tidak dimuat, lalu bagaimana variabel mendapatkan memori?

Sthita
sumber
1
Apa sebenarnya kesalahan dan pesan yang Anda dapatkan?
Patashu
@Patashu, tidak ada kesalahan, itu diragukan
Sthita

Jawaban:

135
  1. Sebuah static final intlapangan adalah kompilasi waktu yang konstan dan nilainya hardcoded ke kelas tujuan tanpa referensi ke asalnya;
  2. oleh karena itu kelas utama Anda tidak memicu pemuatan kelas yang berisi bidang tersebut;
  3. oleh karena itu penginisialisasi statis di kelas itu tidak dijalankan.

Secara spesifik, bytecode yang dikompilasi sesuai dengan ini:

public static void main(String arg[]){    
    System.out.println("**MAIN METHOD");
    System.out.println(9090)
    System.out.println(9190)
}

Segera setelah Anda menghapusnya final, ini bukan lagi konstanta waktu kompilasi dan perilaku khusus yang dijelaskan di atas tidak berlaku. The Mnokelas dimuat seperti yang Anda harapkan dan statis initializer mengeksekusi.

Marko Topolnik
sumber
1
Tapi, lalu bagaimana nilai variabel terakhir di kelas dievaluasi tanpa memuat kelas?
Sumit Desai
18
Semua evaluasi terjadi pada waktu kompilasi dan hasil akhirnya di-hardcode ke semua tempat yang mereferensikan variabel.
Marko Topolnik
1
Jadi, jika alih-alih variabel primitif, itu adalah beberapa Objek, maka pengkodean keras seperti itu tidak akan mungkin dilakukan. Bukan? Jadi, dalam kasus ini, apakah kelas itu akan dimuat dan blok statis akan dieksekusi?
Sumit Desai
2
Marko, keraguan Sumit benar juga jika bukannya primitif, itu adalah beberapa Object, maka hardcoding seperti itu tidak akan mungkin dilakukan. Bukan? Jadi, dalam kasus ini, apakah kelas itu akan dimuat dan blok statis akan dieksekusi?
Sthita
8
@SumitDesai Tepat, ini hanya berfungsi untuk nilai primitif dan string literal. Untuk detail lengkap, baca bab yang relevan dalam Spesifikasi Bahasa Java
Marko Topolnik
8

Alasan mengapa kelas tidak dimuat adalah bahwa VALadalah final DAN itu dijalankan dengan ekspresi konstan (9090). Jika, dan hanya jika, kedua ketentuan tersebut terpenuhi, konstanta dievaluasi pada waktu kompilasi dan "hardcode" jika diperlukan.

Untuk mencegah ekspresi dievaluasi pada waktu kompilasi (dan agar JVM memuat kelas Anda), Anda dapat:

  • hapus kata kunci terakhir:

    static int VAL = 9090; //not a constant variable any more
    
  • atau ubah ekspresi sisi kanan menjadi sesuatu yang tidak konstan (bahkan jika variabel masih final):

    final static int VAL = getInt(); //not a constant expression any more
    static int getInt() { return 9090; }
    
assylias
sumber
5

Jika Anda melihat bytecode yang dihasilkan menggunakan javap -v Test.class, main () keluar seperti:

public static void main(java.lang.String[]) throws java.lang.Exception;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String **MAIN METHOD
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: sipush        9090
        14: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        17: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        20: sipush        9190
        23: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        26: return        

Anda dapat melihat dengan jelas di " 11: sipush 9090" bahwa nilai akhir statis langsung digunakan, karena Mno.VAL adalah konstanta waktu kompilasi. Oleh karena itu tidak perlu memuat kelas Mno. Karenanya blok statis Mno tidak dieksekusi.

Anda dapat menjalankan blok statis dengan memuat Mno secara manual seperti di bawah ini:

class Test{
    public static void main(String arg[]) throws Exception {
        System.out.println("**MAIN METHOD");
        Class.forName("Mno");                 // Load Mno
        System.out.println(Mno.VAL);
        System.out.println(Mno.VAL+100);
    }

}

class Mno{
    final static int VAL=9090;
    static{
        System.out.println("**STATIC BLOCK OF Mno\t:"+VAL);
    }
}
Xolve
sumber
1
  1. Sebenarnya Anda belum memperluas kelas Mno itu sehingga ketika kompilasi dimulai akan menghasilkan konstanta variabel VAL dan saat eksekusi dimulai ketika variabel itu dibutuhkan bebannya itu dari memori. Jadi tidak diperlukan referensi kelas Anda sehingga bock statis tidak dijalankan.

  2. jika kelas Amemperluas kelas Mno, blok statis disertakan dalam kelas Ajika Anda melakukan ini maka blok statis itu dijalankan. Sebagai contoh..

    public class A extends Mno {
    
        public static void main(String arg[]){    
            System.out.println("**MAIN METHOD");
            System.out.println(Mno.VAL);//SOP(9090);
            System.out.println(Mno.VAL+100);//SOP(9190);
        }
    
    }
    
    class Mno {
        final static int VAL=9090;
        static {
            System.out.println("**STATIC BLOCK OF Mno\t:"+VAL);
        }
    }
    
Ketan_Patel
sumber
0

Sejauh yang saya tahu, itu akan dieksekusi dalam urutan tampilan. Misalnya :

 public class Statique {
     public static final String value1 = init1();

     static {
         System.out.println("trace middle");
     }
     public static final String value2 = init2();


     public static String init1() {
         System.out.println("trace init1");
         return "1";
     }
     public static String init2() {
         System.out.println("trace init2");
         return "2";
     }
 }

akan mencetak

  trace init1
  trace middle
  trace init2

Saya baru saja mengujinya dan statika diinisialisasi (=> print) ketika kelas "Statique" benar-benar digunakan dan "dieksekusi" di bagian kode lain (kasus saya, saya melakukan "Statique baru ()".

Fabyen
sumber
2
Anda mendapatkan keluaran ini karena Anda memuat Statiquekelas dengan melakukan new Statique(). Sedangkan pada pertanyaan yang diajukan, Mnokelas tidak dimuat sama sekali.
RAS
@ Fabyen, jika saya membuat objek Mno di kelas uji seperti ini: Mno anc = New Mno (); maka tidak apa-apa, tetapi skenario saat ini saya tidak melakukan itu, keraguan saya adalah jika saya menghapus final maka blok statis dijalankan dengan baik jika tidak maka tidak akan dijalankan, mengapa demikian ??
Sthita
1
Ya jawaban di bawah ini sempurna. Dalam bytecode Main.class (menggunakan Mno.VAL), 9090 ditemukan kode keras. Hapus final, kompilasi, lalu gunakan javap Main, Anda akan melihat getstatic # 16; // Lapangan Statique.VAL: Saya . Masukkan kembali final, kompilasi, lalu gunakan javap Main, Anda akan melihat sipush 9090 .
Fabyen
1
Karena ini di-hardcode di Main.class, tidak ada alasan untuk memuat kelas MNO, maka tidak ada inisialisasi statis.
Fabyen
Ini menjawab pertanyaan kedua: "Memori mana yang akan dialokasikan pertama, variabel terakhir statis atau blok statis?" (urutan leksikal)
Hauke ​​Ingmar Schmidt