Skip to main content

Ch 3.6 Other Operators

Beyond the arithmetic, comparison, logical, and assignment operators covered earlier, we will explore the ternary operator, bitwise operators, and instanceof operator that are frequently used in practice. Bitwise operators are especially useful in performance-critical system programming and flag management.


1. Ternary Operatorโ€‹

The ternary operator is the only operator in Java with three operands. It can express a simple if-else on a single line.

Syntax:

condition ? value_if_true : value_if_false
public class TernaryBasic {
public static void main(String[] args) {
int score = 85;

// if-else approach
String result1;
if (score >= 80) {
result1 = "Pass";
} else {
result1 = "Fail";
}

// Ternary operator approach (exactly equivalent to above)
String result2 = (score >= 80) ? "Pass" : "Fail";

System.out.println(result1); // Pass
System.out.println(result2); // Pass

// Returning a number
int a = 10, b = 20;
int max = (a > b) ? a : b;
System.out.println("Max: " + max); // 20

// Absolute value
int n = -15;
int abs = (n >= 0) ? n : -n;
System.out.println("Absolute value: " + abs); // 15
}
}

When to Use Ternary vs if-elseโ€‹

SituationRecommendation
Simple value selection (fits on one line)Ternary operator
Complex logic, multiple statementsif-else
Requires 2+ levels of nestingif-else
Includes method calls or exception handlingif-else

Nested Ternary and Readability Issuesโ€‹

public class NestedTernary {
public static void main(String[] args) {
int score = 75;

// Bad: nested ternary (very poor readability)
String grade = (score >= 90) ? "A" :
(score >= 80) ? "B" :
(score >= 70) ? "C" :
(score >= 60) ? "D" : "F";
System.out.println("Grade: " + grade); // C (works but...)

// Good: clearly expressed with if-else
String grade2;
if (score >= 90) grade2 = "A";
else if (score >= 80) grade2 = "B";
else if (score >= 70) grade2 = "C";
else if (score >= 60) grade2 = "D";
else grade2 = "F";
System.out.println("Grade: " + grade2); // C
}
}
Guidelines for using the ternary operator

The ternary operator is most effective for simple binary choices. If more than 2 options or nesting is needed, use if-else or switch.


2. Bitwise Operators โ€” Complete Guideโ€‹

Bitwise operators operate on integers at the binary (bit) level. They are very fast and memory-efficient, used in system programming, encryption, and flag management.

OperatorNameDescription
&AND1 only when both bits are 1
|OR1 when at least one bit is 1
^XOR1 when bits differ
~NOTInverts all bits (0->1, 1->0)
<<Left ShiftShift bits left by n positions (ร— 2โฟ)
>>Right ShiftShift bits right by n positions (รท 2โฟ), preserves sign
>>>Unsigned Right ShiftShift right by n, fills vacated bits with 0
public class BitwiseBasic {
public static void main(String[] args) {
int a = 0b1010; // 10
int b = 0b1100; // 12

System.out.printf("a = %4s (%d)%n", Integer.toBinaryString(a), a);
System.out.printf("b = %4s (%d)%n", Integer.toBinaryString(b), b);
System.out.println();
System.out.printf("a & b = %4s (%d)%n", Integer.toBinaryString(a & b), a & b); // 1000 = 8
System.out.printf("a | b = %4s (%d)%n", Integer.toBinaryString(a | b), a | b); // 1110 = 14
System.out.printf("a ^ b = %4s (%d)%n", Integer.toBinaryString(a ^ b), a ^ b); // 0110 = 6
System.out.printf("~a = %s (%d)%n", Integer.toBinaryString(~a), ~a); // -11
}
}

3. Bitwise AND for Checking Specific Bits (Masking)โ€‹

Use bitwise AND with a mask to check whether a specific bit is 1 or 0.

public class BitMasking {
public static void main(String[] args) {
int value = 0b10110101; // 181

// Check bit 0 (LSB): odd/even check
System.out.println("Bit 0: " + ((value & 0b00000001) != 0)); // true (odd)

// Check bit 2
System.out.println("Bit 2: " + ((value & 0b00000100) != 0)); // true

// Check bit 3
System.out.println("Bit 3: " + ((value & 0b00001000) != 0)); // false

// Extract specific bits (mask & shift combination)
int rgb = 0xFF8040; // R=255, G=128, B=64
int red = (rgb >> 16) & 0xFF;
int green = (rgb >> 8) & 0xFF;
int blue = rgb & 0xFF;
System.out.printf("R=%d, G=%d, B=%d%n", red, green, blue); // R=255, G=128, B=64
}
}

