Bitwise operators defined by Java performs operations on digits of the integer type directly. These integer types include long, Int, short, Char, and byte. Table 4-2 lists bitwise operations:
Table 4.2-bit operators and their results
Operator result
~ Bitwise non (not) (mona1 Operation)
& Bitwise AND (and)
| Bitwise OR (OR)
^ XOR)
> Right shift
>>> Right shift: Fill in the blank places on the left with 0
Operator result
<Move left
& = Bitwise AND value assignment
| = By bit or value
^ = By-bit variance or value assignment
>>= Right shift value
>>>= Shifts the value to the right, and the blank places on the left are filled with 0
<= Left shift assignment
Continue table
Since bitwise operators perform bitwise operations within the Integer Range, it is important to understand the effect of such operations on a value. Specifically, it is useful to know how Java stores integers and represents negative numbers. Therefore, before continuing the discussion, let's give a brief overview of these two topics.
All Integer types are represented by changes in binary digits and their widths. For example, the binary code of byte type 42 is 00101010, where each location represents the power of 2, and the rightmost bit starts with 20. The next position to the left will be 21, or 2, and then 22, or 4 to the left, then 8, 16, 32, and so on. Therefore, the value of 42 at the position 1, 3, and 5 is 1 (starting from 0 on the right). In this way, 42 is the sum of 21 + 23 + 25, that is, 2 + 8 + 32.
All Integer types (except char) are signed integers. This means they can both represent positive numbers and negative numbers. Java uses the code of two's complement (known as 2's complement) to represent a negative number, that is, it is to reverse the binary code of the corresponding positive number (that is, 1 is changed to 0, convert 0 to 1), and then add 1 to the result. For example,-42 is to get 00101010 for every bit of the binary code of 42, that is, get 11010101 for 11010110, and then add 1 to get, that is,-42. To decode a negative number, first obtain the inverse of all its bits, and then add 1. For example,-42, or 11010110 is reversed to 00101001, or 41, and then 1 is added, then 42 is obtained.
If zero crossing is taken into account, you can easily understand why Java (and most other languages) uses the 2's complement. Assume that the value of the byte type is represented by 00000000. Its Complement code is to reverse each of its bits, that is, to generate 11111111, which indicates negative zero. But the problem is that negative zero is invalid in integer mathematics. To solve the negative zero problem, add 1 to the negative value when the 2's complement code is used. That is, the value after adding a negative zero 11111111 and 1 is 100000000. In this way, the first digit is too left-aligned and is not suitable for returning the value of the byte type. Therefore, it is stipulated that the expression of-0 and 0 is the same, and the decoding of-1 is 11111111. Although we use byte type values in this example, the same basic principle applies to all Java integer types.
Because Java uses the 2's complement code to store negative numbers, and because all integers in Java are signed, applying bitwise operators can easily achieve unexpected results. For example, no matter what you plan, Java uses a high level to represent a negative number. To avoid this annoying accident, remember that, regardless of the order of high positions, it determines the symbol of an integer.
4.2.1 bit logical operators
Bitwise logical operators include "and", "or" (OR), "XOR", and "not )", use "&", "|", "^", and "~" Table 4-3 shows the result of each bit logical operation. Before proceeding, remember to apply the bitwise operator to each individual bitwise in each operation number.
Table 4-3 logical operators
A 0 1 0 1 B 0 0 1 A | B 0 1 1 1 1 A & B 0 0 0 1 A ^ B 0 1 0 ~ A 1 0 1 0
Non-(not) by bit)
Bitwise is also called complement. The unary operator not "~" Returns the inverse of each bit of the number of operations. For example, if the number is 42, its binary code is:
00101010
After bitwise non-calculation
11010101
Bitwise AND (and)
Bitwise AND operator "&". If the number of both operations is 1, the result is 1. In other cases, the result is zero. See the following example:
00101010 42 & 00001111 15
00001010 10
Bitwise OR (OR)
Bitwise OR operator "|". If any operation is 1, the result is 1. The following is an example:
00101010 42 | 00001111 15
00101111 47
Bitwise exclusive or (XOR)
Bitwise XOR or operator "^", the result is 1 only when two comparison bits are different. Otherwise, the result is zero. The following example shows the effect of the "^" operator. This example also shows a useful property of the XOR operator. Note that the second operation number has a digit of 1, and 42 corresponds to the corresponding bit of binary code. The second operation has a digit of 0, and the number corresponding to the first operation does not change. When bitwise operations are performed on some types, you will see the usefulness of this attribute.
00101010 42 ^ 00001111 15
00100101 37
Application of bit logical operators
The following example illustrates the bit logical operators:
// 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/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) | (&~ 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 | &~ B = "+ binary [f]);
System. Out. println ("~ A = "+ binary [g]);
}
}
In this example, the combination of the corresponding bits of variable A and variable B represents the four combination modes of binary numbers: 0---0, and 1-1. The "|" operator and the "&" operator calculate the corresponding bits of variable A and variable B to obtain the values of variable C and variable D. The assignment of variables e and f illustrates the functions of the "^" operator. The string array binary represents the binary value between 0 and 15. In this example, the binary code of the values corresponding to the variables is displayed in the order of the elements in the array. The array is constructed in this way because the binary code corresponding to the value n of the variable can be correctly stored in the binary [N] element of the array. For example, if the value of variable A is 3, its binary code is stored in the array element binary [3 .~ The value of a and the number 0x0f (corresponding to binary 0000) are bitwise AND calculated to reduce ~ A value to ensure that the result of variable G is smaller than 16. Therefore, the running result of this program can be represented by elements corresponding to the array binary. The output of this program is as follows:
A = 0011 B = 0110 A | B = 0111 A & B = 0010 a ^ B = 0101 ~ A & B | &~ B = 0101 ~ A = 1100
4.2.2 left shift operator
Shift left operator <indicates the number of times that all bits of the specified value are moved left. Its common format is as follows:
Value <num
Here, num specifies the number of digits to move the value. That is, the Left shift operator <shifts all bits of the specified value to the num bits left. Each time one bit is moved to the left, the higher bit is removed (and discarded), and 0 is used to fill the right. This means that when the number of operations to move left is int type, every 31st bits to move one bit will be removed and discarded; when the number of operations to move left is long type, every time one bit is moved, its 63rd bits will be removed and discarded.
Be careful when performing shift operations on values of the byte and short types. Because you know that Java will automatically extend these types to int type when evaluating expressions, and the expression value is also int type. The result of the shift operation on values of the byte and short types is int type. If the left shift does not exceed 31 bits, the original values will not be discarded. However, if you perform a shift operation on a negative byte or short value, after it is extended to the int type, its symbols are also extended. In this way, the result of the integer value is filled with 1. Therefore, in order to get the correct result, you must discard the high level of the result. The simplest way to do this is to convert the result to the byte type. The following program illustrates this:
// 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:" + );
System. Out. println ("I and B:" + I + "" + B );
}
}
The output of this program is as follows:
Original value of A: 64
I and B: 256 0
The variable A is extended to the int type in the value assignment expression. 64 (0100 0000) is shifted left twice and the generated value 256 (10000 0000) is assigned to the variable I. However, after the Left shift, the only 1 in variable B is removed, and the low position is all 0, so the value of B is also changed to 0.
Since each left shift can double the original operand, programmers often use this method to quickly multiply 2. But be careful, if you move 1 to a higher-order bit (31 or 63 digits), the value will change to a negative value. The following program illustrates this:
// Left shifting as a quick way to multiply by 2.
Class multbytwo {
Public static void main (string ARGs []) {
Int I;
Int num = 0 xffffffe;
For (I = 0; I <4; I ++ ){
Num = num <1;
System. Out. println (Num );
}
}
Here, num specifies the number of digits to move the value. That is, the Left shift operator <shifts all bits of the specified value to the num bits left. Each time one bit is moved to the left, the higher bit is removed (and discarded), and 0 is used to fill the right. This means that when the number of operations to move left is int type, every 31st bits to move one bit will be removed and discarded; when the number of operations to move left is long type, every time one bit is moved, its 63rd bits will be removed and discarded.
Be careful when performing shift operations on values of the byte and short types. Because you know that Java will automatically extend these types to int type when evaluating expressions, and the expression value is also int type. The result of the shift operation on values of the byte and short types is int type. If the left shift does not exceed 31 bits, the original values will not be discarded. However, if you perform a shift operation on a negative byte or short value, after it is extended to the int type, its symbols are also extended. In this way, the result of the integer value is filled with 1. Therefore, in order to get the correct result, you must discard the high level of the result. The simplest way to do this is to convert the result to the byte type. The following program illustrates this:
// 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:" + );
System. Out. println ("I and B:" + I + "" + B );
}
}
The output of this program is as follows:
Original value of A: 64
I and B: 256 0
The variable A is extended to the int type in the value assignment expression. 64 (0100 0000) is shifted left twice and the generated value 256 (10000 0000) is assigned to the variable I. However, after the Left shift, the only 1 in variable B is removed, and the low position is all 0, so the value of B is also changed to 0.
Since each left shift can double the original operand, programmers often use this method to quickly multiply 2. But be careful, if you move 1 to a higher-order bit (31 or 63 digits), the value will change to a negative value. The following program illustrates this:
// Left shifting as a quick way to multiply by 2.
Class multbytwo {
Public static void main (string ARGs []) {
Int I;
Int num = 0 xffffffe;
For (I = 0; I <4; I ++ ){
Num = num <1;
System. Out. println (Num );
}
}
}
The program output is as follows:
536870908
1073741816
2147483632
-32
The initial value is carefully selected so that it will generate-32 after four digits are shifted to the left. As you can see, when 1 is moved to 31 bits, the number is interpreted as a negative value.
4.2.3 right shift operator
Right Shift Operator> specifies the number of times that all bits of the specified value are shifted to the right. Its common format is as follows:
Value> num
Here, num specifies the number of digits to move the value. That is, the right shift operator> shifts all bits of the specified value to the right. The following program segment shifts the value 32 to the right twice and assigns result 8 to variable:
Int A = 32;
A = A> 2; // a now contains 8
When some bits in the value are "removed", the values of these bits are discarded. For example, the following program fragment shifts 35 twice to the right, and its two low positions are removed and discarded, and result 8 is also assigned to variable:
Int A = 35;
A = A> 2; // a still contains 8
The following code uses binary to indicate the running process of the program:
00100011 35
> 2
00001000 8
Moving the value to the right is equivalent to dividing the value by 2 and dropping the remainder. You can use this feature to quickly divide an integer by two. Of course, you must ensure that you do not remove any of the original digits of the number.
When the right shift is performed, the highest bit (leftmost bit) to be removed is supplemented by the number of the original highest bit. For example, if the value to be removed is a negative number, each right shift is supplemented with 1 on the left. If the value to be removed is a positive number, each right shift is supplemented with 0 on the left, this is called the sign extension (retain the sign bit), which is used to maintain a negative number during the right shift operation. For example, if-8> 1 is-4, the binary representation is as follows:
11111000-8> 1 11111100-4
An interesting problem to note is that because the symbol bit extension (reserve the symbol bit) fills in 1 at a high position each time, the result of-1 right shift is always-1. Sometimes you do not want to retain the symbol when moving the right. For example, the following example converts a byte value
In hexadecimal notation. Pay attention to the bitwise and operation of the value after right shift and 0x0f, so that you can discard any symbol-bit extension so that the obtained value can be used as the subscript of the defined array, in this way, the hexadecimal characters represented by the corresponding array elements are obtained.
// 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]);}
The output of this program is as follows:
B = 0xf1
4.2.4 unsigned right shift
As we can see above, every right shift,> the operator always automatically uses its previous highest bit content to fill in its highest bit. In this way, the original value symbol is retained. But sometimes this is not what we want. For example, if the number of shifts is not a numeric value, you do not want to extend the number of signs (reserve the number of signs ). This situation is quite common when you process pixel values or graphs. In this case, no matter what the initial value of the number of operations is, you want to add 0 at the highest (leftmost) position after the shift. This is what people call unsigned shift ). In this case, you can use the unsigned right shift operator of Java >>> to always add 0 to the left.
The following program section describes the unsigned right shift operator >>>. In this example, variable A is assigned a value of-1, which is expressed in binary as 32 bits and all values are 1. This value is then moved to the right by the unsigned 24 bits. Of course, it ignores the extension of the symbol bit and always fills in 0 on its left. The value 255 is assigned to variable.
Int A =-1; A = A >>> 24;
The following describes the operation in binary format:
11111111 11111111 11111111 11111111 int-1 binary code >>> 24 unsigned shifts right 24-bit 00000000 00000000 00000000 11111111 int 255 binary code
Because the unsigned right shift operator >>> is only meaningful to 32-bit and 64-bit values, it is not as useful as you think. Because you must remember that the value that is too small in the expression is always automatically extended to the int type. This means that the extension and movement of the symbol bit always occur in 32 bits instead of 8 bits or 16 bits. In this way, it is impossible to perform the unsigned movement of the 7th-bit byte value starting with 0, because in the actual moving operation, the expanded 32-bit value is operated. The following example illustrates this:
// Unsigned shifting a byte value.
Class byteushift {
Static public void main (string ARGs []) {
In hexadecimal notation. Pay attention to the bitwise and operation of the value after right shift and 0x0f, so that you can discard any symbol-bit extension so that the obtained value can be used as the subscript of the defined array, in this way, the hexadecimal characters represented by the corresponding array elements are obtained.
// 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]);}
The output of this program is as follows:
B = 0xf1
4.2.4 unsigned right shift
As we can see above, every right shift,> the operator always automatically uses its previous highest bit content to fill in its highest bit. In this way, the original value symbol is retained. But sometimes this is not what we want. For example, if the number of shifts is not a numeric value, you do not want to extend the number of signs (reserve the number of signs ). This situation is quite common when you process pixel values or graphs. In this case, no matter what the initial value of the number of operations is, you want to add 0 at the highest (leftmost) position after the shift. This is what people call unsigned shift ). In this case, you can use the unsigned right shift operator of Java >>> to always add 0 to the left.
The following program section describes the unsigned right shift operator >>>. In this example, variable A is assigned a value of-1, which is expressed in binary as 32 bits and all values are 1. This value is then moved to the right by the unsigned 24 bits. Of course, it ignores the extension of the symbol bit and always fills in 0 on its left. The value 255 is assigned to variable.
Int A =-1; A = A >>> 24;
The following describes the operation in binary format:
11111111 11111111 11111111 11111111 int-1 binary code >>> 24 unsigned shifts right 24-bit 00000000 00000000 00000000 11111111 int 255 binary code
Because the unsigned right shift operator >>> is only meaningful to 32-bit and 64-bit values, it is not as useful as you think. Because you must remember that the value that is too small in the expression is always automatically extended to the int type. This means that the extension and movement of the symbol bit always occur in 32 bits instead of 8 bits or 16 bits. In this way, it is impossible to perform the unsigned movement of the 7th-bit byte value starting with 0, because in the actual moving operation, the expanded 32-bit value is operated. The following example illustrates this:
// 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 =" + );
System. Out. println ("B =" + B );
System. Out. println ("c =" + C );
}
}
The program output is as follows:
A = 3
B = 1
C = 6