深入理解Java中的位操作

與、或、非、位移

原碼、反碼、補(bǔ)碼

字節(jié)、位、超區(qū)間......

開(kāi)始本章節(jié)之前,我們先思考一個(gè)問(wèn)題:

byte a = 33;

byte b = -3;

若我們輸出a、b的二進(jìn)制字符串是多少?

答案是這樣的么?

a->// 00100001

b->// 10100001

當(dāng)然同學(xué)們可能會(huì)覺(jué)得我既然問(wèn)了就肯定不是這樣;是吧~別著急你們?cè)囋嚲椭懒恕?/p>

在Java中輸出一個(gè)值對(duì)應(yīng)的二進(jìn)制方法有很多,這里提供一個(gè)簡(jiǎn)單的方法:

int value = 33;

String bs = String.format("%32s", Integer.toBinaryString(value)).replace(" ", "0");

在方法中是int值,int占4字節(jié)32位,所以是:“%32s”?若是byte將32改成8即可;當(dāng)然對(duì)于byte你還需要加上**“&0xFF”**來(lái)做高位清零操作。

String bs = String.format("%8s", Integer.toBinaryString(value&0xFF)).replace(" ", "0");

基本原則

在Java中是采用的有符號(hào)的運(yùn)算方式,故:高位為符號(hào)位,其余位存儲(chǔ)數(shù)據(jù)信息。

簡(jiǎn)單來(lái)說(shuō):

+1 ->// 00000001

-1 ->// 10000001

默認(rèn)例子中的值都按byte來(lái)算,占8位,減少大家的記憶負(fù)擔(dān)。

因?yàn)閎yte占8位,所以有效數(shù)據(jù)存儲(chǔ)7位,最高位為符號(hào)位。int值則是31位存儲(chǔ)數(shù)據(jù)。

0 代表正數(shù)

1 代表負(fù)數(shù)

上述的-1的表示方法其實(shí)并不是機(jī)器碼,而是人腦的理解方式。

我們認(rèn)為+1與-1的差異就是高位不同而已,這是我們基于自然規(guī)律來(lái)看的;而機(jī)器真正存儲(chǔ)的值其實(shí)是:11111111;這里其實(shí)就給大家提到了最初的問(wèn)題。

二進(jìn)制的計(jì)算規(guī)則是:逢2進(jìn)1

這個(gè)很好理解,因?yàn)楸硎镜臄?shù)字就是:0、1兩個(gè)數(shù)字,想要表示更大的值就只能往前遞增進(jìn)步。

在平時(shí)生活中是逢10進(jìn)1;因?yàn)樵蹅冇?0個(gè)數(shù)字:9、8、7、6、5、4、3、2、1、0;所以11就是:當(dāng)為0|9增加為10的時(shí)候就進(jìn)一格所以變成:1|0,個(gè)位再把剩余的1補(bǔ)上就是:1|1;所以就是11。

那么:

1就是:0|0|0|0|0|0|0|1

2就是:0|0|0|0|0|0|1|0

3就是:0|0|0|0|0|0|1|1

4就是:0|0|0|0|0|1|0|0

運(yùn)算法則

設(shè)

byte a = (byte) 0b01011000; // 88

byte b = (byte) 0b10101000; // -88

int n = 1;

按位與 a & b

輸入2個(gè)參數(shù)

a、b對(duì)應(yīng)位都為1時(shí),c對(duì)應(yīng)位為1;反之為0。

按位或 a | b**

輸入2個(gè)參數(shù)

a、b對(duì)應(yīng)位只要有一個(gè)為1,c對(duì)應(yīng)位就為1;反之為0。

按位異或 a^b

輸入2個(gè)參數(shù)

a、b對(duì)應(yīng)位只要不同,則c對(duì)應(yīng)位就為1;反之為0。

按位取反(非)

輸入1個(gè)參數(shù)

c對(duì)應(yīng)位與輸入?yún)?shù)a完全相反;a對(duì)應(yīng)位為1,則c對(duì)應(yīng)位就為0;a對(duì)應(yīng)位為0,則c對(duì)應(yīng)位就為1。

左移

輸入1個(gè)參數(shù)a;n = 1

