Evaluate Java expressions with operators

A complete guide to the Java operator types and writing Java expressions in Java 12

1 2 3 4 Page 2
Page 2 of 4

The bitwise operators modify the binary values of their operands, which must be of an integer (byte, short, int, or long) or character type. These operators include bitwise AND (&), bitwise complement (~), bitwise exclusive OR (^), and bitwise inclusive OR (|); and are formally defined below:

  • Bitwise AND: Given operand1 & operand2, where each operand must be of character or an integer type, bitwise AND their corresponding bits and return the result. A result bit is set to 1 when each operand's corresponding bit is 1. Otherwise, the result bit is set to 0. Example: 1 & 0.
  • Bitwise complement: Given ~operand, where operand must be of character or an integer type, flip operand's bits (1s to 0s and 0s to 1s) and return the result. Example: ~1.
  • Bitwise exclusive OR: Given operand1 ^ operand2, where each operand must be of character or an integer type, bitwise exclusive OR their corresponding bits and return the result. A result bit is set to 1 when one operand's corresponding bit is 1 and the other operand's corresponding bit is 0. Otherwise, the result bit is set to 0. Example: 1 ^ 0.
  • Bitwise inclusive OR: Given operand1 | operand2, which must be of character or an integer type, bitwise inclusive OR their corresponding bits and return the result. A result bit is set to 1 when either (or both) of the operands' corresponding bits is 1. Otherwise, the result bit is set to 0. Example: 1 | 0.

Example application: Bitwise operators

Listing 3 presents the source code to a BitwiseOp application that lets you play with the bitwise operators.

Listing 3. Bitwise operators in Java (BitwiseOp.java)

class BitwiseOp
{
   public static void main(String[] args)
   {
      short x = 0B0011010101110010;
      short y = 0B0110101011101011;

      System.out.println(x & y);
      System.out.println(~x);
      System.out.println(x ^ y);
      System.out.println(x | y);
   }
}

Listing 3's main() method initializes a pair of short integer variables and subsequently uses the bitwise operators to produce new values by operating on their bits; these values are then output.

Compile Listing 3 (javac BitwiseOp.java) and run the application (java BitwiseOp). You should observe the following output:

8290
-13683
24473
32763

