Bagaimana pengubah statis mempengaruhi kode ini?


109

Ini kode saya:

class A {
    static A obj = new A();
    static int num1;
    static int num2=0;

    private A() {
        num1++;
        num2++;
    }
    public static A getInstance() {
        return obj;
    }
}

public class Main{
    public static void main(String[] arg) {
        A obj = A.getInstance();
        System.out.println(obj.num1);
        System.out.println(obj.num2);
    }
}

Outputnya adalah 1 0, tapi saya tidak bisa mengerti.

Adakah yang bisa menjelaskannya kepada saya?


10
Pertanyaan bagus! Apa yang harus kita pelajari dari ini: Jangan lakukan itu! ;)
isnot2bad

Jawaban:


116

Di Jawa terjadi dua fase: 1. Identifikasi, 2. Eksekusi

  1. Dalam tahap identifikasi semua variabel statis terdeteksi dan diinisialisasi dengan nilai default.

    Jadi sekarang nilainya adalah:
    A obj=null
    num1=0
    num2=0

  2. Tahap kedua, eksekusi , dimulai dari atas ke bawah. Di Java, eksekusi dimulai dari anggota statis pertama.
    Di sini variabel statis pertama Anda adalah static A obj = new A();, jadi pertama-tama ia akan membuat objek dari variabel itu dan memanggil konstruktor, maka nilai dari num1dan num2menjadi 1.
    Dan kemudian, lagi, static int num2=0;akan dieksekusi, yang membuat num2 = 0;.

Sekarang, misalkan konstruktor Anda seperti ini:

 private A(){
    num1++;
    num2++;
    System.out.println(obj.toString());
 }

Ini akan membuang NullPointerExceptionkarena objmasih belum mendapat referensi class A.


11
Saya akan memperpanjang: pindahkan garis di static A obj = new A();bawah static int num2=0;dan Anda akan mendapatkan 1 dan 1.
Thomas

2
Yang masih membingungkan saya adalah kenyataan bahwa meskipun num1 tidak memiliki inisialisasi eksplisit, itu IS (secara implisit) diinisialisasi dengan 0. Seharusnya tidak ada perbedaan antara inisialisasi eksplisit dan implisit ...
isnot2bad

@ isnot2bad "inisialisasi implisit" terjadi sebagai bagian dari deklarasi. Deklarasi terjadi sebelum tugas tidak peduli apa memesan Anda sekarang mereka dalam. A obj = new A(); int num1; int num2 = 0;Akan berubah menjadi ini: A obj; int num1; int num2; obj = new A(); num2 = 0;. Java melakukan ini sehingga num1, num2ditentukan pada saat Anda mencapai new A()konstruktor.
Hans Z

31

Apa yang staticdimaksud pengubah ketika diterapkan ke deklarasi variabel adalah bahwa variabel tersebut adalah variabel kelas daripada variabel instan. Dengan kata lain ... hanya ada satu num1variabel, dan hanya satu num2variabel.

(Selain: variabel statis seperti variabel global dalam beberapa bahasa lain, kecuali bahwa namanya tidak terlihat di mana-mana. Bahkan jika dideklarasikan sebagai public static, nama yang tidak memenuhi syarat hanya terlihat jika dideklarasikan di kelas saat ini atau superclass , atau jika diimpor menggunakan impor statis. Itulah perbedaannya. Global sejati terlihat tanpa kualifikasi di mana pun.)

Jadi, saat Anda merujuk ke obj.num1dan obj.num2, Anda sebenarnya mengacu pada variabel statis yang sebutan sebenarnya adalah A.num1dan A.num2. Demikian pula, ketika konstruktor menambah num1dan num2, itu menambah variabel yang sama (masing-masing).

Kerutan yang membingungkan dalam contoh Anda ada pada inisialisasi kelas. Kelas diinisialisasi dengan default pertama menginisialisasi semua variabel statis, dan kemudian mengeksekusi penginisialisasi statis yang dideklarasikan (dan blok penginisialisasi statis) sesuai urutan kemunculannya di kelas. Dalam kasus ini, Anda memiliki ini:

