Statik değiştirici bu kodu nasıl etkiler?


109

İşte kodum:

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);
    }
}

Çıktı 1 0, ama anlayamıyorum.

Biri bana açıklayabilir mi?


10
Güzel soru! Bundan ne öğrenmeliyiz: Yapma! ;)
isnot2bad

Yanıtlar:


116

Java'da iki aşama gerçekleşir: 1. Tanımlama, 2. Yürütme

  1. Gelen kimlik fazında tüm statik değişkenler tespit edilir ve varsayılan değerlerle başlatıldı.

    Şimdi değerler şu şekildedir:
    A obj=null
    num1=0
    num2=0

  2. İkinci aşama, yürütme , yukarıdan aşağıya doğru başlar. Java'da, yürütme ilk statik üyelerden başlar.
    Burada ilk statik değişkeniniz static A obj = new A();, yani önce o değişkenin nesnesini yaratacak ve kurucuyu çağıracak, dolayısıyla değeri num1ve num2olur 1.
    Ve sonra yine static int num2=0;idam edilecek ki bu da yapar num2 = 0;.

Şimdi, kurucunuzun şöyle olduğunu varsayalım:

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

Bu hala bir referansı olmadığı NullPointerExceptiongibi atacak .objclass A


11
Uzatacağım: static A obj = new A();aşağıdaki satırı kaydırın static int num2=0;ve 1 ve 1'i almalısınız.
Thomas

2
Ne hala kafamı karıştırıyor gerçekten açık ve kapalı başlatma arasında hiçbir fark ... olmamalıdır 0'dan ile başlatıldı num1 açık bir başlatma yok olsa bile, (dolaylı olarak) IS gerçektir
isnot2bad

@ isnot2bad "örtük başlatma" bildirimin bir parçası olarak gerçekleşir. Bildirge olursa olsun onları mevcut sipariş neyi atama önce olur. A obj = new A(); int num1; int num2 = 0;Bu dönüştü alır: A obj; int num1; int num2; obj = new A(); num2 = 0;. Java bunu yapar, bu nedenle num1, num2kurucuya ulaştığınız zamana göre tanımlanır new A().
Hans Z

31

Ne staticmodifiye edici aracı bir değişken bildiriminde uygulanan değişken bir sınıf değişkeni yerine bir örneği değişken olmasıdır. Başka bir deyişle ... sadece bir num1değişken ve sadece bir num2değişken vardır.

(Bir kenara: statik değişken, isminin her yerde görünmemesi dışında, diğer bazı dillerde global bir değişken gibidir . A olarak bildirilmiş olsa bile public static, nitelenmemiş isim yalnızca mevcut sınıfta veya bir üst sınıfta bildirilmişse görülebilir. veya statik bir içe aktarım kullanılarak içe aktarılmışsa. Fark budur. Gerçek bir global, hiçbir yerde nitelendirme olmadan görülebilir.)

Eğer başvurmak yüzden obj.num1ve obj.num2, aslında başvuruyorsunuz Asıl belirtme olan statik değişkenler ve . Ve benzer şekilde, kurucu arttığında ve , aynı değişkenleri (sırasıyla) artırıyor.A.num1A.num2num1num2

Örneğinizdeki kafa karıştırıcı kırışıklık, sınıf ilklendirmesindedir. Bir sınıf, ilk olarak tüm statik değişkenlerin varsayılan olarak başlatılması ve ardından bildirilen statik başlatıcıların (ve statik başlatıcı bloklarının) sınıfta göründükleri sırayla çalıştırılmasıyla başlatılır. Bu durumda, şuna sahipsiniz:

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

Şöyle olur:

  1. Statikler, varsayılan başlangıç ​​değerleriyle başlar; A.objolduğu nullve A.num1/ A.num2sıfırdır.

  2. İlk bildirimi ( A.obj) bir örneğini oluşturur A()ve kurucusunu Aartışlarla A.num1ve A.num2. Bildirim tamamlandığında A.num1ve A.num2her ikisi de 1olduğunda ve A.objyeni oluşturulan Aörneğe başvurduğunda.

  3. İkinci bildirimin ( A.num1) başlatıcısı yoktur, dolayısıyla A.num1değişmez.

  4. Üçüncü bildirim ( A.num2), sıfır atayan bir başlatıcıya sahiptir A.num2.

Böylece, sınıfın ilklendirilmesinin sonunda, A.num1öyle 1ve A.num2öyle 0... ve baskı ifadelerinizin gösterdiği şey budur.

Bu kafa karıştırıcı davranış, statik başlatma tamamlanmadan önce bir örnek oluşturduğunuza ve kullanmakta olduğunuz kurucunun henüz başlatılmamış bir durağıya bağlı olup değiştirdiği gerçeğine bağlıdır . Bu, gerçek kodda yapmaktan kaçınmanız gereken bir şey.


16

1,0 doğru.

Sınıf yüklendiğinde, tüm statik veriler bildirilir veya bildirilir. Varsayılan olarak int 0'dır.

  • ilk A oluşturulur. num1 ve num2 1 ve 1 oluyor
  • daha static int num1;hiçbir şey yapmaz
  • bundan static int num2=0;num2'ye 0 yazıyor

9

Statik başlatıcıların sırasından kaynaklanmaktadır. Sınıflardaki statik ifadeler yukarıdan aşağıya sırayla değerlendirilir.

