Java 位運 算 符
位 運 算 符 用 來(lái) 對 二 進(jìn)制 位 進(jìn) 行 操 作 ,Java中提 供 了 如 下所 示 的 位 運 算符 :
位 運 算 符 (>>,<<,>>>,&,|,^,~) ,位運 算 符 中 ,除 ~ 以 外 ,其余 均 為 二 元 運 算 符 。 操 作 數 只 能 為 整 型 和字 符 型 數 據 。
基礎知識
補碼
所有的整數類(lèi)型(除了char類(lèi)型之外)都是有符號的整數。這意味著(zhù)他們既能表示正數,又能表示負數。Java使用 補 碼 來(lái) 表 示 二 進(jìn) 制 數 ,在補 碼 表 示 中 ,最高 位 為 符號 位 ,正數 的 符 號 位 為 0,負數 為 1。補 碼 的 規 定 如 下 :
對 正 數 來(lái) 說(shuō) ,最高 位 為 0,其余 各 位 代 表 數 值 本 身 (以二 進(jìn)制 表 示 ),如+42的補碼 為 00101010。
對 負 數 而 言 ,把該 數 絕 對 值 的 補 碼 按 位 取 反 ,然后 對 整 個(gè)數 加 1,即得 該 數的 補 碼 。 如 -42的補 碼 為 11010110 (00101010按 位 取 反 11010101+1=11010110 )
用 補 碼 來(lái) 表 示 數,0的補 碼 是 唯 一 的 ,都為 00000000。(而在 原碼 ,反碼 表 示中 ,+0和-0的表 示 是 不 唯 一 的 ,可參 見(jiàn) 相 應 的 書(shū) 籍 )。而 且 可 以用 111111表示 -1的補 碼 (這也 是 補 碼 與 原 碼 和 反 碼 的 區 別 )。
類(lèi)型長(cháng)度
整 型
整型常 量 在 機 器 中 占32位,具有 int型的 值 ,對于 long型值 ,則要在 數 字 后 加 L或l,如123L表示 一 個(gè) 長(cháng) 整 數 ,它在 機 器 中 占 64位。整 型 變 量 的 類(lèi) 型 有 byte、short、int、long四種 。 下面 列 出各 類(lèi) 型 所 在 內 存 的 位 數 和 其表 示 范 圍 。
數據類(lèi)型 描述 所占位數
Integers
byte Byte-length integer 8-bit two‘s complement
short Short integer 16-bit two‘s complement
int Integer 32-bit two‘s complement
long
Long integer 64-bit two‘s complement
Realnumbers
float Single-precision floating point 32-bit IEEE 754
double Double-precision floating point 64-bit IEEE 754
Othertypes
char A single character 16-bit Unicode character
boolean A boolean value (true or false) true or false
int類(lèi)型 是 最 常 使 用 的 一 種 整 數 類(lèi) 型 。 它 所 表 示的 數 據 范 圍 足 夠 大 ,而且 適 合 于 32位、64位處 理 器 。 但 對 于 大 型 計 算 ,常會(huì ) 遇 到 很 大 的 整 數 ,超出 int類(lèi)型 所 表 示 的 范 圍 ,這時(shí) 要 使 用long類(lèi)型 。
由 于 不 同 的 機 器 對 于 多字 節 數 據 的 存 儲 方 式 不 同 ,可能 是 從 低 字 節 向 高 字 節 存 儲 ,也
可能 是 從 高 字 節 向 低 字 節 存 儲 ,這樣 ,在分 析 網(wǎng) 絡(luò ) 協(xié) 議 或 文 件 格 式 時(shí) ,為了 解 決 不 同 機 器 上
的字 節 存 儲 順 序 問(wèn) 題 ,用byte類(lèi)型 來(lái) 表 示 數 據 是 合 適 的 。 而 通 常 情 況 下,由于 其 表 示 的 數 據
范圍 很 小 ,容易 造 成 溢 出 ,應避 免 使 用 。
short類(lèi) 型 則 很 少 使 用 ,它限 制 數 據 的 存 儲 為 先 高 字 節 ,后低 字 節 ,這樣 在 某 些 機 器 中 會(huì )出錯 。
整型 變 量 的 定 義 ,如:
byteb; //指定變量b為byte型
shorts; //指定變量s為short型
inti; //指定變量i為int型
longl; //指定變量l為long型
浮 點(diǎn) 型 (實(shí)型 )數據
實(shí)型 變 量 的 類(lèi) 型 有 float和double兩種 ,下表 列 出 這 兩 種 類(lèi) 型 所 占 內 存 的 位 數 和 其 表示 范
圍。
數據類(lèi)型 所占位數 數的范圍
float 32 3.4e-038~3.4e+038
double 64 1.7e-308~1.7e+308
雙精 度 類(lèi) 型 double比單 精 度 類(lèi) 型 float具有 更 高 的 精 度 和 更 大 的 表 示 范 圍 ,常常 使 用 。
(三)實(shí)型 變 量 定 義 ,如
floatf; //指 定 變 量 f為float型
doubled; //指 定 變 量 d為double型
[注]與C、C++不同 ,Java中沒(méi) 有 無(wú) 符 號 型 整 數 ,而且 明 確 規 定 了 整 型 和 浮 點(diǎn) 型 數 據 所 占 的
內存 字 節 數 ,這樣 就 保 證 了 安 全 性 、 魯 棒 性 和 平 臺 無(wú) 關(guān) 性。
Java 位運算符
Java 定義的位運算(bitwiseoperators )直接對整數類(lèi)型的位進(jìn)行操作,這些整數類(lèi)型包括long,int,hort,char,andbyte 。表4-2列出了位運算:
| 運算符 | 結果 |
| ~ | 按位非(NOT)(一元運算) |
| & | 按位與(AND) |
| | | 按位或(OR) |
| ^ | 按位異或(XOR) |
| >> | 右移 |
| >>> | 右移,左邊空出的位以0填充 ;無(wú)符號右移 |
| << | 左移 |
| &= | 按位與賦值 |
| |= | 按位或賦值 |
| ^= | 按位異或賦值 |
| >>= | 右移賦值 |
| >>>= | 右移賦值,左邊空出的位以0填充 ;無(wú)符號左移 |
| <<= | 左移賦值 |
詳細解釋
按位非(NOT)
按位非也叫做補,一元運算符NOT“~”是對其運算數的每一位取反。例如,數字42,它的二進(jìn)制代碼為:
00101010
經(jīng)過(guò)按位非運算成為
11010101
按位與(AND)
按位與運算符“&”,如果兩個(gè)運算數都是1,則結果為1。其他情況下,結果均為零??聪旅娴睦樱?
00101010 42 &00001111 15
00001010 10
按位或(OR)
按位或運算符“|”,任何一個(gè)運算數為1,則結果為1。如下面的例子所示:
00101010 42 | 00001111 15
00101111 47
按位異或(XOR)
按 位異或運算符“^”,只有在兩個(gè)比較的位不同時(shí)其結果是 1。否則,結果是零。下面的例子顯示了“^”運算符的效果。這個(gè)例子也表明了XOR 運算符的一個(gè)有用的屬性。注意第二個(gè)運算數有數字1的位,42對應二進(jìn)制代碼的對應位是如何被轉換的。第二個(gè)運算數有數字0的位,第一個(gè)運算數對應位的數 字不變。當對某些類(lèi)型進(jìn)行位運算時(shí),你將會(huì )看到這個(gè)屬性的用處。
00101010 42 ^ 00001111 15
00100101 37
位邏輯運算符的應用
下面的例子說(shuō)明了位邏輯運算符:
// Demonstrate the bitwise logical operators.
class BitLogic {
public static void main(String args[]) {
String binary[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"
};
int a = 3; // 0 + 2 + 1 or 0011 in binary
int b = 6; // 4 + 2 + 0 or 0110 in binary
int c = a | b;
int d = a & b;
int e = a ^ b;
int f = (~a & b) | (a & ~b);
int g = ~a & 0x0f;
System.out.println(" a = " + binary[a]);
System.out.println(" b = " + binary[b]);
System.out.println(" a|b = " + binary[c]);
System.out.println(" a&b = " + binary[d]);
System.out.println(" a^b = " + binary[e]);
System.out.println("~a&b|a&~b = " + binary[f]);
System.out.println(" ~a = " + binary[g]);
}
}
在 本例中,變量a與b對應位的組合代表了二進(jìn)制數所有的 4 種組合模式:0-0,0-1,1-0 ,和1-1 。“|”運算符和“&”運算符分別對變量a與b各個(gè)對應位的運算得到了變量c和變量d的值。對變量e和f的賦值說(shuō)明了“^”運算符的功能。字符串 數組binary 代表了0到15 對應的二進(jìn)制的值。在本例中,數組各元素的排列順序顯示了變量對應值的二進(jìn)制代碼。數組之所以這樣構造是因為變量的值n對應的二進(jìn)制代碼可以被正確的存儲 在數組對應元素binary[n] 中。例如變量a的值為3,則它的二進(jìn)制代碼對應地存儲在數組元素binary[3] 中。~a的值與數字0x0f (對應二進(jìn)制為0000 1111 )進(jìn)行按位與運算的目的是減小~a的值,保證變量g的結果小于16。因此該程序的運行結果可以用數組binary 對應的元素來(lái)表示。該程序的輸出如下:
a = 0011 b = 0110 a|b = 0111 a&b = 0010 a^b = 0101 ~a&b|a&~b = 0101 ~a = 1100
左移運算符
左移運算符<<使指定值的所有位都左移規定的次數。它的通用格式如下所示:
value << num
這 里,num 指定要移位值value 移動(dòng)的位數。也就是,左移運算符<<使指定值的所有位都左移num位。每左移一個(gè)位,高階位都被移出(并且丟棄),并用0填充右邊。這意味著(zhù) 當左移的運算數是int 類(lèi)型時(shí),每移動(dòng)1位它的第31位就要被移出并且丟棄;當左移的運算數是long 類(lèi)型時(shí),每移動(dòng)1位它的第63位就要被移出并且丟棄。
在對byte 和short類(lèi)型的值進(jìn)行移位運算時(shí),你必須小心。因為你知道Java 在對表達式求值時(shí),將自動(dòng)把這些類(lèi)型擴大為 int 型,而且,表達式的值也是int 型。對byte 和short類(lèi)型的值進(jìn)行移位運算的結果是int 型,而且如果左移不超過(guò)31位,原來(lái)對應各位的值也不會(huì )丟棄。但是,如果你對一個(gè)負的byte 或者short類(lèi)型的值進(jìn)行移位運算,它被擴大為int 型后,它的符號也被擴展。這樣,整數值結果的高位就會(huì )被1填充。因此,為了得到正確的結果,你就要舍棄得到結果的高位。這樣做的最簡(jiǎn)單辦法是將結果轉換為 byte 型。下面的程序說(shuō)明了這一點(diǎn):
// Left shifting a byte value.
class ByteShift {
public static void main(String args[]) {
byte a = 64, b;
int i;
i = a << 2;
b = (byte) (a << 2);
System.out.println("Original value of a: " + a);
System.out.println("i and b: " + i + " " + b);
}
}
該程序產(chǎn)生的輸出下所示:
Original value of a: 64
i and b: 256 0
因變量a在賦值表達式中,故被擴大為int 型,64(0100 0000 )被左移兩次生成值256 (10000 0000 )被賦給變量i。然而,經(jīng)過(guò)左移后,變量b中惟一的1被移出,低位全部成了0,因此b的值也變成了0。
既然每次左移都可以使原來(lái)的操作數翻倍,程序員們經(jīng)常使用這個(gè)辦法來(lái)進(jìn)行快速的2 的乘法。但是你要小心,如果你將1移進(jìn)高階位(31或63位),那么該值將變?yōu)樨撝?/font>。下面的程序說(shuō)明了這一點(diǎn):
// Left shifting as a quick way to multiply by 2.
class MultByTwo {
public static void main(String args[]) {
int i;
int num = 0xFFFFFFE;
for(i=0; i<4; i++) {
num = num << 1;
System.out.println(num);
}
}
該程序的輸出如下所示:
536870908
1073741816
2147483632
-32
初值經(jīng)過(guò)仔細選擇,以便在左移 4 位后,它會(huì )產(chǎn)生-32。正如你看到的,當1被移進(jìn)31 位時(shí),數字被解釋為負值。
右移運算符
右移運算符>>使指定值的所有位都右移規定的次數。它的通用格式如下所示:
value >> num
這里,num 指定要移位值value 移動(dòng)的位數。也就是,右移運算符>>使指定值的所有位都右移num位。下面的程序片段將值32右移2次,將結果8賦給變量a:
int a = 32;
a = a >> 2; // a now contains 8
當值中的某些位被“移出”時(shí),這些位的值將丟棄。例如,下面的程序片段將35右移2 次,它的2個(gè)低位被移出丟棄,也將結果8賦給變量a:
int a = 35;
a = a >> 2; // a still contains 8
用二進(jìn)制表示該過(guò)程可以更清楚地看到程序的運行過(guò)程:
00100011 35
>> 2
00001000 8
將值每右移一次,就相當于將該值除以2并且舍棄了余數。你可以利用這個(gè)特點(diǎn)將一個(gè)整數進(jìn)行快速的2的除法。當然,你一定要確保你不會(huì )將該數原有的任何一位移出。
右 移時(shí),被移走的最高位(最左邊的位)由原來(lái)最高位的數字補充。例如,如果要移走的值為負數,每一次右移都在左邊補1,如果要移走的值為正數,每一次右移都 在左邊補0,這叫做符號位擴展(保留符號位)(sign extension ),在進(jìn)行右移操作時(shí)用來(lái)保持負數的符號。例如,–8 >> 1 是–4,用二進(jìn)制表示如下:
11111000 –8 >>1 11111100 –4
一個(gè)要注意的有趣問(wèn)題是,由于符號位擴展(保留符號位)每次都會(huì )在高位補1,因此-1右移的結果總是–1。有時(shí)你不希望在右移時(shí)保留符號。例如,下面的例子將一個(gè)byte 型的值轉換為用十六進(jìn)制表示。注意右移后的值與0x0f進(jìn)行按位與運算,這樣可以舍棄任何的符號位擴展,以便得到的值可以作為定義數組的下標,從而得到對應數組元素代表的十六進(jìn)制字符。
// Masking sign extension.
class HexByte {
static public void main(String args[]) {
char hex[] = {
’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’,
’8’, ’9’, ’a’, ’b’, ’c’, ’d’, ’e’, ’f’’
};
byte b = (byte) 0xf1;
System.out.println("b = 0x" + hex[(b >> 4) & 0x0f] + hex[b & 0x0f]);
}
}
該程序的輸出如下:
b = 0xf1
無(wú)符號右移
正 如上面剛剛看到的,每一次右移,>>運算符總是自動(dòng)地用它的先前最高位的內容補它的最高位。這樣做保留了原值的符號。但有時(shí)這并不是我們想要 的。例如,如果你進(jìn)行移位操作的運算數不是數字值,你就不希望進(jìn)行符號位擴展(保留符號位)。當你處理像素值或圖形時(shí),這種情況是相當普遍的。在這種情況 下,不管運算數的初值是什么,你希望移位后總是在高位(最左邊)補0。這就是人們所說(shuō)的無(wú)符號移動(dòng)(unsigned shift )。這時(shí)你可以使用Java 的無(wú)符號右移運算符>>> ,它總是在左邊補0。
下面的程序段說(shuō)明了無(wú)符號右移運算符>>> 。在本例中,變量a被賦值為-1,用二進(jìn)制表示就是32位全是1。這個(gè)值然后被無(wú)符號右移24位,當然它忽略了符號位擴展,在它的左邊總是補0。這樣得到的值255被賦給變量a。
int a = -1; a = a >>> 24;
下面用二進(jìn)制形式進(jìn)一步說(shuō)明該操作:
11111111 11111111 11111111 11111111 int型-1的二進(jìn)制代碼>>> 24 無(wú)符號右移24位00000000 00000000 00000000 11111111 int型255的二進(jìn)制代碼
由 于無(wú)符號右移運算符>>> 只是對32位和64位的值有意義,所以它并不像你想象的那樣有用。因為你要記住,在表達式中過(guò)小的值總是被自動(dòng)擴大為int 型。這意味著(zhù)符號位擴展和移動(dòng)總是發(fā)生在32位而不是8位或16位。這樣,對第7位以0開(kāi)始的byte 型的值進(jìn)行無(wú)符號移動(dòng)是不可能的,因為在實(shí)際移動(dòng)運算時(shí),是對擴大后的32位值進(jìn)行操作。下面的例子說(shuō)明了這一點(diǎn):
// Unsigned shifting a byte value.
class ByteUShift {
static public void main(String args[]) {
int b = 2;
int c = 3;
a |= 4;
b >>= 1;
c <<= 1;
a ^= c;
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
}
}
該程序的輸出如下所示:
a = 3
b = 1
c = 6
對于java移位運算的總結:
1. 對于左移運算,每左移一個(gè)位,高階位都被移出(并且丟棄),并用0填充右邊。這意味著(zhù)當左移的運算數是int 類(lèi)型時(shí),每移動(dòng)1位,它的第31位就要被移出并且丟棄;當左移的運算數是long 類(lèi)型時(shí),每移動(dòng)1位它的第63位就要被移出并且丟棄。
2. 左移都可以使原來(lái)的操作數翻倍,程序員們經(jīng)常使用這個(gè)辦法來(lái)進(jìn)行快速的2 的乘法。但是你要小心,如果你將1移進(jìn)高階位(31或63位),那么該值將變?yōu)樨撝?/span>。
3. 在對byte 和short類(lèi)型的值進(jìn)行移位運算時(shí) , Java將自動(dòng)把這些類(lèi)型擴大為 int 型,而且,移位后的值也是int 型;如果左移不超過(guò)31位,原來(lái)對應各位的值不會(huì )丟棄。但是,如果你對一個(gè)負的byte 或者short類(lèi)型的值進(jìn)行移位運算,它被擴大為int 型后,它的符號也被擴展,結果的高位就會(huì )被1填充。因此,為了得到正確的結果,你就要舍棄得到結果的高位。這樣做的最簡(jiǎn)單辦法是將移位運算的結果再轉換成byte 型。
4. 每右移一次,就相當于將該值除以2并且舍棄了余數。你可以利用這個(gè)特點(diǎn)將一個(gè)整數進(jìn)行快速的2的除法。當然,你一定要確保你不會(huì )將該數原有的任何一位移出。
5. 無(wú)符號右移(>>>)與右移的區別:
(1) 每一次右移,>>運算符總是自動(dòng)地用它的先前最高位的內容補它的最高位。這樣做保留了原值的符號
(2) 無(wú)符號移動(dòng)總是在高位(最左邊)補0。
6. 與C、C++不同,Java中沒(méi)有無(wú)符號型整數,而且明確規定了整型和浮點(diǎn)型數據所占的內存字節數,這樣就保證了安全性、魯棒性和平臺無(wú)關(guān)性。
人們通常習慣使用十進(jìn)制數,而計算機內部多采用二進(jìn)制表示和處理數值數據,因此在計算機輸入和輸出數據時(shí),就要進(jìn)行由十進(jìn)制到二進(jìn)制的轉換處理。
把十進(jìn)制數的每一位分別寫(xiě)成二進(jìn)制形式的編碼,稱(chēng)為二進(jìn)制編碼的十進(jìn)制數,即二到十進(jìn)制編碼或BCD(Binary Coded Decimal)編碼。
BCD碼編碼方法很多,通常采用8421編碼,這種編碼方法最自然簡(jiǎn)單。其方法使用四位二進(jìn)制數表示一位十進(jìn)制數,從左到右每一位對應的權分別是23、22、21、20,即8、4、2、1。例如十進(jìn)制數1975的8421碼可以這樣得出
1975(D)=0001 1001 0111 0101(BCD)
在計算機中,對非數值的文字和其他符號進(jìn)行處理時(shí),首先要對其進(jìn)行數字化處理,即用二進(jìn)制編碼來(lái)表示文字和符號。字符編碼就是以二進(jìn)制的數字來(lái)對應字符集的字符,目前用得最普遍的字符集是ANSI,對應ANSI字符集的二進(jìn)制編碼就稱(chēng)為ANSI碼,DOS和Windows系統都使用了ANSI碼。在輸入過(guò)程中,系統自動(dòng)將用戶(hù)輸入的各種數據按編碼的類(lèi)型轉換成相應的二進(jìn)制形式存入計算機存儲單元中;在輸出過(guò)程中,再由系統自動(dòng)將二進(jìn)制編碼數據轉換成用戶(hù)可以識別的數據格式輸出給用戶(hù)。
聯(lián)系客服