I can think of 3 reasons to do bit-shift operations:
One, as an alternate way to do integer multiplication and division. Left shifting 1 place is equivalent to multiplying by 2; right shifting 1 place is equivalent to dividing by 2. I rarely do this because it makes the code harder to read and understand in order to gain a minor performance improvement. Some compilers are smart enough to do this for you if you are multiplying or dividing by a constant, in which case the gain is zero. The only time I use this technique these days is in something that is very computation-intensive. If I was writing a math library with lots of evaluation of many terms of an infinite series -- like evaluating logs and sines -- I'd probably use something like this. Otherwise, in my humble opinion it's just not worth the price of confusing the next programmer to come along.
Two, In conjunction with ANDs and ORs, as a way to pack multiple logical fields into a single field. Say you have a field whose range of possible values is 0-3 and another which is 0-7. The first could fit in 2 bits and the second in 3. So you could pack both into a single byte. You could put them in with code like:
byte b=(f1<<3) | f2;
You could get them out with:
f2=b & 0x07;
f1=(b>>3) & 0x03;
In the Good Old Days when I worked on computers with 6kB of RAM, I did this sort of thing a lot to cram all the data I needed into memory. Today, when I have a gigabyte on my desktop and multiple gigabytes on the server, the extra complexity of doing this -- read "more places to make mistakes" -- makes it not worth it. If you're writing embedded code for a cell phone or some other very memory-constrained environment, maybe you still need to do stuff like this. I suppose if you're working on a project where you need some truly huge amount of data in memory -- if you're analyzing a human genome or something -- this could be necessary. For the most part, though, knowing how to do this is like knowing how to use a slide rule: an interesting historical relic of little practical value today.
Three, converting between data types when reading from or writing to disk, or communicating with another system with different data storage formats. For example, suppose you want to write a 4-byte binary number to disk. You know that some computers that read this file store integers with the most significant byte first, while others store it with the least significant byte first. To make sure that both can read the data correctly, you must store it and retrieve it in a consistent way. So you pick one byte order and force it to store that way. Like:
for (int p=0;p<4;++p)
{
byte b=i & 0xff;
out.write(b);
i=i>>8;
}
This is about the only time I use bit-shifting these days.