Java'da sonsuz döngüler


82

whileJava'da aşağıdaki sonsuz döngüye bakın. Altındaki ifade için derleme zamanı hatasına neden olur.

while(true) {
    System.out.println("inside while");
}

System.out.println("while terminated"); //Unreachable statement - compiler-error.

Aşağıdaki aynı sonsuz whiledöngü, ancak iyi çalışıyor ve koşulu bir boole değişkeniyle değiştirdiğim herhangi bir hata vermiyor.

boolean b=true;

while(b) {
    System.out.println("inside while");
}

System.out.println("while terminated"); //No error here.

İkinci durumda da, döngüden sonraki ifadeye açıkça ulaşılamaz çünkü boolean değişkeni bdoğrudur, yine de derleyici hiç şikayet etmez. Neden?


Düzenleme: Aşağıdaki sürüm, whileaçık olduğu gibi sonsuz bir döngüye sıkışmış olur, ancak ifdöngü içindeki koşul her zaman falseve sonuç olarak, döngü hiçbir zaman geri dönemez ve derleyici tarafından belirlenebilir. derleme zamanının kendisi.

while(true) {

    if(false) {
        break;
    }

    System.out.println("inside while");
}

System.out.println("while terminated"); //No error here.

while(true) {

    if(false)  { //if true then also
        return;  //Replacing return with break fixes the following error.
    }

    System.out.println("inside while");
}

System.out.println("while terminated"); //Compiler-error - unreachable statement.

while(true) {

    if(true) {
        System.out.println("inside if");
        return;
    }

    System.out.println("inside while"); //No error here.
}

System.out.println("while terminated"); //Compiler-error - unreachable statement.

Düzenleme:if ve ile aynı şey while.

if(false) {
    System.out.println("inside if"); //No error here.
}

while(false) {
    System.out.println("inside while");
    // Compiler's complain - unreachable statement.
}

while(true) {

    if(true) {
        System.out.println("inside if");
        break;
    }

    System.out.println("inside while"); //No error here.
}      

Aşağıdaki sürüm whilede sonsuz bir döngüye sıkışmış durumda.

while(true) {

    try {
        System.out.println("inside while");
        return;   //Replacing return with break makes no difference here.
    } finally {
        continue;
    }
}

Bunun nedeni, ifade kendisinden önce bloğun içinde karşılaşsa finallybile bloğun her zaman yürütülmesidir .returntry


46
Kimin umrunda? Belli ki derleyicinin sadece bir özelliği. Bu tür şeylerle ilgilenmeyin.
CJ7

17
Başka bir iş parçacığı yerel statik olmayan bir değişkeni nasıl değiştirebilir?
CJ7

4
Nesnenin iç durumu, yansıtma yoluyla eşzamanlı olarak değiştirilebilir. Bu nedenle JLS, yalnızca son (sabit) ifadelerin kontrol edilmesini zorunlu kılar.
lsoliveira

6
Bu aptalca hatalardan nefret ediyorum. Ulaşılamayan kod bir hata değil uyarı olmalıdır.
user606723

2
@ CJ7: Ben buna "özellik" demezdim ve uyumlu bir Java derleyicisini (sebepsiz yere) uygulamak çok yorucu oluyor. Tasarım gereği satıcı bağımlılığınızın tadını çıkarın.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

Yanıtlar:


105

Derleyici, ilk ifadenin her zaman sonsuz bir döngü ile sonuçlandığını kolayca ve kesin olarak kanıtlayabilir , ancak ikincisi için o kadar kolay değildir. Oyuncak örneğinizde basit, ama ya eğer:

  • değişkenin içeriği bir dosyadan okundu mu?
  • değişken yerel değildi ve başka bir iş parçacığı tarafından değiştirilebilir mi?
  • değişken bazı kullanıcı girdilerine dayanıyor mu?

Derleyici açıkça basit durumunuzu kontrol etmiyor çünkü o yoldan tamamen ayrılıyor. Neden? Çünkü şartnameye göre çok daha zor . Bölüm 14.21'e bakınız :

(Bu arada, benim derleyici gelmez değişken ilan edildiğinde şikayet final.)


26
-1 - Derleyicinin neler yapabileceğiyle ilgili değil. Kontrollerin daha kolay veya daha zor olmasıyla ilgili bir şey değil. Java Dil Spesifikasyonu tarafından derleyicinin ne yapmasına izin verildiğiyle ilgilidir . Kodun ikinci sürümü JLS'ye göre geçerli Java'dır , bu nedenle bir derleme hatası yanlış olur.
Stephen C

10
@StephenC - Bu bilgi için teşekkürler. Yansıtmak için mutlu bir şekilde güncellendi.
Wayne