a對(duì)應(yīng)位全部左移動(dòng)n位得到c;a最左邊的n個(gè)位全部丟棄(紅色框),c最右邊n個(gè)位補(bǔ)充0(綠色框)。

右移(帶符號(hào))

輸入1個(gè)參數(shù)b;n = 1

這里將參數(shù)換為b是因?yàn)閎為負(fù)數(shù),第一個(gè)位為1

b對(duì)應(yīng)位全部右移動(dòng)n位得到c;b最右邊n個(gè)位全部丟掉(紅色框),c最左邊n個(gè)位補(bǔ)充1(綠色框)。

這里需要注意的是其左邊補(bǔ)充的值取決于b的最高位也就是符號(hào)位:符號(hào)位是1則補(bǔ)充1,符號(hào)位是0則補(bǔ)充0。

右移(無(wú)符號(hào))

輸入1個(gè)參數(shù)b;n = 1

這里將參數(shù)換為b是因?yàn)閎為負(fù)數(shù),第一個(gè)位為1

b對(duì)應(yīng)位全部右移動(dòng)n位得到c;b最右邊n個(gè)位全部丟掉(紅色框),c最左邊n個(gè)位補(bǔ)充0(綠色框)。

這里需要注意的是其左邊補(bǔ)充的值永遠(yuǎn)為0,不管其最高位(符號(hào)位)的值。

進(jìn)制表示規(guī)范

這個(gè)小節(jié)是插曲,部分同學(xué)可能注意到上面寫(xiě)的進(jìn)制定義是:0b01011000,部分同學(xué) 可能疑惑為什么不是 0x 之類(lèi)的。

前綴

十進(jìn)制:直接寫(xiě)數(shù)字即可

二進(jìn)制:0b或0B開(kāi)頭;如:0b01011000 代表十進(jìn)制 88

八進(jìn)制:0 開(kāi)頭;如:0130 代表十進(jìn)制 88 (1x64+3x8)

十六進(jìn)制:0x或0X開(kāi)頭;如:0x58 代表 88 (5x16+8)

后綴

0x?? 若小于127 則按byte算,大于則按int類(lèi)型算

0xFF默認(rèn)為int類(lèi)型

若聲明為long添加后綴:L或l:如:0xFFL 或 0xFFl

帶小數(shù)的值默認(rèn)為double類(lèi)型;如:0.1

若聲明為float添加后綴:f 或 F:如:0.1F

若聲明為double添加后綴:d或D:如:1D

范圍

二進(jìn)制:1、0

八進(jìn)制:0~7

十進(jìn)制:0~9

十六進(jìn)制:0~9 + A~F

類(lèi)型轉(zhuǎn)換

在上述運(yùn)算法則中:兩個(gè)不同長(zhǎng)度的數(shù)據(jù)進(jìn)行位運(yùn)算時(shí),系統(tǒng)會(huì)將二者按右端對(duì)齊左端補(bǔ)齊,然后進(jìn)行位運(yùn)算

設(shè)

a 為 int 占32位

b 為 byte 占8位

執(zhí)行: a&b 、a|b 、a^b….等操作時(shí):

若b為正數(shù),則左邊補(bǔ)齊24個(gè)0

若b為負(fù)數(shù),則左邊補(bǔ)齊24個(gè)1

若b = 0b01011000 補(bǔ)齊后:0b 00000000 00000000 00000000 01011000

若b = (byte) 0b10101000 補(bǔ)齊后:0b 11111111 11111111 11111111 10101000

為什么 b = 0b10101000 需要加上 (byte) 強(qiáng)轉(zhuǎn)?

因?yàn)槟J(rèn)的0b10101000會(huì)被理解為:0b 00000000 00000000 00000000 10101000,這個(gè)值是一個(gè)超byte范圍的int值(正數(shù)):168。

當(dāng)強(qiáng)轉(zhuǎn) byte 后高位丟棄,保留低8位,對(duì)于byte來(lái)說(shuō)低8中的高位就是符號(hào)位;所以運(yùn)算后就是:-88(byte)。

原碼、反碼、補(bǔ)碼

