Java 堆栈跟踪中的神秘位置

Mysterious Location in Java stack trace

就像下面的例子,当我运行测试用例testSortArrayNull()在测试class中命名为ComparatorAbstractTestCase时,一个RuntimeException 被抛出,生成的堆栈跟踪如下,

这是堆栈跟踪的结果,

testSortArrayNull(org.apache.commons.io.comparator.CompositeFileComparatorTest)  Time elapsed: 0.016 sec  <<< ERROR!
java.lang.RuntimeException: null
  at org.apache.commons.io.comparator.AbstractFileComparator.sort(AbstractFileComparator.java:48)
  at org.apache.commons.io.comparator.CompositeFileComparator.sort(CompositeFileComparator.java:45)
  at org.apache.commons.io.comparator.ComparatorAbstractTestCase.testSortArrayNull(ComparatorAbstractTestCase.java:96)
  ...

这里是测试的部分代码class ComparatorAbstractTestCase,

public abstract class ComparatorAbstractTestCase extends FileBasedTestCase {

    /** comparator instance */
    protected CompositeFileComparator comparator;
    ...
    @Test
    public void testSortArrayNull() {
        assertNull(comparator.sort((File[])null)); /** LINE 96 */
    }
    ...
 }

这里是classCompositeFileComparator的部分代码,用classAbstractFileComparator.

扩展
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class CompositeFileComparator extends AbstractFileComparator implements Serializable { /** LINE 45 */
    ... // no sort() method
}

这里是 class AbstractFileComparator 的部分代码,其中有 sort() 方法。

import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

abstract class AbstractFileComparator implements Comparator<File> {

    public File[] sort(final File... files) {
        if (files != null) {
            Arrays.sort(files, this);
        }
        if (files == null) { 
            throw new RuntimeException(); /** LINE 48 */
        }
        return null;
    }
    ...
}

从上面3个代码片段可以看出,classCompositeFileComparator中的第45行是class声明的位置,为什么stack trace会定位到这个神秘的位置呢?第 45 行出现在堆栈跟踪中是否有意义?感谢大家的热心帮助:)

这是因为您的 class AbstractFileComparator 具有仅限包的访问权限。即使里面的sort方法是public,也不能从包外引用。

为了让其他代码通过publicsubclassCompositeFileComparator访问sort方法,方法sort被编译里面重新声明CompositeFileComparator。这是您的源文件中不存在的合成 Java 代码,因此编译器必须为其选择一个行号。

您使用的编译器选择了 class 声明的行号。其他编译器(例如我特定版本的 Eclipse 中的编译器)选择行号 1。由于该方法在您的源代码中并不真正存在,因此行号没有意义。

编译器生成的方法只会调用该方法的超级class版本。

如果您不希望这种情况发生,您可以将 AbstractFileComparator 设置为 public class。