Java Bitweise Operatoren
Einzelne Bits in Java manipulieren mit &, |, ^, ~, <<, >> und >>> Bitweise-Operatoren.
Der größte Teil des Java-Codes arbeitet nicht mit einzelnen Bits. Aber hin und wieder — beim Verpacken von Flags in ein int, beim Lesen eines binären Dateiformats, beim Berechnen eines Hashs oder beim Arbeiten mit Berechtigungs-Bitmasks — müssen Sie Werte auf Bit-Ebene manipulieren. Javas bitweise Operatoren sind der C-ähnliche Satz: &, |, ^, ~ und die drei Shifts. Sie unterscheiden sich von den logischen Operatoren && und ||, die kurzschließen und nur auf boolean-Werten funktionieren — bitweise Operatoren wirken auf jedes Bit einer ganzen Zahl.
Die Operatoren
| Operator | Name | Was er tut |
|---|---|---|
& | AND | Bit ist 1 nur wenn beide Bits 1 sind |
| | OR | Bit ist 1 wenn eines der Bits 1 ist |
^ | XOR | Bit ist 1 wenn die beiden Bits unterschiedlich sind |
~ | NOT (Komplement) | Dreht jedes Bit um |
<< | Linksverschiebung | Verschiebt Bits nach links, füllt rechts mit 0 |
>> | Vorzeichenbehaftete Rechtsverschiebung | Verschiebt nach rechts, füllt links mit dem Vorzeichenbit |
>>> | Vorzeichenlose Rechtsverschiebung | Verschiebt nach rechts, füllt links mit 0 |
Alle operieren auf int- und long-Operanden. byte, short und char werden zuerst zu int hochgestuft.
Binäre Literale (0b...) machen die Bit-Muster leicht erkennbar:
int a = 0b1100; // 12
int b = 0b1010; // 10
System.out.println(Integer.toBinaryString(a & b)); // 1000 (8)
System.out.println(Integer.toBinaryString(a | b)); // 1110 (14)
System.out.println(Integer.toBinaryString(a ^ b)); // 110 (6)Beachten Sie, dass Integer.toBinaryString führende Nullen weglässt — 6 wird als 110 ausgegeben, nicht als 0110. Wenn Sie eine feste Breite zur Anzeige benötigen, müssen Sie selbst auffüllen.
NOT — ~
~ dreht jedes Bit um, einschließlich des Vorzeichenbits. Für 32-Bit int bedeutet das Zweierkomplement: ~x entspricht -x - 1:
System.out.println(~0); // -1
System.out.println(~5); // -6
System.out.println(~-1); // 0Shifts
<< verschiebt nach links und multipliziert mit Zweierpotenzen:
System.out.println(1 << 0); // 1
System.out.println(1 << 1); // 2
System.out.println(1 << 4); // 16>> verschiebt nach rechts und bewahrt das Vorzeichen — nützlich für die Division vorzeichenbehafteter ganzer Zahlen:
System.out.println(16 >> 2); // 4
System.out.println(-16 >> 2); // -4 — sign extended>>> verschiebt nach rechts und füllt immer mit null auf — sinnvoll, wenn Sie ein int als vorzeichenlose Bits behandeln:
System.out.println(-1 >>> 28); // 15
System.out.println(-1 >> 28); // -1Praktische Anwendungen
Flag-Bitmasks
Mehrere Ja/Nein-Flags in einem einzigen int verpacken:
final int READ = 1 << 0; // 0001
final int WRITE = 1 << 1; // 0010
final int EXECUTE = 1 << 2; // 0100
int perms = READ | WRITE; // set both
boolean canRead = (perms & READ) != 0; // true
boolean canExecute = (perms & EXECUTE) != 0; // false
perms |= EXECUTE; // grant execute
perms &= ~WRITE; // revoke write
perms ^= READ; // toggle readDas ist dieselbe Idee wie Unix-Dateiberechtigungen.
Multiplizieren oder Dividieren durch Zweierpotenzen
x << n entspricht x * 2ⁿ; x >> n entspricht x / 2ⁿ (für nicht-negative x):
int doubled = x << 1;
int halved = x >> 1;Der Compiler optimiert in der Regel einfache Multiplikation und Division durch konstante Zweierpotenzen selbständig zu Shifts, also schreiben Sie, was klarer ist.
Zwei ints ohne temporäre Variable tauschen
Ein klassischer XOR-Trick:
int a = 5, b = 3;
a ^= b;
b ^= a;
a ^= b;
System.out.println(a + " " + b); // 3 5Nett, aber selten lohnenswert gegenüber einer temporären Variable — moderne Compiler behandeln den temporären Fall problemlos.
Eine Demonstration
Wann diese vs. EnumSet verwenden
Für eine kleine feste Menge von Flags in modernem Java ist EnumSet<MyFlag> in der Regel klarer und genauso effizient — intern speichert es Enum-Werte als einzelne long-Bitmask, sodass Sie die Lesbarkeit von Set<MyFlag> mit bitweise-schnellen Operationen erhalten:
enum Permission { READ, WRITE, EXECUTE }
EnumSet<Permission> perms = EnumSet.of(Permission.READ, Permission.WRITE);
perms.add(Permission.EXECUTE);
perms.contains(Permission.READ); // trueGreifen Sie auf rohe Bit-Operationen nur zurück, wenn Sie mit binären Formaten, Hardware-Registern oder kritischen Pfaden arbeiten, bei denen int-Packing wichtig ist.
Was kommt als Nächstes
Java Strings — der Referenztyp, mit dem Sie mehr als mit jedem anderen arbeiten werden.