Index

Operators and Expressions

Java Syntax

3.1 Arithmetic Operators

Java supports the basic arithmetic operators used to perform mathematical calculations on numeric values. These operators work on both integer and floating-point types.

Basic Arithmetic Operators

Operator Description Example Result
+ Addition 5 + 3 8
- Subtraction 10 - 4 6
* Multiplication 7 * 6 42
/ Division 20 / 5 4
% Modulus (remainder) 17 % 3 2

Sample Expressions

int a = 15;
int b = 4;

System.out.println(a + b);  // Output: 19
System.out.println(a - b);  // Output: 11
System.out.println(a * b);  // Output: 60
System.out.println(a / b);  // Output: 3 (integer division)
System.out.println(a % b);  // Output: 3 (remainder)

Division Behavior

int result = 7 / 2;
System.out.println(result);  // Output: 3

Here, 7 / 2 evaluates to 3, not 3.5, because both operands are integers.

double result = 7.0 / 2;
System.out.println(result);  // Output: 3.5

At least one operand must be a floating-point type (float or double) to get a precise result.

Click to view full runnable Code

public class ExpressionDemo {
    public static void main(String[] args) {
        // ===== Sample Expressions =====
        int a = 15;
        int b = 4;

        System.out.println("=== Basic Arithmetic ===");
        System.out.println("a + b = " + (a + b));  // 19
        System.out.println("a - b = " + (a - b));  // 11
        System.out.println("a * b = " + (a * b));  // 60
        System.out.println("a / b = " + (a / b));  // 3 (integer division)
        System.out.println("a % b = " + (a % b));  // 3 (remainder)

        // ===== Division Behavior =====
        System.out.println("\n=== Division Behavior ===");

        int intResult = 7 / 2;
        System.out.println("7 / 2 (int division) = " + intResult);  // 3

        double floatResult = 7.0 / 2;
        System.out.println("7.0 / 2 (floating-point) = " + floatResult);  // 3.5
    }
}

Reflection

Understanding how arithmetic operators work, especially division, is crucial to avoid subtle bugs. Using integers can unintentionally truncate results, so be mindful of operand types when performing division. The modulus operator % is also useful for tasks like checking even/odd numbers or wrapping values within a range. Mastering arithmetic operators is a foundational step in writing effective Java expressions.

Index

3.2 Assignment and Compound Assignment

In Java, the assignment operator = is used to store a value in a variable. Additionally, compound assignment operators combine arithmetic with assignment for more concise code.

Basic Assignment

int a = 10;  // Assigns 10 to variable a

Compound Assignment Operators

These operators perform an operation on the variable and assign the result back to it:

Operator Equivalent To Example Result
+= a = a + value a += 5; (if a=10) a = 15
-= a = a - value a -= 3; a = 7
*= a = a * value a *= 2; a = 20
/= a = a / value a /= 4; a = 5
%= a = a % value a %= 3; a = 2

Example:

int a = 10;
a += 5;     // a is now 15
a *= 2;     // a is now 30

Operator Chaining

You can assign multiple variables in one statement by chaining assignments right to left:

int a, b, c;
a = b = c = 5;
System.out.println(a + ", " + b + ", " + c);  // Output: 5, 5, 5

Here, c is assigned 5, then b is assigned c, and finally a is assigned b.

Click to view full runnable Code

public class AssignmentDemo {
    public static void main(String[] args) {
        // ===== Basic Assignment =====
        int a = 10;  // Assign 10 to a
        System.out.println("Basic Assignment: a = " + a);

        // ===== Compound Assignment Operators =====
        a += 5;  // a = a + 5
        System.out.println("After a += 5: a = " + a);  // 15

        a -= 3;  // a = a - 3
        System.out.println("After a -= 3: a = " + a);  // 12

        a *= 2;  // a = a * 2
        System.out.println("After a *= 2: a = " + a);  // 24

        a /= 4;  // a = a / 4
        System.out.println("After a /= 4: a = " + a);  // 6

        a %= 3;  // a = a % 3
        System.out.println("After a %= 3: a = " + a);  // 0

        // ===== Operator Chaining =====
        int x, y, z;
        x = y = z = 5;  // Assign 5 to z, then y = z, then x = y
        System.out.println("Chained Assignment: x = " + x + ", y = " + y + ", z = " + z);
    }
}

