Operators
Table of Contents
Introduction
Up to this point, we've discussed data, how that data is stored in variables, and how those variables must all have types. This gives us everything we need in order to understand how we can write Java code to make these typed variables interact with one another. These interactions can be effaced through the use of operators in Java, i.e., special symbols which have semantic meaning for computations involving data.
Infix Notation
When language designers begin the long, exciting process of building out a language, they have to make a choice on how exactly that language is going to look. One of the first considerations is what sort of notation will be used for parsing operations: prefix, infix, or postfix. A quick demonstration will show what I mean:
- Prefix →
+ 2 1 - Infix →
2 + 1 - Postfix →
2 1 +
All of the above are ways of representing the semantic action of "add together the constants two and one". For the programmer, the difference is purely visual. For the language designer, or, more accurately, the compiler developer, the difference will manifest itself in how the language is parsed1. (We'll come back to this when we discuss stacks and queues.) For now, all we need to know is that Java uses infix notation for all of its binary operators.
Operators in Java
Here, I will enumerate all of the operators in Java and provide examples for how each one functions.
Assignment
Assignment operators are used to capture values and place them into variables.
=
= is the basic assignment operator. The left operand must be a lvalue while the right operand must have a rvalue. For the following examples, assume int x and int y have already been declared"
x = 1→1is now stored in the memory location tied toxx = y→ The value tied to the memory location ofyat the time of evaluation is now stored in the memory location tied tox
Without getting into too much theory, lvalues are locations in memory (variables, or pointer dereferences) whereas rvalues are data (which can include memory locations). The variable x returns an l or r value based on its surrounding context.
Unary Operators
Most operators in Java are binary operators which means they take two operands. In contrast, a unary operator only takes one operand.
++ / --
++/-- are the unary prefix increment/decrement operators. They perform an increment or decrement of 1 to the variable given. Assume int x = 0; has already been processed:
++x→1and a side effect of settingxequal to1--x→-1and a side effect of settingxequal to-1
The prefix operators above can be supplied to the end of a variable to create postfix operators. The difference is that x++ would return 0, the value of x prior to the increment/decrement. The side effect remains unchanged (x would be equal to 1).
+
+ can be used as a unary operator. It has limited use and only exists to maintain symmetry between + and -.
+1→1
-
- can be used as a unary operator for negation.
-1→-1-(-1)→1
~
~ is the bitwise complement operator. I will flip the bits in the underlying integral type.
~((byte) 1)→-2(0b00000001→0b11111110)~0→-1(0x00000000→0xFFFFFFFE)
!
! is the logical complement operator. It takes a boolean true/false and flips it to its opposite.
!true→false!false→true
Multiplicative Operators
*
* is the multiplication operator. It is used to multiply two numeric values.
4 * 2→8
/
/ is the division operator. It is used to divide two numeric values.
4 / 2→2
When two integers are involved integer division is performed. Functionally, that means the fractional result of the operation is removed. 2 / 4 equals 0.
%
% is the remainder operator. It is used to get the remainder after the left operand is divided by the right operand.
4 % 2→0(2goes into4two times with0remainder)2 % 4→2(4goes into20 times with2remainder)
Additive Operators
+ (Numerics)
+ is the addition operator when both operands are numerics. It is used to add two numeric values.
4 + 2→6
-
- is the subtraction operator. It is used to subtract two numeric values.
4 - 2→2
+ (Strings)
+ is the string concatenation operator when one of the operands is a String type. It is used to create a new String object.
"cat" + "dog"→"catdog"4 + "2"→"42""2" + 4→"24"
Implicit Conversion, Again
Something that often trips students up is the fact that implicit conversion kicks in during the use of binary arithmetic operators. For example,
1 + 1.5→double1 + 1.5f→float1 / 2.0→double
And so on. In all of the above cases 1 is converted to the wider type.
Shift Operators
<<
<< is the left bitshift operator. From the specification: "The value of n << s is n left-shifted s bit positions; this is equivalent (even if overflow occurs) to multiplication by two to the power s."
0xf << 4→0xf00x80000000 << 1→0x00
>>
>> is the signed right bitshift operator. From the specification: "The value of n >> s is n right-shifted s bit positions with sign-extension. The resulting value is floor(n / 2^s). For non-negative values of n, this is equivalent to truncating integer division, as computed by the integer division operator /, by two to the power s."
0xf0 >> 4→0x0f0xf >> 4→0x000x80000000 >> 1→0xc0000000
>>>
>>> is the signed right bitshift operator. From the specification:
The value of n >>> s is n right-shifted s bit positions with zero-extension, where:
- If n is positive, then the result is the same as that of n >> s.
- If n is negative and the type of the left-hand operand is int, then the result is equal to that of the expression (n >> s) + (2 << ~s).
- If n is negative and the type of the left-hand operand is long, then the result is equal to that of the expression (n >> s) + (2L << ~s).
0xf0 >>> 4→0x0f0xf >>> 4→0x000x80000000 >>> 1→0x40000000
Relational Operators
Relational operators are only capable of being used with numeric types2. They are used to perform comparisons between two values. All relational operations return a boolean type, either true or false.
>
> is the "greater than" operator. It is used to check if the left operand is greather than the right operand.
2 > 4→false
<
< is the "less than" operator. It is used to check if the left operand is less than the right operand.
2 < 4→true
>=
>= is the "greater than or equal to" operator. It is used to check if the left operand is greater than or equal to the right operand.
2 >= 4→false4 >= 4→true
<=
<= is the "less than or equal to" operator. It is used to check if the left operand is less than or equal to the right operand.
2 <= 4→true4 <= 4→true
Equality Operators
Equality operators are used to check equality between two values. All equality operations return either a boolean type.
==
== is the equality operator. It is used to check if the left operand is equal to the right operand.
2 == 4→false2 == 2→true
Strict equality is dangerous when comparing floating point values! Relational operators should be used instead when dealing with floating points.
Similarly, equality is dangerous when comparing reference types (non-primitives)! We will learn more about this when we discuss reference types in more detail.
!=
!= is the inequality operator. It is used to check if the left operand is not equal to the right operand.
2 != 4→12 != 2→0
Bitwise Operators
Bitwise operators require an integral type. They are used to perform "bit-twiddling", i.e. setting/unsetting bits in integer values.
&
& is the bitwise AND operator. Each bit in the resulting value is set if and only if each of the corresponding bits in the converted operands is set.
0xff & 0xf0→0xf00xfc & 0xf3→0xf00xff & 0x0f→0x0f
|
| is the bitwise OR operator. Each bit in the resulting value is set if one of the corresponding bits in the converted operands is set.
0xff | 0xf0→0xff0xfc | 0xf3→0xff0xff | 0x0f→0xff
^
^ is the bitwise XOR operator. Each bit in the resulting value is set if and only if exactly one of the corresponding bits in the converted operands is set.
0xff ^ 0xf0→0x0f0xfc ^ 0xf3→0x0f0xff ^ 0x0f→0xf0
~
~ is the bitwise NOT operator. Each bit in the resulting value is set if and only if the corresponding bits in the operand is not set.
~((char)0xff)→0x00~((char)0xfc)→0x03~((char)0x0f)→0xf0
If I don't explicitly cast the hexadecimal constant to a char, the javac compiler will keep it as an int and flip more bits than I wanted to show.
Conditional Operators
Conditional operators can only take the boolean type.
&&
&& is the conditional AND operator. It is used for boolean conjunction.
true && false→falsetrue && true→true
&& is guaranteed to be evaluated from left to right. This means that if the left operand is false, the right operand will not be evaluated. This is often called "short circuiting".
||
|| is the conditional OR operator. It is used for boolean disjunction. Much like &&, || will short circuit if the left operand resolves to a true.
false || false→falsetrue || false→true
Operators in Other Programming Languages
It just so happens that Java-style syntax borrows heavily from the C programming language. Many of the symbols chosen by C are used in other programming languages. For example, almost all of the operators and their symbols in C are identical in Python—and by extension, Java. Even if they weren't, many programming languages share the exact same logic and abstractions that C does (at least in the general sense). In Java, C and Python, 2 * 3 represents the multiplication of the integers 2 and 3. Knowing the ins and outs of one programming language prepares you for learning another!
Exercises
- Predict the result of each of the following expressions before writing a Java program to verify them. Pay close attention to integer division:
7 / 27 / 2.07.0 / 2(double) 7 / 2(double) (7 / 2)
- Write a Java program that declares two
intvariablesa = 15andb = 4and prints the result of all five arithmetic operators applied to them. What type is the result of each operation? - The chapter that strict equality is dangerous with floating point values. Write a Java program that computes
0.1 + 0.2and checks if it equals0.3using==. Print the result. Then print the raw value of0.1 + 0.2usingSystem.out.printf("%.20f%n", 0.1 + 0.2). Explain what you observe. - Write a Java program that prints the results of all six relational operators (
>,<,>=,<=,==,!=) comparinga = 5andb = 5. Predict each result before running. - The chapter states that
&&and||short circuit. Write a Java program that demonstrates short circuiting by placing a division by zero on the right side of a&&where the left side isfalse, and on the right side of a||where the left side istrue. Does the program crash? Explain why or why not. Using only bitwise operators, write a Java program that:
- Sets the 4th bit of
0x00to 1 using| - Clears the 4th bit of
0xffusing& - Flips the 4th bit of
0xffusing^
Print each result in hexadecimal using
System.out.printf("%x%n", result).- Sets the 4th bit of
- Write a Java program that demonstrates left and right bitshifting on the value
0x01. Left shift it 4 times, printing the result each time. Then right shift the final result 4 times back, printing each time. What do you observe? Express each result in both hex and decimal. Then repeat using>>>instead of>>on a negative value and explain the difference. - The chapter states that
1 + 1.5produces adouble. Write a Java program that performs the following mixed-type operations and prints each result. UseSystem.out.printlnand observe what type is inferred from the output:int + floatint + doublefloat + double
- Evaluate the following expressions by hand using the bitwise operator rules, then verify in Java using
System.out.printf("%x%n", result):0xAB & 0x0F0xAB | 0x0F0xAB ^ 0x0F~((byte)0xAB)
- The chapter introduces three different notations for writing operations: prefix, infix, and postfix. Rewrite the following Java expressions (which use infix notation) in both prefix and postfix notation:
2 + 310 - 43 * 58 / 2
Footnotes:
Most popular languages nowadays default to infix notation; however, there are still some languages which do not. For example, Lisp, a relatively popular functional programming language, and all of its various dialects use prefix notation. To my knowledge, Forth is the only programming language that anyone uses which sticks to postfix notation.