The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.
An operator is one or more symbols in combination, such as the well-known arithmetic operators minus (-
) and plus (+
) or the more advanced instanceof
. When you apply operators on values or variables, you get a result from the operation. Such operations are fundamental in programming because their result is assigned to a variable or further evaluated until the final goal of the program is accomplished.
For this tutorial, you have to be familiar with an operand, which is the value or variable on which the operators are applied. Depending on the number of operands, operators can be divided into three groups. First, when there is only one operand in the operation, the operators are called unary. Similarly, binary operators involve two operands. Finally, when there are three operands, the operator is ternary. Following this categorization, this tutorial is organized into three main parts for each type of operator.
In this tutorial, you will use all three types of operators to manipulate primitive data types, such as in math equations. You will also use operators in more advanced scenarios with reference types and explore some of the rules for operator precedence.
To follow this tutorial, you will need:
An environment in which you can execute Java programs to follow along with the examples. To set this up on your local machine, you will need the following:
Familiarity with Java and object-oriented programming, which you can find in our tutorial, How To Write Your First Program in Java.
An understanding of Java data types, which is discussed in our tutorial, Understanding Data Types in Java .
Unary operators are applied to one operand, which makes them the most straightforward. Unary operators are often used because they make your code more concise and readable. They replace the need to explicitly describe operations like increasing and decreasing values. However, when combined with other operators, unary operators can also be challenging to use, as you’ll discover later in this section.
Next, you’ll use unary operators to increase and decrease values, as well as flip boolean values.
Increment and decrement operators, as their names suggest, increase and decrease numbers. The increment operator is the combination of two plus signs (++
) and the decrement operator is two minus signs (--
). These operators are used before and after operands.
When you use the operators before an operand, you are preincrementing or predecrementing depending on whether you use ++
or --
. When you use the pre
operators, you change the value of the operand before using it. Thus, when you actually use the value, it is already changed.
Info: To follow along with the example code in this tutorial, open the Java Shell tool on your local system by running the jshell
command. Then you can copy, paste, or edit the examples by adding them after the jshell>
prompt and hitting ENTER
. To exit jshell
, type /exit
.
To use the preincrementing operator, type the following into jshell
:
- int theAnswer = 42;
- System.out.println("Preincrementing: " + ++theAnswer);
On the first line, you define a variable theAnswer
with the value 42
. On the second line, you use the println()
method to print it and thus demonstrate how it has changed.
The preincrement operator in the above example is ++
, and it is placed before theAnswer
. By using the preincrement operator in this way, you are first incrementing the value of theAnswer
to 43
. After that, when println()
processes it, it is already 43
, and thus you see printed:
OutputtheAnswer ==> 42
Preincrementing: 43
Predecrementing works similarly, but instead of incrementing, you are decrementing the value of the operand. As an exercise, modify the above example so that instead of the preincrement operator ++
, you use the predecrement --
.
In contrast to the pre
operators, the post
operators change the value of an operand after it is used. There are some specific cases in which post
or pre
operators are commonly used, but as a whole, it is a matter of personal preference.
To demonstrate how post
operators work, you will postincrement the value of theAnswer
and examine how its value changes. Add the following lines to jshell
:
- int theAnswer = 42;
- System.out.println("Postincrementing: " + theAnswer++);
- System.out.println("Final value: " + theAnswer);
The variable theAnswer
first equals to 42
. Then, it is printed and postincremented. On the last line, you print it again to see its final value.
Your output should be:
OutputtheAnswer ==> 42
Postincrementing: 42
Final value: 43
As you can see, theAnswer
remains 42
during the postincrementing. It is when you print it again after postincrementing that it is 43
(Final value: 43
).
Postdecrementing works the same way. The value is first retrieved and used, and only after that, is it decremented. As an exercise, try replacing the postincrement operator ++
with the postdecrement operator --
, or even include one of the pre
operators.
The NOT
operator, also known as the logical complement operator, flips the value of a boolean operand. It is represented by the exclamation mark !
. Usually, you use the NOT
operator when you have a boolean variable or value, and you want to reuse it with the opposite value. Thus, you don’t have to create another variable with the opposite value unnecessarily.
Here is an example of how the NOT
operator works. For simplicity, you’ll flip the value of true
:
- boolean isJavaFun = !true;
- System.out.println(isJavaFun);
You define the boolean variable isJavaFun
as true
. However, the NOT
operator precedes true
; thus, the value of true
is flipped to false
. When you run the above code, the following output will print:
OutputisJavaFun ==> false
false
This is how the NOT
operator works. It might be confusing and hard to spot it sometimes, so you should use it sparingly.
In the above case, instead of !true
, you could have used false
. This is the right approach because it’s cleaner and more intuitive. As a general rule, it’s best practice to use a literal or method directly, rather than alternatives that require additional operations. However, in some cases, it may not always make sense or even be possible. For example, it’s common to use the NOT
operator to flip the result from a boolean method.
As an example, to check if a string contains another string, you can use the method contains()
. However, if you want to check the opposite (that is, when a string does not contain another string), there is no alternative built-in method. You’ll need to use contains()
with the NOT
operator.
Imagine you have the string Java is smart.
and you want to check whether:
smart
.hard
.To check these, you’ll use the following code:
- String javaIsSmart = "Java is smart.";
- boolean isSmartPartOfJava = javaIsSmart.contains("smart");
- boolean isHardNotPartOfJava = !javaIsSmart.contains("hard");
On the first line, you define a String
variable javaIsSmart
. On the second line, you define a boolean variable isSmartPartOfJava
as the result of the operation from the method contains()
— in this case, whether the string smart
is part of the javaIsSmart
string. Similarly, on the third line, you define a boolean variable isHardNotPartOfJava
, which is determined by whether hard
is not found in javaIsSmart
.
When you run this code in jshell
, you will get the following output:
OutputjavaIsSmart ==> "Java is smart."
isSmartPartOfJava ==> true
isHardNotPartOfJava ==> true
According to the above output:
isSmartPartOfJava
is true
because smart
is found in javaIsSmart
.isHardNotPartOfJava
is also true
because hard
is not found in javaIsSmart
.In this section, you explored incrementing, decrementing, and the NOT operator using one operand. Even though these operators have only one operand, they can be challenging to use, as demonstrated by the NOT
operator. In the next step, you’ll build on this knowledge by using operators with two operands.
Binary operators act on two operands and are commonly associated with arithmetic operations such as addition and subtraction. There are also other non-math related binary operators, such as logical operators and the special relational operator instanceof
. In this section, you’ll begin with the arithmetic binary operators, which may be more familiar.
These are the well-known operators used for arithmetic operations, such as addition (+
) and subtraction (-
). Here is an example with addition:
- int theAnswer = 40 + 2;
- System.out.println("The result is: " + theAnswer);
On the first line, you add 40
to 2
and assign the result to theAnswer
variable. When you print it, you get the final value 42
:
OutputThe result is: 42
**NOTE:**In addition to arithmetic operations, the plus sign (+
) is also used for concatenating strings. You have seen it in action in most of our examples with printing values, such as the one above. There, using the plus sign, you have concatenated "The result is: "
with the variable theAnswer
. However, this use of the plus sign is an exception, and no other arithmetic operators can be used similarly on reference types. So, for example, you cannot use the minus sign to remove parts of a string.
For additional practice, try using the other arithmetic operators, which you can find in the Java documentation.
The assignment operators assign the left operand to the value of the right operand. Usually, the left operand is a variable and the right one is a value or a reference to an object. This may sound familiar because you’ve used such assignments in all of your examples. In this section, you’ll practice using the basic assignment operator, some compound assignment operators, and the casting operator.
The basic assignment operator (=
) is a well-known and commonly used operator.
- int x = 1;
In this example, you declare an int
variable x
and assign it the value 1
. Using the equals sign (=
) is how you assign a value to a variable.
The compound assignment operators (+=
, -=
, *=
, \=
) combine assignment along with an additional arithmetic operation such as addition or subtraction. These operators allow you to avoid boilerplate code, especially in arithmetic operations that are straightforward to follow and understand.
For example, use the compound +=
assignment operator to combine addition and assignment like this:
- int x = 1;
- int y = 1;
- x += y;
- System.out.println("x is: " + x);
In the first two lines, you declare two integer variables called x
and y
, both with a value of 1
. Next, you reassign x
using the compound +=
assignment, which means that x
is added to y
and then is assigned back to x
.
The above code will return an output similar to this:
Outputx ==> 1
y ==> 1
$11 ==> 2
x is: 2
According to the above output, x
and y
get a value of 1
. On the third line, there is a temporary variable with a randomly-assigned name ($11
). It holds the value of x
as a result of the compound assignment operation. On the last line, the value of x
is printed: 2
.
The same code can be rewritten without the compound assignment operator like this:
- int x = 1;
- int y = 1;
- x = x + y;
- System.out.println("x is: " + x);
In contrast to the previous example, you write additional code to describe explicitly the addition of x
plus y
on line 3.
Running this code will return the following output:
Outputx ==> 1
y ==> 1
x ==> 2
x is: 2
Ultimately, in both examples, x
equals 2
. However, in the second example, jshell
didn’t print a temporary variable name such as $11
. Instead, it used x
directly to show that its value has changed (x ==> 2
). Such verbose output is very helpful for learning and is available only in jshell
.
The rest of the compound operators combine subtraction (-=
), multiplication (*=
) and division (/=
) along with assignment. Try changing the above examples to see how they can work.
It’s good to know about compound operators because they are frequently used. However, there is no performance benefit to using them, so using compound operators is a matter of personal choice. If they seem unnecessarily confusing, you don’t have to use them.
The last assignment operator you’ll review is the casting operator, which is a data type surrounded by parentheses: (data type)
. The casting operator is used for casting values, which is interpreting one data type as another.
The data types have to be compatible, though. Whether one data type is compatible with another is determined by their relation, such as whether one class is a parent or sibling to another. For example, you can cast int
to short
because both data types are used for storing whole numbers. However, you cannot cast int
to boolean
because the two data types are incompatible.
In this section, you’ll explore some common examples of and problems with casting. For educational purposes, you’ll begin with an incorrect and incompatible casting:
- boolean y = (boolean) 1;
With this line, you are trying to cast the integer 1
to a boolean value and assign it to the variable y
. When you paste this in jshell
, you’ll get the following error:
Output| Error:
| incompatible types: int cannot be converted to boolean
| boolean y = (boolean) 1;
|
As the error message explains, you cannot convert an int
value to a boolean
. Boolean values are either true
or false
, and it’s impossible to determine which boolean value 1
should be.
Now, you’ll try an example with compatible data types. You’ll use two primitive types for storing whole numbers: int
and short
. The difference is in their capacity, that is, the amount of memory available to store information. int
has a larger capacity and thus can store larger numbers.
Add the following lines to jshell
:
- int prize = 32767;
- short wonPrize = (short) prize;
- System.out.println("You won: " + wonPrize);
In the first line, you define the lottery prize as an int
primitive type with the value of 32767
. On the second line, however, you decide that a short
primitive type will be more suitable for the value of the wonprize
, and you cast prize
to short
using (short)
.
When you run the above code in jshell
, the output is:
Outputprize ==> 32767
wonPrize ==> 32767
You won: 32767
The above output confirms that prize
and wonPrize
values have been correctly set to 32767
. The last row uses the wonPrize
variable to state how much you have won.
In the case of int
and short
, casting may seem unnecessary and you will probably not see such casting in reality. However, this example is useful for demonstrating the idea of casting.
Casting seems straightforward, but there is one caveat. When you cast from a data type with a larger capacity to a data type with a smaller capacity, you could exceed the smaller capacity limit, which is called overflow. To demonstrate this problem, reuse the previous example and increase the prize from 32767
to 32768
, like so:
- int prize = 32768;
- short wonPrize = (short) prize;
- System.out.println("You won: " + wonPrize);
When you run the above in jshell
, you will get the following output:
Outputprize ==> 32768
wonPrize ==> -32768
You won: -32768
In this case, you lose information and get unexpected results. When cast to short
, the value of 32768
becomes -32768
. This is because short
’s storage capacity ranges from -32768
to 32767
. When you try to store a value larger than the maximum value, you overflow it and start from the beginning. In this case, you exceed the maximum capacity (32767
) by 1
when you try to store 32768
. Because of this, the next value is assigned, starting from the lowest possible. In this case, this is the minimum value of -32768
.
That’s why the above output seems unexpected — the final prize has become a negative number. These kinds of problems are not always easy to spot, so you should use casting carefully. You may use casting in more complex scenarios, which will be addressed in future tutorials from the Java series.
Relational operators compare two operands and return a boolean result. If the relation is asserted, the result is true
. If not, the result is false
.
The first types of relational operators are equals ==
and not equals !=
. They are used to assert the equality of values and objects. With primitive values and literals, their use is similar to mathematics.
To demonstrate the equals equality operator, compare two integer literals. In fact, it will be one and the same number: 1
. You’ll compare whether it is equal to itself so that you can get a true
result. Paste the following code into jshell
:
- System.out.println(1==1);
In the above code, you are asserting whether 1
equals 1
. Since the numbers are equal, this expression evaluates to true. Thus, println()
prints true
:
Outputtrue
As an exercise, try changing one of the values in order to get a false result.
Note: Make sure to differentiate between the operator for equality ==
and the assignment operator =
. Even when you know they’re different, it is easy to confuse them. You may not always get a syntax error in your code, which can lead to problems that are hard to debug.
In contrast to comparing primitive values, comparing objects for equality is more complex because you are asserting whether two variables point to the same object. Try comparing two Integers
as an example:
- Integer myAnswer = Integer.valueOf(42);
- Integer yourAnswer = Integer.valueOf(42);
- System.out.println(myAnswer == yourAnswer);
In the above code, you create two Integer
variables, each with a value of 42
. On the last line, you compare them for equality and print true
if they are equal. From our previous tutorial Understanding Data Types in Java, you may know that Integer.valueOf()
first checks the cache for an object with the same value and returns the same object if there is one already with this value. That’s how both myAnswer
and yourAnswer
receive the same object.
When you paste the above code in jshell
, you will get the following output:
OutputmyAnswer ==> 42
yourAnswer ==> 42
true
Such object comparison seems straightforward, but sometimes it’s challenging. The most confusing example is with strings. Try comparing two strings with the same values:
- String answer1 = new String("yes");
- String answer2 = new String("yes");
- System.out.println(answer1 == answer2);
First, you declare two new String
variables (answer1
and answer2
) with the values "yes"
. However, you used the new
keyword to create the new String
objects. Because of that, the two variables do not point to the same object — they actually point to two different objects (with the same value).
When you paste the above code in jshell
, you will get the following output:
Outputanswer1 ==> "yes"
answer2 ==> "yes"
false
Even though answer1
and answer2
have the same value ("yes"
), their equality is evaluated to false
, which means they are not equal. It can be confusing if you intend to compare values, such as whether both answers are affirmative, and you’re not interested in the underlying objects. For this purpose, many classes, including String
, have dedicated methods for asserting equality.
In the case of String, this method is equals()
. Try changing the code to use equals()
(instead of ==
) like this:
- String answer1 = new String("yes");
- String answer2 = new String("yes");
- System.out.println(answer1.equals(answer2));
The equals()
method verifies that the strings contained in the compared objects are equal.
When you paste this code in jshell
, you will get the following output:
Outputanswer1 ==> "yes"
answer2 ==> "yes"
true
The above output is similar to the previous output, but it finishes with true
, confirming that the values of the two objects are equal. This example demonstrates that you have to be careful when comparing reference types and use corresponding methods when available.
The alternative equality operator, not equals !=
, is used similarly, but it asserts whether two variables or values are not the same (or unequal). As an exercise, try replacing ==
with !=
in some of the previous examples.
Similar to ==
and !=
, the next four relational operators also come from mathematics: less than <
, less than or equal to <=
, greater than >
, and greater than or equal to =>
.
Here’s an example using the greater than operator:
- System.out.println(4 > 5);
When you run this code in jshell
, you will get the following output:
Outputfalse
The above code first compares whether 4
is greater than 5
. Since it is not, the expression evaluates to false
. The result of the comparison (false
) is then printed by the println()
method.
The last relational operator is instanceof
, which evaluates whether a variable is an instance of a given class (or subclass) or an implementation of an interface. As explained in our Understanding Data Types in Java tutorial, an interface is an abstract entity with a group of requirements.
Use the following example to explore how instanceof
works:
- String greeting = "hello";
- System.out.println(greeting instanceof String);
First, you create a String
variable called greeting
. Then, you evaluate in the parentheses whether greeting
is an instance of String
.
When you paste the code in jshell
, you will get the following output:
Outputgreeting ==> "hello"
true
Since greeting
is an instance of String
, the expression evaluates to true
, which is printed on the screen by println()
.
The logical operators are logical AND
(&
), logical OR
(|
), and exclusive OR
(^
). They all evaluate two values as follows:
AND
is true when both values are true
.OR
is true when at least one of the values is true
.OR
is true if one value is true
and the other is false
.When the logical operators are not true as per the above conditions, they are false.
To use the logical AND (&
) operator, paste the following example into jshell
:
- boolean isJavaFun = true;
- boolean isJavaPowerful = true;
- System.out.println(isJavaFun & isJavaPowerful);
The first two lines define the boolean
variables isJavaFun
and isJavaPowerful
both to true
. On line three, in the parentheses, you perform a logical AND
operation on isJavaFun
and isJavaPowerful
and the result is printed by println()
.
When you paste this code in jshell
, you will get the following output:
OutputisJavaFun ==> true
isJavaPowerful ==> true
true
Both variables are set to true
, and a final true
is printed as the result of the logical AND
operation.
To extend your skills, try using the previous code example with some variations. You can try switching the values of the variables between true
and false
. You can also try changing the logical operators to logical OR (|
) and exclusive OR (^
).
An extended version of the logical operators are the so-called short-circuit logical operators: short-circuit AND
(&&
) and short-circuit OR
(||
). They are similar to the regular logical AND
and OR
operators, but they have one important difference: if evaluating the first operator is sufficient for the operation, the second one is not evaluated. Thus, in order for the short-circuit AND
to be true, both sides surrounding it have to be true
. However, if the left side is false
, the right side is not evaluated. Similarly, with the short-circuit OR
, if the left side is false
, the right is not evaluated.
Here is an example with short-circuit OR
:
- boolean isJavaFun = true;
- Boolean isNullFun = null;
- System.out.println(isJavaFun || isNullFun);
First, you assign the variable isJavaFun
to true
. For consistency with the previous examples, this variable is of the primitive type boolean
. However, for the next variable, isNullFun
, you use the Boolean
reference type so that you can assign it to null
. For the example, it’s important that you have a null
pointing variable, but as you may recall from the tutorial on Understanding Java Data Types, primitives types cannot be null, and that’s why you are using a reference type.
When the short circuit OR
takes place in the parentheses, isNullFun
is ignored because the left side is true
, and this is enough for the whole expression to be true
. Thus, when you run the code in jshell
, the following output will print:
OutputisJavaFun ==> true
isNullFun ==> null
true
The first and the second lines confirm that the variables are assigned to true
and null
. The third line prints true
because the short-circuit OR
operation has returned true
.
The above example was chosen specifically with null
in order to demonstrate how short-circuit operators work and that isNullFun
will not be evaluated. To see isNullFun
evaluated, try changing the short-circuit OR
with a regular OR
like this:
- boolean isJavaFun = true;
- Boolean isNullFun = null;
- System.out.println(isJavaFun | isNullFun);
The regular logical OR
evaluates both sides of the expression.
When you run this code in jshell
, you will get the following output:
isJavaFun ==> true
isNullFun ==> null
| Exception java.lang.NullPointerException
| at (#3:1)
When the logical OR
tries to evaluate null
, you get the java.lang.NullPointerException
. Because of this, short-circuit operators are preferred and are almost always used instead of regular logical operators.
As an exercise, practice using the short-circuit &&
and ||
, which are the most popular and useful of all.
In this section, you used binary operators in a range of examples, from basic arithmetic to more challenging operations involving casting and comparing objects for equality. In this next section, you’ll work with three operands.
In the previous sections, you practiced using operators with one and two operands. In this final section, you’ll use the ternary operator, the only operator for three operands. Its syntax is this: first operand ? second operand : third operand
. The first operand must be a boolean. If it is true
, then the second operand is returned from the expression. If the first operand is false
, then the third operand is returned.
The ternary operator is popular and frequently used because it can save you from writing complex statements, such as conditionals, and storing their results in temporary variables.
Try a ternary operator with the following example:
- boolean isJavaFun = true;
- String shouldILearnJava = isJavaFun ? "yes" : "no";
- System.out.println("Should I learn Java: " + shouldILearnJava);
isJavaFun
is set to true
, and the variable shouldILearnJava
is determined by it in a ternary operation. Because the first operand, isJavaFun
, is true, the second operand is returned, which is the string "yes"
. To verify this, the third line prints the variable shouldILearnJava
, which should be yes
at this point.
When you run the above code, you will get the following output:
OutputShould I learn Java: yes
For additional practice, try using the NOT
operator in front of isJavaFun
:
- boolean isJavaFun = true;
- String shouldILearnJava = !isJavaFun ? "yes" : "no";
- System.out.println("Should I learn java: " + shouldILearnJava);
By flipping the value of isJavaFun
, you set it from true
to false
. As a result, the ternary expression will return the last operand, which is "no"
.
Ternary expressions can be confusing, especially when you use additional operators such as the NOT
operator. However, they can save you some boilerplate code and that’s why they are popular.
Once you know the important operators, you will be able to use and even combine them. But before you start combining them, you will need to know the rules for operator precedence.
Operator precedence determines in what order operators are evaluated. Since you are likely to use more than one operator, it’s important to understand operator precedence. While the rules are not always intuitive, you will likely need to know only a few essential rules in practice.
Understanding operator precedence helps you write clean code, which is the de-facto standard for modern programming. To write clean code means to write understandable and maintainable code. In regards to operators, the clean code paradigm translates to using as few operators as possible and creating separate statements instead of nesting and combining operators.
For example, statements such as the one below should be avoided because they are too confusing:
- boolean isThisCleanCode = !true || false && true;
Even if you consult the operators documentation, you might not be able to guess the end result (isThisCleanCode
is false
).
Here are some of the most important and commonly used precedence rules, starting with the rules with the highest precedence:
Pre
and post
increment and decrement operators: They have the highest precedence and take effect before any other operators./
has higher precedence than addition +
. Parentheses override the precedence; that is, you can group operations with parentheses and they will be evaluated with priority.OR
and AND
operators (including short-circuit): Similar to the ternary operator, they all need the final values of their operands, hence their low precedence.Now, you’ll use a math problem to explore operator precedence:
- int x = 1 + 10 / 2;
When you paste the above code in jshell
, you will get the following output:
Outputx ==> 6
According to the above output, x
gets the value of 6
. This is because the following operations have been completed with decreasing priority:
10 / 2
is evaluated first. It results in 5
.1
is added to the result of the first operation (5
). This results in 6
.6
) to assign it to the variable x
.Even though this math equation is relatively straightforward, you can always make it more verbose and ensure that precedence is easily understood by using parentheses. Consider rewriting it like this:
- int x = 1 + (10 / 2);
When you paste the above in jshell
, the same result as before will print:
Outputx ==> 6
In the last example, you didn’t change the precedence by using parentheses around 10 / 2
. Instead, their purpose was only to make the precedence of the operation inside them more obvious. Using parentheses like this helps you make your code more clean and understandable, which is especially important when the operations are more complex.
Precedence rules are interesting and spending some time learning them is a good idea. Keep in mind the clean code paradigm and consider the fact that unnecessary nesting and combining of operators is seen as a weakness in the code.
In this tutorial, you learned about the primary operators in Java. You wrote a few test code snippets in which you saw some of the most useful and interesting scenarios related to operators. You also learned about clean code and the fact that operators shouldn’t be overused unnecessarily.
For more on Java, check out our How To Code in Java series.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
Java is a mature and well-designed programming language with a wide range of uses. One of its unique benefits is that it is cross-platform: once you create a Java program, you can run it on many operating systems, including servers (Linux/Unix), desktop (Windows, macOS, Linux), and mobile operating systems (Android, iOS).
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!