4. Bitwise OR for Setting Flagsโ€‹

Use bitwise OR to manage multiple flags (options) as a single integer.

public class BitOrFlag {
public static void main(String[] args) {
// Represent each permission as a bit (powers of 2)
final int READ = 0b0001; // 1
final int WRITE = 0b0010; // 2
final int EXECUTE = 0b0100; // 4
final int ADMIN = 0b1000; // 8

// Set flags: combine multiple permissions with OR
int userPerms = READ | WRITE; // 0011 = 3
int adminPerms = READ | WRITE | EXECUTE | ADMIN; // 1111 = 15

System.out.println("User permissions: " + Integer.toBinaryString(userPerms)); // 11
System.out.println("Admin permissions: " + Integer.toBinaryString(adminPerms)); // 1111

// Add permission: set bit with OR
userPerms |= EXECUTE; // Add EXECUTE permission
System.out.println("After adding: " + Integer.toBinaryString(userPerms)); // 111

// Check permission: test bit with AND
System.out.println("Has WRITE: " + ((userPerms & WRITE) != 0)); // true
System.out.println("Has ADMIN: " + ((userPerms & ADMIN) != 0)); // false

// Remove permission: clear bit with AND NOT
userPerms &= ~WRITE; // Remove WRITE permission
System.out.println("After removing: " + Integer.toBinaryString(userPerms)); // 101
}
}

5. Bitwise XOR: Toggle and Simple Encryptionโ€‹

public class BitXor {
public static void main(String[] args) {
// Toggle specific bits with XOR
int flags = 0b1010;
int toggleMask = 0b0110;

System.out.println("Original: " + Integer.toBinaryString(flags));
flags ^= toggleMask;
System.out.println("Toggled: " + Integer.toBinaryString(flags)); // 1100
flags ^= toggleMask;
System.out.println("Restored: " + Integer.toBinaryString(flags)); // 1010

// XOR encryption idea (simple example)
String message = "Hello";
int key = 0x42; // Encryption key

// Encrypt: apply XOR to each character
char[] encrypted = new char[message.length()];
for (int i = 0; i < message.length(); i++) {
encrypted[i] = (char)(message.charAt(i) ^ key);
}
System.out.println("Encrypted: " + new String(encrypted));

// Decrypt: apply the same key again with XOR (XOR is its own inverse)
char[] decrypted = new char[encrypted.length];
for (int i = 0; i < encrypted.length; i++) {
decrypted[i] = (char)(encrypted[i] ^ key);
}
System.out.println("Decrypted: " + new String(decrypted)); // Hello
}
}

6. Shift Operators: <<, >>, >>>โ€‹

public class ShiftOperator {
public static void main(String[] args) {
int n = 8; // 0000 1000

// Left Shift (<<): n * 2^k
System.out.println(n << 1); // 16 (8 * 2^1 = 16)
System.out.println(n << 2); // 32 (8 * 2^2 = 32)
System.out.println(n << 3); // 64 (8 * 2^3 = 64)

// Right Shift (>>): n / 2^k (preserves sign)
System.out.println(n >> 1); // 4 (8 / 2^1 = 4)
System.out.println(n >> 2); // 2 (8 / 2^2 = 2)
System.out.println(n >> 3); // 1 (8 / 2^3 = 1)

// Negative with >> (sign bit copied, arithmetic shift)
int neg = -8;
System.out.println(neg >> 1); // -4 (sign preserved)
System.out.println(neg >> 2); // -2
System.out.println(neg >>> 1); // 2147483644 (sign ignored, filled with 0)

// Shift is faster than multiplication/division (for optimization)
int x = 100;
int doubled = x << 1; // x * 2 = 200
int halved = x >> 1; // x / 2 = 50
System.out.println("Doubled: " + doubled + ", Halved: " + halved);
}
}

Difference Between >> and >>>โ€‹

