You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

特定场景下javac生成的行号是否不如ecj准确?

问题现象

下面是一个Java类,其equals()方法的特点是将return关键字单独占一行,后续的布尔表达式分行书写:

package jd.core.test;

import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;

public class TimeZoneDisplayKey {

    private final TimeZone mTimeZone;
    private final int mStyle;
    private final Locale mLocale;

    public TimeZoneDisplayKey(TimeZone mTimeZone, int mStyle, Locale mLocale) {
        this.mTimeZone = mTimeZone;
        this.mStyle = mStyle;
        this.mLocale = mLocale;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mLocale, mStyle, mTimeZone);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        TimeZoneDisplayKey other = (TimeZoneDisplayKey) obj;
        return
                Objects.equals(mLocale, other.mLocale) &&
                mStyle == other.mStyle &&
                Objects.equals(mTimeZone, other.mTimeZone);
    }
}

使用JDK 17的javac -g命令编译后,通过javap -l提取equals()方法的行号表,出现了不符合预期的映射:

31: aload_0
      32: getfield      #17                 // Field mLocale:Ljava/util/Locale;
      35: aload_2
      36: getfield      #17                 // Field mLocale:Ljava/util/Locale;
      39: invokestatic  #37                 // Method java/util/Objects.equals:(Ljava/lang/Object;Ljava/lang/Object;)Z
      42: ifeq          74
      45: aload_0
      46: getfield      #13                 // Field mStyle:I
      49: aload_2
      50: getfield      #13                 // Field mStyle:I
      53: if_icmpne     74
      56: aload_0
      57: getfield      #7                  // Field mTimeZone:Ljava/util/TimeZone;
      60: aload_2
      61: getfield      #7                  // Field mTimeZone:Ljava/util/TimeZone;
      64: invokestatic  #37                 // Method java/util/Objects.equals:(Ljava/lang/Object;Ljava/lang/Object;)Z
      67: ifeq          74
      70: iconst_1
      71: goto          75
      74: iconst_0
      75: ireturn
    LineNumberTable:
      line 26: 0
      line 27: 5
      line 29: 7
      line 30: 11
      line 32: 13
      line 33: 24
      line 35: 26
      line 36: 31
      line 37: 39
      line 39: 64
      line 36: 75

行号表显示,第36行(即单独写return的那一行)同时对应了字节码的31: aload_0(读取this)和75: ireturn(返回操作),但源码中return关键字和后续表达式是分开展开的。

而使用ecj编译生成的行号表则没有这个问题,每个字节码指令都对应到了正确的源码行:

31: aload_0
      32: getfield      #21                 // Field mLocale:Ljava/util/Locale;
      35: aload_2
      36: getfield      #21                 // Field mLocale:Ljava/util/Locale;
      39: invokestatic  #47                 // Method java/util/Objects.equals:(Ljava/lang/Object;Ljava/lang/Object;)Z
      42: ifeq          72
      45: aload_0
      46: getfield      #19                 // Field mStyle:I
      49: aload_2
      50: getfield      #19                 // Field mStyle:I
      53: if_icmpne     72
      56: aload_0
      57: getfield      #17                 // Field mTimeZone:Ljava/util/TimeZone;
      60: aload_2
      61: getfield      #17                 // Field mTimeZone:Ljava/util/TimeZone;
      64: invokestatic  #47                 // Method java/util/Objects.equals:(Ljava/lang/Object;Ljava/lang/Object;)Z
      67: ifeq          72
      70: iconst_1
      71: ireturn
      72: iconst_0
      73: ireturn
    LineNumberTable:
      line 26: 0
      line 27: 5
      line 29: 7
      line 30: 11
      line 32: 13
      line 33: 24
      line 35: 26
      line 37: 31
      line 38: 45
      line 39: 56
      line 36: 70
原因解析

这是javac和ecj在调试行号表生成策略上的差异:

  • javac在处理跨多行的return表达式时,会将整个表达式对应的所有字节码指令的起始和结束位置,都关联到return关键字所在的源码行。因此表达式中读取字段、调用方法的字节码,都会被映射到return所在行,最终出现同一行对应多个不相关字节码的情况。
  • ecj则采用了更精细的行号映射策略,会将每个子表达式对应的字节码,关联到该子表达式实际所在的源码行,确保行号和代码逻辑的对应关系更准确。
总结

这种现象不属于编译错误,不会影响程序的运行逻辑,但会干扰调试过程(比如断点命中位置不符合预期)。如果需要更准确的行号映射,可以:

  • 改用ecj编译代码
  • 调整代码风格,将return关键字和表达式写在同一行

内容的提问来源于stack exchange,提问作者Nicolas Baumann

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 14:25:33