我有一个作业,其中我必须解析java .class文件的字段访问标志。 可以在此处找到.class文件的规范:Class File Format(第26页& 27具有访问标志和十六进制数)。
这很好,我可以做到这一点无后顾之忧。 我的问题是有很多组合。
我知道公共,私有和受保护是相互排斥的,这在一定程度上减少了组合。最终和瞬态也是相互排斥的。然而其余的不是。
目前,我有一个大的switch语句来进行比较。我读取访问标志的十六进制值,然后递增计数器,具体取决于它是公共的,私有的还是受保护的。这样可以正常工作,但是在switch语句中列出每个组合似乎很麻烦。即公共静态,公共决赛,公共静态决赛等。
我想过对访问标志和公共,私有或受保护的适当十六进制值进行模数,但是public是0x0001,所以这不起作用。
是否有其他人对如何减少switch语句中的案例数有任何想法?
答案 0 :(得分:5)
有什么问题?规范说它是一个标志,这意味着你应该将一个值看作二进制数,并且你可以通过按位AND来测试是否设置了特定的值。
E.g
/*
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;
答案 1 :(得分:5)
如果你试图避免像我这样的模式,我只是偷走了:
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来存储你的值,直到它们被需要为止(它比将它们存储在类变量中更简单,并且比使用数组或集合更清晰)
现在编写课程代码。请注意,构造函数现在使用变量参数 - 更少的意外/更常规,并且对哈希实现更有意义。
顺便说一句,我知道这似乎是缓慢而低效的,但它对于大多数用途来说可能并不坏 - 如果是,它可以改进,但这应该比交换机更短,更少冗余声明(实际上与此相同,只是展开 - 但是这个使用散列和自动装箱会产生额外的惩罚)。
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;
}
这很有趣。
答案 2 :(得分:2)
我不确定你正在寻找什么,但我会使用if-cases with binary AND来检查是否设置了一个标志:
if (access_flag & ACC_PUBLIC != 0)
{
// class is public
}
if (access_flag & ACC_FINAL != 0)
{
// class is final
}
....