相信看了上面那么多的各種規(guī)定后,大家有一定的疑問(wèn),為什么正數(shù)與負(fù)數(shù)與大家所想的不大一樣呢?

我相信大家覺(jué)得正數(shù)負(fù)數(shù)就是這樣的:

// 錯(cuò)誤的理解

// 0b01011000 -> 88 : (64+16+8)

// 0b11011000 -> -88 : -(64+16+8)

大家可能會(huì)想,正數(shù)與負(fù)數(shù)不就應(yīng)該只是差符號(hào)位的變化么?

// 正確的理解

// 0b01011000 -> 88 : (64+16+8)

// 0b10101000 -> -88 : -(64+16+8)

0b10101000 : -(64+16+8) ??WTF?? 除了符號(hào)位能懂以外請(qǐng)你告訴我是怎么得出 64、16、8的?

在這里我們先設(shè)兩個(gè)基本的概念:

原碼:人所能直接理解的編碼

機(jī)器碼:計(jì)算機(jī)能直接理解的編碼

允許我先說(shuō)一個(gè)小故事:對(duì)于在坐的各位來(lái)說(shuō)計(jì)算1-1是非常簡(jiǎn)單的,但是對(duì)于計(jì)算機(jī)來(lái)說(shuō)就是計(jì)算:00000001 與 10000001 (暫且按8位,原碼)。

計(jì)算機(jī)需要識(shí)別出橙色部分的符號(hào)位,然后提取出粉色部分的數(shù)據(jù)進(jìn)行計(jì)算;這里有兩個(gè)問(wèn)題:

識(shí)別橙色符號(hào)位是困難的

若橙色部分是負(fù)數(shù)則需要增加減法計(jì)算模塊

但對(duì)于計(jì)算機(jī)來(lái)說(shuō)做加法就夠了,將1-1換算為:1+(-1);OK這一步就是將所有的減法都換算為加法進(jìn)行計(jì)算,減少了減法硬件模塊的設(shè)計(jì),提升了計(jì)算機(jī)的硬件利用率。

但是這里就有一個(gè)問(wèn)題了,既然是將-1當(dāng)作了一個(gè)值來(lái)進(jìn)行運(yùn)算,那么必然這個(gè)值需要方便做加法才行;按上圖來(lái)說(shuō)我們必不可免的需要去做一次符號(hào)位的判斷,然后再做數(shù)據(jù)位的減法操作,簡(jiǎn)單來(lái)說(shuō)還是在做減法。

所以若計(jì)算機(jī)的機(jī)器碼直接采用原碼則會(huì)導(dǎo)致硬件資源的設(shè)計(jì)問(wèn)題。

有沒(méi)有一種辦法將符號(hào)位直接存儲(chǔ)到整個(gè)結(jié)構(gòu)中,讓計(jì)算機(jī)在計(jì)算過(guò)程中不去管所謂的符號(hào)位與數(shù)據(jù)位?有的!就是反碼。

反碼

正數(shù)的反碼是其本身

負(fù)數(shù)的反碼是在其原碼的基礎(chǔ)上, 符號(hào)位不變,其余各個(gè)位取反??梢院?jiǎn)單理解為?"~a | 10000000"

[+1] = [00000001]原 = [00000001]反

[-1] = [10000001]原 = [11111110]反

如上圖,咱們將 -1 的原碼轉(zhuǎn)化為了反碼;此時(shí)我們使用?反[+1] + 反[-1]?進(jìn)行一次運(yùn)算:

此時(shí)咱們可以得到一個(gè)值x,這個(gè)值可以確定的是符號(hào)位為1,為負(fù)數(shù),后面數(shù)據(jù)位全部為1;因?yàn)榇藭r(shí)是反碼狀態(tài),所以要想我們能直接讀取數(shù)據(jù)是不是應(yīng)該轉(zhuǎn)化為原碼狀態(tài)啊。

// 反碼轉(zhuǎn)原碼流程就是倒過(guò)來(lái),符號(hào)位不變,其余位為取反即可。

1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原= [00000001]反 + [11111110]反 = [11111111]反 = [10000000]原 = -0

可以看出我們已經(jīng)解決好了運(yùn)算的問(wèn)題了,計(jì)算機(jī)只需要按照反碼的方式去計(jì)算即可,只需要做加法,不需要做減法就可以運(yùn)算減法流程。計(jì)算完成后對(duì)于人腦來(lái)說(shuō)需要將反碼轉(zhuǎn)化為原碼就是可讀的數(shù)據(jù)了。

但上述也暴露一個(gè)問(wèn)題:-0 的問(wèn)題;對(duì)于0的表示將會(huì)出現(xiàn)兩種情況:

[11111111]反 = [10000000]原 = -0

[01111111]反 = [00000000]原 = +0

也就是出現(xiàn)兩種為0的表示值,-0與+0;但對(duì)于我們來(lái)說(shuō)0就是0,不需要做區(qū)分。所以又引入了補(bǔ)碼。

補(bǔ)碼

正數(shù)與反碼規(guī)則一樣無(wú)需變化:補(bǔ)碼=反碼=原碼

負(fù)數(shù)在反碼基礎(chǔ)上保證符號(hào)位不變,從右端+1

[+1] = [00000001]原 = [00000001]反 = [00000001]補(bǔ)

[-1] = [10000001]原 = [11111110]反 = [11111111]補(bǔ)

此時(shí)若計(jì)算機(jī)使用補(bǔ)碼直接進(jìn)行計(jì)算會(huì)怎樣?

當(dāng)我們使用補(bǔ)碼計(jì)算時(shí),因?yàn)槟┪驳膬晌痪鶠?,1+1 = 2;對(duì)于二進(jìn)制來(lái)說(shuō)滿(mǎn)2進(jìn)1,所以往前進(jìn)位1,進(jìn)位后又遇到 1+1 = 2的情形,所以依次進(jìn)位,當(dāng)前位置0。

最終計(jì)算后就是:1 00000000?,一共9位,因?yàn)楫?dāng)前只有8位,所以自然就只剩下:00000000?。

請(qǐng)注意:在當(dāng)前運(yùn)算過(guò)程中符號(hào)位并無(wú)差別也直接當(dāng)作普通值進(jìn)行步進(jìn)運(yùn)算!

如此我們就完成了整個(gè)流程的運(yùn)算,但你還需注意的是,當(dāng)前運(yùn)算后的值是補(bǔ)碼,也就是機(jī)器直接操作的編碼;如果要還原為我們可讀的值需要反向轉(zhuǎn)化為原碼。由最初定義可知,正數(shù):原碼=補(bǔ)碼;上述補(bǔ)碼為正數(shù),所以原碼也是:00000000。整個(gè)流程如下:

// 補(bǔ)碼計(jì)算流程

1 - 1 = 1 + (-1)

= [00000001]原 + [10000001]原

= [00000001]反 + [11111110]反

= [00000001]補(bǔ) + [11111111]補(bǔ)

= [00000000]補(bǔ)

= [00000000]原

= 0

補(bǔ)碼->原碼

正數(shù)的補(bǔ)碼就是原碼

負(fù)數(shù):

直接倒敘流程,保證符號(hào)位不變右端減1,再保證符號(hào)位不變其余位取反即可

再走一遍補(bǔ)碼流程;補(bǔ)碼的補(bǔ)碼就是原碼(先取反再+1即可)【敲黑板】

思考[10000000]代表什么?

若是某個(gè)計(jì)算完成后的補(bǔ)碼值為:10000000 那么他對(duì)應(yīng)的值是什么呢?

// 按方案1來(lái)看:

[10000000]補(bǔ) = [11111111]反 = [10000000]原

// 按方案2來(lái)看:

[10000000]補(bǔ) = [11111111]補(bǔ)反 = [10000000]補(bǔ)補(bǔ) = [10000000]原

可見(jiàn)方案1、方案2都是一樣的,補(bǔ)碼的補(bǔ)碼就是原碼。

[10000000]原 = 是等于0呢?還是-0呢?還是-128呢?

因?yàn)槲覀円呀?jīng)規(guī)定了:[00000000]原 = 0;為了充分利用位的存儲(chǔ)區(qū)間,所以將:[10000000]原 = -128

一般情況下不會(huì)對(duì)[10000000]補(bǔ)碼求原碼,因?yàn)橐矝](méi)啥意義~

