赞
踩
为什么是二进制呢?因为计算机在计算的时候全部都是基于二进制计算的。在Java中声明一个普通的int类型变量:
private int age = 10;
此时的变量 age 是默认十进制的,转化为二进制也就是:1010。如果想要在代码中声明一个十六进制的int类型变量呢?
private int age = 0x10;
此时变量age在10的前面加了一个 “0x” , 这就是告诉计算机我这个age是一个十六进制的10,转化为二进制也就是:0001 0000,由此可见虽然都是10,但是进制不一样,最终的结果也是完全不一样的。如何在代码中直接声明一个二进制的变量呢?这个真没有。。。但是可以通过各种操作来间接实现:
private int age = 1 << 3;
此时变量age是通过位移运算得到的一个变量,他的过程是这样:首先 1 转化为二进制是 0001,将 0001 左移三位,也就变成了 1000,因此此时的变量age实际是十进制的 8 。
首先说什么是标志位?在代码中定义一个变量:
private boolean is_eat_food_flag;
定义一个变量 is_eat_food_flag 来表示是否吃饭,然后在代码中就可以对这个变量进行赋值,并且根据这个变量的值做一些 if…else 的判断逻辑,这就是标志位。要想通过二进制数实现标志位的效果,那当然是对二进制数有要求的,并不是任意拿来一个二进制数都能当做标志位,下面看可以当做标志位的二进制数都有哪些:
private static final int FLAG_A = 1;
private static final int FLAG_B = 1 << 1;
private static final int FLAG_C = 1 << 2;
private static final int FLAG_D = 1 << 3;
private static final int FLAG_E = 1 << 4;
将上边的5个变量全部转化为二进制就是:
0001
0010
0100
1000
0001 0000
他们的规律就是每一个二进制标志位都 “只占一个1” 并且随着 “1” 不断的往前移动可以出现很多的标志位, “只占一个1” 这个条件是实现二进制标志位的精髓所在。
上面我们定义了 A B C D E 五个标志位,下面就通过位运算来感受下二进制标志位的魅力:
public class FlagClass {
private static final int FLAG_A = 1;
private static final int FLAG_B = 1 << 1;
private static final int FLAG_C = 1 << 2;
private static final int FLAG_D = 1 << 3;
private static final int FLAG_E = 1 << 4;
//注意这个是一个变量,上边的都是静态常量
private int mFlag = 0;
}
在原来的基础上,我添加了一个变量 mFlag , mFlag 默认值为十进制的 0,转化为二进制也就是:0000 , 下面先让 mFlag 与 FLAG_A 做 “或” 操作:
mFlag |= FLAG_A; // 解释:0000 | 0001 , 所以mFlag最终等于 0001
经过上边的一个操作,mFlag的值由 “0000” 变为 “0001” , 这有什么作用呢?此时的 mFlag 如果分别与 FLAG_A 和 FLAG_B 做 “与” 操作,会有什么结果呢?
int a = mFlag & FLAG_A; //解释: 0001 & 0001 , 所以 a = 0001
int b = mFLAG & FLAG_B; //解释: 0001 & 0010 , 所以 b = 0000;
a 和 b 的值一个是 1, 一个是 0 ,这就说明:在第一步中 mFlag |= FLAG_A 的操作中,我们给 mFlag 变量添加了一个 FLAG_A 的标志位,所以在第二步中 a = 1, 由于 mFlag 没有添加 FLAG_B 的标志位, 所以第二步中 b = 0 。
所以我们就可以通过位运算来做这样的判断操作:
public class FlagClass { private static final int FLAG_A = 1; private static final int FLAG_B = 1 << 1; private static final int FLAG_C = 1 << 2; private static final int FLAG_D = 1 << 3; private static final int FLAG_E = 1 << 4; //注意这个是一个变量,上边的都是静态常量 private int mFlag = 0; public void main() { mFlag |= FLAG_B; //添加FLAG_B 的标志位,mFlag变为 0010 if ( (mFlag & FLAG_B) == FLAG_B ) { //判断成立,因为上边添加了FLAG_B的标志位 } if ( (mFlag & FLAG_C) == FLAG_C ) { //判断不成立,因为没有FLAG_C的标志位 } //解释: 先将FLAG_B 按位取反变成 1101, 然后 0010 & 1101 ,结果 mFlag = 0000 , 又变回了初始值, 相当于去掉了 FLAG_B的标志位。 mFlag &= ~FLAG_B; if ( (mFlag & FLAG_B) == FLAG_B ) { //判断又不成立了,因为上边去掉了FLAG_B的标志位 } } }
好吧,上边的例子还是挺晦涩的,并不能感受到二进制标志位的威力,下面就通过实际的例子感受下。
在一个类中有两个网络请求的异步回调方法:drinkWaterCallback 和 eatFoodCallback ,这两个回调方法被回调的先后顺序是不定的,并且只有当这两个异步的网络请求回调方法都被调用了,才能调用 playGame 方法,所以普通的实现思路是这样的:
public class Normal { /** * 是否喝水 */ private boolean isDrinkWater = false; /** * 是否吃饭 */ private boolean isEatFood = false; /** * 喝水的网络请求回调 */ public void drinkWaterCallback() { isDrinkWater = true; if (isEatFood) { playGame(); } } /** * 吃饭的网络请求回调 */ public void eatFoodCallback() { isEatFood = true; if (isDrinkWater) { playGame(); } } private void playGame() { Log.d("", "play game!"); } }
普通的实现思路:因为有两个异步的回调方法,所以我需要声明两个 boolean 类型的变量来分别表示这两种状态,然后在每一个回调方法中要做两件事:第一件事就是给自己的状态置为 true , 然后判断另一个标志位的状态从而决定是否调用 playGame 方法。这种实现方式在状态少的时候比较易读,但是如果要增加状态呢?比如在现在两个状态的基础上要新增: 洗漱、做作业、练钢琴 三种状态,那就需要再新增三种状态的标志位,也就是说随着状态的无限增多,我们在类中声明的标志位就会无限增多。那有没有可能 不管有多少状态,我只用一个标志位来实现呢? , 看一下二进制标志位如何实现这个:
public class BinaryFlag { /** * 是否喝水 */ private static final int DRINK_WATER_FLAG = 1 << 1; /** * 是否吃饭 */ private static final int EAT_FOOD_FLAG = 1 << 2; /** * 注意此处,只用了一个普通的 int 类型的变量flag */ private volatile int mFlag = 0; /** * 喝水的网络请求回调 */ public void drinkWaterCallback() { //添加喝水的标志位 mFlag |= DRINK_WATER_FLAG; //判断是否吃饭了 if ((mFlag & EAT_FOOD_FLAG) == EAT_FOOD_FLAG) { playGame(); } } /** * 吃饭的网络请求回调 */ public void eatFoodCallback() { //添加吃饭的标志位 mFlag |= EAT_FOOD_FLAG; //判断是否喝水了 if ((mFlag & DRINK_WATER_FLAG) == DRINK_WATER_FLAG) { playGame(); } } private void playGame() { Log.d("", "play game!"); } }
在二进制标志位的情况下,不管新增多少状态,我只需要增加相应的一个 静态常量的状态 就可以了,至于在某一个时刻我现在类中方法的回调情况只需要一个 mFlag变量 就足可以表示所有的状态了。
如果这些还不能打动你,试想一种情况,如果现在新增一个需求:需要随时保存类中方法的回调状态到数据库中。按照常规的做法,需要把所有状态的boolean类型的标志位都放在一个实体类中存下来,然后再根据自己数据库类型存储这些数据:
public class Status {
private boolean isEatFood;
private boolean isDrinkWater;
//....洗漱、做作业、弹钢琴
}
常规方法中随着状态的增加,就需要增加对应实体类的属性,这样才能把状态保存。但是如果使用 二进制状态标志位 ,不管有多少状态,我只需要保存一个 int 类型的 mFlag 数据就完事了,因为这个 int 类型的数据中包含了我所有的状态信息,这足够能看出二进制标志位的威力了吧!其实Google的Android SDK中用了大量的二进制标志位,比如说在 View类 的 measure、layout、draw方法 中都贯穿着大量的二进制位运算,看起来很高大上但其实所运用的思想原理就是这么简单。
ok!大功告成!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。