【问题标题】:Parsing field access flags in java在java中解析字段访问标志
【发布时间】:2011-05-17 05:58:52
【问题描述】:

我有一个任务,我必须解析 java .class 文件的字段访问标志。 .class 文件的规范可以在这里找到:Class File Format(第 26 和 27 页有访问标志和十六进制值)。

这很好,我可以不用担心。 我的问题是有大量的组合。

我知道 public、private 和 protected 是相互排斥的,这在一定程度上减少了组合。 final和transient也是互斥的。其余的则不是。

目前,我有一个大的 switch 语句来进行比较。我读入访问标志的十六进制值,然后增加一个计数器,具体取决于它是公共的、私有的还是受保护的。这很好用,但是将每个组合都列在 switch 语句中似乎很麻烦。即公共静态、公共最终、公共静态最终等。

我想对访问标志和公共、私有或受保护的适当十六进制值进行取模,但公共是 0x0001,所以这行不通。

还有其他人对如何减少 switch 语句中的案例数量有任何想法吗?

【问题讨论】:

    标签: java parsing


    【解决方案1】:

    有什么问题?规范说它是一个位标志,这意味着您应该将一个值视为二进制数,并且您可以通过按位与来测试是否设置了特定值。

    例如

    /*
    ACC_VOLATILE = 0x0040 = 10000000
    ACC_PUBLIC   = 0x0001 = 00000001
    Public and volatile is= 10000001
    */
    
    
    publicCount += flag & ACC_PUBLIC > 0 ? 1 : 0;
    volatileCount += flag & ACC_VOLATILE > 0 ? 1 : 0;
    

    【讨论】:

      【解决方案2】:

      如果你想避免这样的模式,我只是偷了一个:

      if (access_flag & ACC_PUBLIC != 0)
      {
          public++;
      }
      if (access_flag & ACC_FINAL != 0)
      {
          final++;
      }
      ...
      

      这是一种伟大的直觉。我制定了一条规则,永远不要编写看起来像那样多余的代码。它不仅容易出错,而且你的类中的代码更多,而且复制和粘贴代码真的很无聊。

      所以最大的技巧是让这个访问“通用”并且易于从调用类中理解——去掉所有重复的废话,只留下“肉”,将复杂性推到通用例程中。

      因此,调用方法的一种简单方法是提供一个包含许多需要计数的位组合的位域数组以及您感兴趣的字段列表(这样您就不会浪费时间进行测试)您不关心的字段):

      int[] counts = sumUpBits(arrayOfFlagBitfields, ACC_PUBLIC | ACC_FINAL | ACC_...);
      

      这真的很干净,但是您如何访问返回字段?我原本是这么想的:

      System.out.println("Number of public classes="+counts[findBitPosition(ACC_PUBLIC]));
      System.out.println("Number of final classes="+counts[findBitPosition(ACC_FINAL)]);
      

      除了需要将位域更改为它们的位置之外,这里的大部分样板都已消失。我认为两项更改可能会使其更好-将其封装在一个类中并使用散列来跟踪位置,这样您就不必一直转换 bitPosition (如果您不想使用散列,findBitPosition 在最后)。

      让我们尝试一个成熟的课程。从调用者的角度来看,这应该如何看待?

      BitSummer bitSums=new BitSummer(arrayOfFlagBitfields, ACC_PUBLIC, ACC_FINAL);
      System.out.println("Number of public classes="+bitSums.getCount(ACC_PUBLIC));
      System.out.println("Number of final classes="+bitSums.getCount(ACC_FINAL));
      

      这很干净也很简单——我真的很喜欢 OO!现在您只需使用 bitSums 来存储您的值直到需要它们(它比将它们存储在类变量中更少样板,并且比使用数组或集合更清晰)

      所以现在对类进行编码。请注意,构造函数现在使用可变参数 - 更少惊喜/更传统,并且对哈希实现更有意义。

      顺便说一句,我知道这看起来会很慢而且效率低下,但对于大多数用途来说可能还不错——如果是的话,它可以改进,但这应该比 switch 更短且冗余更少声明(实际上与此相同,只是展开-但是此声明使用哈希和自动装箱,这将导致额外的惩罚)。

      public class BitSummer {
          // sums will store the "sum" as <flag, count>
          private final HashMap<Integer, Integer> sums=new HashMap<Integer, Integer>();
      
          // Constructor does all the work, the rest is just an easy lookup.
          public BitSummer(int[] arrayOfFlagBitfields, int ... positionsToCount) {
      
              // Loop over each bitfield we want to count
              for(int bitfield : arrayOfFlagBitfields) {
      
                  // and over each flag to check
                  for(int flag : positionsToCount) {
                      // Test to see if we actually should count this bitfield as having the flag set
                      if((bitfield & flag) != 0) {
                          sums.put(flag, sums.get(flag) +1); // Increment value
                      }
                  }
              }
          }
      
          // Return the count for a given bit position
          public int getCount(int bit) {
              return sums.get(bit);
          }
      }
      

      我没有对此进行测试,但我认为它相当接近。我不会用它来实时处理视频包或任何东西,但对于大多数用途来说,它应该足够快。

      至于与原始示例相比,维护代码可能看起来“长”,但如果要检查的字段超过 5 或 6 个,这实际上将是比链式 if 语句更短的解决方案,并且错误/容易发生的问题明显更少等等可维护——写起来也更有趣。

      如果您真的觉得需要消除哈希表,您可以轻松地将其替换为以标志位置为索引的稀疏数组(例如,标志 00001000/0x08 的计数将存储在第四个数组位置)。这将需要一个这样的函数来计算数组访问的位位置(存储在数组中和检索)

      private int findBitPosition(int flag) {
          int ret;
          while( ( flag << 1 ) != 0 )
              ret++;
          return ret;
      }
      

      那很有趣。

      【讨论】:

      • 顺便说一下,如果您需要了解某些组合(例如计算静态和公共的类),我认为您可以传入“STATIC | PUBLIC”作为标志之一。它应该可以正常工作,但您可能需要调整中心的“if”语句——我在编写它时并没有将其视为场景之一,但由于它使用哈希映射,所以应该没问题。跨度>
      【解决方案3】:

      我不确定这就是您要查找的内容,但我会使用带有二进制 AND 的 if-cases 来检查是否设置了标志:

      if (access_flag & ACC_PUBLIC != 0)
      {
          // class is public
      }
      if (access_flag & ACC_FINAL != 0)
      {
          // class is final
      }
      ....
      

      【讨论】:

        猜你喜欢
        • 2021-06-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-22
        • 1970-01-01
        相关资源
        最近更新 更多