Hala -1; "Ya eğer" lerinizden hiçbiri burada geçerli değildir. Olduğu gibi, derleyici olabilir bu sabit-yayılma denir ve yaygın birçok başka durumlarda başarıyla kullanılmaktadır statik analiz açısından - gerçekten sorunu çözmek. JLS buradaki tek nedendir ve sorunun çözülmesinin ne kadar zor olduğu önemsizdir. Elbette, JLS'nin ilk etapta bu şekilde yazılmasının nedeni, bu sorunun zorluğuyla ilgili olabilir, ancak şahsen bunun aslında farklı derleyiciler arasında standartlaştırılmış bir sabit yayılım çözümünü uygulamak zor olduğundan şüpheleniyorum .
Oak

2
@Oak - Cevabımda "[OP'nin] oyuncak örneğinde basit" olduğunu ve Stephen'ın yorumuna göre JLS'nin sınırlayıcı faktör olduğunu kabul ettim. Kabul ettiğimize oldukça eminim.
Wayne

55

Spesifikasyonlara göre while ifadeleri hakkında şu söyleniyor.

Bir while ifadesi, aşağıdakilerden en az biri doğruysa normal olarak tamamlayabilir:

  • While ifadesi ulaşılabilirdir ve koşul ifadesi true değerine sahip sabit bir ifade değildir.
  • While ifadesinden çıkan ulaşılabilir bir break ifadesi vardır. \

Bu nedenle, derleyici yalnızca while ifadesini izleyen kodun, while koşulu gerçek bir değere sahip bir sabitse veya while içinde bir break ifadesi varsa erişilemez olduğunu söyleyecektir. İkinci durumda, b'nin değeri sabit olmadığından, onu takip eden kodu erişilemez olarak kabul etmez. Neyin ulaşılamaz olduğu ve neyin ulaşılamayacağı hakkında size daha fazla ayrıntı vermek için bu bağlantının arkasında çok daha fazla bilgi var.


3
+1, sadece derleyici yazarlarının düşünebildikleri veya düşünemeyecekleri şey olmadığını belirtmek için JLS onlara ne yapabileceklerini ve ulaşılamaz olduğunu düşünebileceklerini söyler.
yshavit

14

Çünkü true sabittir ve döngüde b değiştirilebilir.


1
Doğru, ama ilgisiz, b olarak edilir DEĞİL döngüde değiştiriliyor. True kullanan döngünün bir break ifadesini içerebileceğini de eşit olarak iddia edebilirsiniz .
deworde

@deworde - değiştirilme şansı olduğu sürece (diyelim başka bir iş parçacığı tarafından) derleyici buna hata diyemez. Döngü çalışırken b'nin değişip değişmeyeceğini sadece döngünün kendisine bakarak söyleyemezsiniz.
Peter Recore

10

Değişken durumu analiz etmek zor olduğundan, derleyici hemen hemen pes etti ve dilediğinizi yapmanıza izin verdi. Ek olarak, Java Dil Spesifikasyonu, derleyicinin erişilemez kodu tespit etmesine nasıl izin verildiği konusunda açık kurallara sahiptir .

Derleyiciyi kandırmanın birçok yolu vardır - başka bir yaygın örnek de

public void test()
{
    return;
    System.out.println("Hello");
}

bu da işe yaramazdı, çünkü derleyici bölgenin ulaşılamaz olduğunu fark edecekti. Bunun yerine yapabilirsin

public void test()
{
    if (2 > 1) return;
    System.out.println("Hello");
}

Derleyici ifadenin asla yanlış olmayacağını anlayamadığı için bu işe yarar.


6
Diğer yanıtların da belirttiği gibi, bu (doğrudan) derleyicinin ulaşılamayan kodu algılaması için neyin kolay veya zor olabileceğiyle ilgili değildir. JLS, erişilemez ifadeleri tespit etme kurallarını belirlemek için büyük çaba gösterir . Bu kurallara göre, ilk örnek geçerli Java değildir ve ikinci örnek geçerli Java'dır. Bu kadar. Derleyici kuralları belirtildiği gibi uygular.
Stephen C

JLS argümanını takip etmiyorum. Derleyiciler gerçekten tüm yasal Java'yı bayt koduna dönüştürmek zorunda mı? mantıksız olduğu ispatlansa bile.
emory

@emory Evet, standarda uyan ve yasal Java kodunu derleyen standartlara uyumlu bir tarayıcının tanımı budur. Ve standartlara uygun olmayan bir tarayıcı kullanıyorsanız, bazı güzel özelliklere sahip olsa da, artık meşgul bir derleyici tasarımcısının "mantıklı" bir kuralın ne olduğuna dair teorisine güveniyorsunuz. Bu gerçekten korkunç bir senaryo: "Neden biri aynı şekilde İKİ koşulu kullanmak istesin ki? Hiç kullanmadım")
deworde

