简体   繁体   中英

How are Exception classes linked at compilation time in Java

As part of an introduction to object oriented programming with Java, we carry out a large (20-36 hours) practical exercise - this year based upon Tetris. The students follow a guide explaining -via textual descriptions and uml class diagrams - how to implement their game. Connected to this guide is a tool that runs a suite of unit tests (written using TestNg) to check that their implementation conforms to the instructions. The unit tests are precompiled with a 100% conform version of the code and are launched via a custom user interface which displays the results of the test(s). The number of tests executed depends upon the section that the students have reached.

The unit test classes are all structured in a similar way:

public static void classExists(String fullClassName) {
    try {
        @SuppressWarnings("unused")
        Class<?> clazz = Class.forName(fullClassName);
    } catch (ClassNotFoundException cnfe) {
        fail(fullClassName + " could not be found on the classpath.");
    }
}
  1. Test the class exists

  2. If (1) succeeds: Test the class visibility

  3. If (2) succeeds: Test the methods / attributes exist

  4. if (3) succeeds: Test the functionality of the methods

This works well, except for a class that extends Exception, if when we are testing (4) we write:

BloxException be = new BloxException("Message",value);

The tests work as expected, in particular if BloxException does not exist, test (1) fails and the other tests are not executed, however if we try to throw the exception:

throw new BloxException("Message", value);

The tests are not executed, as TestNg cannot find the class BloxException. We presume this is due to how Java compiles the two lines above, can any one give me the details of why when we throw the exception we need the class, but if we create a new exception, we do not need the code to execute the tests?

Here is the Exception StackTrace which only occurs when using the throw new BloxException:

java.lang.NoClassDefFoundError: fr/eseo/blox/model/BloxException
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2436)
at java.lang.Class.privateGetPublicMethods(Class.java:2556)
at java.lang.Class.getMethods(Class.java:1412)
at org.testng.internal.TestNGClassFinder.<init>(TestNGClassFinder.java:59)
at org.testng.TestRunner.initMethods(TestRunner.java:409)
at org.testng.TestRunner.init(TestRunner.java:235)
at org.testng.TestRunner.init(TestRunner.java:205)
at org.testng.TestRunner.<init>(TestRunner.java:153)
at org.testng.SuiteRunner$DefaultTestRunnerFactory.newTestRunner(SuiteRunner.java:522)
at org.testng.SuiteRunner.init(SuiteRunner.java:157)
at org.testng.SuiteRunner.<init>(SuiteRunner.java:111)
at org.testng.TestNG.createSuiteRunner(TestNG.java:1273)
at org.testng.TestNG.createSuiteRunners(TestNG.java:1260)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1114)
at org.testng.TestNG.run(TestNG.java:1031)
at fr.eseo.ac20.core.validation.runners.Validator.<init>(Validator.java:131)
at fr.eseo.ac20.core.validation.runners.CustomValidator.<init>(CustomValidator.java:21)
at fr.eseo.ac20.core.validation.runners.CustomValidator.main(CustomValidator.java:17)
Caused by: java.lang.ClassNotFoundException: fr.eseo.blox.model.BloxException
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    ... 19 more

As a help, I have created two simple test cases to show what is going on, they are available to download from examples.zip . Unzip the files and from the commandline change into the tng_good folder and execute

java -cp testng-6.0.1.jar:. org.testng.TestNG tng.xml

Then change into the tng_bad folder and execute the same

java -cp testng-6.0.1.jar:. org.testng.TestNG tng.xml

The first executes, with the exists test failing, the second gives the exception. (The code source for the test methods are inside the example subfolder)

It is nothing to do with this being an exception. The issue here is in the way that you are loading the code.

In the first case, you call

Class.forName(fullClassName);

on a class that is not available, you will get a ClassNotFoundException , and the class won't be loaded.

In the second case, the TestNG TestRunner class has loaded a class that depends on another class that is not available. Then a little later it is attempting to get the type information for a method ... and that is failing because of the missing dependency. Since the JVM now has a told the application about a class with a missing dependency, it is now in a state where that class is "broken". As a result, it is throwing a different exception NoClassDefFoundError .


Basically, your idea of loading and testing the classes independently cannot work. When you load a class, any class that it depends on (including any exceptions that it throws) needs to be available for loading ... or else you will get exceptions.

They shouldn't run it, but students being students, they sometimes try to run all the tests even though they have not completed the section.

Easy. Explain to them that it won't work if they do that :-)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM