GARBA.ORG
Java 2 Basics > Operators Index - Previous - Next

Operators

Table of Operators in Descending Order of Precedence

Category Operators
Unary ++ -- + - ! ~ ()
Arithmetic * / %
+ -
Shift << >> >>>>
Comparision < <= > >= instanceof
== !=
Bitwise & ^ |
Short-circuit && ||
Conditional ?:
Assingment = "op="

Evaluation Order

Consider this example:

	int a[] = { 1,2,3 };
	int b = 1;
	a[b] = b = 0;
	System.out.println(a[0] + "-" + a[1] + "-" + a[2]);

	Result: 1-0-3

The program was interpreted as follows:

	a[b] = (b = 0);

At the moment of the assignment a[b]=, b is still 1, so it still points to the second element of the array. Then, the operation b = 0 takes place, where its result is assigned to a[1]. So, this is what happened:

	b = 0;
	a[1] = b;

What about this?

	int a[] = { 1,2,3 };
	int b = 1;
	int c = 2;
	int d = 0;
	a[b] = b = c = d = 3;
	System.out.println(a[0] + "-" + a[1] + "-" + a[2]);
	
	Result: 1-3-3

The assignment was interpreted this way:

	d = 3;
	c = d;
	b = c;
	a[1] = b;

The Unary Operators

Java provides 7 unary operators:

Operator Description
++ Increment operator
-- Decrement operator
+ Plus operator
- Minus operator
~ Bitwise inversion operator
! Boolean complement operator
() Cast operator

The cast is not an operator strictly speaking. It is however included in this category for simplicity.

The Increment and Decrement Operators: ++ and --

Examples:

	++x;
	x++;
	y--;
	--y;
	x--;

The use of operands before the identifier is called pre-increment or pre-decrement (depending of the operand used, ++ and -- respectively) and it changes the evaluation order.

Example:

	x = 1;
	y = x++;

	Results: 

	y = 1
	x = 2

These couple of sentences are interpreted this way:

	x = 1;
	y = x;
	x = x + 1;

A bit of pre-decrement now:

	x = 1;
	y = --x;

	Results:

	y = 0
	x = 0

The example above was interpreted as follows:

	x = 1;
	x = x - 1;
	y = x;

The Unary Plus and Minus Operators: + and -

The unary operators + and - are distinct from the more common binary + and - operators used for subtraction and addition.

The unary + has no effect on the value of its operand, but the expression is promoted to at least int.

Examples:

	int x = +5;   // OK. The positive value 5 is assigned to x.

	short a = +7  // OK. The positive value 7 is assigned to a. 
	short b = +a  // Error. +a is promoted to int and there is
	              // a "loss of precision" error.

Unary - negates an expression. It is mostly applied to expressions.

Examples:

	int y = -(5 * 6);
	int x = -3 * -5     // Minus by minus produces a positive result.

It also promotes the expression to int, so be careful.

The Bitwise Inversion Operator: ~

Example:

	int a = 0xFFFFFFFF;
	a = ~a;
	System.out.println(a);

	Result: 0

The Boolean Complement Operator: !

Example #1:

	boolean married = flase;
	if (!married) {
		System.out.println("Why not?");
	}

	Result: Why not?

Example #2:

	boolean oxygen = false;
	boolean hydrogen = true;

	oxygen = !oxygen;

	if (!(oxygen && hydrogen)) {
		System.out.println("No water today");
	} 

	Result: None

The Cast Operator: (type)

Casting is used for explicit conversion of the type of the expression. This is only possible for plausible target types. The compiler and the runtime system check for conformance with typing rules. There is obviously loss of precision when you cast a value like "2.3" into an int.

Examples:

	int a = (int)2.3;                 // Result: 2
	char b = (char)(1500 / 23);       // Result: Character 'A'

The Arithmetic Operators

Multiplication and Division Operators: * and /

If you multiply two integers, the result will be calculated using integer arithmetic in either int or long representation. It is very important to keep in mind the loss of precision and overflow problems that arise when you deal with integer values. Consider these two examples:

Example #1:

	int x = 6;
	int z = 15;

	int a = x * z / 3;
	int b = z / 3 * x;

	Results:

	a = 30
	b = 30

Let's change the value of z to 14 now:

Example #2:

	int x = 6;
	int z = 14;

	int a = x * z / 3;
	int b = z / 3 * x;

	Results: 

	a = 28
	b = 24

So why a and b aren't equal? This is what happens:

Example #1:

	a = 6 * 15;             // Result = 90
	a = 90 / 3;             // Result = 30

	b = 15 / 3;             // Result = 5
	b = 5 * 6;              // Result = 30

Example #2:

	a = 6 * 14;             // Result = 84
	a = 84 / 3;             // Result = 28    (Right value)

	b = 14 / 3;             // Result = 4 (4,666...)
	b = 4 * 6;              // Result = 24

4,6666 got rounded to just "4" loosing a lot of precision, and of course, a very inaccurate result was produced. If you still want to work with integers, maybe you should handle your expressions under a safe floating-point environment:

	int x = 6;
	float z = 14;

	int a = (int)( x * z / 3);
	int b = (int)( z / 3 * x);

	Results:
	 
	a = 28
	b = 28

The Modulo Operator

The modules operators gives the remainder of a division. It is generally applied to two integers, although it can be applied to floating-point numbers too.

Examples:

	int a = 14 % 5;         // Result = 4

Some problems arise when you apply the % operator to negative values. In such cases, try to do the modulo on the positive version of the number and then add the negative sign. The Addition and Subtraction Operators: + and -

The + and - operators are used for addition and subtraction but also on String to perform operations such as string concatenation.

Please note that Java does not allow the programmer to perform operator overloading, but the + is overloaded by the language itself in the case of strings.

For an expression with two operands involving the + operator, the result:

For a + expression with any operand that is not of primitive type:

Arithmetic Error Conditions

Comparisions with Not a Number

To test for a NaN result you should use the static methods of the Float or Double classes.

	float a = Float.NaN;
	double b = Double.NaN;

	if (Float.isNaN(a)) {
		System.out.println("It is Not a Number!");
	}
	if (!Double.isNaN(a)) {
		System.out.println("Yes, this is a number!");
	}

Never try to compare the constants .NaN against other values, as the result won't be what you expect:

	y > Float.NaN              // False 
	x == Double.NaN            // False
	x < Float.Nan              // False

The Shift Operators: <<, >> and >>>

To know how to perform shift operations on integer types, you have to understand how negative and positive values are represented in bits. Although most bit manipulations are performed on ints, we will use bytes to reduce large-number-headache.

Bytes are signed. They can represent the minimum value of -128 and the maximum value of +127. But, a byte is still a byte, that means that it can hold 256 different bit patterns.

Consider this example:

	byte a = 127;  
	a++;

	Result: a = -128

The maximum value a signed byte can represent is of +127. When we assign a positive number that exceeds the maximum allowed, tha corresponding bit pattern is assigned. So, 128 = 10000000 = -128.

	byte a = 127;  
	a = (byte)(a + 128);  

	Result: a = -1

You can't do a = 255 directly as a value beyond 127 is assumed to be an int. A simple cast is all what you need:

	byte a = (byte)255;
	byte a = (byte)0xFF;

	Result: In both cases, a = -1

So, how do negatives numbers match their binary and unsigned versions?. Check this table:

	Signed value | Unsigned value | Binary value
	--------------------------------------------
	  0          | 0              | 00000000
	  1          | 1              | 00000001
	  2          | 2              | 00000010
	  3          | 3              | 00000011
	  .          | .              | .
	  .          | .              | .
	 126         | 126            | 01111110
	 127         | 127            | 01111111
	-128         | 128            | 10000000
	-127         | 129            | 10000001
	-126         | 130            | 10000010
	  .          | .              | .
	  .          | .              | .
	 -3          | 253            | 11111101
	 -2          | 254            | 11111110
	 -1          | 255            | 11111111

You might get some conclusions by examining this table:

Shifting Negative and Positive Numbers

Sadly, Java shifts numbers under a "signed" environment. When negative numbers are shifted, the missing spaces are filled with the MSB bit, that means 1. Of course, this only occurs on right shifting. Let's take a look at some examples to get a better picture:

Original value = 01000000 (64)

Exp. Binary Decimal (signed)
None - Original value 01000000 64
Shifted left 1 bit 64 << 1 10000000 -128
Shifted right 1 bit 64 >> 1 00100000 32
Shifted left 2 bits 64 << 2 00000000 0
Shifted right 2 bits 64 >> 2 00010000 16

Original value = 10111111 (-65)

Exp. Binary Decimal (signed)
None - Original value 10111111 -65
Shifted left 1 bit 65 << 1 01111110 126
Shifted right 1 bit 65 >> 1 11011111 -33 (MSB carried)
Shifted left 2 bits 65 << 2 11111100 -4
Shifted right 2 bits 65 >> 2 11101111 -17 (MSB carried)

To avoid this (sometimes undesirable) effect, you can use the right >>> operator rather than the plain ">>" one.

Examine now an example using the >>> operator:

	int a = -65;
	int r = 0;

	r = a >> 1;   // Result: -33
	r = a >>> 1;  // Result: 2147483615

Special note: A byte-sized value such as -65 is promoted to int so shifting always takes place on 32-bit values. We use 8-bit examples just to explain the concept painlessly.

Be aware, really. For example, -65, shifted 1 bit to the left, as a byte, gives 126, but, as an int, gives -130. Why? Take a look:

	byte pattern:                              10111111                        
	int pattern:    11111111 11111111 11111111 10111111

Shifted 1 bit to the left:

	byte pattern:                              01111110 = 126                  
	int pattern:    11111111 11111111 11111111 01111110 = -130

Examine also this example:

	int a = -65;
	int r = 0;

	r = a << 1;               // Result: -130
	r = (int)(byte)(a << 1);  // Result: 126

When you cast an int into a byte, Java takes only the last byte, ignoring completely the information contained on the left three bytes.

The Comparison Operators

They are:

	<  <=  >  >=  == !=

They only return a boolean result; true or false. They are commonly used to form conditions, such as if, while, etc.

There are 3 comparison types:

  1. Ordinal comparison: Test the relative value of numeric operands
  2. Object-type comparison: Determine wether the runtime type of an object is of a particular type or a subclass of that particular type.
  3. Equality comparison: Test whether two values are the same and may be applied to values of non-numeric types.

Ordinal Comparisons with <, <=, > and >=

	 < Less than
	=< Less than or equal to
	 > Greater than
	=> Greater than or equal to

These ones are applicable to all numeric types, including char.

Example #1:

	char x = 'A';
	char y = 'Z';
	char z = 'G';

	if (z >= x && z <= y) {
		System.out.println("It's part of the alphabet");
	}

	Result: It's part of the alphabet

Example #2:

	float a = 2.999999f;
	int b = 3;

	if (b > a) {
		System.out.println("Sorry, b is greater");
	}

	Result: Sorry, b is greater

As you can see, it is b that gets promoted to 3.0f and not a to 3.

Ordinal comparisons operate satisfactorily on dissimilar numeric types, but they are not applicable to any non-numeric types. The instanceof Operator

Example #1:

	TextField a = new TextField();

	if (a instanceof TextField) {
		System.out.println("Yes, it is!");
	} else {
		System.out.println("Sorry, it isn't");
	}

	Result: Yes, it is!   

	a is an instace of the TextField class.

Example #2:

	class SuperTextField extends TextField {}

	TextField a = new SuperTextField();

	if (a instanceof TextField) {
		System.out.println("Yes, it is!");
	} else {
		System.out.println("Sorry, it isn't");
	}

	Result: Yes, it is!

	a is an instance of a subclass of TextField

Example #2.b:

	TextField a = new TextField();

	if (a instanceof SuperTextField) {
		System.out.println("Yes, it is!");
	} else {
		System.out.println("Sorry, it isn't");
	}

	Result: Sorry, it isn't

	a is an instance of the superclass of SuperTextField.

If you want to know wether a reference is pointing to an array, you can use the method isArray() of the Class. The Equality Comparison Operators: == and !=

They test for equality or inequality respectively, returning a boolean value. Equality is subject to promotion rules so that, for example, a float value of 20.0 is considered equal to a byte of value 20. For variables of object type, the value is taken as the reference to the object, typically memory address.

For string values you should use the equals() method rather than the == or != operators.

Example:

	String var1 = "Hello"; 
	TextField field = new TextField("Hello");

	if (field.getText().equals(var1)) {
		System.out.println("Equal!");
	} 

	Result: Equal!

The Bitwise Operators: &, ^, and |

They perform AND, eXclusive-OR and OR operations as follows:

	& AND
	^ XOR
	| OR

These operands may be applied to integer types and also boolean types. When applied to an integer, the operation will take place considering the binary representation of integer. In the case of boolean types, a 0 result would produce false and 1 true.

The bitwise operations calculate each bit of their results by comparing the corresponding bits of the two operands on the basis of these rules:

AND (Op1 & Op2)

	Op1	Op2	Result
	0	0	0
	0	1	0
	1	0	0
	1	1	1

Result is 1(true) only if both operands are 1(true), otherwise, it is 0(false).

Example:

	10011100 = 156
	00011001 = 25
	-------- (AND)
	00011000 = 24

XOR (Op1 ^ Op2)

	Op1	Op2	Result
	0	0	0
	0	1	1
	1	0	1
	1	1	0

If both operands are the same (either 0 or 1) the result is 0(false). Otherwise, the result is 1(true).

Example:

	10011100 = 156
	00011001 = 25
	-------- (XOR)
	10000101 = 133

OR (Op1 | Op2)

	Op1	Op2	Result
	0	0	0
	0	1	1
	1	0	1
	1	1	1

If one of the operands is 1(true), the result is 1(true).

Example:

	10011100 = 156
	00011001 = 25
	-------- (OR)
	10011101 = 157

Special note: Both operands must be of the same type. You can compare either booleans, where there will be only one answer (true or false) or other primitive types where the bit patterns will be modified accordingly.

Example:

	boolean her_status = true;
	int my_status = 1;

	if (her_status & my_status) {
		// Do something
	}

	Result: WRONG! this example doesn't compile.

