In Java, an operator is a symbol or a set of symbols that performs an operation on one or more operands. These operands can be variables, values, or literals, and they are the entities on which the operation is carried out. The operator, together with its operands, forms an expression that produces a result.
int x = 5; // Variable as operand
int y = 3; // Variable as operand
int result = x + y; // Addition operator applied to variables
In this example, the addition operator (+
) is applied to the variables x
and y
, producing a result stored in the variable result
. The terms “operator,” “operand,” and “result” are fundamental in understanding how Java expressions operate, forming the foundation for concise and expressive coding.
Table of Contents
1. Types of Operators
Java operators can be categorized into three types: unary, binary, and ternary.
- Unary Operators: Operate on a single operand. Examples include the unary minus (
-x
) or the logical NOT (!flag
). - Binary Operators: Work with two operands. Common binary operators include addition (
x + y
), subtraction (x - y
), and multiplication (x * y
). - Ternary Operator: Takes three operands. The most common ternary operator is the conditional operator (
condition ? trueExpression : falseExpression
), providing a concise way to express conditional statements.
Each operator type has a specific precedence, determining the order in which operations are executed within an expression.
2. Operator Precedence
In Java, operator precedence refers to the order in which operators are evaluated in an expression. Unlike simple left-to-right evaluation, certain operators have higher precedence and are evaluated first. This behavior can be likened to the rules of mathematics.
For example, the multiplication (*
) operator has higher precedence than the addition (+
) operator. Consider the following coding example:
int result = 5 + 3 * 2;
Here, due to operator precedence, the multiplication is performed first, resulting in 5 + (3 * 2)
, and the final result
is 11
.
Unless overridden by parentheses, Java operators generally follow the order of operations. If two operators have the same level of precedence, the left-to-right evaluation is applied.
Here’s a table summarizing the order of operator precedence in Java:
Operator | Symbols and examples |
Post-unary operators | expression++, expression— |
Pre-unary operators | ++expression, —expression |
Other unary operators | -, !, ~, +, (type) |
Multiplications/division/modulus | *, /, % |
Addition/substraction | +, – |
Shift operators | <<, >>, >>> |
Relational operators | <, >, <=, >=, instanceof |
Equal to / not equal to | ==, != |
Logical operators | &, ^, | |
Short-circuit logical operators | &&, || |
Ternary operators | boolean expression ? expression1 : expression2 |
Assignment operators | =, +=, -=, *=, /=, %=, ^=, |=, <<=, >>=, >>>= |
3. Unary Operators
These operators require exactly one operand and are employed to carry out various operations, from incrementing and decrementing values to changing the sign of a numerical expression.
Java supports several unary operators, each serving a unique purpose. Let’s break down the most commonly used ones along with their descriptions:
Operator | Description | Example |
---|---|---|
++ | Increment the value of the operand by 1 | int x = 5; ++x; |
— | Decrement the value of the operand by 1 | int y = 10; --y; |
+ | Positive sign (explicit, rarely used) | int a = -7; +a; |
– | Negation or change the sign of the operand | int b = 3; -b; |
! | Logical NOT (negates the boolean value of the operand) | boolean flag = true; !flag; |
(type) | Type casting (converts one data type to another) | double d = (double) 5; |
3.1. Increment and decrement operators
Increment (++
) and decrement (--
) operators are essential tools in Java for manipulating numerical values. These unary operators have a high order of precedence, often being applied before binary operators, and they come in two flavors: pre-increment/post-increment and pre-decrement/post-decrement.
3.1.1. Pre-Increment and Post-Increment:
Pre-Increment (++variable
):
In pre-increment, the value of the variable is increased by 1 before its value is used in an expression.
int x = 5;
int result = ++x; // pre-increment
System.out.println(result); // Output: 6
System.out.println(x); // Output: 6
In this example, x
is incremented by 1 before being assigned to result
. Both result
and x
end up with the value 6.
Post-Increment (variable++
):
In post-increment, the current value of the variable is used in an expression before being increased by 1.
int y = 10;
int result = y++; // post-increment
System.out.println(result); // Output: 10
System.out.println(y); // Output: 11
Here, result
gets the original value of y
(10), and then y
is incremented by 1.
Pre-Decrement and Post-Decrement:
The principles of pre-decrement (--variable
) and post-decrement (variable--
) are analogous to their increment counterparts.
int a = 8;
int result = --a; // pre-decrement
System.out.println(result); // Output: 7
System.out.println(a); // Output: 7
int b = 15;
int result = b--; // post-decrement
System.out.println(result); // Output: 15
System.out.println(b); // Output: 14
Combining Pre-Increment and Post-Increment:
int count = 3;
int result = ++count + count++;
System.out.println(result); // Output: 8
System.out.println(count); // Output: 5
In this example, both pre-increment and post-increment operators are used together. The pre-increment increases the value of count
by 1 before being added to the post-incremented value of count
. The final value of count
is 5, and the result is 8.
3.2. Logical Complement Operator and Negation Operators
The logical complement operator is a unary operator that operates on a single boolean operand. Its primary purpose is to negate or invert the value of the boolean expression it precedes.
boolean isJavaCool = true;
boolean notCool = !isJavaCool;
System.out.println(notCool); // Output: false
In this example, the !
operator flips the value of isJavaCool
from true
to false
. The logical complement is particularly useful in conditions and decision-making, where you want to consider the opposite of a given boolean state.
While the logical complement operator handles boolean values, the negation operator (-
) takes center stage when dealing with numeric expressions. This unary operator reverses the sign of a numeric value, turning positive numbers into negatives and vice versa:
int positiveNumber = 7;
int negativeNumber = -positiveNumber;
System.out.println(negativeNumber); // Output: -7
Here, the negation operator transforms the value of positiveNumber
from 7 to -7.
The power of the negation operator becomes evident when applied multiple times, especially when enclosed within parentheses. Let’s examine an example:
int number = 10;
int doubleNegation = -(-number);
System.out.println(doubleNegation); // Output: 10
In this scenario, the net effect is that the original value is preserved despite the negations.
Attempting to use the logical complement operator with a numeric expression or the negation operator with a boolean expression results in a compilation error.
// Logical complement applied to a boolean
boolean flag = true;
// The following line won't compile
// boolean result = -flag;
// Negation applied to a numeric expression
int numericValue = 85;
// The following line won't compile
// int negatedValue = !numericValue;
4. Binary Operators
In the world of Java programming, binary operators are the workhorses that perform a myriad of tasks, from mathematical operations to logical expressions and variable assignments. These operators take two operands and combine them to produce a new value.
Operator | Description | Example |
---|---|---|
+ | Addition | int sum = 5 + 3; |
– | Subtraction | int difference = 7 - 4; |
* | Multiplication | int product = 2 * 6; |
/ | Division | double quotient = 10 / 2; |
% | Modulus (remainder) | int remainder = 11 % 3; |
4.1. Arithmetic Operators
Arithmetic operators in Java, including addition (+
), subtraction (-
), multiplication (*
), division (/
), and modulus (%
), are fundamental tools for numerical computations. The modulus operator, represented by the symbol %
in Java, is a mathematical operator that calculates the remainder when one integer is divided by another. In other words, it returns the amount left over after the division of two integers.
int sum = 5 + 3; // sum is 8
int difference = 7 - 4; // difference is 3
int product = 2 * 6; // product is 12
double quotient = 10 / 2; // quotient is 5.0
int remainder = 11 % 3; // remainder is 2
Understanding operator precedence is crucial when expressions involve multiple operators. The default precedence follows the order: multiplication and division have higher precedence than addition and subtraction. To override default precedence, parentheses can be used to explicitly indicate the desired order of operations:
int result = 5 + 3 * 2; // result is 11 (multiplication has higher precedence)
int newResult = (5 + 3) * 2; // modifiedResult is 16 (addition is performed first)
Numeric Promotion
Each primitive numeric type has a bit-length. A long takes up more space than an int, which in turn takes up more space than a short,… Let’s start by explaining the rules for numeric promotion:
- Smaller to Larger:
- Smaller data types are automatically promoted to larger data types during arithmetic operations.
- The order of promotion is:
byte
→short
→int
→long
→float
→double
.
- Integer Promotion:
- If an expression involves
int
,byte
, orshort
, the result is promoted toint
even if neither of the operands is of typeint
.
- If an expression involves
- Floating-Point Promotion:
- If an expression involves
float
andint
operands, theint
is promoted tofloat
. - If an expression involves
double
andfloat
operands, thefloat
is promoted todouble
.
- If an expression involves
- Mixed-Type Arithmetic:
- If an expression involves operands of different types, the smaller type is promoted to the larger type.
- If one operand is of type
double
, the other is promoted todouble
.
- Constants and Literals:
- Numeric literals, such as
5
or10.5
, have a default type (e.g.,int
ordouble
). - Constants without a specified type are treated as
int
, but they can be promoted.
- Numeric literals, such as
examples:
int intValue = 5;
long longValue = 10L;
var result = intValue + longValue; // int is promoted to long
System.out.println(((Object) result).getClass()); //class java.lang.Long
double doubleValue = 10.5;
var result2 = intValue * doubleValue; // int is promoted to double
System.out.println(((Object) result2).getClass()); // class java.lang.Double
byte byteValue = 3;
short shortValue = 7;
var result3 = byteValue + shortValue; // Both bytes are promoted to int
System.out.println(((Object) result3).getClass());; // class java.lang.Integer
4.2. Assignment Operators and Casting Values
Operator | Description | Example |
---|---|---|
= | Assigns the value on the right to the left operand | int x = 10; |
When working with variables in Java, the assignment operator (=
) plays a fundamental role in assigning values to variables. Understanding how the compiler handles numeric promotion is crucial, especially when casting is required for compatibility between different data types.
basics:
At its core, the assignment operator is used to assign the value on the right side of the equation to the variable on the left side. Let’s dive into a simple example:
int x = 10; // Assigning the value 10 to the variable x
numeric promotion:
Numeric promotion refers to the automatic conversion of “smaller” data types to “larger” ones during expressions. Java will automatically promote from smaller to larger data types to avoid loss of precision. Consider the following example:
int intValue = 5;
double doubleValue = 10.5;
double result = intValue + doubleValue;
In this example, intValue
is automatically promoted to a double
before the addition operation. This promotion allows the result to be a double
without losing precision.
Casting values:
Casting is the explicit interpretation of one data type as another. It becomes crucial when converting between data types of different sizes.
- Widening, or implicit casting, occurs when converting to a larger data type. This is done automatically by the compiler, and no explicit casting is required.
- Narrowing, or explicit casting, is necessary when converting to a smaller data type. It involves placing the data type in parentheses before the value. Be cautious, as this may result in loss of precision.
int intValue = 50;
long longValue = intValue; // Implicit casting to a larger data type
long longValue = (long) intValue; // same as previous example, cast is optional
double doubleValue = 123.456;
int intValue = (int) doubleValue; // Explicit casting to a smaller data type
int intValue = doubleValue; // Compiler error without casting
Now, let’s consider an example where we multiply two short
values and attempt to assign the result directly to a short
. As we mentioned, Java automatically promotes the result of such operations to int
. To overcome this, we’ll use explicit casting to assign the result to a short
.
public class ShortMultiplicationExample {
public static void main(String[] args) {
short shortValue1 = 5;
short shortValue2 = 3;
// short result = shortValue1 * shortValue2; // Compilation error
// Using explicit casting to assign the result to a short
short result = (short) (shortValue1 * shortValue2);
}
}
In the given example, we have two short
variables, shortValue1
and shortValue2
. When attempting to multiply these two short
values and directly assign the result to another short
, a compilation error occurs due to automatic promotion to int
. To resolve this issue, explicit casting is employed. The multiplication result, enclosed in parentheses, is explicitly cast to short
. Subsequently, the result, now of type short
, is displayed.
4.3. Compound Assignment Operators
Operator | Description | Example |
---|---|---|
= | Assigns the value on the right to the left operand | int x = 10; |
+= | Adds the right operand to the left operand | x += 5; // equivalent to x = x + 5; |
-= | Subtracts the right operand from the left operand | x -= 3; // equivalent to x = x - 3; |
*= | Multiplies the left operand by the right operand | x *= 2; // equivalent to x = x * 2; |
/= | Divides the left operand by the right operand | x /= 4; // equivalent to x = x / 4; |
%= | Computes the remainder of the division | x %= 3; // equivalent to x = x % 3; |
Compound assignment operators in Java are a shorthand notation for combining an assignment with a built-in arithmetic or logical operation. These operators provide a concise way to perform an operation on a variable and update its value in a single step.
int x = 5;
x += 3; // Equivalent to x = x + 3;
System.out.println("Result: " + x); // Output: Result: 8
double y = 10.0;
y /= 2; // Equivalent to y = y / 2;
System.out.println("Result: " + y); // Output: Result: 5.0
Compound operators are useful for more than just shorthand. They can save us from having to explicitly cast values. Here’s an example:
short shortValue = 5;
shortValue += 3; // the result is implicitly cast to short
long longValue = 10;
int intValue = 5;
intValue = intValue * longValue; // compilation Error
intValue = (int) (intValue * longValue); // compiles because of explicit cast
intValue *= longValue; // compiles because of implicit cast
4.4. Comparing values
Determining equality in Java can be nuanced, especially when dealing with different data types and object comparisons. Let’s explore the equality operators, their application on primitives and objects, and provide examples of scenarios where equality checks can be tricky.
4.4.1. Equality operators
Operator | Applied to primitives | Applied to objects |
---|---|---|
== | Returns true if two values represent the same value | Returns true if the two values reference the same object |
!= | Returns true if two values represent different values | Returns true if the two values do not reference the same object |
4.4.1.1. Comparing Numeric or Character Primitive Types
When comparing two numeric or character primitive types, the equality operators can be used straightforwardly. If the numeric values are of different data types, Java will automatically promote them.
int num1 = 5;
double num2 = 5.0;
System.out.println(num1 == num2); // true (automatic promotion of int to double)
4.4.1.2. Comparing Boolean Values
Boolean values can be compared directly using the equality operators.
boolean bool1 = true;
boolean bool2 = false;
System.out.println(bool1 == bool2); // false
4.4.1.2. Comparing Objects (including null)
When comparing objects, the ==
operator compares the memory addresses (references) of the objects. This is crucial to understand because it checks whether the two references point to the same object, not whether the objects have the same content.
String str1 = new String("Hello");
String str2 = new String("Hello");
String str3 = str1;
System.out.println(str1 == str2); //false (same content, but different objects)
System.out.println(str1 == str3); //true (the references point to the same object)
System.out.println(null == null); // true
4.4.1.2. Pitfalls: Attempting to mix and match the types
Attempting to compare different types, without explicit casting will result in a compilation error.
int num = 5;
boolean someValue = false;
String word = "Hello";
// This will not compile
System.out.println(num == someValue); // cannot compare int to boolean
System.out.println(num == word); // cannot compare int to String
4.4.2. Relational Operators
Relational operators in Java are used to compare values and determine relationships between them.
Operator | Description |
---|---|
< | Returns true if the value on the left is less than the value on the right |
<= | Returns true if the value on the left is less than or equal to the value on the right |
> | Returns true if the value on the left is greater than the value on the right |
>= | Returns true if the value on the left is greater than or equal to the value on the right |
instanceof | Returns true if the object on the left is an instance of the specified class on the right |
4.4.2.1. Numeric Comparison Operators
When using the numeric comparison operators (>
, <
, >=
, <=
), if the two numeric operands are not of the same data type, the smaller one is promoted. This automatic promotion ensures that the comparison is done with compatible types.
int intValue = 5;
double doubleValue = 5.0;
System.out.println(intValue >= doubleValue); /* true (automatic promotion of int to double) */
4.4.2.2. ‘instanceof’ Operator
The instanceof
operator is useful for determining whether an arbitrary object is an instance of a particular class at runtime. It’s often employed in scenarios involving polymorphism.
public class TimeProcessor {
public static int processTime(Number time) {
if (time instanceof Integer) {
int intValue = (Integer) time;
return intValue;
} else {
System.out.println("The type is not an Integer.");
return time.intValue();
}
}
public static void main(String[] args) {
Number timeInteger = 42;
Number timeDouble = 24.5;
System.out.println(processTime(timeInteger)); // Output: 42
System.out.println(processTime(timeDouble)); /* Output: The type is not an Integer. */
}
}
In this example, the processTime
method takes a Number
type, and at runtime, it checks if it is an instance of Integer
. If true, it casts it to int
, and if false, it prints a message and returns the integer value of the time.
Attempting to use instanceof
with incompatible types, like comparing a String
with Integer
, will result in a compilation error.
String text = "Hello, World!";
// This is invalid and will not compile
System.out.println(text instanceof Integer);
The instanceof
operator in Java can be used to check if an object is an instance of a particular class, but it behaves differently when used with null
. When null
is used with instanceof
, it always returns false
.
public class InstanceOfExample {
public static void main(String[] args) {
String text = null;
// Using instanceof with null
if (text instanceof String) {
System.out.println("text is an instance of String.");
} else {
System.out.println("text is not an instance of String.");
}
}
}
In this example, text
is assigned the value null
, and when the instanceof
operator is used to check if it’s an instance of String
, the output will be “text is not an instance of String.”
4.4.3. Logical Operators
Logical operators are indispensable tools for creating sophisticated conditional expressions in Java. Understanding how these operators work and incorporating them into your code can significantly enhance the expressiveness and logic of your programs. Whether combining conditions with &&
and ||
or negating values with !
, logical operators are powerful elements of Java programming that contribute to writing more flexible and dynamic code.
Operator | Description |
---|---|
& | Logical AND is true only if both values are true |
| | Inclusive OR is true if at least one of the values is true |
^ | Eclusive XOR is true only if one value is true and the other is false |
The truth tables for these operators look like this:
A | B | A & B |
---|---|---|
false | false | false |
false | true | false |
true | false | false |
true | true | true |
A | B | A | B |
---|---|---|
false | false | false |
false | true | true |
true | false | true |
true | true | true |
A | B | A ^ B |
---|---|---|
false | false | false |
false | true | true |
true | false | true |
true | true | false |
public class LogicalAndExample {
public static void main(String[] args) {
boolean x = true;
boolean y = false;
boolean result = x & y;
System.out.println("x & y: " + result); // Output: x & y: false
}
}
public class LogicalOrExample {
public static void main(String[] args) {
boolean x = true;
boolean y = false;
boolean result = x | y;
System.out.println("x | y: " + result); // Output: x | y: true
}
}
public class LogicalXorExample {
public static void main(String[] args) {
boolean x = true;
boolean y = false;
boolean result = x ^ y;
System.out.println("x ^ y: " + result); // Output: x ^ y: true
}
}
Short-circuit operators:
the short-circuit logical operators are &&
(logical AND) and ||
(logical OR). These operators exhibit short-circuiting behavior, meaning they evaluate the second operand only if necessary. This behavior can be useful for optimizing code and preventing unnecessary evaluations.
Operator | Description | Short-Circuiting Behavior |
---|---|---|
&& | Short-circuit AND – Returns true if both operands are true | Skips evaluation of the right operand if the left is false |
|| | Short-circuit OR – Returns true if at least one operand is true; otherwise, returns false. | If the left side is true, then the right side will not be evaluated |
public class ShortCircuitAndExample {
public static void main(String[] args) {
int x = 5;
boolean y = false;
if (x < 0 && computeResult()) {
System.out.println("Condition is true.");
} else {
System.out.println("Condition is false.");
}
}
private static boolean computeResult() {
System.out.println("Computing result...");
return true;
}
}
In this example, the computeResult()
method is not called because the left operand (x < 0
) is false.
Using short-circuit operators is not only efficient but also provides a way to handle certain conditions more gracefully, particularly when dealing with potential null values.
public class AvoidNullPointerExceptionExample {
public static void main(String[] args) {
String text = null;
if (text != null && text.length() > 0) {
System.out.println("Length is greater than 0.");
} else {
System.out.println("Length is 0 or text is null.");
}
}
}
In this example, the short-circuit AND (&&
) prevents the evaluation of text.length()
if text
is null, thus avoiding a potential null pointer exception.
5. Ternary Operator
The ternary operator in Java, represented by ? :
, is a unique operator that takes three operands. It provides a concise way to express a conditional statement in a single line. The syntax of the ternary operator is:
booleanExpression ? expression1 : expression2
The ternary operator is essentially a condensed form of an if-else statement. The above expression can be equivalent to the following if-else statement:
if (booleanExpression) {
result = expression1;
} else {
result = expression2;
}
example:
public class TernaryOperatorExample {
public static void main(String[] args) {
int x = 5;
int y = 10;
// Using the ternary operator to find the maximum
int max = (x > y) ? x : y;
System.out.println("Maximum value: " + max); // Output: Maximum value: 10
}
}
In this example, the ternary operator is used to find the maximum value between x
and y
. If x
is greater than y
, it assigns the value of x
to max
; otherwise, it assigns the value of y
.
public class TernaryOperatorStringExample {
public static void main(String[] args) {
int score = 85;
// Using the ternary operator to determine the result string
String result = (score >= 70) ? "Pass" : "Fail";
System.out.println("Result: " + result); // Output: Result: Pass
}
}
Here, the ternary operator is employed to determine the result string based on the value of the score
. If the score is greater than or equal to 70, it assigns “Pass” to the result
; otherwise, it assigns “Fail”.