public class SignedUnsignedShift {
public static void main(String[] args) {
int negative = -1;
// -1 in binary: 1111 1111 1111 1111 1111 1111 1111 1111

// >> (Signed): fills vacated bits with sign bit (1) -> remains -1
System.out.println(negative >> 1); // -1 (11111111...1111)

// >>> (Unsigned): always fills vacated bits with 0 -> positive number
System.out.println(negative >>> 1); // 2147483647 (01111111...1111)

// Use case: average of two integers (avoids overflow)
int a = Integer.MAX_VALUE;
int b = Integer.MAX_VALUE - 1;
// int wrong = (a + b) / 2; // Overflow occurs!
int correct = (a + b) >>> 1; // Unsigned right shift prevents overflow
System.out.println("Safe average: " + correct); // 2147483646
}
}
Practical use of >>>

Java's standard library classes like HashMap and Arrays.sort() internally use >>> for midpoint calculation. When a + b might overflow, use the pattern (a + b) >>> 1.


7. instanceof Operatorโ€‹

instanceof checks whether an object is an instance of a specific class or interface. It returns true or false.

public class InstanceofBasic {
public static void main(String[] args) {
Object obj = "Hello";

System.out.println(obj instanceof String); // true
System.out.println(obj instanceof Integer); // false
System.out.println(obj instanceof Object); // true (all objects are instances of Object)

// null always returns false with instanceof
String s = null;
System.out.println(s instanceof String); // false (no NPE)

// instanceof with polymorphism
Object num = Integer.valueOf(42);
if (num instanceof Integer) {
int value = (Integer) num; // Explicit cast
System.out.println("Integer value: " + value); // 42
}
}
}

Pattern Matching instanceof (Java 16+)โ€‹

Since Java 16, pattern matching combines instanceof with variable declaration. It handles type checking and casting in a single step.

public class PatternMatchingInstanceof {
public static void main(String[] args) {
Object[] items = {"Hello", 42, 3.14, true, null};

for (Object item : items) {
// Pre-Java 16 approach
if (item instanceof String) {
String s = (String) item; // Explicit cast required
System.out.println("String (length " + s.length() + "): " + s);
}
// Java 16+ pattern matching (type check + cast at once)
else if (item instanceof Integer i) {
System.out.println("Integer: " + i);
} else if (item instanceof Double d) {
System.out.printf("Double: %.2f%n", d);
} else if (item instanceof Boolean b) {
System.out.println("Boolean: " + b);
} else {
System.out.println("null or unknown type");
}
}
}
}

Output:

String (length 5): Hello
Integer: 42
Double: 3.14
Boolean: true
null or unknown type

8. String + Operator and StringBuilder Performanceโ€‹

Concatenating strings with + inside a loop creates a new String object every iteration, causing significant performance degradation.

public class StringConcatPerformance {
public static void main(String[] args) {
int ITERATIONS = 10_000;

// Method 1: String + (slow - new object created every iteration)
long start1 = System.currentTimeMillis();
String s = "";
for (int i = 0; i < ITERATIONS; i++) {
s += i; // New String object created each iteration
}
long time1 = System.currentTimeMillis() - start1;

// Method 2: StringBuilder (fast - appends to internal buffer)
long start2 = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < ITERATIONS; i++) {
sb.append(i); // Appends to the same object's buffer
}
String result = sb.toString();
long time2 = System.currentTimeMillis() - start2;

System.out.println("String + : " + time1 + "ms");
System.out.println("StringBuilder: " + time2 + "ms");
System.out.println("Same length: " + (s.length() == result.length())); // true
}
}
String Concatenation Guidelines
  • Simple concatenation (compile-time constants): String s = "Hello" + " " + "World"; -> compiler automatically optimizes to "Hello World". OK.
  • Concatenation inside loops: Use StringBuilder (important for performance).
  • Multi-threaded environments: Use StringBuffer (thread-safe).

9. Practical Example: User Permission System with Bit Flagsโ€‹