Reflection

Compound assignments help write shorter, cleaner code, especially for operations updating the same variable repeatedly. However, overusing chained assignments or compound operators in complex expressions can reduce clarity, making code harder to read and debug. Striking a balance between compactness and readability is key to maintainable Java code.

Index

3.3 Relational and Equality Operators

Java provides relational operators to compare values and return a boolean result (true or false). These are essential for decision-making in programs.

Relational Operators

Operator Meaning Example Result
< Less than 5 < 10 true
> Greater than 10 > 3 true
<= Less than or equal 7 <= 7 true
>= Greater than or equal 9 >= 12 false

These operators are commonly used with numeric primitives (int, double, etc.).

Equality Operators

Operator Meaning Example Result
== Equal to 5 == 5 true
!= Not equal to 5 != 10 true

For primitive types, == compares values directly. For example:

int a = 5, b = 5;
System.out.println(a == b);  // true

Comparing Objects (Strings)

For objects like String, == compares references (memory addresses), not the content. To compare the actual text, use .equals():

String s1 = new String("Java");
String s2 = new String("Java");

System.out.println(s1 == s2);         // false, different objects
System.out.println(s1.equals(s2));    // true, same content
Click to view full runnable Code

public class RelationalAndEqualityDemo {
    public static void main(String[] args) {
        // ===== Relational Operators =====
        int a = 5, b = 10, c = 7, d = 9;

        System.out.println("=== Relational Operators ===");
        System.out.println("a < b: " + (a < b));        // true
        System.out.println("b > a: " + (b > a));        // true
        System.out.println("c <= 7: " + (c <= 7));      // true
        System.out.println("d >= 12: " + (d >= 12));    // false

        // ===== Equality Operators (Primitive Types) =====
        int x = 5, y = 5, z = 10;

        System.out.println("\n=== Equality Operators (Primitives) ===");
        System.out.println("x == y: " + (x == y));      // true
        System.out.println("x != z: " + (x != z));      // true

        // ===== Comparing Objects (Strings) =====
        String s1 = new String("Java");
        String s2 = new String("Java");
        String s3 = "Java";
        String s4 = "Java";

        System.out.println("\n=== String Comparison ===");
        System.out.println("s1 == s2: " + (s1 == s2));               // false
        System.out.println("s1.equals(s2): " + s1.equals(s2));       // true

        System.out.println("s3 == s4: " + (s3 == s4));               // true (same string pool object)
        System.out.println("s3.equals(s4): " + s3.equals(s4));       // true
    }
}

Reflection

A common mistake is using == to compare objects, leading to unexpected false results even when contents match. Always use .equals() for object content comparison. For primitives, == works as expected. Understanding this distinction helps avoid subtle bugs in Java programs.

Index

3.4 Logical Operators (&&, ||, !)

Logical Operators (&&, ||, !)

Logical operators are used in Java to combine or invert boolean expressions, which is essential for controlling program flow in conditional statements.

Logical AND (&&)

Example:

boolean a = true;
boolean b = false;

System.out.println(a && b);  // Output: false
System.out.println(b && a);  // Output: false (second operand not evaluated)

Logical OR (||)

Example:

boolean a = true;
boolean b = false;

System.out.println(a || b);  // Output: true (second operand not evaluated)
System.out.println(b || a);  // Output: true

Logical NOT (!)

Example:

boolean a = false;
System.out.println(!a);  // Output: true

Short-Circuiting and Method Calls

Short-circuiting means that Java stops evaluating as soon as the result is known. This can affect method calls in conditions:

boolean checkA() {
    System.out.println("checkA called");
    return false;
}

boolean checkB() {
    System.out.println("checkB called");
    return true;
}

if (checkA() && checkB()) {
    System.out.println("Both true");
}
// Output:
// checkA called
// (checkB not called because checkA is false)

Because checkA() returns false, checkB() is never called.

Click to view full runnable Code

public class LogicalOperatorsDemo {

    public static void main(String[] args) {
        // === Logical AND (&&) ===
        boolean a = true;
        boolean b = false;

        System.out.println("=== Logical AND (&&) ===");
        System.out.println("a && b: " + (a && b));  // false
        System.out.println("b && a: " + (b && a));  // false (a not evaluated)

        // === Logical OR (||) ===
        System.out.println("\n=== Logical OR (||) ===");
        System.out.println("a || b: " + (a || b));  // true (b not evaluated)
        System.out.println("b || a: " + (b || a));  // true

        // === Logical NOT (!) ===
        boolean c = false;
        System.out.println("\n=== Logical NOT (!) ===");
        System.out.println("!c: " + (!c));  // true

        // === Short-circuiting and method calls ===
        System.out.println("\n=== Short-Circuiting with && ===");
        if (checkA() && checkB()) {
            System.out.println("Both returned true");
        }

        System.out.println("\n=== Short-Circuiting with || ===");
        if (checkA() || checkB()) {
            System.out.println("At least one returned true");
        }
    }

    static boolean checkA() {
        System.out.println("checkA called");
        return false;
    }

    static boolean checkB() {
        System.out.println("checkB called");
        return true;
    }
}

Reflection

Logical operators combined with short-circuiting allow efficient condition checks and prevent unnecessary method executions. Understanding this behavior helps avoid side effects and improves program performance. Use &&, ||, and ! thoughtfully in conditions to create clear, concise, and correct logic.

Index

3.5 Bitwise Operators (&, |, ^, ~)

Bitwise operators work at the binary level, manipulating individual bits of integer values. They are powerful tools for low-level programming tasks such as flags, permissions, and performance optimizations.

Common Bitwise Operators

Operator Description Example
& Bitwise AND 0b1100 & 0b1010
| Bitwise OR 0b1100 | 0b1010
^ Bitwise XOR (exclusive) 0b1100 ^ 0b1010
~ Bitwise NOT (complement) ~0b1100

Bit-Level Examples

Let's consider two 4-bit numbers:

int a = 0b1100;  // binary 1100 (decimal 12)
int b = 0b1010;  // binary 1010 (decimal 10)
Operation Binary Result Decimal Result
a & b (AND) 1000 8
a | b (OR) 1110 14
a ^ b (XOR) 0110 6
~a (NOT) ...0011 (depends on bit-width, e.g., -13 for 32-bit)

Truth Table for &, |, and ^

A B A & B A | B A ^ B
0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0
Click to view full runnable Code

public class BitwiseOperatorsDemo {
    public static void main(String[] args) {
        int a = 0b1100;  // 12 in decimal
        int b = 0b1010;  // 10 in decimal

        System.out.println("=== Bit-Level Values ===");
        System.out.println("a (0b1100): " + a + " => " + toBinary(a));
        System.out.println("b (0b1010): " + b + " => " + toBinary(b));

        // Bitwise AND
        int andResult = a & b;
        System.out.println("\nBitwise AND (a & b): " + andResult + " => " + toBinary(andResult));

        // Bitwise OR
        int orResult = a | b;
        System.out.println("Bitwise OR (a | b): " + orResult + " => " + toBinary(orResult));

        // Bitwise XOR
        int xorResult = a ^ b;
        System.out.println("Bitwise XOR (a ^ b): " + xorResult + " => " + toBinary(xorResult));

        // Bitwise NOT
        int notResult = ~a;
        System.out.println("Bitwise NOT (~a): " + notResult + " => " + toBinary(notResult));
    }