Bu örnek kullanım a'dan ifbiraz farklıdır whileçünkü derleyici diziyi if(true) return; System.out.println("Hello");bile şikayet etmeden derleyecektir . IIRC, bu JLS'deki özel bir istisnadır. Derleyici, erişilemeyen kodu olduğu ifkadar kolay bir şekilde tespit edebilecektir while.
Christian Semrau

2
@emory - 3. taraf bir açıklama işlemcisi aracılığıyla bir Java programını beslediğinizde, bu ... gerçek anlamda ... artık Java değil. Daha ziyade, Java bindirilmiş açıklama işlemci aletlerin ne olursa olsun dil uzantıları ve değişiklik ile. Ama burada olan bu DEĞİL. OP'nin soruları vanilya Java ile ilgilidir ve JLS kuralları geçerli bir vanilya Java programının neyin olup olmadığını yönetir.
Stephen C

6

İkincisi ulaşılamaz değil. Boolean b, döngü içinde bir yerde bitiş durumuna neden olacak şekilde yanlış olarak değiştirilme olasılığına sahiptir.


Doğru, ancak alakasız, çünkü b döngüde DEĞİŞTİRİLMİYOR. True kullanan döngünün, bir sonlandırma koşulu verecek bir break ifadesi içerebileceğini de eşit olarak iddia edebilirsiniz.
deworde

4

Benim tahminim "b" değişkeninin değerini değiştirme olasılığı var, bu yüzden derleyiciye System.out.println("while terminated"); ulaşılabileceğini düşünüyorum .


4

Derleyiciler mükemmel değiller de olmamalılar

Derleyicinin sorumluluğu sözdizimini onaylamaktır - yürütmeyi onaylamak değildir. Derleyiciler, kesin olarak yazılmış bir dildeki birçok çalışma zamanı problemini nihayetinde yakalayabilir ve önleyebilir - ancak bu tür hataları yakalayamazlar.

Pratik çözüm, derleyicilerinizin kontrollerini tamamlayacak birim testleri pillerine sahip olmak VEYA ilkel değişkenlere ve durdurma koşullarına dayanmak yerine sağlam olduğu bilinen mantığı uygulamak için nesneye yönelik bileşenleri kullanmaktır.

Güçlü Yazma ve OO: derleyicinin etkinliğini artırma

Bazı hatalar doğası gereği sözdizimseldir ve Java'da güçlü yazım, birçok çalışma zamanı istisnasını yakalanabilir hale getirir. Ancak, daha iyi türleri kullanarak, derleyicinizin daha iyi bir mantık yürütmesine yardımcı olabilirsiniz.

Derleyicinin mantığı daha etkili bir şekilde zorlamasını istiyorsanız, Java'da çözüm, bu tür mantığı uygulayabilen sağlam, gerekli nesneler oluşturmak ve bu nesneleri ilkel öğeler yerine uygulamanızı oluşturmak için kullanmaktır.

Bunun klasik bir örneği, Java'nın foreach döngüsü ile birlikte yineleyici modelinin kullanılmasıdır. Bu yapı, basit bir while döngüsüne göre, gösterdiğiniz hata türüne karşı daha az savunmasızdır.


Statik güçlü yazım mekanizmalarının hataları bulmasına yardımcı olmanın tek yolu OO değildir. Haskell'in parametrik türleri ve tür sınıfları (bunlar, OO dillerinde sınıf denilenlerden biraz farklıdır) aslında bu konuda oldukça tartışmalı bir şekilde daha iyidir.
leftaroundabout

3

Derleyici, biçerebilecek değerlerin üzerinden geçecek kadar karmaşık değildir (ancak bunu yalnızca bir kez atarsınız). İlk örnek, derleyicinin sonsuz bir döngü olacağını görmesi kolaydır çünkü koşul değişken değildir.


4
Bu, derleyicinin "karmaşıklığı" ile ilgili değildir. JLS, erişilemez ifadeleri tespit etme kurallarını belirlemek için büyük çaba gösterir. Bu kurallara göre, ilk örnek geçerli Java değildir ve ikinci örnek geçerli Java'dır. Bu kadar. Derleyici yazarı kuralları belirtildiği gibi uygulamalıdır ... aksi takdirde derleyicisi uygun değildir.
Stephen C

3

Derleyicinizin ilk vakayı derlemeyi reddetmesine şaşırdım. Bu bana garip geliyor.

Ancak ikinci durum birinci durum için optimize edilmemiştir çünkü (a) başka bir iş parçacığı b(b) 'nin değerini güncelleyebilir , çağrılan işlevin değerini bbir yan etki olarak değiştirebilir .


2
Şaşırırsanız, Java Dil Spesifikasyonunu yeterince okumamışsınızdır :-)
Stephen C

