| Java 2 Basics > Operators | Index - Previous - Next |
Category Operators Unary ++ -- + - ! ~ () Arithmetic * / % + - Shift << >> >>>> Comparision < <= > >= instanceof == != Bitwise & ^ | Short-circuit && || Conditional ?: Assingment = "op="
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
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;
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
d = 3; c = d; b = c; a[1] = b;
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.
++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.
x = 1; y = x++; Results: y = 1 x = 2
These couple of sentences are interpreted this way:
x = 1; y = x; x = x + 1;
x = 1; y = --x; Results: y = 0 x = 0
x = 1; x = x - 1; y = x;
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.
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.
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.
int a = 0xFFFFFFFF; a = ~a; System.out.println(a); Result: 0
boolean married = flase;
if (!married) {
System.out.println("Why not?");
}
Result: Why not?
boolean oxygen = false;
boolean hydrogen = true;
oxygen = !oxygen;
if (!(oxygen && hydrogen)) {
System.out.println("No water today");
}
Result: None
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.
int a = (int)2.3; // Result: 2 char b = (char)(1500 / 23); // Result: Character 'A'
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:
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:
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:
a = 6 * 15; // Result = 90 a = 90 / 3; // Result = 30 b = 15 / 3; // Result = 5 b = 5 * 6; // Result = 30
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 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.
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.
short x = 17; short y = 3; short z = x + y; // Error. The result is an int short z = (short) (x + y); // Ok. Int result is casted to short.
int x = 17; int y = 3; float z = 1.0; int a = x + y + z; // Error. The result is float because // one of the operators is float int a = (int)(x+y+z); // Ok.
To convert primitive types to String, use the static class:
Integer.toString();To convert some unknown object to some sort of string representation, use the provided .toString() method. This method is defined in the root of the class hierarchy.
int a = 2; int b = 23; String s = " years old "; String my_string = "Taffy is " + a + s + "and I'm "+b; System.out.println(my_string); Result: Taffy is 2 years old and I'm 23
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
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.
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
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:
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
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.
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
byte pattern: 01111110 = 126 int pattern: 11111111 11111111 11111111 01111110 = -130
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.
< <= > >= == !=
They only return a boolean result; true or false. They are commonly used to form conditions, such as if, while, etc.
< Less than =< Less than or equal to > Greater than => Greater than or equal to
These ones are applicable to all numeric types, including char.
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
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
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.
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
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.
String var1 = "Hello";
TextField field = new TextField("Hello");
if (field.getText().equals(var1)) {
System.out.println("Equal!");
}
Result: Equal!
& 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:
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).
10011100 = 156 00011001 = 25 -------- (AND) 00011000 = 24
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).
10011100 = 156 00011001 = 25 -------- (XOR) 10000101 = 133
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).
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.
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 && 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:
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.
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);
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
Assignment operators set the value of a variable or expression to a new value. Assignments are supported by many operators:
*= /= %= += -= <<= >>= >>>= &= ^= |=
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
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
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.
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 |