我有一个任务,我必须解析java .class文件的字段访问标志.可以在此处找到.class文件的规范:类文件格式(第26和27页具有访问标志和十六进制值).
这很好,我可以做到这一点无后顾之忧.我的问题是有大量的组合.
我知道公共,私人和受保护是相互排斥的,这在一定程度上减少了组合.最终和瞬态也是相互排斥的.然而其余的则没有.
目前,我有一个大的switch语句来进行比较.我读取访问标志的十六进制值,然后递增计数器,具体取决于它是公共的,私有的还是受保护的.这样可以正常工作,但是在switch语句中列出每个组合似乎很麻烦.即公共静态,公共决赛,公共静态决赛等.
我想在访问标志和公共,私有或受保护的适当十六进制值上做模数,但public是0x0001,所以这不起作用.
有没有其他人对如何减少switch语句中的案例数有任何想法?
问题是什么?规范说它是一个标志,这意味着您应该将值视为二进制数,并且您可以通过执行按位AND来测试是否设置了特定值.
例如
/*
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;
Run Code Online (Sandbox Code Playgroud)
如果你试图避免像这样的模式,我只是偷走了:
if (access_flag & ACC_PUBLIC != 0)
{
public++;
}
if (access_flag & ACC_FINAL != 0)
{
final++;
}
...
Run Code Online (Sandbox Code Playgroud)
这是一种很好的直觉.我规定永远不要编写看起来多余的代码.它不仅容易出错,而且您的课程中有更多代码,但复制和粘贴代码真的很无聊.
因此,最大的诀窍是使这种访问"通用"并且易于从调用类中理解 - 拉出所有重复的废话并且只留下"肉",将复杂性推向通用例程.
所以调用方法的简单方法就是这样,它给出了一个包含许多需要计数的位组合的位域数组和一个你感兴趣的字段列表(这样你就不会浪费时间测试你不喜欢的字段了不关心):
int[] counts = sumUpBits(arrayOfFlagBitfields, ACC_PUBLIC | ACC_FINAL | ACC_...);
Run Code Online (Sandbox Code Playgroud)
这真的很干净,但是你怎么访问返回字段?我本来想的是这样的:
System.out.println("Number of public classes="+counts[findBitPosition(ACC_PUBLIC]));
System.out.println("Number of final classes="+counts[findBitPosition(ACC_FINAL)]);
Run Code Online (Sandbox Code Playgroud)
除了需要将位域更改为其位置之外,这里的大部分样板都已消失.我认为两个更改可能会更好 - 将它封装在一个类中并使用哈希来跟踪位置,这样您就不必一直转换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));
Run Code Online (Sandbox Code Playgroud)
那很干净也很容易 - 我真的很喜欢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);
}
}
Run Code Online (Sandbox Code Playgroud)
我没有对此进行测试,但我认为它相当接近.我不会用它来实时或任何处理视频数据包,但在大多数情况下,它应该足够快.
至于维护代码与原始示例相比可能看起来"长"但如果要检查的字段数超过5或6个,这实际上是比链式if语句更短的解决方案,并且错误/倾向性更小且更易于维护 - 写起来也更有意思.
如果你真的觉得需要消除散列表,你可以很容易地用标志位置作为索引的稀疏数组替换它(例如,标志00001000/0x08的计数将存储在第四个数组位置).这将需要这样的函数来计算数组访问的位位置(存储在数组中并检索)
private int findBitPosition(int flag) {
int ret;
while( ( flag << 1 ) != 0 )
ret++;
return ret;
}
Run Code Online (Sandbox Code Playgroud)
那很有趣.
| 归档时间: |
|
| 查看次数: |
2041 次 |
| 最近记录: |