1
Haha :) Nerede durduğumu bilmek güzel. Teşekkürler!
sarnold

3

Aslında kimsenin bunu ÇOK doğru anladığını sanmıyorum (en azından orijinal sorgulayıcı anlamında değil). OQ şunları söylemeye devam ediyor:

Doğru, ancak ilgisiz, çünkü b döngüde DEĞİŞTİRİLMİYOR

Ama önemli değil çünkü son satıra ulaşılabilir. Bu kodu aldıysanız, bir sınıf dosyasına derlediyseniz ve sınıf dosyasını başka birine (örneğin bir kitaplık olarak) verdiyseniz, derlenmiş sınıfı yansıtma yoluyla "b" yi değiştiren, döngüden çıkıp sonuncu yürütmek için satır.

Bu, sabit olmayan (veya kullanıldığı konumda bir sabite derleyen son) herhangi bir değişken için geçerlidir - bazen sınıfı son ile yeniden derlerseniz ve ona başvuran bir sınıfla değil, tuhaf hatalara neden olur, referanslama sınıf yine de herhangi bir hata olmadan eski değeri tutacaktır)

Satın alınan bir kitaplıktaki bir sınıfa maymunla yama uygulamak için başka bir sınıfın nihai olmayan özel değişkenlerini değiştirmek için yansıtma yeteneğini kullandım - bir hatayı düzelterek satıcının resmi yamalarını beklerken geliştirmeye devam edebildik.

Bu arada, bu bugünlerde gerçekten işe yaramayabilir - daha önce yapmış olsam da, bu kadar küçük bir döngünün CPU önbelleğinde önbelleğe alınması ihtimali var ve değişken geçici olarak işaretlenmediğinden önbelleğe alınan kod asla yeni değeri al. Bunu eylemde hiç görmedim ama teorik olarak doğru olduğuna inanıyorum.


İyi bir nokta. Bunun bbir yöntem değişkeni olduğunu varsayıyorum . Yansıma kullanarak bir yöntem değişkenini gerçekten değiştirebilir misiniz? Genel nokta ne olursa olsun, son satırın ulaşılabilir olmadığını varsaymamalıyız.
emory

Ah, haklısın, b'nin üye olduğunu sanıyordum. Eğer b bir metot değişkeni ise, derleyicinin "Can" ın değişmeyeceğini bildiğini ancak bilmediğini düşünüyorum (bu da diğer tüm cevapları kesinlikle DOĞRU doğru yapar)
Bill K

3

Bunun nedeni, derleyicinin mümkün olsa da çok fazla bebek bakıcılığı çalışması yapmamasıdır.

Gösterilen örnek, derleyicinin sonsuz döngüyü algılaması için basit ve mantıklıdır. Ama değişkenle herhangi bir ilişkisi olmadan 1000 satırlık kod eklemeye ne dersiniz b? Peki ya tüm bu ifadeler b = true;? Derleyici sonucu kesinlikle değerlendirebilir ve sonunda whiledöngüde doğru olduğunu söyleyebilir , ancak gerçek bir projeyi derlemek ne kadar yavaş olur?

Not: tüy bırakmayan araç kesinlikle sizin için yapmalı.


2

Derleyicinin bakış açısından, bin bir while(b)yerde yanlışa dönüşebilir. Derleyici kontrol etme zahmetine girmez.

Eğlenmek için deneyin while(1 < 2), for(int i = 0; i < 1; i--)vb.


2

İfadeler çalışma zamanında değerlendirilir, bu nedenle, "true" skaler değerini boolean değişkeni gibi bir şeyle değiştirirken, skaler bir değeri boole ifadesine değiştirmişsinizdir ve bu nedenle derleyicinin bunu derleme zamanında bilmesinin bir yolu yoktur.


2

Derleyici, mantıksal değerin trueçalışma zamanında değerlendirileceğini kesin olarak belirleyebilirse, bu hatayı atar. Derleyici bildirilen değişken olduğunu varsayar olabilir (biz insanlarda böyle olmaz burada biliyorum olsa) değiştirilmelidir.

Bu gerçeği vurgulamak için, değişkenler finalJava'da olduğu gibi bildirilirse , çoğu derleyici, değerin yerini almışsınız gibi aynı hatayı atar. Bunun nedeni, değişkenin derleme zamanında tanımlanması (ve çalışma zamanında değiştirilememesi) ve bu nedenle derleyicinin, ifadenin trueçalışma zamanında değerlendirileceğini kesin olarak belirleyebilmesidir .


2

İlk İfade her zaman sonsuz bir döngü ile sonuçlanır, çünkü while döngüsü koşulunda bir sabit belirledik, burada ikinci durumda derleyici, döngü içindeki b'nin değerinin değişme olasılığı olduğunu varsayar.

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.