Ja, vermutlich ist das der Hauptgrund. % ist deutlich teurer als &. Ganz allgemein kann man sagen, dass die Operationen + und -, aber insbesondere *, / und % eben (u.U. deutlich effizienter) abgebildet werden können, indem man sie durch Bit-Operationen nachbaut - was natürlich nur funktioniert, wenn die Operanden bestimmte Zweierpotenzen sind. Zusätzlich braucht man natürlich für die Berechnung der Zweierpotenzen selbst die Shift-Operatoren - das würde man ja nicht mit Math.pow machen.
Hab' da vor einiger Zeit eine hübsche Übersichtsseite mit
Bit Twiddling Hacks gefunden - da sieht man was man damit alles machen kann. Dabei sind auch solche Klassiker: Bei OpenGL müssen Texturgrößen manchmal Zweierpotenzen sein - wie kann man feststellen, ob eine Zahl eine Zweierpotenz ist?
if ((a & (a-1)) == 0) ja();
Bei solchen Methoden wie "Integer.toString(...)" wird in der Methode "getChars(...)" z.B. auch sowas gemacht:
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
Und ich vermute mal, dass man sich solche Krämpfe nicht antun würde, wenn es sich in bezug auf die Performance nicht lohnen würde.
Dazu kommt natürlich noch das nächstliegende: Bits sind die kompakteste Form, viele boolsche Werte auf einmal zu speichern - z.B. die Modifiers bei MouseEvents (SHIFT_DOWN, CONTROL_DOWN usw.) die mit |= kombiniert und mit (a&X)!=0 abgefragt werden können.