    // Helper method to display binary with 32 bits
    private static String toBinary(int value) {
        return String.format("%32s", Integer.toBinaryString(value)).replace(' ', '0');
    }
}

Reflection

Bitwise operators are niche but powerful, enabling efficient manipulation of individual bits. Common real-world uses include:

While often unnecessary in everyday Java programming, mastering bitwise operators opens up advanced capabilities for working with binary data and system-level tasks.

Index

3.6 Bit Shift Operators (>>, <<, >>>)

Bit shift operators move the bits of an integer value to the left or right, effectively multiplying or dividing the number by powers of two at the binary level. They are useful for optimization and low-level data manipulation.

Left Shift (<<)

Example:

int a = 0b0001_0101;  // decimal 21
int leftShift = a << 2; // shifts bits 2 places left
System.out.println(Integer.toBinaryString(leftShift));  // Output: 1010100 (decimal 84)

Right Shift (>>) Arithmetic Shift

Example:

int b = 0b1111_1000;  // decimal -8 (two's complement)
int rightShift = b >> 2;
System.out.println(Integer.toBinaryString(rightShift));  // Output: 11111110 (decimal -2)

Unsigned Right Shift (>>>) Logical Shift

Example:

int c = 0b1111_1000;  // decimal -8
int unsignedShift = c >>> 2;
System.out.println(Integer.toBinaryString(unsignedShift)); // Output: 00111111 (decimal 1073741821)
Click to view full runnable Code

public class ShiftOperatorsDemo {
    public static void main(String[] args) {
        // === Left Shift (<<) ===
        int a = 0b0001_0101;  // 21 in decimal
        int leftShift = a << 2;

        System.out.println("=== Left Shift (<<) ===");
        System.out.println("Original a        : " + toBinary(a) + " = " + a);
        System.out.println("a << 2 (mult by 4): " + toBinary(leftShift) + " = " + leftShift);

        // === Arithmetic Right Shift (>>) ===
        int b = (byte) 0b1111_1000;  // -8 in decimal (signed byte extended to int)
        int rightShift = b >> 2;

        System.out.println("\n=== Right Shift (>>) ===");
        System.out.println("Original b        : " + toBinary(b) + " = " + b);
        System.out.println("b >> 2            : " + toBinary(rightShift) + " = " + rightShift);

        // === Unsigned Right Shift (>>>) ===
        int c = (byte) 0b1111_1000;  // -8 again
        int unsignedShift = c >>> 2;

        System.out.println("\n=== Unsigned Right Shift (>>>) ===");
        System.out.println("Original c        : " + toBinary(c) + " = " + c);
        System.out.println("c >>> 2           : " + toBinary(unsignedShift) + " = " + unsignedShift);
    }

    // Helper: returns 32-bit binary string
    private static String toBinary(int value) {
        return String.format("%32s", Integer.toBinaryString(value)).replace(' ', '0');
    }
}

Reflection

Bit shift operators provide efficient ways to multiply or divide integers by powers of two, which is faster than arithmetic operations in some contexts. They are critical in low-level programming, such as encoding/decoding protocols, graphics, and cryptography. Understanding arithmetic vs. logical shifts is key when dealing with signed vs. unsigned data to avoid unexpected behavior. Mastery of these operators enables more control over binary data and optimized performance in Java programs.

Index

3.7 Unary Operators (++, --, +, -, !)

Unary operators operate on a single operand and are commonly used to modify or evaluate values quickly.

Unary Operators in Java

Prefix vs. Postfix ++ and --

Example:

int a = 5;
int x = ++a;  // a is incremented to 6, then x is assigned 6
int y = a++;  // y is assigned 6, then a is incremented to 7

Subtle Behavior in Expressions

Expressions combining prefix and postfix operators can be confusing:

int a = 3;
int result = ++a + a++;
// Step 1: ++a increments a to 4, then uses 4
// Step 2: a++ uses current a (4), then increments to 5
// result = 4 + 4 = 8
System.out.println(result);  // Output: 8
Click to view full runnable Code

public class UnaryOperatorsDemo {
    public static void main(String[] args) {
        // Basic unary operators
        int a = 5;
        System.out.println("Initial a: " + a);

        int x = ++a;  // prefix increment: a=6, x=6
        System.out.println("After ++a: a = " + a + ", x = " + x);

        int y = a++;  // postfix increment: y=6, a=7
        System.out.println("After a++: a = " + a + ", y = " + y);

        // Unary plus and minus
        int positive = +a;
        int negative = -a;
        System.out.println("Unary plus (+a): " + positive);
        System.out.println("Unary minus (-a): " + negative);

        // Logical NOT
        boolean flag = true;
        System.out.println("Original flag: " + flag);
        System.out.println("Logical NOT (!flag): " + !flag);

        // Subtle behavior in expressions combining prefix and postfix
        int b = 3;
        int result = ++b + b++;
        // ++b increments b to 4, returns 4
        // b++ returns 4, then increments to 5
        // result = 4 + 4 = 8
        System.out.println("b after expression (++b + b++): " + b);
        System.out.println("Result of ++b + b++: " + result);
    }
}

Reflection

Understanding the difference between prefix and postfix is crucial for writing clear, bug-free code. Misusing them in complex expressions can lead to unexpected results and maintenance challenges. It's often best to keep increments and decrements on separate lines for clarity. Unary operators remain essential tools for concise value changes and logical negation in Java.

Index

3.8 Operator Precedence and Associativity

Understanding operator precedence and associativity is essential for predicting how Java evaluates complex expressions without parentheses.

Operator Precedence

Precedence determines the order in which operators are evaluated in an expression. Operators with higher precedence are evaluated before operators with lower precedence.

Example without parentheses:

int result = 5 + 3 * 2;  // Multiplication (*) has higher precedence than addition (+)
System.out.println(result);  // Output: 11

Here, 3 * 2 is evaluated first, then added to 5.

Using Parentheses

Parentheses can override the default precedence to specify the intended order explicitly:

int result = (5 + 3) * 2;  // Parentheses force addition first
System.out.println(result);  // Output: 16

Operator Associativity

Associativity determines the order in which operators of the same precedence are evaluated:

Example:

int a = 10;
int b = 5;
int c = 2;
int result = a - b - c;  // Evaluated as (a - b) - c
System.out.println(result);  // Output: 3
Click to view full runnable Code

public class OperatorPrecedenceDemo {
    public static void main(String[] args) {
        // Operator precedence: multiplication before addition
        int result1 = 5 + 3 * 2;  // 3 * 2 = 6, then 5 + 6 = 11
        System.out.println("5 + 3 * 2 = " + result1);

        // Using parentheses to override precedence
        int result2 = (5 + 3) * 2;  // (5 + 3) = 8, then 8 * 2 = 16
        System.out.println("(5 + 3) * 2 = " + result2);

        // Operator associativity: left to right for subtraction
        int a = 10, b = 5, c = 2;
        int result3 = a - b - c;  // (10 - 5) - 2 = 3
        System.out.println("10 - 5 - 2 = " + result3);

        // Right-associative example: assignment operators
        int x, y, z;
        x = y = z = 100;  // Right to left: z=100, y=100, x=100
        System.out.println("x = " + x + ", y = " + y + ", z = " + z);
    }
}

Operator Precedence Table

Java's operator precedence table is available in official documentation and reference sites, showing exact precedence and associativity for all operators. This table is vital for writing and understanding complex expressions correctly.

Reflection

While Java follows clear rules for precedence and associativity, relying solely on these can make code hard to read and error-prone. Using parentheses enhances readability and prevents subtle bugs by making the order of evaluation explicit. Always prioritize clarity in your expressions to produce maintainable and reliable Java code.

Index