The Short-Circuit Logical Operators

The short-circuit logical operators && and || provide logical AND and OR operations on boolean types. There is not XOR operation provided. So something like ^^ doesn't exist.

The main difference between the & and && and between the | and the || operators is that the right operand might not be evaluated in the latter cases:

So, for any boolean value x:

What is this really all about? Consider you have this segment of code:

	if (s != null) {
		if (s.length() > 5) {
			System.out.println(s);
		}
	}

You have to be sure that s is not null, otherwise it would be illegal to obtain the length of a null string. You can produce achieve the same goal like this:

if (s != null && s.length() > 5) {
	System.out.println(s);
}

As described lately, if the first operand is false, Java would not try the rest of the operands. Java will try operand by operand until a false boolean value is produced. If the scanning is over and not false value was found, then the result is true.

Word of advice: Be really careful as common sense makes you think that all expressions will be evaluated first, and then each resulting operand will be subject to the desired boolean operation.

The Ternary or Conditional Operator: ?:

It is called the ternary operator because it take 3 operands. It provides a way to code simple conditions into a single expression.

	a = x ? b : c;

It means that if x = true, then a = b, otherwise a = c:

	if (x) {
		a = b;
	} else {
		a = c;
	}

As you can see, x must be a valid boolean value (true or false);

Example:

	int customer_age = 17;

	boolean adults_only = true;
	int minimum_age = adults_only ? 18 : 3;

	if (customer_age < minimum_age) {
		System.out.println("Sorry, grow up a little bit");
	} 

	Result: Sorry, grow up a little bit

The Assignment Operators

Assignment operators set the value of a variable or expression to a new value. Assignments are supported by many operators:

	*=  /=  %=  +=  -=  <<=  >>=  >>>=  &= ^= |=

Examples:

	byte a = 16;
	x += 4;            // x = (byte)(x + 4);

	short b = 5;
	b *= 9;            // b = (short)(b * 9);

As you can see, the compound operators don't promote the expression to int. "b * 9" alone is promoted to an int, so you need to cast the value to a short in order to assign it to a variable of that type.

When a string is part of the expression, the same + operator rules, apply:

	String s = "Hello ";
	s += "dude"; 
	System.out.println(s);

	Result: Hello dude

Puzzling Issues

Shifting numbers

A number shifted by the size in bits of the primitive that contains it gives as result the same number:

	int a = 128;

	int b = a >>> 32;         // Result: b = 128
	int c = a << 32;          // Result: c = 128
	int d = a >> 32;          // Result: d = 128

	byte x = -1;              
	x >>= 8;                  // Result: x = -1

Primitives and Objects.

There are classes for each basic primitive, but the objects created from these classes are not primitives types, likewise, primitive types are not instances of these classes.

	int a = 15;
	Integer b = new Integer(15);

In the above case, a is a primitive type of value 15. b is a reference to an Integer object. So, this is a mistake:

	if (a == b) {
		// Wrong.
	}

But this is ok:

if (a == b.intValue()) {
	// Fine.
}

Be aware of strings, because they show a special behaviour. Consider this example:

	String x = "Hello world";
	String y = "Hello world";
	String a = new String("Hello world");
	String b = new String("Hello world");
		
	if (x == y) {
		System.out.println("x == y");
	} 
	if (x == a) {
		System.out.println("x == a");
	} 
	if (a == b) {
		System.out.println("a == b");
	} 

	Result: x == y

The variables x and y are assumed to be constants because "Hello world" is the same literal for both. The compiler thinks "x and y will be Hello World forever, so why waste space storing this stuff twice?".

You read forever, that's right. Now, let's modify the example a little bit:

	String x = "Hello world";
	String y = "Hello world";
	String a = new String("Hello world");
	String b = new String("Hello world");

	y += " dude!";
	System.out.println("y = " + y);
	y = y.substring(0,11);
	System.out.println("y = " + y);

	if (x == y) {
		System.out.println("x == y");
	} 
	if (x == a) {
		System.out.println("x == a");
	} 
	if (a == b) {
		System.out.println("a == b");
	}	

	Result:

	y = Hello World dude!
	y = Hello World

We append " dude!" to y, and then we get rid of the extra word by using the substring() method, so we end up with the same initial "Hello world" string. Not quite the very same, at least as a reference. This time, the compiler detects that y won't be immutable so "Hello World" for x and y are saved at different "memory addresses". Hence, x == y is false, and is not printed.

The Ternary Operator

There is promotion to the wider type of the results when both are of different type.

For example:
	byte a = 5;
	float c = 2;
	boolean yes = true;

	System.out.println( yes ? a : c );

	Result: 5.0

Even if you are sure that the condition will return one of the results, the compiler just doesn't know and makes the appropriate promotion "just in case".


© Ernesto Garbarino Top