Java sınıfı neden boş bir satırla farklı derleniyor?


207

Aşağıdaki Java sınıfına sahibim

public class HelloWorld {
  public static void main(String []args) {
  }
}

Bu dosyayı derlediğimde ve elde edilen sınıf dosyasında bir sha256 çalıştırdığımda

9c8d09e27ea78319ddb85fcf4f8085aa7762b0ab36dc5ba5fd000dccb63960ff  HelloWorld.class

Sonra sınıfı değiştirdim ve böyle boş bir satır ekledim:

public class HelloWorld {

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

Yine aynı sonucu almayı bekleyen bir sha256 çıktı ama bunun yerine aldım

11f7ad3ad03eb9e0bb7bfa3b97bbe0f17d31194d8d92cc683cfbd7852e2d189f  HelloWorld.class

Bu TutorialsPoint makalesinde okudum :

Büyük olasılıkla bir yorum içeren yalnızca beyaz boşluk içeren bir satır boş satır olarak bilinir ve Java bunu tamamen yok sayar.

Benim sorum şu, Java boş satırları göz ardı ettiği için derlenmiş bayt kodu her iki program için neden farklı?

Yani bu fark HelloWorld.classbir 0x03byte bir değiştirilir 0x04bayt.


45
Derleyici, normalde de olsa, sınıf dosyaları üretirken deterministik olmak zorunda değildir. Bu soruya bakın . Varsayılan olarak Jar dosyalardır değil yani hatta derleme, tekrarlanabilir aynı kod iki farklı kavanozları sonuçlanacaktır. Bunun nedeni, dosyaların sırası ve zaman damgalarının eşleşmemesidir. Özel konfigürasyon ile tekrarlanabilir yapılar mümkündür.
Giacomo Alzetta

22
TutorialsPoint, "Java'nın boş satırları tamamen yok saydığını" iddia eder . Java Dil Spesifikasyonu Bölüm 3.4 aksini söylüyor. Hangisine inanacaksınız? ...
skomisa

37
@skomisa Şartname.
wizzwizz4

4
@GiacomoAlzetta, tek bir bayt kodu dosyası için belirli bir bayt kodu formu bile yoktur. Örneğin, üyelerin sırası belirtilmez, bu nedenle derleyici yeni değiştirilemez değişkenleri Setdahili olarak rastgele kullanırsa, her çalıştırmada farklı bir düzen üretebilir. Ayrıca, derleme zamanını içeren özel bir özellik de ekleyebilir. Ve benzeri…
Holger

15
@DioPhung öğrenilen başka bir ders: tutorialspoint iyi öğreticiler için güvenilir bir kaynak değildir
jwenting

Yanıtlar:


331

Temel olarak, satır numaraları hata ayıklama için tutulur, bu nedenle kaynak kodunuzu yaptığınız şekilde değiştirirseniz, yönteminiz farklı bir satırda başlar ve derlenen sınıf farkı yansıtır.


11
Bu aynı zamanda OP tarafından bildirilen end-of-transmissionend-of-text
Baytlarda

160
Deneysel olarak bunu kanıtlamak için OP kaynağının sınıf dosyalarının karmalarını -g:nonederlerken (tüm hata ayıklama bilgilerini kaldırır, buraya bakın ) bayrağını kullanarak karşılaştırdım ve her iki senaryoda da aynı karma değerini aldım.
Kaptan Adam

14
Cevabınızı resmi olarak destekleyerek, Java SE 11 için Java Dil Spesifikasyonu bölüm 3.4'ten ( "Hat Sonlandırıcılar" ) : "Bir Java derleyici, daha sonra Unicode giriş karakterlerinin sırasını satır sonlandırıcılarını tanıyarak satırlara böler ... Tanımlanan satırlar msgstr " satır sonlandırıcıları bir Java derleyicisi tarafından üretilen satır numaralarını belirleyebilir " .
skomisa

4
Bu satır numaralarının önemli kullanımlarından biri, bir istisna oluşmasıdır; yığın izlemesindeki istisnanın satır numarasını söyleyebilir.
gparyani

114

Değişikliği, javap -vayrıntılı bilgi verecek olanı kullanarak görebilirsiniz . Daha önce de belirtildiği gibi, fark satır numaralarında olacaktır:

$ javap -v HelloWorld.class > with-line.txt
$ javap -v HelloWorld.class > no-line.txt
$ diff -C 1 no-line.txt with-line.txt
*** no-line.txt 2018-10-03 11:43:32.719400000 +0100
--- with-line.txt       2018-10-03 11:43:04.378500000 +0100
***************
*** 2,4 ****
    Last modified 03-Oct-2018; size 373 bytes
!   MD5 checksum 058baea07fb787bdd81c3fb3f9c586bc
    Compiled from "HelloWorld.java"
--- 2,4 ----
    Last modified 03-Oct-2018; size 373 bytes
!   MD5 checksum 435dbce605c21f84dda48de1a76e961f
    Compiled from "HelloWorld.java"
***************
*** 50,52 ****
        LineNumberTable:
!         line 3: 0
        LocalVariableTable:
--- 50,52 ----
        LineNumberTable:
!         line 4: 0
        LocalVariableTable:

Daha doğrusu, sınıf dosyası LineNumberTablebölümde farklıdır :

LineNumberTable özniteliği, Code özniteliğinin öznitelikler tablosundaki isteğe bağlı bir değişken uzunluk özniteliğidir (§4.7.3). Hata ayıklayıcılar tarafından, kod dizisinin hangi bölümünün orijinal kaynak dosyadaki belirli bir satır numarasına karşılık geldiğini belirlemek için kullanılabilir.

Bir Code özniteliğinin öznitelikler tablosunda birden fazla LineNumberTable özniteliği varsa, bunlar herhangi bir sırada görünebilir.

Kod özniteliğinin öznitelikler tablosunda, bir kaynak dosyanın satırı başına birden fazla LineNumberTable özniteliği olabilir. Diğer bir deyişle, LineNumberTable öznitelikleri birlikte bir kaynak dosyanın belirli bir satırını temsil edebilir ve kaynak satırlarıyla bire bir olması gerekmez.


57

"Java boş satırları yok sayar" varsayımı yanlıştır. Yöntemden önceki boş satır sayısına bağlı olarak farklı davranan bir kod snippet'i şunlardır main:

class NewlineDependent {