İlk çağrılacak olan yapıcıdır ve her ikisi de 1'e Aayarlanır :num1num2

static A obj = new A();

Sonra,

static int num2=0;

çağrılır ve tekrar num2 = 0 olarak ayarlanır.

Bu yüzden num11 ve num20'dır.

Bir yan not olarak, bir kurucu statik değişkenleri değiştirmemelidir, bu çok kötü bir tasarımdır. Bunun yerine, Java'da bir Singleton uygulamak için farklı bir yaklaşım deneyin .


6

JLS'de bir bölüm bulunabilir: §12.4.2 .

Ayrıntılı Başlatma Prosedürü:

9. Ardından, sınıfın sınıf değişkeni başlatıcılarını ve statik başlatıcılarını veya arayüzün alan başlatıcılarını tek bir blokmuş gibi, değerleri derlenen son sınıf değişkenleri ve arabirim alanları hariç , metin sırasına göre çalıştırın. -zaman sabitleri önce başlatılır

Böylece üç statik değişken metin sırasına göre tek tek başlatılacaktır.

Yani

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;

Sırayı şu şekilde değiştirirsem:

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

Sonuç olacak 1,1.

static int num1;Bir değişken başlatıcı olmadığına dikkat edin çünkü ( §8.3.2 ):

Bir alan tanımlayıcı bir değişken başlatıcı içeriyorsa, o zaman bildirilen değişkene bir atamanın (§15.26) anlamsallığına sahiptir ve: Eğer tanımlayıcı bir sınıf değişkeni içinse (yani, bir statik alan), o zaman değişken başlatıcı değerlendirilir ve sınıf başlatıldığında ödev tam olarak bir kez gerçekleştirilir

Ve bu sınıf değişkeni, sınıf yaratıldığında başlatılır. Bu önce gerçekleşir ( §4.12.5 ).

Bir programdaki her değişken, değeri kullanılmadan önce bir değere sahip olmalıdır: Her sınıf değişkeni, örnek değişkeni veya dizi bileşeni oluşturulduğunda varsayılan bir değerle başlatılır (§15.9, §15.10): Bayt türü için varsayılan değer sıfırdır, yani (bayt) 0'ın değeri. Kısa tip için varsayılan değer sıfırdır, yani (kısa) 0 değeridir. İnt türü için varsayılan değer sıfırdır, yani 0'dır. Uzun tür için varsayılan değer sıfır, yani 0L'dir. Float türü için varsayılan değer pozitif sıfırdır, yani 0.0f. Double türü için varsayılan değer pozitif sıfırdır, yani 0.0d. Char türü için varsayılan değer null karakterdir, yani "\ u0000". Boolean türü için varsayılan değer yanlıştır. Tüm başvuru türleri için (§4.3), varsayılan değer null'dur.


2

Belki bu şekilde düşünmek yardımcı olur.

Sınıflar, nesnelerin taslağıdır.

Nesnelerin somutlaştırıldıklarında değişkenleri olabilir.

Sınıfların değişkenleri de olabilir. Bunlar statik olarak ilan edilir. Dolayısıyla, nesne örneklerinden ziyade sınıfta ayarlanırlar.

Bir uygulamada her sınıftan yalnızca birine sahip olabilirsiniz, bu nedenle bu, özellikle o sınıfa yönelik küresel depolama gibidir. Bu statik değişkenlere tabii ki uygulamanızın herhangi bir yerinden erişilebilir ve değiştirilebilir (herkese açık oldukları varsayılarak).

İşte, oluşturduğu örneklerin sayısını izlemek için statik değişkeni kullanan bir "Dog" sınıfı örneği.

"Köpek" sınıfı bulut, Turuncu kutular ise "Köpek" örnekleridir.

Köpek sınıfı

daha fazla oku

Bu yardımcı olur umarım!

Biraz önemsiz hissediyorsanız, bu fikir ilk olarak Platon tarafından tanıtıldı.


1

Statik anahtar kelime java'da esas olarak bellek yönetimi için kullanılır. Statik anahtar kelimeyi değişkenler, yöntemler, bloklar ve iç içe sınıf ile uygulayabiliriz. Static anahtar sözcüğü, sınıfın örneğinden çok sınıfa aittir. Static anahtar sözcüğü hakkında kısa açıklama için:

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


0

Yukarıdaki cevapların çoğu doğrudur. Ama gerçekten neler olduğunu göstermek için aşağıda bazı küçük değişiklikler yaptım.

Yukarıda birçok kez bahsedildiği gibi, olan şey, A sınıfı tamamen yüklenmeden önce A sınıfının bir örneğinin yaratılmasıdır. Yani normal 'davranış' olarak kabul edilen şey gözlenmez. Bu, geçersiz kılınabilen bir kurucudan yöntemleri çağırmaktan çok farklı değildir. Bu durumda, örnek değişkenleri sezgisel bir durumda olmayabilir. Bu örnekte sınıf değişkenleri sezgisel bir durumda değildir.

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);
    }
}

Çıktı

Constructing singleton instance of A
Setting num2 to 0
1
0

0

java, çağrılana kadar statik veya statik olmayan herhangi bir veri üyesinin değerini başlatmaz, ancak onu oluşturur.

Böylece burada num1 ve num2 esas olarak çağrıldığında, o zaman değerlerle başlatılacaktır.

num1 = 0 + 1; ve

num2 = 0;

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.