Apa perbedaan antara blok kode inisialisasi statis dan non-statis

357

Pertanyaan saya adalah tentang penggunaan kata kunci statis tertentu. Dimungkinkan untuk menggunakan statickata kunci untuk menutupi blok kode dalam kelas yang bukan milik fungsi apa pun. Misalnya kompilasi kode berikut:

public class Test {
    private static final int a;    
    static {
        a = 5;
        doSomething(a);
    }
    private static int doSomething(int x) {
        return (x+5);
    }
}

Jika Anda menghapus statickata kunci itu mengeluh karena variabel aadalah final. Namun dimungkinkan untuk menghapus keduanya finaldan statickata kunci dan membuatnya kompilasi.

Ini membingungkan bagi saya dalam dua hal. Bagaimana saya bisa memiliki bagian kode yang bukan milik metode apa pun? Bagaimana mungkin memohonnya? Secara umum, apa tujuan dari penggunaan ini? Atau lebih baik, di mana saya dapat menemukan dokumentasi tentang ini?

Szere Dyeri
sumber

Jawaban:

403

Blok kode dengan pengubah statis menandakan kelas penginisialisasi; tanpa pengubah statis blok kode adalah penginisialisasi instance .

Inisialisasi kelas dieksekusi dalam urutan yang ditentukan (top down, seperti inisialisasi variabel sederhana) ketika kelas dimuat (sebenarnya, ketika itu diselesaikan, tapi itu teknis).

Inisialisasi instance dijalankan dalam urutan yang ditentukan ketika kelas instantiated, segera sebelum kode konstruktor dieksekusi, segera setelah doa dari konstruktor super.

Jika Anda menghapus staticdari int a, itu menjadi variabel instan, yang Anda tidak dapat mengakses dari blok penginisialisasi statis. Ini akan gagal dikompilasi dengan kesalahan "variabel non-statis a tidak dapat dirujuk dari konteks statis".

Jika Anda juga menghapus staticdari blok penginisialisasi, itu menjadi penginisialisasi instance dan int adiinisialisasi pada konstruksi.

Lawrence Dol
sumber
Penginisialisasi statis sebenarnya dipanggil nanti, ketika kelas diinisialisasi, setelah itu dimuat dan ditautkan. Itu terjadi ketika Anda instantiate objek kelas atau mengakses variabel statis atau metode di kelas. Bahkan jika Anda memiliki kelas dengan penginisialisasi statis dan metode public static void staticMethod(){}, jika Anda mengeksekusi TestStatic.class.getMethod("staticMethod");. Penginisialisasi statis tidak akan dipanggil. Info lebih lanjut di sini docs.oracle.com/javase/specs/jvms/se10/html/…
Totò
@ Totò: Ya, itulah yang diperlukan oleh resolusi kelas (setidaknya mereka biasa menyebutnya sebagai tautan + init sebagai "resolusi" ketika kembali). Saya tidak terkejut Anda dapat menggunakan refleksi untuk menemukan hal - hal tentang kelas tanpa menyelesaikannya.
Lawrence Dol
166

Uff! apa itu penginisialisasi statis?

Penginisialisasi statis adalah static {}blok kode di dalam kelas java, dan dijalankan hanya satu kali sebelum konstruktor atau metode utama dipanggil.

BAIK! Ceritakan lebih banyak lagi ...

  • adalah blok kode static { ... }di dalam setiap kelas java. dan dieksekusi oleh mesin virtual ketika kelas dipanggil.
  • Tidak ada returnpernyataan yang didukung.
  • Tidak ada argumen yang didukung.
  • Tidak thisatau superdidukung.

Hmm dimana saya bisa menggunakannya?

Dapat digunakan dimanapun Anda merasa oke :) sesederhana itu. Tapi saya melihat sebagian besar waktu digunakan ketika melakukan koneksi database, API init, Logging dan lain-lain.

Jangan hanya menggonggong! dimana contohnya

package com.example.learnjava;

import java.util.ArrayList;

public class Fruit {

    static {
        System.out.println("Inside Static Initializer.");

        // fruits array
        ArrayList<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Orange");
        fruits.add("Pear");

        // print fruits
        for (String fruit : fruits) {
            System.out.println(fruit);
        }
        System.out.println("End Static Initializer.\n");
    }

    public static void main(String[] args) {
        System.out.println("Inside Main Method.");
    }
}

Keluaran???

Di dalam Static Inisialisasi.

apel

jeruk

Pir

End Static Inisialisasi.