  public static void main(String[] args) {
    int i = Thread.currentThread().getStackTrace()[1].getLineNumber();
    System.out.println((new String[]{"foo", "bar"})[((i % 2) + 2) % 2]);
  }
}

Daha önce boş satır yoksa mainyazdırır "foo", ancak daha önce bir boş satır mainyazdırır "bar".

Çalışma zamanı davranışı farklı olduğundan, .classdosyalar zaman damgalarından veya diğer meta verilerden bağımsız olarak farklı olmalıdır .

Bu, yalnızca Java için değil, satır numaralarına sahip yığın çerçevelerine erişimi olan her dil için geçerlidir.

Not: -g:none(herhangi bir hata ayıklama bilgisi olmadan) ile derlenmişse , satır numaraları dahil edilmez, getLineNumber()her zaman geri döner -1ve "bar"satır sonlarından bağımsız olarak program her zaman yazdırılır .


11
Ayrıca yazdırabilir Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -1.
18'de xehpuk

1
@xehpuk Almanın tek yolu bayrağı -1kullanmaktı -g:none. Bu istisnayı sıradan kullanarak almanın başka bir yolu var mı javac?
Andrey Tyukin

3
Sadece -gseçenek ile sanırım . Orada da -g:varsve -g:sourcehangi oluşumunu önler LineNumberTable.
xehpuk

14

Hata ayıklama için herhangi bir satır numarası ayrıntısının yanı sıra, bildiriminizde derleme zamanı ve tarihi de saklanabilir. Bu, her derlediğinizde doğal olarak farklı olacaktır.


14
C # da bu sorunu var; Yakın zamana kadar derleyici her zaman taze bir GUID gömülü montaj böylece iki ediyorum kurar garanti olacağını oluşturulan değil bu yüzden onlara verebilecek arayla aynı ikili olun!
Eric Lippert

3
@EricLippert, iki derleme yalnızca oluşturulan zamanlarından farklıysa (yani özdeş kod tabanı), onlara aynı şekilde davranmamalıyız? Modern CI / CD derleme boru hattıyla (Jenkins, TeamCity, CircleCI), derlemeler arasında ayrım yapmanın bir yolu olacak, ancak uygulama açısından, aynı kod tabanına sahip yeni ikili dosyaları dağıtmak yararlı görünmüyor.
Dio Phung

2
@DioPhung Tam tersi. İki farklı yapının aynı GUID'ye sahip olmasını istemezsiniz , çünkü sistem hangisini kullanacağına bu şekilde karar verebilir. Bu nedenle, her seferinde yeni bir GUID oluşturmak en kolay yöntemdir; ve sonra Eric'in istenmeyen bir sonuç olarak tanımladığı yan etkiyi elde edersiniz.
Graham

3
@vikingsteve Dediğim gibi, iki farklı yapıların aynı GUID ile raporlanması daha az yardımcı olacaktır, bu da daha sonra aynı yazılım olarak sisteme rapor edilecektir. Bu, her türlü sağlama düzeninin tamamen başarısız olmasına neden olacağından, GUID'lerin hiçbir zaman çoğaltılmaması kritik önemdedir (makul olasılık içinde!). Aynı kaynak kodun iki ayrı derlemesi için farklı GUID'lere sahip olmak en fazla önemsiz bir sıkıntıdır. Kritik görev başarısızlığı senaryosu karşısında, biraz yararsız olduğunu düşündüğünüz şey gerçekten anlamıyor.
Graham

4
@vikingsteve İkili kod kısmı hala aynı (eğer anlıyorum, ben bir C # dev değilim), sadece ikili bağlı bir meta veri.
Kaptan Adam
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.