static A obj = new A();
static int num1;
static int num2=0;

Ini terjadi seperti ini:

  1. Statika memulai dengan nilai awal default mereka; A.objadalah nulldan A.num1/ A.num2adalah nol.

  2. Deklarasi pertama ( A.obj) membuat turunan dari A(), dan konstruktor untuk Apenambahan A.num1dan A.num2. Ketika deklarasi selesai, A.num1dan A.num2keduanya 1, dan A.objmengacu pada Ainstance yang baru dibangun .

  3. Deklarasi kedua ( A.num1) tidak memiliki penginisialisasi, jadi A.num1tidak berubah.

  4. Deklarasi ketiga ( A.num2) memiliki penginisialisasi yang menetapkan nol A.num2.

Jadi, di akhir inisialisasi kelas, A.num1is 1dan A.num2is 0... dan itulah yang ditunjukkan oleh pernyataan cetak Anda.

Perilaku membingungkan ini sebenarnya karena Anda membuat instance sebelum inisialisasi statis selesai, dan bahwa konstruktor yang Anda gunakan bergantung dan memodifikasi statis yang belum diinisialisasi. Ini sesuatu yang harus Anda hindari dalam kode nyata.


16

1,0 benar.

Ketika kelas dimuat, semua data statis diinisialisasi di tempat mereka dideklarasikan. Secara default int adalah 0.

  • pertama A dibuat. num1 dan num2 menjadi 1 dan 1
  • daripada static int num1;tidak melakukan apa-apa
  • dari static int num2=0;ini menulis 0 sampai num2

9

Ini karena urutan penginisialisasi statis. Ekspresi statis di kelas dievaluasi dalam urutan dari atas ke bawah.

Yang pertama dipanggil adalah konstruktor A, yang menetapkan num1dan num2keduanya menjadi 1:

static A obj = new A();

Kemudian,

static int num2=0;

dipanggil dan set num2 = 0 lagi.

Itulah mengapa num11 dan num20.

Sebagai catatan tambahan, konstruktor tidak boleh memodifikasi variabel statis, itu adalah desain yang sangat buruk. Sebagai gantinya, coba pendekatan berbeda untuk mengimplementasikan Singleton di Java .


6

Bagian di JLS dapat ditemukan: §12.4.2 .

Prosedur Inisialisasi Terperinci:

9. Selanjutnya, jalankan baik penginisialisasi variabel kelas dan penginisialisasi statis kelas, atau penginisialisasi bidang antarmuka, dalam urutan tekstual, seolah-olah mereka adalah satu blok, kecuali variabel kelas terakhir dan bidang antarmuka yang nilainya dikompilasi konstanta -time diinisialisasi terlebih dahulu

Jadi ketiga variabel statis tersebut akan diinisialisasi satu per satu dalam urutan tekstual.

Begitu

static A obj = new A();
//num1 = 1, num2 = 1;
static int num1;
//this is initilized first, see below.
static int num2=0;
//num1 = 1, num2 = 0;

Jika saya mengubah urutan menjadi:

static int num1;
static int num2=0;
static A obj = new A();

Hasilnya akan seperti itu 1,1.

Perhatikan bahwa static int num1;ini bukan penginisialisasi variabel karena ( §8.3.2 ):

Jika deklarator bidang berisi penginisialisasi variabel, maka deklarator tersebut memiliki semantik tugas (§15.26) ke variabel yang dideklarasikan, dan: Jika deklarator untuk variabel kelas (yaitu, bidang statis), maka penginisialisasi variabel adalah dievaluasi dan tugas dilakukan tepat satu kali, ketika kelas diinisialisasi

Dan variabel kelas ini diinisialisasi saat kelas dibuat. Ini terjadi pertama kali ( §4.12.5 ).

