web-dev-qa-db-fra.com

Pourquoi une classe Java se compile-t-elle différemment avec une ligne vide?

J'ai la classe Java suivante

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

Quand je compile ce fichier et que je lance un sha256 sur le fichier de classe résultant, je reçois

9c8d09e27ea78319ddb85fcf4f8085aa7762b0ab36dc5ba5fd000dccb63960ff  HelloWorld.class

Ensuite, j'ai modifié la classe et ajouté une ligne vide comme ceci:

public class HelloWorld {

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

Encore une fois, j’ai lancé un sha256 à la sortie, espérant obtenir le même résultat, mais j’ai eu la

11f7ad3ad03eb9e0bb7bfa3b97bbe0f17d31194d8d92cc683cfbd7852e2d189f  HelloWorld.class

J'ai lu sur cet article de TutorialsPoint que:

Une ligne contenant uniquement des espaces, éventuellement avec un commentaire, est appelée une ligne vide et Java l'ignore totalement.

Ma question est donc la suivante: étant donné que Java ignore les lignes vides, pourquoi le code secondaire compilé est-il différent pour les deux programmes?

C'est-à-dire que la différence entre le HelloWorld.class et le 0x03 octet est remplacée par un 0x04 octet.

205
KNejad

Fondamentalement, les numéros de ligne sont conservés pour le débogage. Ainsi, si vous modifiez votre code source comme vous l'avez fait, votre méthode démarre à une ligne différente et la classe compilée reflète la différence.

326

Vous pouvez voir le changement en utilisant javap -v qui produira des informations détaillées. Comme les autres déjà mentionnés, la différence sera dans les numéros de ligne:

$ 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:

Plus précisément, le fichier de classe diffère dans la section LineNumberTable :

L'attribut LineNumberTable est un attribut optionnel de longueur variable dans la table d'attributs d'un attribut Code (§4.7.3). Il peut être utilisé par les débogueurs pour déterminer quelle partie du tableau de code correspond à un numéro de ligne donné dans le fichier source d'origine.

Si plusieurs attributs LineNumberTable sont présents dans la table d'attributs d'un attribut Code, ils peuvent apparaître dans n'importe quel ordre.

Il peut y avoir plus d'un attribut LineNumberTable par ligne d'un fichier source dans la table d'attributs d'un attribut Code. C'est-à-dire que les attributs LineNumberTable peuvent représenter ensemble une ligne donnée d'un fichier source et ne doivent pas nécessairement être un-à-un avec les lignes source.

111
Karol Dowbecki

L'hypothèse selon laquelle "Java ignore les lignes vides" est fausse. Voici un extrait de code qui se comporte différemment selon le nombre de lignes vides avant la méthode 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]);
  }
}

S'il n'y a pas de lignes vides avant main, il imprime "foo", mais avec une ligne vide avant main, il imprime "bar".

Étant donné que le comportement à l'exécution est différent, les fichiers .classdoivent doivent être différents, indépendamment de l'horodatage ou des autres métadonnées.

Ceci est valable pour toutes les langues ayant accès aux cadres de pile avec des numéros de ligne, pas seulement pour Java.

Remarque: s'il est compilé avec -g:none (sans aucune information de débogage), les numéros de ligne ne seront pas inclus, getLineNumber() renvoie toujours -1 et le programme imprime toujours "bar". , quel que soit le nombre de sauts de ligne.

51
Andrey Tyukin

En plus des détails de numéro de ligne pour le débogage, votre manifeste peut également stocker l'heure et la date de génération. Ce sera naturellement différent chaque fois que vous compilerez.

11
Graham