Java Final değişkenlerinin varsayılan değerleri olacak mı?


81

Bunun gibi bir programım var:

class Test {

    final int x;

    {
        printX();
    }

    Test() {
        System.out.println("const called");
    }

    void printX() {
        System.out.println("Here x is " + x);
    }

    public static void main(String[] args) {
        Test t = new Test();
    }

}

Çalıştırmayı denersem, derleyici hatası alıyorum: variable x might not have been initializedjava varsayılan değerlerine göre aşağıdaki çıktıyı doğru almalıyım?

"Here x is 0".

Nihai değişkenlerin dafault değerleri olacak mı?

kodumu böyle değiştirirsem,

class Test {

    final int x;

    {
        printX();
        x = 7;
        printX();
    }

    Test() {
        System.out.println("const called");
    }

    void printX() {
        System.out.println("Here x is " + x);
    }

    public static void main(String[] args) {
        Test t = new Test();
    }

}

Şu şekilde çıktı alıyorum:

Here x is 0                                                                                      
Here x is 7                                                                                     
const called

Lütfen bu davranışı açıklayabilir misiniz?

Yanıtlar:


62

http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html , "Örnek Üyelerini Başlatma" bölümü:

Java derleyicisi, başlatıcı bloklarını her kurucuya kopyalar.

Demek ki:

{
    printX();
}

Test() {
    System.out.println("const called");
}

tam olarak şöyle davranır:

Test() {
    printX();
    System.out.println("const called");
}