Metode Utama Di Dalam.

Semoga ini membantu!

Madan Sapkota
sumber
Terima kasih Madan! Dapat blok statis digunakan sebagai pengganti afterPropertiesSet()dari InitializingBean?
Alexander Suraphel
3
Ya kamu bisa! Inisialisasi statis menjadi dipanggil ketika kelas dimuat oleh jvm. Jadi ini adalah fase pertama di mana kode dieksekusi. Jika Anda memiliki konstruktor juga, urutannya adalah: initializer statis, konstruktor, afterPropertiesSet
Martin Baumgartner
57

The staticblok adalah "initializer statis".

Itu secara otomatis dipanggil ketika kelas dimuat, dan tidak ada cara lain untuk memanggilnya (bahkan melalui Refleksi).

Saya pribadi hanya pernah menggunakannya ketika menulis kode JNI:

class JNIGlue {
    static {
        System.loadLibrary("foo");
    }
}
Alnitak
sumber
6
Tidak, tidak ada cara eksplisit untuk menjalankannya, kelas initializer tidak pernah diwakili oleh Methodinstance tetapi hanya dipanggil oleh mesin virtual Java.
Rafael Winterhalter
46

Ini langsung dari http://www.programcreek.com/2011/10/10/java-class-instance-initializers/

1. Perintah Eksekusi

Lihatlah kelas berikut, apakah Anda tahu yang mana yang dieksekusi lebih dulu?

public class Foo {

    //instance variable initializer
    String s = "abc";

    //constructor
    public Foo() {
        System.out.println("constructor called");
    }

    //static initializer
    static {
        System.out.println("static initializer called");
    }

    //instance initializer
    {
        System.out.println("instance initializer called");
    }

    public static void main(String[] args) {
        new Foo();
        new Foo();
    }
}

Keluaran:

initializer statis disebut

initializer contoh disebut

disebut konstruktor

initializer contoh disebut

disebut konstruktor

2. Bagaimana cara kerja initializer instance Java?

Inisialisasi instance di atas berisi pernyataan println. Untuk memahami cara kerjanya, kita bisa memperlakukannya sebagai pernyataan penugasan variabel, misalnya b = 0,. Ini bisa membuatnya lebih jelas untuk dipahami.

Dari pada

int b = 0, kamu bisa menulis

int b;
b = 0;

Oleh karena itu, inisialisasi instance dan inisialisasi variabel instan hampir sama.

3. Kapan inisialisasi instance berguna?

Penggunaan inisialisasi instance jarang terjadi, tetapi masih bisa menjadi alternatif yang berguna untuk inisialisasi variabel instan jika:

  1. Kode inisialisasi harus menangani pengecualian
  2. Lakukan perhitungan yang tidak dapat diekspresikan dengan initializer variabel instan.

Tentu saja, kode tersebut dapat ditulis dalam konstruktor. Tetapi jika suatu kelas memiliki banyak konstruktor, Anda harus mengulang kode di setiap konstruktor.

Dengan penginisialisasi instan, Anda bisa menulis kode sekali saja, dan itu akan dieksekusi tidak peduli konstruktor apa yang digunakan untuk membuat objek. (Saya kira ini hanya sebuah konsep, dan tidak sering digunakan.)

Kasus lain di mana inisialisasi instance berguna adalah kelas dalam anonim, yang tidak dapat mendeklarasikan konstruktor sama sekali. (Apakah ini akan menjadi tempat yang baik untuk menempatkan fungsi logging?)

Terima kasih untuk Derhein.

Perhatikan juga bahwa kelas Anonim yang mengimplementasikan antarmuka [1] tidak memiliki konstruktor. Karena itu inisialisasi instance diperlukan untuk mengeksekusi segala jenis ekspresi pada waktu konstruksi.

Alexei Fando
sumber
12

"final" menjamin bahwa suatu variabel harus diinisialisasi sebelum akhir kode penginisialisasi objek. Demikian juga "final statis" menjamin bahwa suatu variabel akan diinisialisasi pada akhir kode inisialisasi kelas. Menghilangkan "statis" dari kode inisialisasi Anda mengubahnya menjadi kode inisialisasi objek; dengan demikian variabel Anda tidak lagi memenuhi jaminannya.

DJClayworth
sumber
8

Anda tidak akan menulis kode ke blok statis yang perlu dipanggil di mana saja di program Anda. Jika tujuan dari kode yang akan dipanggil maka Anda harus menempatkannya dalam suatu metode.