Setiap variabel dalam program harus memiliki nilai sebelum nilainya digunakan: Setiap variabel kelas, variabel instan, atau komponen array diinisialisasi dengan nilai default ketika dibuat (§15.9, §15.10): Untuk tipe byte, nilai default adalah nol, yaitu nilai (byte) 0. Untuk tipe short, nilai defaultnya adalah nol, yaitu nilai (short) 0. Untuk tipe int, nilai defaultnya adalah nol, yaitu 0. Untuk tipe long, nilai defaultnya adalah nol, yaitu 0L. Untuk tipe float, nilai defaultnya adalah nol positif, yaitu 0.0f. Untuk tipe double, nilai defaultnya adalah nol positif, yaitu 0.0d. Untuk tipe char, nilai defaultnya adalah karakter null, yaitu '\ u0000'. Untuk tipe boolean, nilai defaultnya salah. Untuk semua jenis referensi (§4.3), nilai defaultnya adalah null.


2

Mungkin akan membantu jika memikirkannya seperti ini.

Kelas adalah cetak biru untuk objek.

Objek dapat memiliki variabel saat dibuat instance-nya.

Kelas juga dapat memiliki variabel. Ini dinyatakan sebagai statis. Jadi mereka ditetapkan pada kelas daripada contoh objek.

Anda hanya dapat memiliki satu dari setiap kelas dalam sebuah aplikasi jadi ini seperti penyimpanan global khusus untuk kelas itu. Variabel statis ini tentu saja dapat diakses dan dimodifikasi dari mana saja di aplikasi Anda (dengan asumsi variabel tersebut bersifat publik).

Berikut ini dan contoh kelas "Anjing" yang menggunakan variabel statis untuk melacak jumlah instance yang telah dibuat.

Kelas "Anjing" adalah cloud sedangkan kotak Oranye adalah instance "Anjing".

Kelas anjing

Baca lebih lajut

Semoga ini membantu!

Jika Anda merasa seperti beberapa hal sepele, ide ini pertama kali diperkenalkan oleh Plato


1

Kata kunci statis digunakan di java terutama untuk manajemen memori. Kami dapat menerapkan kata kunci statis dengan variabel, metode, blok dan kelas bersarang. Kata kunci statis termasuk dalam kelas daripada instance kelas. Untuk penjelasan singkat tentang kata kunci statis:

http://www.javatpoint.com/static-keyword-in-java


0

Banyak dari jawaban di atas benar. Tetapi untuk menggambarkan apa yang terjadi, saya telah membuat beberapa modifikasi kecil di bawah ini.

Seperti yang disebutkan beberapa kali di atas, yang terjadi adalah instance kelas A dibuat sebelum kelas A dimuat sepenuhnya. Jadi apa yang dianggap sebagai 'perilaku' normal tidak diamati. Ini tidak terlalu berbeda dengan memanggil metode dari konstruktor yang dapat diganti. Dalam hal ini, variabel instan mungkin tidak dalam keadaan intuitif. Dalam contoh ini variabel kelas tidak dalam keadaan intuitif.

class A {
    static A obj = new A();
    static int num1;
    static int num2;
    static {
        System.out.println("Setting num2 to 0");
        num2 = 0;
    }

    private A() {
        System.out.println("Constructing singleton instance of A");
        num1++;
        num2++;
    }

    public static A getInstance() {
        return obj;
    }
}

public class Main {

    public static void main(String[] arg) {
        A obj = A.getInstance();
        System.out.println(obj.num1);
        System.out.println(obj.num2);
    }
}

Outputnya adalah

Constructing singleton instance of A
Setting num2 to 0
1
0

0

java tidak menginisialisasi nilai dari setiap anggota data statis atau non statis sampai ia tidak dipanggil tetapi membuatnya.

Jadi disini ketika num1 dan num2 akan dipanggil main maka akan diinisialisasi dengan nilai

num1 = 0 + 1; dan

num2 = 0;

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.