public class PermissionSystem {

// Permission flag constants (bit positions)
static final int PERM_READ = 1 << 0; // 0001 = 1
static final int PERM_WRITE = 1 << 1; // 0010 = 2
static final int PERM_DELETE = 1 << 2; // 0100 = 4
static final int PERM_ADMIN = 1 << 3; // 1000 = 8

// Predefined roles
static final int ROLE_GUEST = PERM_READ; // Read only
static final int ROLE_EDITOR = PERM_READ | PERM_WRITE; // Read + Write
static final int ROLE_MANAGER = PERM_READ | PERM_WRITE | PERM_DELETE; // Read + Write + Delete
static final int ROLE_ADMIN = PERM_READ | PERM_WRITE | PERM_DELETE | PERM_ADMIN; // All

// Check permission
static boolean hasPermission(int userPerms, int requiredPerm) {
return (userPerms & requiredPerm) != 0;
}

// Grant permission
static int grantPermission(int userPerms, int perm) {
return userPerms | perm;
}

// Revoke permission
static int revokePermission(int userPerms, int perm) {
return userPerms & ~perm;
}

// Toggle permission
static int togglePermission(int userPerms, int perm) {
return userPerms ^ perm;
}

// Print permissions
static void printPermissions(String name, int perms) {
System.out.println("[" + name + "] Permissions (binary: " + String.format("%4s", Integer.toBinaryString(perms)).replace(' ', '0') + ")");
System.out.println(" Read: " + (hasPermission(perms, PERM_READ) ? "O" : "X"));
System.out.println(" Write: " + (hasPermission(perms, PERM_WRITE) ? "O" : "X"));
System.out.println(" Delete: " + (hasPermission(perms, PERM_DELETE) ? "O" : "X"));
System.out.println(" Admin: " + (hasPermission(perms, PERM_ADMIN) ? "O" : "X"));
System.out.println();
}

public static void main(String[] args) {
// Set initial permissions
int alicePerms = ROLE_EDITOR; // Read + Write
int bobPerms = ROLE_GUEST; // Read only
int carolPerms = ROLE_ADMIN; // All

printPermissions("Alice (Editor)", alicePerms);
printPermissions("Bob (Guest)", bobPerms);
printPermissions("Carol (Admin)", carolPerms);

// Grant: add Write permission for Bob
System.out.println(">>> Granting Write permission to Bob");
bobPerms = grantPermission(bobPerms, PERM_WRITE);
printPermissions("Bob (Upgraded)", bobPerms);

// Revoke: remove Write permission from Alice
System.out.println(">>> Revoking Write permission from Alice");
alicePerms = revokePermission(alicePerms, PERM_WRITE);
printPermissions("Alice (Downgraded)", alicePerms);

// Access control with ternary operator
System.out.println("=== File Delete Access Control ===");
String[] users = {"Alice", "Bob", "Carol"};
int[] perms = {alicePerms, bobPerms, carolPerms};

for (int i = 0; i < users.length; i++) {
String access = hasPermission(perms[i], PERM_DELETE) ? "Allowed" : "Denied";
System.out.println(users[i] + " delete access: " + access);
}
}
}

Output:

[Alice (Editor)] Permissions (binary: 0011)
Read: O
Write: O
Delete: X
Admin: X

[Bob (Guest)] Permissions (binary: 0001)
Read: O
Write: X
Delete: X
Admin: X

[Carol (Admin)] Permissions (binary: 1111)
Read: O
Write: O
Delete: O
Admin: O

>>> Granting Write permission to Bob
[Bob (Upgraded)] Permissions (binary: 0011)
Read: O
Write: O
Delete: X
Admin: X

>>> Revoking Write permission from Alice
[Alice (Downgraded)] Permissions (binary: 0001)
Read: O
Write: X
Delete: X
Admin: X

=== File Delete Access Control ===
Alice delete access: Denied
Bob delete access: Denied
Carol delete access: Allowed

10. Key Summaryโ€‹

Operator/ConceptDescriptionPrimary Use
? : (ternary)Select value based on conditionReplace simple if-else
& (bitwise AND)Keep only common bitsCheck specific bit (masking)
| (bitwise OR)Combine bitsSet flags
^ (bitwise XOR)Only differing bits are 1Toggle, encryption
~ (bitwise NOT)Invert all bitsRemove flag (& ~mask)
<<Left shift (ร— 2โฟ)Fast power-of-2 multiplication
>>Right shift (รท 2โฟ), preserves signFast division
>>>Right shift, fills with 0Overflow-safe midpoint calculation
instanceofType checkType branching in polymorphic code
Bitwise Operator Tips for Production

Java's EnumSet is a type-safe and more readable alternative to raw bit flags. Directly managing flags with bitwise operations is a C/C++ style; in Java, prefer EnumSet. However, for extremely performance-sensitive scenarios like hardware control or network packet processing, bitwise operations are still very useful.