思考(127、-127)原碼、反碼、補(bǔ)碼是多少?

對(duì)于正數(shù):

127 = [01111111]原 = [01111111]反 = [01111111]補(bǔ)

對(duì)于負(fù)數(shù):

-127 = [11111111]原 = [10000000]反 = [10000001]補(bǔ)

對(duì)于計(jì)算機(jī)來(lái)說(shuō),其存儲(chǔ)的值都是補(bǔ)碼,所以也就造成了一開(kāi)始我們提到的問(wèn)題:為什么88與-88的二進(jìn)制并不只是符號(hào)位不同?

再次強(qiáng)調(diào):計(jì)算機(jī)存儲(chǔ)的是補(bǔ)碼,為了方便運(yùn)算;我們想要理解其表示的值需要轉(zhuǎn)化為原碼。

溢出問(wèn)題

因?yàn)橛?jì)算機(jī)計(jì)算過(guò)程中不再區(qū)別符號(hào)位,直接將符號(hào)位也納入運(yùn)算流程中;所以也就可以解釋2個(gè)基礎(chǔ)問(wèn)題:(溢出)

兩個(gè)正數(shù)相加為負(fù)數(shù)

兩個(gè)負(fù)數(shù)相加為正數(shù)

大家可以分析一下:

88+100

(-66) + (-88)

上述計(jì)算在byte變量范圍下進(jìn)行計(jì)算,嘗試分析一下補(bǔ)碼的計(jì)算流程。

存儲(chǔ)區(qū)間

默認(rèn)的對(duì)于采用補(bǔ)碼的計(jì)算機(jī)系統(tǒng)而言,其存儲(chǔ)值的有效范圍是:-2^(n-1) ~ 2^(n-1) -1 ;n代表當(dāng)前的位數(shù)。

byte,1字節(jié),8位:-2^7 ~ 2^7 -1 = -128~127

short,2字節(jié),16位:-2^15 ~ 2^15 -1 = -32768 ~ 32767

int,4字節(jié),32位:2^31 ~ 2^31 -1

......

若,我想在byte中存儲(chǔ)超過(guò)127的值會(huì)怎樣?

設(shè)

int i = 200

對(duì)應(yīng)補(bǔ)碼為: 0000 0000 0000 0000 0000 0000 1100 1000

因200未超256(2^8)所以依然只會(huì)使用到8個(gè)位

int i = 200; // 0000 0000 0000 0000 0000 0000 1100 1000 (200)

byte b = (byte) 200; // 1100 1000

當(dāng)我們將200強(qiáng)轉(zhuǎn)為byte時(shí)高位丟棄僅剩下低8位:1100 1000

如果我們對(duì)byte進(jìn)行輸出會(huì)怎樣?

System.out.println(b); // "-56"

首先其直接調(diào)用的是:public void println(int x)?方法,OK,既然是int輸出為啥不是200?而是-56?

就算有這樣的方法:public void println(byte x)?方法,會(huì)輸出200么?也不會(huì)??!

首先對(duì)于byte b來(lái)說(shuō):1100 1000?這是一個(gè)負(fù)數(shù)的補(bǔ)碼,其原碼流程是:

[1100 1000]補(bǔ) = [1011 0111] = [1011 1000]原 = -(32+16+8) = -56

這里有一個(gè)有趣的事情,int轉(zhuǎn)byte時(shí)是直接丟掉高位的所有數(shù)據(jù):24個(gè)0;但byte轉(zhuǎn)int時(shí),補(bǔ)充高24位時(shí)是根據(jù)當(dāng)前的符號(hào)位來(lái)補(bǔ)充的,若當(dāng)前符號(hào)位是1則添1,若符號(hào)位是0則添0;對(duì)于byte來(lái)說(shuō)第一位就是符號(hào)位,當(dāng)前的1100 1000符號(hào)位是**“1”**所以添加的就是24位1。

int c = b; // b -> 1100 1000

// c -> 1111 1111 1111 1111 1111 1111 1100 1000

若直接打印的是byte值,就是-56;上面我們分析1100 1000的原碼時(shí)就已經(jīng)證明了。那么打印c是不是呢?