Anda dapat menulis blok penginisialisasi statis untuk menginisialisasi variabel statis ketika kelas dimuat tetapi kode ini bisa lebih kompleks ..

Blok penginisialisasi statis terlihat seperti metode tanpa nama, tanpa argumen, dan tanpa tipe pengembalian. Karena Anda tidak pernah menyebutnya, tidak perlu nama. Satu-satunya waktu disebut adalah ketika mesin virtual memuat kelas.

Vincent Ramdhanie
sumber
6

ketika pengembang menggunakan blok penginisialisasi, Java Compiler menyalin penginisialisasi ke setiap konstruktor dari kelas saat ini.

Contoh:

kode berikut:

class MyClass {

    private int myField = 3;
    {
        myField = myField + 2;
        //myField is worth 5 for all instance
    }

    public MyClass() {
        myField = myField * 4;
        //myField is worth 20 for all instance initialized with this construtor
    }

    public MyClass(int _myParam) {
        if (_myParam > 0) {
            myField = myField * 4;
            //myField is worth 20 for all instance initialized with this construtor
            //if _myParam is greater than 0
        } else {
            myField = myField + 5;
            //myField is worth 10 for all instance initialized with this construtor
            //if _myParam is lower than 0 or if _myParam is worth 0
        }
    }

    public void setMyField(int _myField) {
        myField = _myField;
    }


    public int getMyField() {
        return myField;
    }
}

public class MainClass{

    public static void main(String[] args) {
        MyClass myFirstInstance_ = new MyClass();
        System.out.println(myFirstInstance_.getMyField());//20
        MyClass mySecondInstance_ = new MyClass(1);
        System.out.println(mySecondInstance_.getMyField());//20
        MyClass myThirdInstance_ = new MyClass(-1);
        System.out.println(myThirdInstance_.getMyField());//10
    }
}

setara dengan:

class MyClass {

    private int myField = 3;

    public MyClass() {
        myField = myField + 2;
        myField = myField * 4;
        //myField is worth 20 for all instance initialized with this construtor
    }

    public MyClass(int _myParam) {
        myField = myField + 2;
        if (_myParam > 0) {
            myField = myField * 4;
            //myField is worth 20 for all instance initialized with this construtor
            //if _myParam is greater than 0
        } else {
            myField = myField + 5;
            //myField is worth 10 for all instance initialized with this construtor
            //if _myParam is lower than 0 or if _myParam is worth 0
        }
    }

    public void setMyField(int _myField) {
        myField = _myField;
    }


    public int getMyField() {
        return myField;
    }
}

public class MainClass{

    public static void main(String[] args) {
        MyClass myFirstInstance_ = new MyClass();
        System.out.println(myFirstInstance_.getMyField());//20
        MyClass mySecondInstance_ = new MyClass(1);
        System.out.println(mySecondInstance_.getMyField());//20
        MyClass myThirdInstance_ = new MyClass(-1);
        System.out.println(myThirdInstance_.getMyField());//10
    }
}

Saya harap contoh saya dipahami oleh pengembang.

penjaga kartu
sumber
4

Blok kode statis dapat digunakan untuk instantiate atau menginisialisasi variabel kelas (sebagai lawan variabel objek). Jadi menyatakan "a" statis berarti bahwa hanya satu yang dibagikan oleh semua objek Uji, dan blok kode statis menginisialisasi "a" hanya sekali, ketika kelas Uji pertama kali dimuat, tidak peduli berapa banyak objek Uji yang dibuat.

Paul Tomblin
sumber
Sebagai tindak lanjut, jika saya tidak membuat instance objek tetapi saya memanggil fungsi statis publik. Apakah ini menyiratkan bahwa blok ini dijamin untuk dieksekusi sebelum fungsi publik ini memanggil? Terima kasih.
Szere Dyeri
Jika Anda memanggil fungsi statis publik dari kelas, maka kelas perlu dimuat terlebih dahulu, jadi ya, initializer statis akan dieksekusi terlebih dahulu.
Paul Tomblin
Kecuali itu inisialisasi kelas yang (secara tidak langsung) disebut kode yang mencoba menggunakannya. IFYSWIM. Ketergantungan melingkar dan semua itu.
Tom Hawtin - tackline
1
@ Tom benar - mungkin untuk menulis sesuatu di mana satu penginisialisasi statis memanggil metode statis sebelum penginisialisasi statis lain dipanggil, tetapi pikiran saya mundur memikirkan hal itu jadi saya tidak pernah mempertimbangkannya.
Paul Tomblin