Navigation
Java Full Course: Mastering the Language Operators & Expressions
Operators & Expressions
1. Introduction to Operators and Expressions
In the realm of programming, a program is essentially a set of instructions that manipulate data to produce a desired output. At the heart of this data manipulation lie two fundamental concepts: Operators and Expressions. They form the core vocabulary of any programming language, allowing us to perform computations, make decisions, and control the flow of a program.
More formally:
- An Operator is a symbol or a keyword that tells the compiler or interpreter to perform a specific mathematical, logical, or relational operation. Common examples include
+(addition),-(subtraction),*(multiplication),==(equality check), and&&(logical AND). - An Expression is a combination of variables, constants, literals, operators, and function calls that evaluates to a single value. For instance,
a + b,x > y, andprice * quantity - discountare all expressions.
The power of programming languages stems from their rich set of operators and the flexibility with which they can be combined into expressions. From simple arithmetic calculations to complex logical conditions that determine which part of a program executes, operators and expressions are the fundamental building blocks.
2. Arithmetic Operators in Java
Arithmetic operators are used to perform mathematical operations on numeric data types (byte, short, int, long, float, double) and char (which is treated as a numeric type in arithmetic contexts). They form the foundation for calculations in Java programs.
2.1 List of Arithmetic Operators
Java provides the following arithmetic operators:
| Operator | Name | Type | Description |
|---|---|---|---|
| + | Addition | Binary | Adds two operands |
| - | Subtraction | Binary | Subtracts right operand from left |
| * | Multiplication | Binary | Multiplies two operands |
| / | Division | Binary | Divides left operand by right |
| % | Modulus | Binary | Returns remainder of division |
| ++ | Increment | Unary | Increases value by 1 |
| -- | Decrement | Unary | Decreases value by 1 |
| + | Unary plus | Unary | Indicates positive value (rarely used) |
| - | Unary minus | Unary | Negates the value |
2.2 Binary Arithmetic Operators (+, -, *, /, %)
Binary operators work on two operands.
a) Addition (+)
- Adds numbers.
- If either operand is a
String, it performs string concatenation instead (this is a special case, not arithmetic). - Example:
b) Subtraction (-)
- Subtracts the right operand from the left.
- Example:
c) Multiplication (*)
- Multiplies two operands.
- Example:
d) Division (/)
- Integer division: When both operands are integers, the result is an integer (truncated toward zero). Fractional part is discarded.
- Floating-point division: If at least one operand is
floatordouble, the result is a floating-point value. - Example:
[!WARNING] Division by zero:
- Integer division by zero throws
ArithmeticException. - Floating-point division by zero results in
InfinityorNaN(Not-a-Number).
e) Modulus (%)
- Returns the remainder after division.
- Works with integers and floating-point numbers.
- Sign of the result follows the sign of the dividend (left operand).
- Example:
2.3 Unary Arithmetic Operators
Unary operators operate on a single operand.
a) Unary Plus (+) and Unary Minus (-)
- Unary plus simply returns the operand's value (rarely used).
- Unary minus negates the operand.
- Example:
b) Increment (++) and Decrement (--)
- Increase or decrease a numeric variable by 1.
- Two forms: prefix and postfix.
| Form | Effect |
|---|---|
++var | Increment var by 1, then use the new value. |
var++ | Use the current value of var, then increment it by 1. |
--var | Decrement var by 1, then use the new value. |
var-- | Use the current value of var, then decrement it by 1. |
- Example:
[!IMPORTANT] They can be applied only to variables, not to constants or expressions.
2.4 Type Conversion in Arithmetic
Java performs binary numeric promotion when evaluating arithmetic expressions:
- If either operand is
double, the other is converted todouble. - Else if either operand is
float, the other is converted tofloat. - Else if either operand is
long, the other is converted tolong. - Else both operands are converted to
int(even if they arebyte,short, orchar).
This means that arithmetic on byte, short, or char results in an int. To store the result back, an explicit cast may be required.
2.5 Overflow and Underflow
Arithmetic with integer types does not throw exceptions on overflow/underflow; it wraps around silently.
[!CAUTION]
For long, overflow wraps around. For float and double, overflow results in Infinity or -Infinity.
2.6 Compound Assignment Operators
These combine an arithmetic operation with assignment: +=, -=, *=, /=, %=.
[!TIP]
Compound assignments perform an implicit cast to the left-hand side type, which can prevent compilation errors in some cases.
byte b = 10; b += 5; works perfectly, whereas b = b + 5; would cause an error because b+5 becomes an int.
2.7 Operator Precedence
Arithmetic operators follow standard mathematical precedence:
| Priority | Category | Operators |
|---|---|---|
| 1 (Highest) | Unary | ++, --, +, - |
| 2 | Multiplicative | *, /, % |
| 3 (Lowest) | Additive | +, - |
Parentheses () can be used to override precedence.
2.8 Important Notes
Character Arithmetic
char values are treated as integers (their Unicode code points).
String Concatenation
The + operator, when at least one operand is a String, performs concatenation, not arithmetic.
Evaluation Order
Expressions are evaluated from left to right respecting precedence and associativity. Associativity for binary arithmetic operators is left-to-right.
3. Relational Operators in Java
Relational operators (also called comparison operators) are used to compare two values. They evaluate to a boolean result—either true or false. These operators are fundamental for decision-making, loops, and control flow in Java programs.
3.1 List of Relational Operators
Java provides six relational operators:
| Operator | Name | Description |
|---|---|---|
| == | Equal to | Returns true if both operands are equal |
| != | Not equal to | Returns true if operands are not equal |
| > | Greater than | Returns true if left > right |
| < | Less than | Returns true if left < right |
| >= | Greater than or equal to | Returns true if left >= right |
| <= | Less than or equal to | Returns true if left <= right |
All relational operators are binary operators (they operate on two operands).
3.2 Usage and Examples
a) Equality Operators: == and !=
- Compare two operands for equality or inequality.
- Can be used with numeric types, characters, and reference types (objects).
- For objects,
==checks if the references point to the same object, not whether the objects are logically equivalent. For logical equality, use theequals()method.
Numeric and character examples:
Object reference example:
b) Relational Operators: <, >, <=, >=
- Used only with numeric types (including
char). - Compare magnitudes.
3.3 Relational Operators on Different Types
- Numeric types: All relational operators work seamlessly across numeric types. Java performs binary numeric promotion before comparison (e.g.,
intcompared withdoublepromotes theinttodouble).
- Characters:
charvalues are compared based on their Unicode code points.
- Booleans:
==and!=can be used with boolean values, but<,>, etc., are not allowed.
- Reference types: Only
==and!=are allowed (compare references). Using<or>on objects results in a compilation error unless the objects implementComparableand you usecompareTo().
3.4 Precedence and Associativity
Relational operators have lower precedence than arithmetic operators but higher than logical operators.
- Arithmetic (
+,-,*,/,%) → higher than relational - Relational (
<,>,<=,>=) → higher than equality (==,!=) - Equality (
==,!=) → lower than relational - All relational operators are left associative (evaluated left to right).
Parentheses can be used to clarify or override precedence:
3.5 Common Pitfalls and Best Practices
a) Mixing = and ==
=is assignment;==is comparison.- Accidentally using
=in a condition is a common error.
For boolean variables, this can be particularly tricky:
b) Floating-Point Comparisons
- Floating-point numbers (
float,double) should not be compared directly with==due to precision issues. Instead, use a tolerance.
c) Chaining Comparisons
- Java does not support chained comparisons like
a < b < c. This must be written asa < b && b < c.
3.6 Using Relational Operators in Control Flow
Relational operators are most often used in conditions for if statements, while loops, and for loops.
3.7 Summary Table
| Operator | Use Case | Example (x=5, y=3) | Result |
|---|---|---|---|
== | Equality | x == y | false |
!= | Inequality | x != y | true |
> | Greater than | x > y | true |
< | Less than | x < y | false |
>= | Greater than or equal | x >= 5 | true |
<= | Less than or equal | y <= 2 | false |
3.8 Key Points to Remember
- All relational operators return a boolean (
trueorfalse). - They can be used with all primitive numeric types and
char. ==and!=also work withbooleanand with object references.- For object comparisons, use
equals()for logical equality;==only checks reference identity. - Relational operators cannot be used with arrays or non-primitive types except for reference equality.
- Avoid direct
==comparison of floating-point numbers due to precision issues.
4. Logical Operators in Java
Logical operators are used to combine multiple boolean expressions or values. They operate on boolean operands and return a boolean result. These operators are essential for building complex conditions in decision-making and looping constructs.
4.1 List of Logical Operators
Java provides three primary logical operators:
| Operator | Name | Description |
|---|---|---|
| && | Logical AND | Returns true if both operands are true. Short-circuiting. |
| || | Logical OR | Returns true if at least one operand is true. Short-circuiting. |
| ! | Logical NOT | Returns the opposite of the operand. Unary operator. |
Additionally, Java has non-short-circuiting versions (rarely used):
| Operator | Name | Description |
|---|---|---|
| & | Boolean AND | Same as && but always evaluates both operands. |
| | | Boolean OR | Same as || but always evaluates both operands. |
| ^ | Logical XOR | Returns true if operands are different (exclusive OR). |
[!NOTE]
The &, |, and ^ operators also serve as bitwise operators when applied to integer types. When applied to boolean, they behave as logical operators.
4.2 Short-Circuit Operators: && and ||
a) Logical AND (&&)
- Evaluates the left operand first.
- If the left operand is
false, the result isfalseand the right operand is not evaluated. - This short-circuit behavior prevents unnecessary computations and avoids potential errors.
Truth table for &&:
| a | b | a && b |
|---|---|---|
| true | true | true |
| true | false | false |
| false | true | false |
| false | false | false |
b) Logical OR (||)
- Evaluates the left operand first.
- If the left operand is
true, the result istrueand the right operand is not evaluated.
Truth table for ||:
| a | b | a || b |
|---|---|---|
| true | true | true |
| true | false | true |
| false | true | true |
| false | false | false |
4.3 Non-Short-Circuit Logical Operators: & and |
&(boolean AND): Always evaluates both operands, even if the left isfalse.|(boolean OR): Always evaluates both operands, even if the left istrue.- These are used when you need side effects of the right operand to occur regardless of the left operand's value.
[!CAUTION]
Using & and | with boolean operands is uncommon and can lead to confusing code. Prefer short-circuit operators unless you specifically require both evaluations.
4.4 Logical XOR: ^
- Exclusive OR: returns
trueif the operands are different;falseif they are the same.
Truth table for ^:
| a | b | a ^ b |
|---|---|---|
| true | true | false |
| true | false | true |
| false | true | true |
| false | false | false |
XOR is sometimes used in toggle operations or parity checks.
4.5 Logical NOT: !
- Unary operator that negates a boolean value.
Truth table for !:
| a | !a |
|---|---|
| true | false |
| false | true |
4.6 Precedence and Associativity
The logical operators have the following precedence (from highest to lowest):
!(logical NOT)&(boolean AND)^(logical XOR)|(boolean OR)&&(short-circuit AND)||(short-circuit OR)
Associativity:
!is right associative.&,^,|,&&,||are left associative.
Parentheses can be used to override precedence and improve readability.
4.7 Combining Relational and Logical Operators
Logical operators are typically used to combine relational expressions.
4.8 Short-Circuit Benefits
- Performance: Avoids unnecessary evaluations.
- Safety: Guards against runtime exceptions (e.g., null checks, division by zero).
- Idiomatic usage: The
&&and||operators are the standard in Java for combining conditions.
4.9 Common Pitfalls
a) Using & or | instead of && or || unintentionally
&and|have lower precedence than relational operators but higher than&&and||. Mixing them can lead to unexpected results.
b) Confusing && with & in bitwise contexts
- For integer types,
&is bitwise AND; for booleans, it's logical AND. This overload can cause confusion.
c) Negating complex conditions incorrectly
- De Morgan's laws are essential for simplifying negated conditions:
!(a && b)→!a || !b!(a || b)→!a && !b
4.10 Summary Table
| Operator | Name | Short-circuit? | Example (a=true, b=false) | Result |
|---|---|---|---|---|
&& | Logical AND | Yes | a && b | false |
|| | Logical OR | Yes | a || b | true |
! | Logical NOT | N/A | !a | false |
& | Boolean AND | No | a & b | false |
| | Boolean OR | No | a | b | true |
^ | Logical XOR | No | a ^ b | true |
4.11 Key Points to Remember
- Use
&&and||for most conditional logic; they short-circuit and are safer. - Use
!to negate a boolean expression. &and|are primarily for bitwise operations; when used with booleans they are non-short-circuiting.- Understand precedence to avoid subtle bugs; when in doubt, use parentheses.
- Apply De Morgan's laws to simplify negated conditions.
5. Assignment Operators in Java
Assignment operators are used to assign a value to a variable. They combine the assignment operation with an optional arithmetic or bitwise operation. Java provides a simple assignment operator and several compound assignment operators.
5.1 Simple Assignment Operator (=)
- The most basic assignment operator.
- Assigns the value on the right-hand side (RHS) to the variable on the left-hand side (LHS).
- The LHS must be a variable (or array element, field) — not a constant or expression.
Syntax:
Examples:
- The assignment operator returns the assigned value, so assignments can be chained.
- The associativity of
=is right to left.
Chaining:
5.2 Compound Assignment Operators
Compound assignment operators combine an operation with assignment, providing a concise way to modify a variable.
| Operator | Example | Equivalent To |
|---|---|---|
| += | a += b | a = a + b |
| -= | a -= b | a = a - b |
| *= | a *= b | a = a * b |
| /= | a /= b | a = a / b |
| %= | a %= b | a = a % b |
| &= | a &= b | a = a & b (bitwise AND) |
| |= | a |= b | a = a | b (bitwise OR) |
| ^= | a ^= b | a = a ^ b (bitwise XOR) |
| <<= | a <<= b | a = a << b (left shift) |
| >>= | a >>= b | a = a >> b (signed right shift) |
| >>>= | a >>>= b | a = a >>> b (unsigned right shift) |
Examples:
5.3 Important Characteristics of Compound Assignment
a) Implicit Type Casting
Compound assignment operators perform an implicit narrowing primitive conversion if necessary. This means they automatically cast the result to the type of the left operand, even if that would normally cause a loss of precision.
[!TIP] This implicit cast makes compound assignments safe in situations where the simple assignment would require an explicit cast.
b) They Evaluate the Left Operand Only Once
Compound assignment evaluates the left operand only once. This matters when the left operand has side effects (e.g., array access with index computation).
c) Assignment as an Expression
Like the simple =, compound assignments return the value that was assigned. This allows them to be used within larger expressions.
5.4 Precedence and Associativity
- All assignment operators have the lowest precedence of all operators in Java.
- They are right associative (evaluated from right to left).
Because of low precedence, parentheses are rarely needed when assignments are separate statements. However, when used inside expressions, parentheses may be required to avoid unintended evaluation order.
5.5 Common Pitfalls and Best Practices
a) Confusing = with ==
=is assignment,==is comparison.
b) Overflow and Underflow
Compound assignments do not prevent overflow; they simply perform the operation and assign the potentially truncated result.
c) Using Compound Assignment with String
The += operator is overloaded for String — it performs concatenation, not arithmetic addition.
d) Avoid Unnecessary Compound Assignments
Compound assignments improve conciseness but may reduce readability when used with complex expressions. Use them judiciously.
5.6 Summary Table
| Operator | Type | Example | Equivalent To | Casting Behavior |
|---|---|---|---|---|
= | Simple assignment | a = b | a = b | No implicit cast |
+= | Add & assign | a += b | a = a + b | Implicit cast to a's type |
-= | Subtract & assign | a -= b | a = a - b | Implicit cast |
*= | Multiply & assign | a *= b | a = a * b | Implicit cast |
/= | Divide & assign | a /= b | a = a / b | Implicit cast |
%= | Modulus & assign | a %= b | a = a % b | Implicit cast |
&= | Bitwise AND & assign | a &= b | a = a & b | Implicit cast |
|= | Bitwise OR & assign | a |= b | a = a | b | Implicit cast |
^= | Bitwise XOR & assign | a ^= b | a = a ^ b | Implicit cast |
<<= | Left shift & assign | a <<= b | a = a << b | Implicit cast |
>>= | Right shift & assign | a >>= b | a = a >> b | Implicit cast |
>>>= | Unsigned right shift & assign | a >>>= b | a = a >>> b | Implicit cast |
5.7 Key Points to Remember
- The simple assignment operator
=assigns the value of the right operand to the left operand and returns the assigned value. - Compound assignment operators combine an operation with assignment and implicitly cast the result to the left operand's type.
- Assignment operators have the lowest precedence and are right associative.
- Be cautious with overflow, and remember that
+=onStringperforms concatenation. - Use compound assignments for concise code, but ensure readability.
6. Increment and Decrement Operators in Java
Increment (++) and decrement (--) operators are unary operators that add 1 or subtract 1 from a numeric variable. They are concise and commonly used in loops, counters, and pointer-like operations. These operators have two forms: prefix and postfix, which differ in when the increment or decrement takes effect.
6.1 Syntax and Basic Behavior
| Operator | Name | Effect |
|---|---|---|
++ | Increment | Increases the operand by 1 |
-- | Decrement | Decreases the operand by 1 |
Both operators can appear before (prefix) or after (postfix) the operand.
- Prefix:
++varor--varThe variable is incremented/decremented first, and the new value is used in the surrounding expression. - Postfix:
var++orvar--The original value of the variable is used in the surrounding expression, then the variable is incremented/decremented.
6.2 Prefix vs. Postfix – Detailed Examples
a) Standalone Use
When used alone as a statement, prefix and postfix produce the same final result.
b) Inside Expressions
The distinction becomes critical when the operator is used within a larger expression.
c) In Loops
Both forms are common, but postfix is more conventional in for loops:
However, prefix works equally well here because the loop update is a standalone statement.
6.3 Applicable Types
Increment and decrement operators can be applied to:
- All integer types (
byte,short,int,long) - Floating-point types (
float,double) char(increments the Unicode code point)- Not applicable to
booleanor reference types.
6.4 Precedence and Associativity
- Increment and decrement operators have very high precedence – higher than arithmetic, relational, and logical operators.
- They are right associative (evaluate from right to left when chained).
[!IMPORTANT]
Only one increment/decrement can be applied directly to a variable; ++(++i) is not allowed because the operand of ++ must be a variable, not a value.
6.5 Side Effects and Order of Evaluation
Because increment/decrement modify variables, they can lead to subtle bugs when used in complex expressions. The evaluation order in Java is deterministic: operands are evaluated left to right, but the actual effect of prefix/postfix depends on when the increment occurs.
Example with multiple operators:
Another:
[!WARNING] Such expressions are often confusing and should be avoided in production code.
6.6 Common Use Cases
a) Loop Counters
b) Array/Index Traversal
c) Compact Assignment
d) In Conditions
6.7 Pitfalls and Best Practices
a) Avoid Using Increment/Decrement Inside Complex Expressions
Expressions like result = i++ + ++i are error-prone and hard to read. Prefer splitting into separate statements.
Better:
b) Not Applicable to Constants or Literals
c) Mixed with Assignment – Can Be Confusing
d) In Method Arguments
When passed to a method, the increment happens before or after the argument is evaluated depending on prefix/postfix.
6.8 Type Promotion and Casting
- For
byte,short,char, increment/decrement work without explicit cast because they act as if the variable is temporarily promoted toint, but the result is stored back with an implicit narrowing conversion (if within range).
- For floating-point, increment adds exactly
1.0(or1.0fforfloat).
6.9 Summary Table
| Form | Example | Effect on Variable | Value Used in Expression |
|---|---|---|---|
| Prefix increment | ++x | Increment first | New (incremented) value |
| Postfix increment | x++ | Increment after | Original value |
| Prefix decrement | --x | Decrement first | New (decremented) value |
| Postfix decrement | x-- | Decrement after | Original value |
6.10 Key Points to Remember
- Increment (
++) and decrement (--) add or subtract 1 from a numeric variable. - Prefix updates the variable before using its value; postfix uses the value first, then updates.
- They can be applied to numeric primitives and
char, but not tobooleanor objects. - Use them sparingly in complex expressions to maintain readability.
- In loops, postfix is conventional but not mandatory; both work for standalone updates.
- Watch out for overflow/underflow, especially with
byteandshort.
7. Conditional Operator (Ternary Operator) in Java
The conditional operator (? :) is Java's only ternary operator—it takes three operands. It provides a concise way to evaluate a boolean condition and return one of two values based on the result. Often used as a shorthand for simple if-else statements.
7.1 Syntax
condition: A boolean expression.expressionIfTrue: The value returned if condition evaluates totrue.expressionIfFalse: The value returned if condition evaluates tofalse.
The entire expression evaluates to the value of either expressionIfTrue or expressionIfFalse, depending on the condition.
7.2 Basic Example
This is equivalent to:
7.3 Using the Result
The ternary operator returns a value, so it can be used in assignments, method arguments, return statements, and expressions.
In return statement:
As a method argument:
In assignments with different types (subject to type compatibility):
7.4 Type Rules
The type of the entire ternary expression is determined by the types of expressionIfTrue and expressionIfFalse. Java follows binary numeric promotion rules for numeric types, and for non-numeric types, the result type is the common supertype.
- If both expressions have the same type, that is the result type.
- If one is a primitive and the other is a wrapper, unboxing occurs and the primitive type is used.
- If the types are numeric, the result is the type that would result from numeric promotion.
- If the types are references, the result type is the most specific common supertype (or
Objectif none).
[!IMPORTANT]
Mixing incompatible types can cause compile-time errors.
String s = (true) ? "Hello" : 123; → Error: incompatible types
7.5 Nesting Ternary Operators
Ternary operators can be nested to create multi-way conditions. However, excessive nesting harms readability.
Nested example (three-way branch):
[!TIP]
Deeply nested ternary operators are often discouraged. Consider using if-else if or a switch for complex conditions.
7.6 Precedence and Associativity
- The conditional operator has very low precedence—only assignment operators have lower precedence.
- It is right associative, meaning nesting is evaluated from right to left.
Because of right associativity, the above returns the maximum of three numbers.
Use parentheses to make the grouping explicit:
7.7 Common Use Cases
- Conditional assignment of values.
- Returning values from methods based on a simple condition.
- String concatenation with a conditional result.
- Initializing a variable with a value that depends on a condition.
7.8 Pitfalls and Best Practices
a) Avoid Side Effects
Since both expressionIfTrue and expressionIfFalse are evaluated conditionally, side effects should be used with caution.
b) Type Unification Issues
When mixing numeric and object types, unexpected boxing/unboxing may occur.
[!WARNING]
Always ensure that if one branch uses a wrapper, it is not null when the result type is primitive.
c) Overuse Reduces Readability
Simple ternary is fine; nested ternaries become difficult to read. Prefer explicit if-else or switch for complex logic.
d) Use with Parentheses in Complex Expressions
To avoid precedence surprises, wrap the entire ternary expression in parentheses when used in larger expressions.
e) Cannot Be Used as a Standalone Statement
The ternary operator must be part of an expression; it cannot be used alone as a statement.
7.9 Summary Table
| Aspect | Description |
|---|---|
| Operator | ? : |
| Number of operands | 3 (ternary) |
| Syntax | condition ? exprIfTrue : exprIfFalse |
| Result type | Common type of the two expressions after numeric/promotion/unboxing rules |
| Precedence | Very low, only higher than assignment operators |
| Associativity | Right to left |
| Typical usage | Conditional assignment, simple if-else replacement |
| Nesting | Possible but reduces readability |
| Common pitfall | Unboxing null causing NullPointerException; side effects; readability |
7.10 Key Points to Remember
- The conditional operator is a concise alternative to
if-elsewhen you need to choose between two values. - It returns a value, so it can be used in assignments, returns, and method arguments.
- The types of the two branches must be compatible; Java determines a common result type.
- Avoid nesting more than one level—use
if-elsefor clarity. - Be cautious with side effects and null values to avoid runtime errors.
8. Bitwise Operators in Java
Bitwise operators perform operations on individual bits of integer types (byte, short, int, long, and char). They treat the operands as sequences of bits (binary numbers) and are commonly used in low-level programming, cryptography, graphics, device drivers, and performance-critical applications.
8.1 List of Bitwise Operators
Java provides seven bitwise operators:
| Operator | Name | Type | Description |
|---|---|---|---|
| & | Bitwise AND | Binary | Sets bit to 1 if both bits are 1 |
| | | Bitwise OR | Binary | Sets bit to 1 if at least one bit is 1 |
| ^ | Bitwise XOR (exclusive OR) | Binary | Sets bit to 1 if bits are different |
| ~ | Bitwise NOT (complement) | Unary | Flips all bits (1→0, 0→1) |
| << | Left shift | Binary | Shifts bits left, fills with zero |
| >> | Signed right shift | Binary | Shifts bits right, preserves sign bit |
| >>> | Unsigned right shift | Binary | Shifts bits right, fills with zero |
[!NOTE]
The &, |, and ^ operators also work as logical operators when applied to boolean operands. When used with integer types, they perform bitwise operations.
8.2 Bitwise Logical Operators (&, |, ^, ~)
These operators work on each corresponding pair of bits (or the single operand for ~).
a) Bitwise AND (&)
- Result bit is 1 only if both operand bits are 1.
Truth table:
| Bit A | Bit B | A & B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
b) Bitwise OR (|)
- Result bit is 1 if at least one operand bit is 1.
Truth table:
| Bit A | Bit B | A | B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
c) Bitwise XOR (^)
- Result bit is 1 if the operand bits are different; 0 if they are the same.
Truth table:
| Bit A | Bit B | A ^ B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
d) Bitwise NOT (~)
- Flips every bit (unary operator). Equivalent to
-x - 1because of two's complement representation.
8.3 Shift Operators (<<, >>, >>>)
Shift operators move bits to the left or right. They operate on the binary representation of integers.
a) Left Shift (<<)
- Shifts bits to the left by the specified number of positions.
- Vacant low-order bits are filled with 0.
- Equivalent to multiplying by 2^n (but may overflow).
b) Signed Right Shift (>>)
- Shifts bits to the right by the specified number of positions.
- Vacant high-order bits are filled with the sign bit (0 for positive, 1 for negative). This preserves the sign.
- Equivalent to dividing by 2^n (floor division for negative numbers).
c) Unsigned Right Shift (>>>)
- Shifts bits to the right by the specified number of positions.
- Vacant high-order bits are filled with 0, regardless of sign.
- Also called "logical right shift".
8.4 Shift Distance Masking
For int operands, only the lower 5 bits of the shift distance are used (i.e., distance & 31). For long operands, only the lower 6 bits are used (distance & 63). This means shifting by 32 bits on an int does nothing.
8.5 Type Promotion in Bitwise Operations
- When using bitwise operators on
byte,short, orchar, the operands are promoted tointbefore the operation. - The result is always
int(orlongif any operand islong).
For shift operators, the promoted type is used, and the shift distance is masked accordingly.
8.6 Precedence and Associativity
- Bitwise operators have precedence between relational and logical operators.
- From highest to lowest among bitwise:
~(unary)<<,>>,>>>&^|
Associativity is left to right for binary bitwise operators.
8.7 Common Use Cases
- Flag management: Use individual bits as boolean flags.
- Fast multiplication/division by powers of two (using shift).
- Data encoding/decoding (e.g., packing two 16-bit integers into one 32-bit integer).
-
Cryptography and hash functions often rely on bitwise operations.
-
Color manipulation (e.g., in graphics).
8.8 Pitfalls and Best Practices
a) Mixing with Logical Operators
Using & or | when you meant && or || can cause unexpected behavior because they are not short-circuiting.
b) Shift Overflows
Shifting too far can lose data. Remember the distance masking.
c) Unsigned Right Shift on Negative int Produces Large Positive Number
This is often desired when working with raw bits but can be surprising.
d) Precedence Confusion
Always use parentheses to make bitwise expressions clear, especially when mixing with other operators.
e) Portability
Bitwise operations on int and long are well-defined in Java (two's complement, fixed-width). This makes Java bitwise code portable across platforms.
8.9 Summary Table
| Operator | Name | Example (int a=12, b=10) | Result (binary) | Decimal |
|---|---|---|---|---|
& | Bitwise AND | a & b | 1100 & 1010 = 1000 | 8 |
| | Bitwise OR | a | b | 1100 | 1010 = 1110 | 14 |
^ | Bitwise XOR | a ^ b | 1100 ^ 1010 = 0110 | 6 |
~ | Bitwise NOT | ~a | ~...1100 = ...0011 | -13 |
<< | Left shift | a << 1 | 1100 << 1 = 11000 | 24 |
>> | Signed right shift | a >> 1 | 1100 >> 1 = 0110 | 6 |
>>> | Unsigned right shift | a >>> 1 | 1100 >>> 1 = 0110 | 6 |
8.10 Key Points to Remember
- Bitwise operators work only on integral types (
byte,short,int,long,char). - They manipulate individual bits, making them efficient for low-level tasks.
&,|,^have both bitwise and logical versions; the context (integer vs boolean) determines behavior.- Shift operators
<<,>>,>>>are useful for fast multiplication/division and bit packing. - The shift distance is masked (5 bits for
int, 6 bits forlong). - Use parentheses to avoid precedence mistakes.
- Bitwise operations are fundamental in systems programming, graphics, networking, and performance-sensitive code.
9. The instanceof Operator in Java
The instanceof operator is a binary operator used to test whether an object is an instance of a specific class, subclass, or interface. It returns a boolean value and is commonly used for type checking before downcasting, ensuring type safety at runtime.
9.1 Syntax
objectReference: A reference to an object (cannot be a primitive type).Type: A class, abstract class, interface, or array type.- Result:
trueif the object is an instance of the specified type; otherwisefalse.
9.2 Basic Behavior
a) Simple Class Example
b) Null Handling
- If the object reference is
null,instanceofalways returnsfalse(no exception is thrown).
c) Interface Checking
d) Array Types
- Arrays are objects in Java.
instanceofcan test array types.
9.3 Compile-Time vs. Runtime Behavior
- Compile-time: The compiler checks whether the reference type and the target type are in the same inheritance hierarchy. If it can determine that the test will always be
false, a compilation error occurs.
- Runtime: The actual class of the object is checked. Polymorphism is respected; an object of a subclass is an instance of its parent class.
9.4 Using instanceof for Safe Downcasting
A common use case is to check the type before performing a downcast to avoid ClassCastException.
9.5 Pattern Matching for instanceof (Java 16+)
Starting with Java 16, instanceof can be combined with a pattern variable, making the code more concise and eliminating explicit casting.
Syntax:
Example:
The pattern variable is in scope only in the true block. This improves readability and reduces errors.
9.6 Using instanceof with Generics
Due to type erasure, the generic type information is not available at runtime. Therefore, you cannot use instanceof directly with a generic type parameter.
For checking element types, you would need to examine the elements or use a different approach.
9.7 Precedence
instanceofhas higher precedence than==and!=(equality operators), but lower precedence than relational operators (<,>,<=,>=).- It is non-associative (cannot be chained directly).
- To avoid confusion, always use parentheses when mixing
instanceofwith other operators.
9.8 Common Pitfalls and Best Practices
a) Using instanceof with null
Always remember: null instanceof Anything is false. No need for an explicit null check before instanceof, but be cautious if you use the object after the check.
b) Compiler-Detected Impossibility
If the compiler knows the reference type can never be an instance of the target type, it issues a compilation error. This helps catch logical errors early.
c) Overuse of instanceof
Excessive use of instanceof can indicate poor object-oriented design. Often, it is better to use polymorphism (method overriding) instead of explicit type checks. Reserve instanceof for situations where the type is not known until runtime or when working with external classes.
d) Pattern Matching and Scope
With pattern matching, the pattern variable is only in scope where it is definitely matched. Avoid using it outside the block.
e) Performance
instanceof is a relatively fast operation (a single type comparison), but frequent use in tight loops may have an impact.
9.9 Summary Table
| Aspect | Description |
|---|---|
| Operator | instanceof |
| Type | Binary operator |
| Operands | Left: object reference; Right: class, interface, or array type |
| Result | boolean (true if the object is an instance of the type) |
| Behavior with null | Returns false |
| Compile-time | Checks compatibility; error if types are unrelated |
| Pattern matching | Supported from Java 16: if (obj instanceof Type var) { ... } |
| Generics | Cannot use concrete generic types due to erasure; use wildcards or raw types |
| Precedence | Between relational and equality operators |
| Common use | Safe downcasting, type checks before casting |
9.10 Key Points to Remember
instanceoftests whether an object is an instance of a specific type (class, subclass, or interface).- It returns
falsefornullreferences. - The compiler ensures the test is sensible; unrelated types cause compilation errors.
- Use
instanceofbefore downcasting to avoidClassCastException. - Pattern matching (Java 16+) simplifies the idiom by introducing a scoped pattern variable.
- Avoid overusing
instanceof; prefer polymorphic design when possible. - For generic types, use wildcards or raw types because type parameters are erased at runtime.
10. Expressions in Java
An expression is the fundamental building block of any Java program. It is a construct that evaluates to a single value and may produce side effects. Understanding expressions—their types, evaluation order, and composition—is essential for writing correct and efficient code.
10.1 What Is an Expression?
An expression is a combination of:
- Literals (e.g.,
42,"Hello",true) - Variables (e.g.,
count,name) - Operators (e.g.,
+,>,&&,++) - Method invocations (e.g.,
Math.sqrt(x)) - Parentheses to control grouping
When evaluated, an expression yields a result of some data type and may cause a side effect (e.g., changing a variable's value).
10.2 Types of Expressions
Expressions can be categorized by the kind of result they produce or the operators involved.
a) Arithmetic Expressions
Produce a numeric result (int, long, float, double).
b) Relational Expressions
Produce a boolean result by comparing two values.
c) Logical Expressions
Combine boolean values using logical operators.
d) Assignment Expressions
Use the assignment operator (=) or compound assignment (+=, etc.). They evaluate to the assigned value.
e) Unary Expressions
Use unary operators (+, -, ++, --, !, ~).
f) Conditional (Ternary) Expression
The ? : operator yields one of two values based on a condition.
g) instanceof Expression
Tests object type and returns boolean.
h) Method Invocation Expression
Calls a method and evaluates to the method's return value.
10.3 Evaluation Order
The order in which parts of an expression are evaluated is determined by three rules:
- Operator precedence – which operations are performed first.
- Operator associativity – for operators of the same precedence, the direction of evaluation (left-to-right or right-to-left).
- Operand evaluation order – in Java, operands are always evaluated left to right before the operator is applied.
a) Precedence (highest to lowest)
| Category | Operators | Associativity |
|---|---|---|
| Postfix | expr++, expr-- | Left-to-right |
| Unary | ++expr, --expr, +, -, !, ~ | Right-to-left |
| Multiplicative | *, /, % | Left-to-right |
| Additive | +, - | Left-to-right |
| Shift | <<, >>, >>> | Left-to-right |
| Relational | <, >, <=, >=, instanceof | Left-to-right |
| Equality | ==, != | Left-to-right |
| Bitwise AND | & | Left-to-right |
| Bitwise XOR | ^ | Left-to-right |
| Bitwise OR | | | Left-to-right |
| Logical AND | && | Left-to-right |
| Logical OR | || | Left-to-right |
| Conditional | ? : | Right-to-left |
| Assignment | =, +=, -=, *=, /=, %=, etc. | Right-to-left |
b) Associativity
- Most binary operators are left associative (evaluate left operand, then right, then combine).
- Assignment and unary operators are right associative.
c) Operand Evaluation Order
For any binary operator, Java guarantees that the left operand is fully evaluated before the right operand (except for short-circuiting operators, which may skip the right operand). This is important when operands have side effects.
10.4 Expression Types
Every expression has a compile-time type, determined by the types of its subexpressions and the operators applied. The type dictates what operations can be performed on the expression's result.
- Numeric expressions –
byte,short,int,long,float,doubleafter binary numeric promotion. - Boolean expressions – result of relational, logical, and
instanceofoperators. - Reference expressions – result of
new, method calls returning objects, etc. - Void expressions – method invocations that return
void; cannot be used where a value is expected.
Type of the conditional operator is the common type of the second and third operands after promotion.
10.5 Constant Expressions
A constant expression is an expression that can be evaluated at compile time. Constant expressions are used in annotations, case labels in switch, and for initializing final variables.
A constant expression consists only of:
- Literals
finalvariables of primitive type orStringinitialized with constant expressions- Arithmetic, relational, logical, bitwise, and shift operators applied to constants
- The conditional operator with constant operands
- Parentheses
The compiler performs constant folding to pre-compute the result.
10.6 Expression Statements
Not all expressions can stand alone as statements. In Java, only certain expressions become valid statements when followed by a semicolon:
- Assignment expressions
- Increment/decrement expressions (
++,--) - Method invocations
- Object creation expressions (
new)
[!NOTE]
Other expressions (e.g., a + b, x > y) are not valid statements because they produce a value that is not used.
10.7 Side Effects
Expressions can have side effects—they change the program's state. Common side-effect-causing operators include:
- Assignment operators (
=,+=, etc.) - Increment/decrement (
++,--) - Method invocations that modify state
Understanding side effects and evaluation order is crucial to avoid subtle bugs.
10.8 Common Expression Patterns
a) Chained Assignment
b) Combined Comparison and Assignment
c) Short-Circuiting in Conditions
d) Bitwise Manipulation
10.9 Important Considerations
- Floating-point precision: Arithmetic with
float/doublemay produce rounding errors. Avoid direct equality comparisons. - Integer overflow: Java does not warn about overflow; it wraps around silently.
- Null references: Using
nullin expressions that require a primitive (e.g., unboxing) throwsNullPointerException. - Short-circuiting:
&&and||skip evaluation of the right operand when the left operand decides the result. Use this to guard against potential errors. - Type promotion: Binary numeric promotion can change the result type, sometimes leading to unexpected behavior.
- Generics and erasure: The type of an expression involving generics may be erased at runtime, affecting
instanceofand casts.
10.10 Examples of Complex Expressions
10.11 Summary
- An expression evaluates to a value and may have side effects.
- Java expressions are built from literals, variables, operators, and method calls.
- The type of an expression is known at compile time and determines how the value can be used.
- Evaluation order follows precedence, associativity, and left-to-right operand evaluation (with short-circuit exceptions).
- Only certain expressions can be used as statements.
- Constant expressions are computed at compile time.
- Understanding expressions is fundamental to writing correct, efficient, and maintainable code.