對(duì)于范圍較少的類(lèi)型轉(zhuǎn)換位大類(lèi)型時(shí)不會(huì)丟失數(shù)據(jù),原來(lái)是什么就是什么。

OK,就算不是上面那句話(huà),我們來(lái)看看:

[1111 1111 1111 1111 1111 1111 1100 1000]補(bǔ)

= [1000 0000 0000 0000 0000 0000 0011 0111]

= [1000 0000 0000 0000 0000 0000 0011 1000]原

= -(32+16+8) = -56

若我們轉(zhuǎn)換為int時(shí)想要還原最初的200這個(gè)值該如何辦?

分析上面的補(bǔ)碼,可以看出其與最初的補(bǔ)碼差異僅僅在于左邊24位的不同:

[1111 1111 1111 1111 1111 1111 1100 1000]補(bǔ) = -56

[0000 0000 0000 0000 0000 0000 1100 1000]補(bǔ) = 200

那么我們只需要將前面的24位重置為0即可,這里就有一個(gè)與操作的簡(jiǎn)單用法:

/**

*

* 1111 1111 1111 1111 1111 1111 1100 1000 (the int)

* &

* 0000 0000 0000 0000 0000 0000 1111 1111 (the 0xFF)

* =======================================

* 0000 0000 0000 0000 0000 0000 1100 1000 (200)

*/

System.out.println(b & 0xFF); // "200"

在這里我們做了一次特殊的:b & 0xFF?操作,b 轉(zhuǎn)換為int之后的值與 0xFF 進(jìn)行按位與操作。

0xFF = 255 其int原碼為:0000 0000 0000 0000 0000 0000 1111 1111,恰好最后8位為1,其余24位為0;所以可以用來(lái)做高位擦除操作。

這樣的用法可用以存儲(chǔ)超范圍的數(shù)據(jù),比如對(duì)于文件的大小來(lái)說(shuō)永遠(yuǎn)都是 >= 0,不可能會(huì)使用到 < 0 的值,所以對(duì)于原始的我們可以根據(jù)這個(gè),使用較少的byte表示更多的區(qū)間,簡(jiǎn)單來(lái)說(shuō)就是無(wú)符號(hào)。將符號(hào)位也用以存儲(chǔ)數(shù)據(jù)。

int i = 0xFF60; // 65376

System.out.println(i);

// 00000000000000001111111101100000

System.out.println(String.format("%32s", Integer.toBinaryString(i)).replace(" ", "0"));

byte b1 = (byte) i;

byte b2 = (byte) (i >> 8);

// 01100000

System.out.println(String.format("%8s", Integer.toBinaryString(b1 & 0xFF)).replace(" ", "0"));

// 11111111

System.out.println(String.format("%8s", Integer.toBinaryString(b2 & 0xFF)).replace(" ", "0"));

int ret = (b1 & 0xFF) | (b2 & 0xFF) << 8;

System.out.println(String.format("%32s", Integer.toBinaryString(ret)).replace(" ", "0"));

// 65376

System.out.println(ret);

若沒(méi)有做?& 0xFF?操作,其值應(yīng)是:

/*

* 0000 0000 0000 0000 0000 0000 0110 0000 (b1)

* |

* 1111 1111 1111 1111 1111 1111 0000 0000 (b2<<8)

* =======================================

* 1111 1111 1111 1111 1111 1111 0110 0000 (-160)

*/

System.out.println(b1 | b2 << 8); // "-160"

65376 本質(zhì)來(lái)說(shuō)超過(guò)了short的存儲(chǔ)范圍:-32768~32767 ,但其在int中依然只需占2個(gè)字節(jié)16位:65376<65536。所以我們只需要使用2個(gè)byte即可存儲(chǔ),而不需要int的4個(gè)byte來(lái)存儲(chǔ)。

在Socket傳輸中使用這樣的方式能有效降低傳輸?shù)淖止?jié)冗余。

案例-多Flag存儲(chǔ)在一個(gè)byte中

有這樣一個(gè)情形:一個(gè)四邊形,四條邊可以是虛線(xiàn)也可以是實(shí)線(xiàn),四條邊相互獨(dú)立;定義為 a\b\c\d 四邊;此時(shí)我們需要在畫(huà)布上畫(huà)出這個(gè)四邊形;但是因?yàn)?邊相互獨(dú)立,所以我們常見(jiàn)的就是定義4個(gè)bool值:

boolean a = true;

boolean b = false;

boolean c = false;

boolean d = true;

void changeA(boolean fullLine) {

a = fullLine;

}

簡(jiǎn)單來(lái)說(shuō)我們定義這樣的方式其一比較麻煩,其二總占用的內(nèi)存空間至少是4個(gè)byte,也有可能是16byte(按int存的情況)。

但是我們表示的內(nèi)容無(wú)非就是2種:實(shí)線(xiàn)、虛線(xiàn)

所以我們可以這樣做:

static byte a = 0b00000001;

static byte b = 0b00000010;

static byte c = 0b00000100;

static byte d = 0b00001000;

byte x = 0b00000000;

定義a、b、c、d為static,并且使用最后的4位即可。

若我們想要改變a邊的實(shí)虛:

void changeA(boolean fullLine) {

if (fullLine) {

x = (byte) (x | a);

} else {

x = (byte) (x & ~a);

}

}

通過(guò)該方法,若a邊為實(shí)線(xiàn),則將a flag的值填入x中,反之擦除掉x中的a邊信息;同時(shí)保證其他信息不變。

若要拿,也就是判斷a是否為實(shí)線(xiàn)該如何辦?

boolean isFullLine() {

// return (x & a) != 0;

return (x & a) == a;

}

2種寫(xiě)法都是OK的,不過(guò)需要注意若對(duì)應(yīng)的a使用了符號(hào)位則需要使用0xFF先清理自動(dòng)補(bǔ)充的符號(hào)位。因?yàn)榕c、或、非等操作默認(rèn)會(huì)將參數(shù)轉(zhuǎn)化為int類(lèi)型進(jìn)行;所以會(huì)出現(xiàn)自動(dòng)補(bǔ)充符號(hào)位的情況。

這樣的操作方案在Android或Socket傳輸中都是非常常見(jiàn)的,比如Socket NIO中的SelectorKey中的ops變量就是這樣的機(jī)制;這能有效減少存儲(chǔ)多個(gè)參數(shù)的情況;并且位操作并不會(huì)帶來(lái)多少計(jì)算負(fù)擔(dān)。

了讓學(xué)習(xí)變得輕松、高效,今天給大家免費(fèi)分享一套Java入門(mén)教學(xué)資源。幫助大家在成為Java架構(gòu)師的道路上披荊斬棘。需要資料的歡迎加入學(xué)習(xí)交流群:9285,05736

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 「WTF系列」深入Java中的位操作 關(guān)于WTF系列 引 學(xué)完本章節(jié)你將學(xué)會(huì)位的基礎(chǔ)概念與語(yǔ)法,并且還會(huì)一些騷操作...
    qiujuer閱讀 1,073評(píng)論 0 5
  • 1.編譯程序(1)gcc xx.c,他會(huì)默認(rèn)生成一個(gè)a.out的可執(zhí)行文件,在a.out所在目錄,執(zhí)行./a.o...
    萌面大叔2閱讀 1,422評(píng)論 0 1
  • 進(jìn)制基本概念 什么是進(jìn)制?進(jìn)制是一種計(jì)數(shù)的方式,數(shù)值的表示形式 常見(jiàn)的進(jìn)制十進(jìn)制、二進(jìn)制、八進(jìn)制、十六進(jìn)制 進(jìn)制書(shū)...
    極客江南閱讀 2,176評(píng)論 0 11
  • 1.編譯程序 (1)gcc xx.c,他會(huì)默認(rèn)生成一個(gè)a.out的可執(zhí)行文件,在a.out所在目錄,執(zhí)行./a....
    萌面大叔2閱讀 575評(píng)論 0 1
  • 上一章 男子將事先擬好的合同交給林思凡看,示意她確認(rèn)無(wú)誤后便可簽字入住。林思凡滿(mǎn)腦子都是房租三千,根本來(lái)不及思考別...
    狐貍九閱讀 610評(píng)論 0 7

友情鏈接更多精彩內(nèi)容