Because it's difficult to see each operator's effect on its operands, consider the binary equivalent of the previous operators below. (You'll understand why each binary number is 32 bits long instead of 16 when I introduce type conversions.)

00000000000000000010000001100010
11111111111111111100101010001101
00000000000000000101111110011001
00000000000000000111111111111011

Cast operator

The cast operator--(type)--attempts to convert the type of its operand to type. You can convert from one primitive type to another primitive type or from one reference type to another reference type, but not from primitive type to reference type or vice versa.

For example, to convert double precision floating-point value 1.0 to its 32-bit integer equivalent, specify (int) 1.0. Also, to convert circle (of type Circle) to its Shape supertype, specify (Shape) circle. There will be more to learn about cast when we get into type conversions later in this tutorial. The cast operator is also important for inheritance, which is a subject for another day.

Conditional operators

The conditional operators conditionally evaluate Boolean expressions, which are expressions that have Boolean type and evaluate to true or false. These operators include conditional (?:), conditional AND (&&), and conditional OR (||); and are formally defined below:

  • Conditional: Given operand1 ? operand2 : operand3, where operand1 must be of Boolean type, return operand2 when operand1 is true or operand3 when operand1 is false. The types of operand2 and operand3 must agree. Example: boolean status = true; int statusInt = (status) ? 1 : 0;.
  • Conditional AND: Given operand1 && operand2, where each operand must be of Boolean type, return true when both operands are true. Otherwise, return false. When operand1 is false, operand2 isn't evaluated because the entire expression will be false anyway. This is known as short-circuiting. Example: true && false.
  • Conditional OR: Given operand1 || operand2, where each operand must be of Boolean type, return true when at least one operand is true. Otherwise, return false. When operand1 is true, operand2 isn't evaluated because the entire expression will be true anyway. This is known as short-circuiting. Example: true || false.

Example application: Conditional operators

Listing 4 presents the source code to a CondOp application that lets you play with the conditional operators.

Listing 4. Conditional operators in Java (CondOp.java)

class CondOp
{
   public static void main(String[] args)
   {
      boolean sold_more_than_100_units = true;
      int bonus_dollars = (sold_more_than_100_units) ? 50 : 0;
      System.out.println(bonus_dollars);

      System.out.println(true && true);
      System.out.println(true && false);
      System.out.println(false && true);
      System.out.println(false && false);

      System.out.println(true || true);
      System.out.println(true || false);
      System.out.println(false || true);
      System.out.println(false || false);

      int x = 0;
      boolean status = true && ++x == 0;
      System.out.println(x);
      status = false && ++x == 0;
      System.out.println(x);
      status = true || ++x == 0;
      System.out.println(x);
      status = false || ++x == 0;
      System.out.println(x);
   }
}

Because of short-circuiting, && and || won't always evaluate their right operands. Although short-circuiting can improve performance somewhat because only one expression is evaluated, it can also be a source of bugs when a side-effect (code that is executed as a byproduct of expression evaluation) is involved.

Listing 4 presents side-effects in which variable x is preincremented. Preincrement occurs for expressions true && ++x == 0 and false || ++x == 0. However, this variable isn't incremented for expressions false && ++x == 0 and true || ++x == 0 because there's no need to evaluate the right operands in these contexts.

Compile Listing 4 (javac CondOp.java) and run the application (java CondOp). You should observe the following output:

50
true
false
false
false
true
true
true
false
1
1
1
2

Equality operators

The equality operators compare their operands to determine if they are equal or unequal. These operators include equality (==) and inequality (!=). The former operator returns true when both operands are equal; the latter operator returns true when both operands are unequal. These operators are formally defined below:

  • Equality: Given operand1 == operand2, where both operands must be comparable (you cannot compare an integer with a Boolean value, for example), compare both operands for equality. Return true when these operands are equal. Otherwise, return false. Example: 'A' == 'a'.
  • Inequality: Given operand1 != operand2, where both operands must be comparable (you cannot compare a floating-point value with a string literal, for example), compare both operands for inequality. Return true when these operands are not equal. Otherwise, return false. Example: 'A' != 'a'.

You will need to be careful when comparing floating-point values because not all floating-point values can be represented accurately in memory. For example, 0.1 cannot be represented accurately. For this reason, some expressions involving these operands will return false when you think they should return true.

These operators can be used to compare primitive values or object references. However, you cannot compare a primitive value with an object reference. For example, you might have two Employee objects whose references are stored in e1 and e2. Expression e1 == e2 returns true only when variables e1 and e2 refer to the same Employee object.

String comparison is a little unusual. You can attempt to compare two string literals, as in "A" == "B". However, because string literals are really String objects, you are really comparing references to these objects and not comparing their characters. As a result, true returns only when both operands reference the same String object.

Example application: Equality operators

Listing 5 presents the source code to an EqualityOp application that lets you play with the equality operators.

Listing 5. Equality operators in Java (EqualityOp.java)

class EqualityOp
{
   public static void main(String[] args)
   {
      int x = 0;
      System.out.println(x == 0);
      System.out.println(x != 0);

      double d = 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1;
      System.out.println(d);
      System.out.println(d == 0.9);

      System.out.println("A" == "A");
      System.out.println("A" == "B");
      System.out.println("AB" == "A" + "B");
      String s = "B";
      System.out.println("AB" == "A" + s);
   }
}

Compile Listing 5 (javac EqualityOp.java) and run the application (java EqualityOp). You should observe the following output:

true
false
0.8999999999999999
false
true
false
true
false

The first two output lines are unsurprising given that x contains 0. The next two output lines reveal that 0.1 cannot be stored exactly in memory: summing nine instances of 0.1 doesn't equal nine. For this reason, it's unwise to control an iteration's duration via an equality expression involving floating-point values. You'll learn more about this in the next tutorial in the Java 101 series.

The remaining output lines prove that Java creates exactly one String object for each unique string literal. For example, there is one String object for "A", another String object for "B", and a third String object for "AB". In "A" == "A", both "A" literals refer to the same "A" object, so true is the result. However, in "A" == "B", "A" and "B" refer to the "A" object and to a "B" object. Because of different references, false is the result. Next, in "AB" == "A" + "B", the string concatenation produces a reference to the same "AB" object as is referenced by literal "AB", so true is the result. Finally, String s = "B"; creates a new String object that contains B and whose reference is assigned to s. This object (and reference) is separate from the object (and reference) associated with "B". As a result, in "AB" == "A" + s, we end up with the "AB" String object's reference being compared with a reference to a different String object containing AB, and this comparison results in false as the result. You'll learn more about string comparison in a future tutorial.

Logical operators

The logical operators are the Boolean equivalent of the bitwise operators. Instead of working on the bit values of integral operands, they work on their Boolean operands. These operators include logical AND (&), logical complement (!), logical exclusive OR (^), and logical inclusive OR (|); and are formally defined below:

  • Logical AND: Given operand1 & operand2, where each operand must be of Boolean type, return true when both operands are true. Otherwise, return false. In contrast to conditional AND, logical AND doesn't perform short-circuiting. Example: true & false.
  • Logical complement: Given !operand, where operand must be of Boolean type, flip operand's value (true to false or false to true) and return the result. Example: !false.
  • Logical exclusive OR: Given operand1 ^ operand2, where each operand must be of Boolean type, return true when one operand is true and the other operand is false. Otherwise, return false. Example: true ^ false.
  • Logical inclusive OR: Given operand1 | operand2, where each operand must be of Boolean type, return true when at least one operand is true. Otherwise, return false. In contrast to conditional OR, logical inclusive OR doesn't perform short-circuiting. Example: true | false.

Example application: Logical operators

Listing 6 presents the source code to a LogicalOp application that lets you play with the logical operators.

Listing 6. Logical operators in Java (LogicalOp.java)

class LogicalOp
{
   public static void main(String[] args)
   {
      int x = 0;
      System.out.println(false & ++x == 0);
      System.out.println(x);
      System.out.println(!false);
      System.out.println(true ^ true);
      System.out.println(true ^ false);
      System.out.println(true | ++x == 0);
      System.out.println(x);
   }
}

Compile Listing 6 (javac LogicalOp.java) and run the application (java LogicalOp). You should observe the following output:

false
1
true
false
true
true
2

Member access operator

The member access operator (.) accesses class or object members (e.g., methods). For example, assuming that name is of type String and is initialized to a string, name.length() returns the length of that string. Essentially, this operator is accessing the length() member of the name object.

Java regards arrays as special objects with a single length member whose value (an int) denotes the number of elements in the array. For example, grades.length returns the length of (the number of elements in) the array that grades references. In other words, this operator is accessing the length member of the grades array.

You'll learn more about classes, objects, class/object members, and arrays in future tutorials in the Java 101 series.

Method call operator

The method call operator -- () -- signifies that a method is being called and identifies the number, order, and types of expressions being passed to the method. For example, in System.out.println("Java");, () signifies that method println, which is a member of the System class's out member, is being called with one argument: "Java".

Multiplicative operators

1 2 3 4 Page 2
Page 2 of 4