Having had to use unsigned bytes for the first time, I also had to learn how Java references these datatypes. I did some research, and found a number of good guides out there but was generally dissatisfied with the approach they took to learn the subject matter.
Unsigned Data Types
Java data types are all signed* and are all in Big Endian byte order. An unsigned byte is represented by the following 8 bits:
0 0 0 0 0 0 0 0
128 64 32 16 8 4 2 1
The range of values that can be represented is 0 through to 255. A number like 56 is represented as:
0 0 1 1 1 0 0 0
128 64 32 16 8 4 2 1
However a signed byte uses its Most Significant Bit (MSB) to control the the sign of the number. Java uses a technique known as Two's Complement to perform arithmetic on the numbers. The result of using the MSB as a sign bit is that is changes the range of numbers to -128 through to 127. We don't need to understand how Two's Complement to understand that it changes the byte as follows:
0 0 0 0 0 0 0 0
+/- 64 32 16 8 4 2 1
Note: Both unsigned and signed are just ways of representing a byte. A byte has meaning in either representation.
Conversion to unsigned byte
To convert a number into an unsigned byte, you have to use a larger data type to store the number. So take for example a value 183, must be represented by a data type larger than a byte. Any of the following, short, int, long or double will do. In this case we'll use an integer as all binary operators work with integers which makes them convenient to use.
int i = 183;
00000000 00000000 00000000 10110111 = signed int 183
Then we cast the integer to a byte. This effectively chops the bits at the 8th bit.
byte b = (byte)i;
writeAByteToDisk( b );
10110111 = unsigned byte 183
signed byte -73
This byte can then be written out to file, or network channel in the case that you are interacting with an application that expects an unsigned byte.
Conversion from unsigned byte
Converting from an unsigned byte into an integer we first cast the byte to an integer. This cast takes place with sign extension where by the Most Significant Bit represents the sign of the value and is duplicated up until it forms an integer.
byte b = readAByteFromDisk();
int i = (int)b;
11011011 = unsigned byte 219
^ signed byte -37
|_________ Most Significant Bit
11111111 11111111 11111111 11011011 = signed int -37
^
|____________________________________ extended to make integer
Then we apply a bit mask using the binary AND operator. This effectively masks out the sign extension that has taken place in the cast to an integer.
int i = 0x000000FF & i;
11111111 11111111 11111111 11011011 = signed int -37
00000000 00000000 00000000 11111111 = Mask of 0x000000FF
00000000 00000000 00000000 11011011 = Result of & operation
We use the Hex number 0x000000FF (255) as the mask becuase this will cover the first 8 bits of the integer and the area we are interested in. The entire function can be shorted to a more conscise statement. A byte value, when used with the & operator automatically casts the byte to an integer. The Hex value of 0x000000FF can be represented by 0xFF (the 0's are added automatically up to the size of the integer).
int i = 0xFF & readAByteFromDisk();
Related sites