9
votes

En Java, pourquoi les classes d'exception doivent-elles être à la disposition du chargeur de classe avant leur utilisation?

Je développe une application qui charge de manière dynamique un pot, qui contient la définition d'un tas de classes qu'il utilise. Tout s'est bien passé jusqu'à ce que j'essayais d'attraper une classe dérivée d'exception qui est dans le bocal chargé de manière dynamique.

L'extrait suivant montre le problème ( dynamicjarner code> est la classe qui charge réellement le pot; les deux TestClass code> et MyException code> est dans le pot externe): p> xxx pré>

Quand j'essaie de l'exécuter, je reçois ceci: p> xxx pré>

si je remplace attrape (myException e) code> avec attrape (exception e) code>, le programme fonctionne bien. Cela signifie que Java est em> capable de trouver testclass code> une fois que le pot a déjà été chargé. Il semble donc que le JVM a besoin de toutes les classes d'exception à définir lorsque le programme commence à fonctionner, et non lorsqu'il est nécessaire (c'est-à-dire lorsque ce bloc d'essais particulier est atteint). P>

Pourquoi cela se produit-il ? p>

edit forte> p>

J'ai exécuté des tests supplémentaires, ce qui est vraiment étrange. Ceci est la source complète de myException code>: p> xxx pré>

ce code est exécuté: p> xxx pré>

ceci N'EST PAS: P>

  public static void main(String[] args) {
    DynamicJarLoader.loadFile("../DynamicTestJar.jar");
    String foo = new TestClass().testMethod("42");
    class tempClass {
        public void test() {
            new MyException().printStackTrace();
        }
    }
    new tempClass().test(); // prints the stack trace, as expected
  }


6 commentaires

Je pouvais voir que cela se produira si la réorganisation du code d'octet fait référence à l'exception avant l'instruction Fichier de charge, mais je ne suis pas assez familier avec ces problèmes pour être sûr.


Je ne vois rien ici qui justifie de charger le pot de cette façon. C'est un exemple simple, mais quelle est la raison de votre code complet?


@duffymo: C'est un applet qui a été divisé en deux. La plus grande moitié est installée sur l'ordinateur local afin que l'utilisateur ne le télécharge pas à chaque fois (c'est plutôt grand); L'applet réel est responsable du chargement de l'autre, puis de faire la logique nécessaire. En "chargement", je veux dire en réalité la modification de la classe de classe de manière dynamique afin qu'elle pointe également de ce pot. Peut-être qu'il y a une meilleure façon de y parvenir?


La technique va bien, il vous suffit de mettre MyException dans votre bocal de base, pas dans la "dynamique".


Les réponses ne semblent pas aborder une partie de la question initiale: pourquoi attraper une exception spéciale? Pourquoi pouvez-vous faire référence à diverses classes Java et les faire uniquement être chargée en cas de besoin, mais si vous faites référence à une exception personnalisée dans le cadre d'une prise , que lorsque la classe contenant est chargée, le chargeur de classe insiste également sur le chargement. cette classe d'exception? Nous avons vu ce même comportement et ne comprenons pas pourquoi attraper une exception semble se comporter différemment des autres références à des types externes.


@Cincinnati Joe: C'est juste une supposition, mais je pense qu'il est plausible que le compilateur traite des exceptions différemment et génère un code spécial pour une fonction qui utilise ... Catch. Il y a probablement une initialisation de fantaisie sur le prologue de la fonction, et il a besoin de toutes les classes d'exception capturées à définir. Pourrait être utile d'avoir examiné le code d'octet pour un aperçu de la façon dont cela est fait.


4 Réponses :


6
votes

"Il semble donc que le JVM a besoin de toutes les classes d'exception à définir lorsque le programme commence à courir, et non quand ils sont nécessaires" - Non, je ne pense pas que ce soit correct.

Je parie que votre problème est dû à ces erreurs potentielles de votre part, dont aucune n'a à voir avec le JVM: p>

  1. Vous manquez une instruction de package en haut de votre fichier myException.java. On dirait que vous vouliez dire qu'il s'agissait d'être "forfait dynamicestjar", mais ce n'est pas là. Li>
  2. Vous avez l'instruction de package correcte dans votre fichier myException.java, mais lorsque vous avez compilé, vous n'avez pas fini avec le fichier myException.class dans un dossier nommé "dynamicestjar". Li>
  3. Lorsque vous avez emballé votre code dans dynamicestjar.jar (fan de Star Wars, vous êtes) Vous n'avez pas reçu le chemin, car vous n'avez pas pilié le répertoire contenant le dossier "dynamicestjar", donc le chemin que la classe Le chargeur voit est incorrect. li> ol>

    Aucun de ceux-ci n'est dû à de grands mystères sur le chargeur de classe. Vous devez vérifier et vous assurer que vous faites vos affaires correctement. P>

    Pourquoi avez-vous besoin de charger le pot dynamiquement comme ça? Que faites-vous qui ne pouvait pas être accompli en mettant simplement le pot dans la classe de classe et en laissant le chargeur de classe le ramasser? P>

    J'espère que ce n'est qu'un exemple et non indicatif de vos "meilleures pratiques" En Java: P>

    catch(MyException e) { }
    


5 commentaires

