Java 11 Developer Certification - Java Operators
October 14, 2020
What we are covering in this lesson
- What are Java Operators
- Unary Operators
- Binary Operators
- Ternary Operators
- Special Cases
What are Java Operators
Java operators are symbols that are used to perform mathematical or logical manipulations. They can be classified based on the number of operands the operator has, unary, binary, ternary OR based on the type of operation it performs.
Important things to note about operators
- Always check for the precendence of the operators. Unary operators are evaluated before binary operators.
- Among the unary operators, the postfix increment and decrement operators have the highest precedence.
- Unary operators, with the exception of the prefix and postfix operators, promote the variable to and
int
if it’s smaller than an int. - If all operators have the same precedence, the expression will be evaluated from left to right, with the exception of the simple and compound assignment operators, which are evaluated from right to left.
- Parentheses always take the highest precedence.
Unary Operators
Expressions with unary operators group right to left, so -~x
is equal to -(~x)
.
Lets have a look at some of the unary operators
- Prefix Decrement -
--x
will decrement the value ofx
before expression in evaluated - Postfix Decrement -
x--
will evaluate the expression and then decrement the value ofx
- Prefix Increment -
++x
will increment the value ofx
before expression in evaluated - Postfix Increment -
x++
will evaluate the expression and then increment the value ofx
Note: it is possible in case of postfix increment and decrement that the variable might go out of scope - Unary Minus -
-x
returns negated value ofx
without changing its value - Unary Plus -
+x
this is allowed but it does nothing to the value ofx
Note: Ifx
is a datatype smaller thanint
, then it will get promoted toint
- Logical Complement Operator -
!x
returns the complement of a boolean value - Bitwise Complement Operator -
~x
turns 0 to 1 or 1 to 0 Note: `~x equals (-x)-1 - Cast Operator -
(int) x
used to cast the value ofx
to any datatype
It should always be remembered that the postfix increment and decrement operators do not change the value of its operand until the expression it is operating on is complete OR the same operand is used again in the same statement. If the operation in interrupted, the postfix increment or decrement may not be actually applied. Postfix and prefix operators can be stand alone statements, meaning they are directly incrementing or decrementing the value in the variable. If we use unary minus for example, it doesnt change the value of the varible it is operated upon.
package maxCode.online.operators;
public class UnaryOperators {
public static void main(String[] args) {
int a = 1;
System.out.println("value of a is " + a);
/* ++a and a++ behave the same if not used in any complex expression */
++a; // a = a+1
System.out.println("a after ++a = " + a);
a = 1;
System.out.println("value of a is " + a);
a++; // a = a+1
System.out.println("a after a++ = " + a);
/* difference in pre and postfix operators */
a = 1;
System.out.println("value of a is " + a);
System.out.println("a after ++a = " + ++a);
a = 1;
System.out.println("value of a is " + a);
System.out.println("a after a++ = " + a++);
System.out.println("And now the value of a is: " + a);
/* postfix increment in variable declaration */
a = 1;
System.out.println("value of a is " + a);
int a2 = a++;
System.out.println("The value of a is " + a);
System.out.println("The value of a2 is " + a2);
/* postfix in an expression */
a = 1;
System.out.println("value of a is " + a);
if (++a == 1) {
System.out.println("Inside if statement with value of a = " + a);
}
/* Prefix operator in loop */
int b = 5;
System.out.println("value of b is " + b);
int i = 0;
while (--b > 0) { // Use a prefix decrement
i++;
}
System.out.println("Prefix decrement operator used, loopiterations = " + i + ", b = " + b);
/* Postfix operator in loop */
b = 5;
System.out.println("value of b is " + b);
i = 0;
while (b-- > 0) { // Use a postfix decrement
i++;
}
System.out.println("Postfix decrement operator used, loopiterations = " + i + ", b = " + b);
}
}
Output
value of a is 1
a after ++a = 2
value of a is 1
a after a++ = 2
value of a is 1
a after ++a = 2
value of a is 1
a after a++ = 1
And now the value of a is: 2
value of a is 1
The value of a is 2
The value of a2 is 1
value of a is 1
value of b is 5
Prefix decrement operator used, loopiterations = 4, b = 0
value of b is 5
Postfix decrement operator used, loopiterations = 5, b = -1
Now lets have a look at unary minus and plus. The unary minus returns a negative value if the value in the operand is positive, and positive value if the operand value is negative.
The unary plus returns the same sign as the operand, and has no effect on the value.
Both operators will promote the value to an int
.
The value of the operand reference itself stays unchanged. The expression’s value must be returned to a variable or used in an expression in case of unary plus or minus.
The bitwise complement operator flips the bit for the entire value of the variable. So the binary literal value of the integer zero, gets every bit flipped to one, making it’s integer value minus one. The logical complement operator only works on a Boolean, and changes false to true.
package maxCode.online.operators;
public class UnaryOperators2 {
public static void main(String[] args) {
// Unary Minus
int a = 1, b = -a;
// the value of actual operand a does not change
System.out.println("a = " + a + "; b = " + b);
a = -1;
b = -a;
System.out.println("a = " + a + "; b = " + b);
// Unary Plus
a = 1;
b = +a; // Not to be mistaken for b+=a;
System.out.println("a = " + a + "; b = " + b);
a = -1;
b = +a;
System.out.println("a = " + a + "; b = " + b);
// Bitwise Complement Operator ~x
// when value is x then ~x = (-x)-1;
int bin1 = 0b00000000_00000000_00000000_00000000;
int bin2 = ~bin1;
System.out.println("bin1 = " + bin1 +
" (" + Integer.toBinaryString(bin1) + "), " +
"bin2 = " + bin2 +
" (" + Integer.toBinaryString(bin2) + ")");
// Logical Complement Operator !x
boolean myBoolean = false;
boolean newBoolean = !myBoolean;
System.out.println("myBoolean = " + myBoolean +
", the opposite is = " + newBoolean);
}
}
a = 1; b = -1
a = -1; b = 1
a = 1; b = 1
a = -1; b = -1
bin1 = 0 (0), bin2 = -1 (11111111111111111111111111111111)
myBoolean = false, the opposite is = true
Binary Operators
Binary operators, as the name suggests, operate on 2 operands. Some examples of binary operators are mentioned below
- Multiplicative operators (*, /, %)
- Additive operators (+, -)
- Shift operators (<<, >>, >>>)
- Relational operators (<, >, <=, >=, instanceof)
- Equality operators (==, !=)
- Bitwise and Logical operators (&, ^, |)
- Conditional AND (&&) - evaluates right hand operand only if left hand operand is true
- Conditional OR (||) - evaluates right hand operand only if left hand operand is false
- Conditional operator (?:) - This is a ternary operator
- Assignment operators (=, +=, *=, etc) - (
a=b=c
meansa=(b=c)
so c assigned to b and then b assigned to a) - Lambda operator (->)
For each group, precendence is equal among them, and group left to right.
But for bitwise and logical operators, &
(AND) has highest precendence and |
(OR) has lowest
Unary operators always have higher precedence than binary ones.
Just like unary operator, if any operand type is smaller than int, both operands will be automatically promoted to int. If any operand is larger than int, then it will be promoted to the larger type.
So any operation on numeric values will never result in a value which is smaller than int.
Multiplicative operators have precedence over Additive operators, so they will be evaluated first. So in effect, a + e * b - f / c % b
can be evaluated as a + (e * b) - (f / c) % b
.
Modulus operator returns the remainder of the division operation. So 10 % 5
will return 0
and 10 % 3
will return 1
.
Java also accepts floating point operands for modulus operator (unline C/C++ which only accepts int).
There is one difference in int and floating operands, if we do a 10 % 0
(effectively divide by 0 and get the remainder), int operands line will throw ArithmeticException for / by zero
but floating operands line will return a NaN
or Not A Number value, without throwing any exception.
Lets look at some code to understand the other binary operators.
package maxCode.online.operators;
public class BinaryOperators {
public static void main(String[] args) {
int a = 1;
int b = 2;
int c = 3;
int d = 4;
System.out.println("--- Shift Operators ---");
// left shift (<<)
// bit pattern shifted left by 2 places (right operand = 2)
// 0b0000_0001 becomes 0b0000_0100
System.out.println("result of left shift (00000001 << 2 ) = "
+ String.format("%8s",Integer.toBinaryString(0b00000001 << 2)).replace(' ', '0'));
// right shift (>>)
// bit pattern shifted left by 3 places (right operand = 3)
// 0b10001000 becomes 0b00010001
System.out.println("result of right shift (10001000 >> 3 ) = "
+ String.format("%8s",Integer.toBinaryString(0b10001000 >> 3)).replace(' ', '0'));
// >>> right shift unsigned
System.out.println("result of unsigned right shift (" +
"10000010_00000010_00000010_00000010 >>> 1 ) = "
+ String.format("%32s",Integer.toBinaryString(
0b10000010_00000010_00000010_00000010 >>> 1)).replace(' ', '0'));
// Compare right shift unsigned to right shift results
System.out.println("result of signed right shift (" +
"10000010_00000010_00000010_00000010 >> 1 ) = "
+ String.format("%32s",Integer.toBinaryString(
0b10000010_00000010_00000010_00000010 >> 1)).replace(' ', '0'));
// Relationship operators <, <=
// | - logical or
// || - conditional logical or
System.out.println("\nResults using relationship operators" +
" and logical or operators (| ||) ");
c = 0;
d = 0;
if ((c++ <= d) | (++c < d)) {
System.out.println("Evaluation [(e++ <= f) | (++e < f)] met");
}
System.out.println("Logical | (OR) will evaluate both expressions: e = "
+ c + ", and f = " + d);
c = 0;
d = 0;
if ((c++ <= d) || (++c < d)) {
System.out.println("Evaluation [(e++ <= f) || (++e < f)] met");
}
System.out.println("Conditional Logical || evaluates only first " +
"expression if it evaluates to true: e = "
+ c + ", and f = " + d);
c = 0;
d = 0;
if ((c++ < d) || (++c <= d)) {
System.out.println("Evaluation [(e++ <= f) || (++e < f)] met");
}
System.out.println("Conditional Logical || (OR) will evaluate" +
" both expressions ONLY if first expression is false : e = "
+ c + ", and f = " + d);
System.out.println("\nResults using relationship operators" +
" and logical or operators (& &&) ");
a = 0;
b = 10;
if ((++a > b) & (++a >= b)) {
System.out.println("Evaluation [(++a > b) & (++a >= b)] met");
}
System.out.println("Logical & (AND) will evaluate both expressions: a = "
+ a + ", and b = " + b);
a = 0;
b = 10;
if ((++a > b) && (++a >= b)) {
System.out.println("Evaluation [(++a > b) && (++a >= b)] met");
}
System.out.println("Conditional && (AND) will evaluate only first " +
"expression if it evaluates to false: a = "
+ a + ", and b = " + b);
a = 0;
b = 0;
if ((++a > b) && (++a >= b)) {
System.out.println("Evaluation [(++a > b) && (++a >= b)] met");
}
System.out.println("Conditional && (AND) will evaluate both " +
"expressions if first evaluates to true: a = "
+ a + ", and b = " + b);
}
}
Output
--- Shift Operators ---
result of left shift (00000001 << 2 ) = 00000100
result of right shift (10001000 >> 3 ) = 00010001
result of unsigned right shift (10000010_00000010_00000010_00000010 >>> 1 ) = 01000001000000010000000100000001
result of signed right shift (10000010_00000010_00000010_00000010 >> 1 ) = 11000001000000010000000100000001
Results using relationship operators and logical or operators (| ||)
Evaluation [(e++ <= f) | (++e < f)] met
Logical | (OR) will evaluate both expressions: e = 2, and f = 0
Evaluation [(e++ <= f) || (++e < f)] met
Conditional Logical || evaluates only first expression if it evaluates to true: e = 1, and f = 0
Conditional Logical || (OR) will evaluate both expressions ONLY if first expression is false : e = 2, and f = 0
Results using relationship operators and logical or operators (& &&)
Logical & (AND) will evaluate both expressions: a = 2, and b = 10
Conditional && (AND) will evaluate only first expression if it evaluates to false: a = 1, and b = 10
Evaluation [(++a > b) && (++a >= b)] met
Conditional && (AND) will evaluate both expressions if first evaluates to true: a = 2, and b = 0
Important things to note
- Bitwise OR bit is 1 if one or the other operand bit is 1.
- Bitwise AND bit is 1 if both operand bits are one.
- Bitwise XOR bit is 0 if both operand bits are of the same value, otherwise it’s one.
- The assignment operators, are not promoting the results of the compound assignments, so you can use these operators on smaller than int variables, as we can see in the below java code.
- All binary operators promote results at a minimum to int.
package maxCode.online.operators;
public class BinaryOperators2 {
public static void main(String[] args) {
int a = 1;
int b = 2;
int c = 1;
// == checks equality of values for primitive data types
if (a == c) {
System.out.println("a and c primitive values are equal");
}
if (a != b) {
System.out.println("a and b are not equal because primitive data values are not equal");
}
// == checks equality of String literals or String objects
String s1 = "hello";
String s2 = new String(s1);
String s3 = s2.intern();
if (s1 == s3) {
System.out.println("Strings are equal if they are interned or are String literals");
}
if (s1 != s2) {
System.out.println("Otherwise String objects are not equal");
}
Object o1 = s1;
Object o2 = s1;
Object o3 = new String(s1);
if (o1 == o2) {
System.out.println("Objects are equal if they reference same object");
}
if (o1 != o3) {
System.out.println("Otherwise objects are not equal");
}
System.out.println("\n--- Bitwise Operators, AND, OR, XOR ---");
System.out.println("result of bitwise AND (0b0000_0000 & 0b1111_1111) = "
+ String.format("%8s", Integer.toBinaryString(
0b0000_0000 & 0b1111_1111)).replace(' ', '0'));
System.out.println("result of bitwise AND (0b1111_0000 & 0b1111_1111) = "
+ String.format("%8s", Integer.toBinaryString(
0b1111_0000 & 0b1111_1111)).replace(' ', '0'));
System.out.println("result of bitwise OR (0b0000_0000 | 0b1111_1111) = "
+ String.format("%8s", Integer.toBinaryString(
0b0000_0000 | 0b1111_1111)).replace(' ', '0'));
System.out.println("result of bitwise OR (0b0000_0000 | 0b0000_1111) = "
+ String.format("%8s", Integer.toBinaryString(
0b0000_0000 | 0b0000_1111)).replace(' ', '0'));
System.out.println("result of bitwise XOR (0b0000_0000 ^ 0b1111_1111) = "
+ String.format("%8s", Integer.toBinaryString(
0b0000_0000 ^ 0b1111_1111)).replace(' ', '0'));
System.out.println("result of bitwise XOR (0b0000_1111 ^ 0b0000_1111) = "
+ String.format("%8s", Integer.toBinaryString(
0b0000_1111 ^ 0b0000_1111)).replace(' ', '0'));
System.out.println("\nResults from assignment operators");
// Assignment Operator
byte b1 = 2;
byte b2 = 2;
byte b3 = 5;
// Multiplicative Assignments, note that variables NOT promoted
b1 *= 2;
System.out.println("b1 (after b1 *= 2 ) = " + b1);
b2 /= 2;
System.out.println("b2 (after b2 /= 2) = " + b2);
b3 %= 2;
System.out.println("b3 (after b3 %= 2) = " + b3);
// Additive Assignments, note that variables NOT promoted
b1 = 2;
b2 = 2;
b1 += 2;
System.out.println("b1 (after b1 += 2) = " + b1);
b2 -= 2;
System.out.println("b2 (after b2 -= 2) = " + b2);
// Assignment operators with bit shift
b1 = 16;
b2 = 16;
b3 = 16;
b1 <<= 1;
System.out.println("b1 (after b1 <<= 1) = " + b1);
b2 >>= 1;
System.out.println("b2 (after b2 >>= 1) = " + b2);
b3 >>>= 1;
System.out.println("b3 (after b3 >>>= 1) = " + b3);
// Assignment operators with bit and, xor, or
b1 = 0b0000;
b1 &= 0b1111;
System.out.println("b1 (after b1 &= 0b1111 ) = " + b1);
b1 |= 0b1111;
System.out.println("b1 (after b1 |= 0b1111 ) = " + b1);
b1 ^= 15;
System.out.println("b1 (after b1 ^= 15 )= " + b1);
// This code results in an overflow
byte testByte = 127;
testByte += 1;
System.out.println("testByte = " + testByte);
// This code results in a compiler error, shows incompatible types
// byte testByte2 = 127;
// testByte2 = testByte2 + 1;
// System.out.println("testByte2 = " + testByte2);
}
}
Output
a and c primitive values are equal
a and b are not equal because primitive data values are not equal
Strings are equal if they are interned or are String literals
Otherwise String objects are not equal
Objects are equal if they reference same object
Otherwise objects are not equal
--- Bitwise Operators, AND, OR, XOR ---
result of bitwise AND (0b0000_0000 & 0b1111_1111) = 00000000
result of bitwise AND (0b1111_0000 & 0b1111_1111) = 11110000
result of bitwise OR (0b0000_0000 | 0b1111_1111) = 11111111
result of bitwise OR (0b0000_0000 | 0b0000_1111) = 00001111
result of bitwise XOR (0b0000_0000 ^ 0b1111_1111) = 11111111
result of bitwise XOR (0b0000_1111 ^ 0b0000_1111) = 00000000
Results from assignment operators
b1 (after b1 *= 2 ) = 4
b2 (after b2 /= 2) = 1
b3 (after b3 %= 2) = 1
b1 (after b1 += 2) = 4
b2 (after b2 -= 2) = 0
b1 (after b1 <<= 1) = 32
b2 (after b2 >>= 1) = 8
b3 (after b3 >>>= 1) = 8
b1 (after b1 &= 0b1111 ) = 0
b1 (after b1 |= 0b1111 ) = 15
b1 (after b1 ^= 15 )= 0
testByte = -128
Ternary operators
Format for a ternary operator is operand1 ? operand2 : operand3
. It is usually compared to if
statement, but it is an operator and not a statement and so used in some expression or to return value to a variable.
The first operand must be a boolean value or be an expression whose result is a boolean value.
The other operands datatypes must be a common type. If operand2 and operand3 are of different datatypes, we must assign the returned value to an Object
type variable.
- If operand1 evaluates to true, then the resulting value will be operand2.
- If operand1 evaluates to false, then the resulting value will be operand3.
If operand two and operand three are both expressions, only one of the expressions is ever evaluated, based on the value of operand one. It will never be the case that both expressions are evaluated. It can be pretty much self explanatory by the below java code.
package maxCode.online.operators;
public class TernaryOperator {
public static void main(String[] args) {
// The value returned from this ternary operation is a boolean.
boolean hasArguments = (args.length == 0) ? false : true;
System.out.println("Result of Example 1 = " + hasArguments);
// Value returned from ternary operation is primitive data
boolean b = true;
int result = (b && (hasArguments && args[0].equals("10"))) ? 10 : 0;
System.out.println("Result of Example 2 = " + result);
// Value returned from ternary operation is either an Integer or String
Object objectResult = (b && (hasArguments && args[0].equals("10"))) ? 10 : "Not ten";
System.out.println("Result of Example 3 = " + objectResult);
// Expressions only evaluated in the one of the cases
int x = 0;
int y = 0;
int newResult = (b && (hasArguments && args[0].equals("10"))) ? x++ : y++;
System.out.println("Result of Example 4 = " + newResult + ", x = " + x + ", y = " + y);
}
}
Output
Result of Example 1 = false
Result of Example 2 = 0
Result of Example 3 = Not ten
Result of Example 4 = 0, x = 0, y = 1
Keep in mind the outputs mentioned above will be displayed only if we do not pass any arguments. We can pass different set of arguments to get a different output. The last output shows that since we didnt pass any arguments, operand1 returned false and only y (operand3) got incremented, with no change in x value.
Special Cases
Lets have a look at the below code to explore some special scenarios in case of operators.
package maxCode.online.operators;
public class OperatorsSpecial {
public static void main(String[] args) {
int number = 10;
int result = 0;
// result = --number - number--
// result always resolves to zero
for (int i = 10; i <= 50; i += 10) {
number = i;
result = --number - number--; //9 - 9 = 0
System.out.println("i = " + i + ", number = " + number + ", result = " + result);
}
System.out.println();
// int result = number-- - --number;
// result is always the number 2
for (int i = 10; i <= 50; i += 10) {
number = i;
result = number-- - --number; //10 - 8 = 2
System.out.println("i = " + i + ", number = " + number + ", result = " + result);
}
System.out.println();
for (int i = 10; i <= 20; i += 10) {
number = i;
result = number-- - number++ * --number; //10 - 9 * 9 = -71
System.out.println("i = " + i + ", number = " + number + ", result = " + result);
}
System.out.println();
// (number--) == (number += 1)
// evaluates to true!
number = 10;
boolean isEqual = (number--) == (number += 1);
System.out.println("isEqual = " + isEqual + ", for number = " + number);
System.out.println();
// number = number--; here value in number stays unchanged
number = 10;
number = number--;
System.out.println("number = " + number);
}
}
Output
i = 10, number = 8, result = 0
i = 20, number = 18, result = 0
i = 30, number = 28, result = 0
i = 40, number = 38, result = 0
i = 50, number = 48, result = 0
i = 10, number = 8, result = 2
i = 20, number = 18, result = 2
i = 30, number = 28, result = 2
i = 40, number = 38, result = 2
i = 50, number = 48, result = 2
i = 10, number = 9, result = -71
i = 20, number = 19, result = -341
isEqual = true, for number = 10
number = 10
Looking at the above outputs, we can safely say that
--number - number--
will always be0
since prefix operator reduces the number value and the same number is getting subtracted later due to postfix usage.number-- - --number
will always be2
since postfix operator will update the value after expression and prefix will reduce the new reduces number by one more time, so a difference of 2.(number--) == (number+=1)
will always be true, again due to postfix usage.number = number--
does not change the value of number as we are assigning the result of postfix operator to the same number.
The operators can be a bit confusing when used with loops or post/prefix operators. Best way to understand it is to try and practice as much as possible!