Gördüğünüz gibi, bir örnek oluşturulduktan sonra, son alan kesinlikle atanmamışken ( http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html adresinden # jls-8.3.1.2 ):

Boş bir son durum değişkeni, bildirildiği sınıfın her kurucusunun sonuna kesinlikle atanmalıdır; aksi takdirde derleme zamanı hatası oluşur.

Dokümanlarda açıkça belirtilmiyor gibi görünse de (en azından bulamadım), son bir alan geçici olarak kurucunun sonundan önce varsayılan değerini almalıdır, böylece öngörülebilir bir değere sahip olursanız görevlendirilmeden önce okuyun.

Varsayılan değerler: http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

İkinci kod parçacığınızda, x örnek oluştururken başlatılır, böylece derleyici şikayet etmez:

Test() {
    printX();
    x = 7;
    printX();
    System.out.println("const called");
}

Ayrıca aşağıdaki yaklaşımın işe yaramadığını unutmayın. Son değişkenin varsayılan değerinin kullanılmasına yalnızca bir yöntemle izin verilir.

Test() {
    System.out.println("Here x is " + x); // Compile time error : variable 'x' might not be initialized
    x = 7;
    System.out.println("Here x is " + x);
    System.out.println("const called");
}

1
Örneklerinizden birinde super () için örtük (veya açık) çağrının nereye gittiğini belirtmek faydalı olabilir.
Patrick

2
Bu, son alanın başlatılmamasının neden bir derleme hatasına neden olduğunu yanıtlamaz.
justhalf

@ sp00m İyi referans - Bunu bankaya koyacağım.
Bohemian

2
@justhalf cevabın önemli bir noktası eksik. Bir finale varsayılan durumunda (bir yöntem aracılığıyla) erişebilirsiniz, ancak derleyici, onu inşa sürecinin bitiminden önce başlatmazsanız şikayet edecektir. Bu yüzden ikinci deneme işe yarıyor (aslında x'i başlatıyor) ama ilki değil. Derleyici, doğrudan boş bir finale erişmeye çalışırsanız da şikayet edecektir.
Luca

28

JLS olduğunu söyleyerek bunu gerekir yapıcı boş nihai örnek değişkeni varsayılan değer atamak (veya başlatma bloğu oldukça aynıdır). Bu yüzden ilk durumda hatayı alıyorsunuz. Ancak daha önce yapıcıda erişemeyeceğinizi söylemez. Biraz tuhaf görünüyor, ancak atamadan önce ona erişebilir ve int - 0 için varsayılan değeri görebilirsiniz.

UPD. @ I4mpi'de belirtildiği gibi, JLS , her bir değerin herhangi bir erişimden önce kesinlikle atanması gerektiği kuralını tanımlar :

Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.

Bununla birlikte, inşaatçılar ve alanlar açısından da ilginç bir kuralı vardır:

If C has at least one instance initializer or instance variable initializer then V is [un]assigned after an explicit or implicit superclass constructor invocation if V is [un]assigned after the rightmost instance initializer or instance variable initializer of C.

Yani ikinci durumda değer xolduğunu kesinlikle atanan bunun sonunda atama içerdiğinden, yapıcı başında.


Aslında o yapar sen göreve kadar erişemez demek : "Her yerel değişken (§14.4) ve her boş nihai alanını (§4.12.4, §8.3.1.2) değeri herhangi erişim oluştuğunda belirgin olarak saptanmış değere sahip olmalıdır "
l4mpi

1
"kesinlikle atanmalıdır", ancak bu kuralın kurucu açısından garip davranışı var, cevabı güncelledim
udalmik

Bazı karmaşık koşullara bağlı olarak, bir finalalanı okuyabilen veya okuyamayan bir kod yöntemi varsa ve bu kod, alan yazılmadan önce ve sonra çalıştırılabiliyorsa, genel durumda bir derleyicinin hiçbir yolu yoktur. alanı yazılmadan önce okuyup okuyamayacağını bilmek.
supercat

7

Eğer başlatmazsanız , hiçbir zaman başlatılmadığı xiçin derleme zamanı hatası alırsınız x.

Beyan xSon olarak , yalnızca yapıcıda veya başlatıcı bloğunda başlatılabileceği anlamına gelir (çünkü bu blok derleyici tarafından her kurucuya kopyalanacaktır).

0Değişken başlatılmadan önce yazdırılmanızın nedeni, içinde tanımlanan davranıştır. kılavuzda(bkz: "Varsayılan Değerler" bölümü):

Varsayılan değerler

Bir alan bildirildiğinde her zaman bir değer atamak gerekli değildir. Bildirilen ancak başlatılmayan alanlar, derleyici tarafından makul bir varsayılana ayarlanacaktır. Genel olarak konuşursak, bu varsayılan veri türüne bağlı olarak sıfır veya boş olacaktır. Bununla birlikte, bu tür varsayılan değerlere güvenmek, genellikle kötü programlama stili olarak kabul edilir.

Aşağıdaki grafik, yukarıdaki veri türleri için varsayılan değerleri özetler.

Data Type   Default Value (for fields)
--------------------------------------
byte        0
short       0
int         0
long        0L
float       0.0f
double      0.0d
char        '\u0000'
String (or any object)      null
boolean     false

4

İlk hata, derleyicinin son bir alanınız olduğundan şikayet etmesidir, ancak onu başlatacak kod yoktur - yeterince basit.

İkinci örnekte, ona bir değer atamak için kodunuz vardır, ancak yürütme sırası, alana atamadan önce ve sonra başvurduğunuz anlamına gelir.

Herhangi bir alanın önceden atanmış değeri varsayılan değerdir.


2

Bir sınıfın tüm son olmayan alanları varsayılan bir değere başlar ( 0sayısal veri türleri falseiçin, boole için ve nullbazen karmaşık nesneler olarak adlandırılan başvuru türleri için). Bu alanlar, bir kurucu (veya örnek başlatma bloğu), alanların yapıcıdan önce veya sonra bildirilip bildirilmediğinden bağımsız olarak yürütülmeden önce başlatılır.

Bir sınıfın son alanları varsayılan değere sahip değildir ve bir sınıf oluşturucu işini bitirmeden önce bir kez açıkça başlatılmalıdır.

Bir yürütme bloğunun içindeki yerel değişkenlerin (örneğin, bir yöntem) varsayılan değeri yoktur. Bu alanlar, ilk kullanımlarından önce açıkça başlatılmalıdır ve yerel değişkenin son olarak işaretlenip işaretlenmemesi önemli değildir.


1

Yapabileceğim en basit sözlerle anlatayım.

finaldeğişkenlerin başlatılması gerekir, bu Dil Spesifikasyonu tarafından zorunlu kılınmıştır. Bunu söyledikten sonra, lütfen beyan anında onu başlatmanın gerekli olmadığını unutmayın.

Nesne başlatılmadan önce bunu başlatmak gerekir.

Son değişkenleri başlatmak için başlatıcı blokları kullanabiliriz. Şimdi, başlatıcı blokları iki tiptedir staticvenon-static

Kullandığınız blok statik olmayan bir başlatıcı bloğudur. Bu nedenle, bir nesne oluşturduğunuzda, Runtime kurucuyu çağırır ve bu da üst sınıfın yapıcısını çağırır.

Bundan sonra, tüm başlatıcıları çağıracaktır (sizin durumunuzda statik olmayan başlatıcı).

Sorunuzda, durum 1 : Başlatıcı bloğunun tamamlanmasından sonra bile son değişken başlatılmamış olarak kalır, bu bir hata derleyicisinin algılayacağıdır.

In durumda 2 : başlatıcı dolayısıyla derleyici nesne başlatılmadan önce, nihai zaten başlatıldı olduğunu bilir, son değişkeni başlatmak olacaktır. Dolayısıyla şikayet etmeyecektir.

Şimdi soru şu, neden xsıfır alıyor? Buradaki neden, derleyicinin hata olmadığını zaten bilmesidir ve bu nedenle init yönteminin çağrılması üzerine, tüm finaller varsayılanlara başlatılır ve gerçek atama ifadesine benzer şekilde değiştirebilecekleri bir bayrak seti x=7. Aşağıdaki init çağrısına bakın:

görüntü açıklamasını buraya girin


1

Bildiğim kadarıyla, derleyici sınıf değişkenlerini her zaman varsayılan değerlere (hatta son değişkenler) başlatacak. Örneğin, kendi kendine bir int başlatacak olsaydınız, int varsayılan değeri 0'a ayarlanır. Aşağıya bakın:

class Test {
    final int x;

    {
        printX();
        x = this.x;
        printX();
    }

    Test() {
        System.out.println("const called");
    }

    void printX() {
        System.out.println("Here x is " + x);
    }

    public static void main(String[] args) {
        Test t = new Test();
    }
}

Yukarıdakiler aşağıdakileri yazdırır:

Here x is 0
Here x is 0
const called

1
Son değişken x, OP'nin kodunda statik değildir.
JamesB

OP'nin kodunu this.x olarak başlatmak için kolayca değiştirebilirim ve aynı şey olur. Statik olup olmaması önemli değil.
Michael D.

OP'nin sorusunu okumamışsınız gibi göründüğü için buradaki statik içeriği kaldırmanızı öneririm.
JamesB

OP'nin kodundan yola çıkarsam yardımcı olur mu? Dediğim gibi, değişkenin statik olup olmaması önemli değil. Demek istediğim, bir değişkeni kendi kendine başlatmak ve varsayılan değeri elde etmek, değişkenin açıkça başlatılmadan önce örtük olarak başlatıldığını ima ediyor.
Michael D.

1
derlenmez, çünkü son değişkene başlatılmadan önce 6. satırda (doğrudan) erişmeye çalışıyorsunuz.
Luca

1

Çalıştırmayı denersem, derleyici hatası alıyorum: değişken x java varsayılan değerlerine göre başlatılmamış olabilir i aşağıdaki çıktıyı doğru almalıyım?

"Burada x, 0'dır".

Hayır. Bu çıktıyı görmüyorsunuz çünkü ilk etapta derleme zamanı hatası alıyorsunuz. Son değişkenler varsayılan bir değer alır, ancak Java Dil Spesifikasyonu (JLS) onları kurucunun sonuna kadar başlatmanızı gerektirir (LE: Buraya başlatma bloklarını ekliyorum), aksi takdirde bir derleme zamanı hatası alırsınız kodunuzun derlenmesini ve yürütülmesini engelleyecektir.

İkinci örneğiniz gereksinime uyuyor, bu yüzden (1) kodunuz derleniyor ve (2) beklenen davranışı elde ediyorsunuz.

Gelecekte kendinizi JLS'ye alıştırmaya çalışın. Java dili hakkında daha iyi bir bilgi kaynağı yok.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.