Merci de répondre! Il est possible que j'ai vissé quelque chose, mais considérez ces deux faits: i) testclass est trouvé, et c'est dans le même package que myException . Je viens de vérifier le fichier JAR et myException.class est celui-ci est censé être. ii) Quand je quitte dynamicestjar.jar où Java peut le trouver (par exemple lib /), le programme fonctionne (vient de vérifier). Il va également de Netbeans. Le problème ne se manifeste que lorsque je supprime le pot et le mettre quelque part Java ne regarde pas (dans ce cas, "..").


Oh, oui, je n'invie pas des exceptions comme celle-ci en vrai code! C'est juste l'extrait le plus simple possible qui illustre le problème, mais merci pour la tête.


"... où Java peut le trouver ..." - Il n'y a aucune hypothèse sur la recherche d'un annuaire A / LIB intégré à Java. Dieu nous interdit, vous n'ajoutez pas votre code aux annuaires JDK, n'est-ce pas? Croyez-moi Pedro, c'est toujours toi. Votre code et votre configuration sont toujours le problème. Ne prenez pas de succès à la recherche de TestClass comme une indication que vous êtes "correct". Lorsque vous avez raison, le JVM trouvera votre exception et que le problème du CNF disparaîtra.


@duffymo: Non, je ne suis pas :) Peut-être que Java regarde dans le répertoire "lib" à la suite de la construction de Netbeans?


Je ne sais pas - j'utilise Intellij.



3
votes

MyException est une classe d'exception dans le pot que vous chargez de manière dynamique?

Notez que vous êtes statiquement à l'aide de la classe MyException, comme vous l'avez littéralement dans votre code.

Mettez-vous dynamicestjar.jar dans votre parcours de classe pendant que vous compilez, mais pas pendant que vous exécutez le programme? Ne le mettez pas dans la classe de classe en compilant, de sorte que le compilateur vous indique où vous utilisez le pot de manière statique.


7 commentaires

"Statiquement"? Je ne sais pas ce que tu veux dire par là. Il a appelé "Nouveau" pour créer une instance et invoquer des méthodes dessus.


@duffymo: lorsque la classe contenant la méthode principale est chargée, il essaiera de charger la classe MyException et échouera car le code qui charge de manière dynamique le pot contenant myException < / code> n'aura pas encore été exécuté. C'est ce que les moyens jonts en justice par «statiquement en utilisant».


@jesper, Nick: Comme mes dernières modifications montrent, cela se produit également lorsque j'instaine myException . Est-ce que cela fait de la différence?


Pour récapituler cette réponse (ce qui est essentiellement correct), vous ne devez pas disposer du pot "dynamique" dans votre catégorie de compilation. Toute classe dont vous avez besoin dans la catégorie Compile ClassPath, vous devrez être disponible lorsque la classe est chargée pour la première fois (c'est-à-dire que vous ne pouvez pas le charger de manière dynamique après les charges de la classe principale).


Pourriez-vous élaborer un peu plus (peut-être poster une réponse plus détaillée)? Merci!


@Pedro: Oui, les deux instanciations doivent fonctionner (je ne sais pas pourquoi PrintStackTrace n'est pas). Toutefois, si vous regardez le bytecode (javap -c -private votre classe ) Pour un exemple où vous essayez d'attirer l'exception, vous verrez une section appelée Table d'exception . Maintenant, ceci est une supposition (et Googling n'a pas abouti à une réponse définitive), mais cela ne me surprendrait pas si les classes de la table d'exception ont été chargées avant l'exécution du corps du code.


voir ma réponse dans la question initiale. Si vous avez besoin d'attraper MyException sur votre méthode principale, mettez-la dans votre pot de base. En règle générale, ne compilez pas vos classes JAR de base avec les classes de jar dynamiques dans la classe de classe. Cela vous aidera à éviter la question à l'avenir.



1
votes

Vous chargez le jar dynamicestjar.jar dynamiquement au moment de l'exécution, mais vous l'ajoutez au point de classe lorsque vous compilez le code.

Ainsi, lorsque le chargeur de classe par défaut tente de charger le bytecode pour Main () , il ne peut pas trouver myException sur la classe de classe et jette une exception. Cela arrive avant `` dynamicjarnerser.loadfile ("../ dynamicestjar.jar"); `est exécuté!

Vous devez donc vous assurer que les classes de votre pot dynamique sont disponibles dans le chargeur de classe actuellement actif lorsqu'une classe est chargée qui en a besoin. Vous pouvez ajouter le pot à la classe de classe après, surtout pas dans une classe qui importe quelque chose de celui-ci.


0 commentaires

0
votes

en Java, toutes les classes sont uniquement identifiées par Classloader et FQN de la classe.

Les chargeurs de classes sont hiérarchiques, ce qui signifie que votre classe de chargement dynamique a accès à toutes les classes de ses parents, mais le parent n'a pas d'accès direct. À ses classes. P>

Maintenant, si votre chargeur de classe enfant définit une classe qui étend votre parent, le code de votre parent ne peut faire référence à cette classe que par réflexion ou par la classe mère. P>

Si vous pensez à tout le code Java comme réfléchissant, cela devient plus facile. IE: P>

Class.forName("MyException")


0 commentaires