Java supports the basic arithmetic operators used to perform mathematical calculations on numeric values. These operators work on both integer and floating-point types.
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 |
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)
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.
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
}
}
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.
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.
int a = 10; // Assigns 10 to variable a
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
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
.
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);
}
}
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.
Java provides relational operators to compare values and return a boolean result (true
or false
). These are essential for decision-making in programs.
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.).
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
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
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
}
}
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.
&&
, ||
, !
)&&
, ||
, !
)Logical operators are used in Java to combine or invert boolean expressions, which is essential for controlling program flow in conditional statements.
&&
)true
if both operands are true
.false
, the second operand is not evaluated (short-circuiting).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)
||
)true
if at least one operand is true
.true
, the second operand is not evaluated (short-circuiting).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
!
)true
becomes false
, and false
becomes true
.Example:
boolean a = false;
System.out.println(!a); // Output: true
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.
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;
}
}
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.
&
, |
, ^
, ~
)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.
Operator | Description | Example |
---|---|---|
& |
Bitwise AND | 0b1100 & 0b1010 |
| | Bitwise OR | 0b1100 | 0b1010 |
^ |
Bitwise XOR (exclusive) | 0b1100 ^ 0b1010 |
~ |
Bitwise NOT (complement) | ~0b1100 |
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) |
&
, |
, 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 |
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');
}
}
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.
>>
, <<
, >>>
)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.
<<
)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)
>>
) Arithmetic ShiftExample:
int b = 0b1111_1000; // decimal -8 (two's complement)
int rightShift = b >> 2;
System.out.println(Integer.toBinaryString(rightShift)); // Output: 11111110 (decimal -2)
>>>
) Logical ShiftExample:
int c = 0b1111_1000; // decimal -8
int unsignedShift = c >>> 2;
System.out.println(Integer.toBinaryString(unsignedShift)); // Output: 00111111 (decimal 1073741821)
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');
}
}
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.
++
, --
, +
, -
, !
)Unary operators operate on a single operand and are commonly used to modify or evaluate values quickly.
++
: Increment operator — increases an integer value by 1.--
: Decrement operator — decreases an integer value by 1.+
: Unary plus — indicates a positive value (usually redundant).-
: Unary minus — negates a numeric value.!
: Logical NOT — inverts a boolean value (true
to false
, and vice versa).++
and --
++a
or --a
): The variable is incremented or decremented before the expression is evaluated.a++
or a--
): The current value is used in the expression before the increment or decrement happens.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
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
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);
}
}
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.
Understanding operator precedence and associativity is essential for predicting how Java evaluates complex expressions without parentheses.
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
.
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
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
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);
}
}
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.
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.