Using Java operators

Bruce Eckel's Thinking in Java Contents | Prev | Next

Precedence

Assignment

As an aside, the first thing you see is a package statement for package c03 , indicating this book’s Chapter 3. The first code listing of each chapter will contain a package statement like this to establish the chapter number for the remaining code listings in that chapter. In Chapter 17, you’ll see that as a result, all the listings in chapter 3 (except those that have different package names) will be automatically placed in a subdirectory called c03, Chapter 4’s listings will be in c04 and so on. All this will happen via the CodePackager.java program shown in Chapter 17, and in Chapter 5 the concept of packages will be fully explained. What you need to recognize at this point is that, for this book, lines of code of the form package c03 are used just to establish the chapter subdirectory for the listings in the chapter.

//: Assignment.java
// Assignment with objects is a bit tricky
package c03;
 
class Number {
  int i;
}
 
public class Assignment {
  public static void main(String[] args) {
    Number n1 = new Number();
    Number n2 = new Number();
    n1.i = 9;
    n2.i = 47;
    System.out.println("1: n1.i: " + n1.i +
      ", n2.i: " + n2.i);
    n1 = n2;
    System.out.println("2: n1.i: " + n1.i +
      ", n2.i: " + n2.i);
    n1.i = 27;
    System.out.println("3: n1.i: " + n1.i +
      ", n2.i: " + n2.i);
  }
} ///:~ 

The Number class is simple, and two instances of it ( n1 and n2) are created within main( ). The i value within each Number is given a different value, and then n2 is assigned to n1, and n1 is changed. In many programming languages you would expect n1 and n2 to be independent at all times, but because you’ve assigned a handle here’s the output you’ll see:

1: n1.i: 9, n2.i: 47
2: n1.i: 47, n2.i: 47
3: n1.i: 27, n2.i: 27

Changing the n1 object appears to change the n2 object as well! This is because both n1 and n2 contain the same handle, which is pointing to the same object. (The original handle that was in n1 that pointed to the object holding a value of 9 was overwritten during the assignment and effectively lost; its object will be cleaned up by the garbage collector.)

Aliasing during method calls

Aliasing will also occur when you pass an object into a method:

//: PassObject.java
// Passing objects to methods can be a bit tricky
 
class Letter {
  char c;
}
 
public class PassObject {
  static void f(Letter y) {
    y.c = 'z';
  }
  public static void main(String[] args) {
    Letter x = new Letter();
    x.c = 'a';
    System.out.println("1: x.c: " + x.c);
    f(x);
    System.out.println("2: x.c: " + x.c);
  }
} ///:~ 

In many programming languages, the method f( ) would appear to be making a copy of its argument Letter y inside the scope of the method. But once again a handle is being passed so the line

y.c = 'z';

is actually changing the object outside of f( ). The output shows this:

1: x.c: a
2: x.c: z

Aliasing and its solution is a complex issue and, although you must wait until Chapter 12 for all the answers, you should be aware of it at this point so you can watch for pitfalls.

Mathematical operators

//: MathOps.java
// Demonstrates the mathematical operators
import java.util.*;
 
public class MathOps {
  // Create a shorthand to save typing:
  static void prt(String s) {
    System.out.println(s);
  }
  // shorthand to print a string and an int:
  static void pInt(String s, int i) {
    prt(s + " = " + i);
  }
  // shorthand to print a string and a float:
  static void pFlt(String s, float f) {
    prt(s + " = " + f);
  }
  public static void main(String[] args) {
    // Create a random number generator,
    // seeds with current time by default:
    Random rand = new Random();
    int i, j, k;
    // '%' limits maximum value to 99:
    j = rand.nextInt() % 100;
    k = rand.nextInt() % 100;
    pInt("j",j);  pInt("k",k);
    i = j + k; pInt("j + k", i);
    i = j - k; pInt("j - k", i);
    i = k / j; pInt("k / j", i);
    i = k * j; pInt("k * j", i);
    i = k % j; pInt("k % j", i);
    j %= k; pInt("j %= k", j);
    // Floating-point number tests:
    float u,v,w;  // applies to doubles, too
    v = rand.nextFloat();
    w = rand.nextFloat();
    pFlt("v", v); pFlt("w", w);
    u = v + w; pFlt("v + w", u);
    u = v - w; pFlt("v - w", u);
    u = v * w; pFlt("v * w", u);
    u = v / w; pFlt("v / w", u);
    // the following also works for
    // char, byte, short, int, long,
    // and double:
    u += v; pFlt("u += v", u);
    u -= v; pFlt("u -= v", u);
    u *= v; pFlt("u *= v", u);
    u /= v; pFlt("u /= v", u);
  }
} ///:~ 

The first thing you will see are some shorthand methods for printing: the prt( ) method prints a String, the pInt( ) prints a String followed by an int and the pFlt( ) prints a String followed by a float. Of course, they all ultimately end up using System.out.println( ).

To generate numbers, the program first creates a Random object. Because no arguments are passed during creation, Java uses the current time as a seed for the random number generator. The program generates a number of different types of random numbers with the Random object simply by calling different methods: nextInt( ), nextLong( ), nextFloat( ) or nextDouble( ) .

The modulus operator, when used with the result of the random number generator, limits the result to an upper bound of the operand minus one (99 in this case).

Unary minus and plus operators

Auto increment and decrement

Java, like C, is full of shortcuts. Shortcuts can make code much easier to type, and either easier or harder to read.

//: AutoInc.java
// Demonstrates the ++ and -- operators
 
public class AutoInc {
  public static void main(String[] args) {
    int i = 1;
    prt("i : " + i);
    prt("++i : " + ++i); // Pre-increment
    prt("i++ : " + i++); // Post-increment
    prt("i : " + i);
    prt("--i : " + --i); // Pre-decrement
    prt("i-- : " + i--); // Post-decrement
    prt("i : " + i);
  }
  static void prt(String s) {
    System.out.println(s);
  }
} ///:~ 

The output for this program is:

i : 1
++i : 2
i++ : 2
i : 3
--i : 2
i-- : 2
i : 1

You can see that for the prefix form you get the value after the operation has been performed, but with the postfix form you get the value before the operation is performed. These are the only operators (other than those involving assignment) that have side effects. (That is, they change the operand rather than using just its value.)

Relational operators

Testing object equivalence

//: Equivalence.java
 
public class Equivalence {
  public static void main(String[] args) {
    Integer n1 = new Integer(47);
    Integer n2 = new Integer(47);
    System.out.println(n1 == n2);
    System.out.println(n1 != n2);
  }
} ///:~ 

The expression System.out.println(n1 == n2) will print out the result of the boolean comparison within it. Surely the output should be true and then false, since both Integer objects are the same. But while the contents of the objects are the same, the handles are not the same and the operators == and != compare object handles. So the output is actually false and then true. Naturally, this surprises people at first.

//: EqualsMethod.java
 
public class EqualsMethod {
  public static void main(String[] args) {
    Integer n1 = new Integer(47);
    Integer n2 = new Integer(47);
    System.out.println(n1.equals(n2));
  }
} ///:~ 

The result will be true, as you would expect. Ah, but it’s not as simple as that. If you create your own class, like this:

//: EqualsMethod2.java
 
class Value {
  int i;
}
 
public class EqualsMethod2 {
  public static void main(String[] args) {
    Value v1 = new Value();
    Value v2 = new Value();
    v1.i = v2.i = 100;
    System.out.println(v1.equals(v2));
  }
} ///:~ 

you’re back to square one: the result is false. This is because the default behavior of equals( ) is to compare handles. So unless you override equals( ) in your new class you won’t get the desired behavior. Unfortunately, you won’t learn about overriding until Chapter 7, but being aware of the way equals( ) behaves might save you some grief in the meantime.

Most of the Java library classes implement equals( ) so that it compares the contents of objects instead of their handles.

Logical operators

//: Bool.java
// Relational and logical operators
import java.util.*;
 
public class Bool {
  public static void main(String[] args) {
    Random rand = new Random();
    int i = rand.nextInt() % 100;
    int j = rand.nextInt() % 100;
    prt("i = " + i);
    prt("j = " + j);
    prt("i > j is " + (i > j));
    prt("i < j is " + (i < j));
    prt("i >= j is " + (i >= j));
    prt("i <= j is " + (i <= j));
    prt("i == j is " + (i == j));
    prt("i != j is " + (i != j));
 
    // Treating an int as a boolean is 
    // not legal Java
//! prt("i && j is " + (i && j));
//! prt("i || j is " + (i || j));
//! prt("!i is " + !i);
 
    prt("(i < 10) && (j < 10) is "
       + ((i < 10) && (j < 10)) );
    prt("(i < 10) || (j < 10) is "
       + ((i < 10) || (j < 10)) );
  }
  static void prt(String s) {
    System.out.println(s);
  }
} ///:~ 

i = 85
j = 4
i > j is true
i < j is false
i >= j is true
i <= j is false
i == j is false
i != j is true
(i < 10) && (j < 10) is false
(i < 10) || (j < 10) is true

Note that a boolean value is automatically converted to an appropriate text form if it’s used where a String is expected.

Short-circuiting
//: ShortCircuit.java
// Demonstrates short-circuiting behavior
// with logical operators.
 
public class ShortCircuit {
  static boolean test1(int val) {
    System.out.println("test1(" + val + ")");
    System.out.println("result: " + (val < 1));
    return val < 1;
  }
  static boolean test2(int val) {
    System.out.println("test2(" + val + ")");
    System.out.println("result: " + (val < 2));
    return val < 2;
  }
  static boolean test3(int val) {
    System.out.println("test3(" + val + ")");
    System.out.println("result: " + (val < 3));
    return val < 3;
  }
  public static void main(String[] args) {
    if(test1(0) && test2(2) && test3(2))
      System.out.println("expression is true");
    else
      System.out.println("expression is false");
  }
} ///:~ 

Each test performs a comparison against the argument and returns true or false. It also prints information to show you that it’s being called. The tests are used in the expression:

if(test1(0) && test2(2) && test3(2))

You might naturally think that all three tests would be executed, but the output shows otherwise:

test1(0)
result: true
test2(2)
result: false
expression is false

The first test produced a true result, so the expression evaluation continues. However, the second test produced a false result. Since this means that the whole expression must be false, why continue evaluating the rest of the expression? It could be expensive. The reason for short-circuiting, in fact, is precisely that; you can get a potential performance increase if all the parts of a logical expression do not need to be evaluated.

Bitwise operators

Shift operators

The shift operators also manipulate bits. They can be used solely with primitive, integral types. The left-shift operator ( <<) produces the operand to the left of the operator shifted to the left by the number of bits specified after the operator (inserting zeroes at the lower-order bits). The signed right-shift operator ( >>) produces the operand to the left of the operator shifted to the right by the number of bits specified after the operator. The signed right shift >> uses sign extension : if the value is positive, zeroes are inserted at the higher-order bits; if the value is negative, ones are inserted at the higher-order bits. Java has also added the unsigned right shift >>>, which uses zero extension : regardless of the sign, zeroes are inserted at the higher-order bits. This operator does not exist in C or C++.

//: URShift.java
// Test of unsigned right shift
 
public class URShift {
  public static void main(String[] args) {
    int i = -1;
    i &gt;&gt;&gt;= 10;
    System.out.println(i);
    long l = -1;
    l &gt;&gt;&gt;= 10;
    System.out.println(l);
    short s = -1;
    s &gt;&gt;&gt;= 10;
    System.out.println(s);
    byte b = -1;
    b &gt;&gt;&gt;= 10;
    System.out.println(b);
  }
} ///:~ 

Shifts can be combined with the equal sign ( <<= or >>= or >>>=). The lvalue is replaced by the lvalue shifted by the rvalue.

//: BitManipulation.java
// Using the bitwise operators
import java.util.*;
 
public class BitManipulation {
  public static void main(String[] args) {
    Random rand = new Random();
    int i = rand.nextInt();
    int j = rand.nextInt();
    pBinInt("-1", -1);
    pBinInt("+1", +1);
    int maxpos = 2147483647;
    pBinInt("maxpos", maxpos);
    int maxneg = -2147483648;
    pBinInt("maxneg", maxneg);
    pBinInt("i", i);
    pBinInt("~i", ~i);
    pBinInt("-i", -i);
    pBinInt("j", j);
    pBinInt("i &amp; j", i &amp; j);
    pBinInt("i | j", i | j);
    pBinInt("i ^ j", i ^ j);
    pBinInt("i &lt;&lt; 5", i &lt;&lt; 5);
    pBinInt("i &gt;&gt; 5", i &gt;&gt; 5);
    pBinInt("(~i) &gt;&gt; 5", (~i) &gt;&gt; 5);
    pBinInt("i &gt;&gt;&gt; 5", i &gt;&gt;&gt; 5);
    pBinInt("(~i) &gt;&gt;&gt; 5", (~i) &gt;&gt;&gt; 5);
 
    long l = rand.nextLong();
    long m = rand.nextLong();
    pBinLong("-1L", -1L);
    pBinLong("+1L", +1L);
    long ll = 9223372036854775807L;
    pBinLong("maxpos", ll);
    long lln = -9223372036854775808L;
    pBinLong("maxneg", lln);
    pBinLong("l", l);
    pBinLong("~l", ~l);
    pBinLong("-l", -l);
    pBinLong("m", m);
    pBinLong("l &amp; m", l &amp; m);
    pBinLong("l | m", l | m);
    pBinLong("l ^ m", l ^ m);
    pBinLong("l &lt;&lt; 5", l &lt;&lt; 5);
    pBinLong("l &gt;&gt; 5", l &gt;&gt; 5);
    pBinLong("(~l) &gt;&gt; 5", (~l) &gt;&gt; 5);
    pBinLong("l &gt;&gt;&gt; 5", l &gt;&gt;&gt; 5);
    pBinLong("(~l) &gt;&gt;&gt; 5", (~l) &gt;&gt;&gt; 5);
  }
  static void pBinInt(String s, int i) {
    System.out.println(
      s + ", int: " + i + ", binary: ");
    System.out.print("   ");
    for(int j = 31; j &gt;=0; j--)
      if(((1 &lt;&lt; j) &amp;  i) != 0)
        System.out.print("1");
      else
        System.out.print("0");
    System.out.println();
  }
  static void pBinLong(String s, long l) {
    System.out.println(
      s + ", long: " + l + ", binary: ");
    System.out.print("   ");
    for(int i = 63; i &gt;=0; i--)
      if(((1L &lt;&lt; i) &amp; l) != 0)
        System.out.print("1");
      else
        System.out.print("0");
    System.out.println();
  }
} ///:~ 

The two methods at the end, pBinInt( ) and pBinLong( ) take an int or a long, respectively, and print it out in binary format along with a descriptive string. You can ignore the implementation of these for now.

You’ll note the use of System.out.print( ) instead of System.out.println( ). The print( ) method does not put out a new line, so it allows you to output a line in pieces.

As well as demonstrating the effect of all the bitwise operators for int and long, this example also shows the minimum, maximum, +1 and -1 values for int and long so you can see what they look like. Note that the high bit represents the sign: 0 means positive and 1 means negative. The output for the int portion looks like this:

-1, int: -1, binary: 
   11111111111111111111111111111111
+1, int: 1, binary: 
   00000000000000000000000000000001
maxpos, int: 2147483647, binary: 
   01111111111111111111111111111111
maxneg, int: -2147483648, binary: 
   10000000000000000000000000000000
i, int: 59081716, binary: 
   00000011100001011000001111110100
~i, int: -59081717, binary: 
   11111100011110100111110000001011
-i, int: -59081716, binary: 
   11111100011110100111110000001100
j, int: 198850956, binary: 
   00001011110110100011100110001100
i &amp; j, int: 58720644, binary: 
   00000011100000000000000110000100
i | j, int: 199212028, binary: 
   00001011110111111011101111111100
i ^ j, int: 140491384, binary: 
   00001000010111111011101001111000
i &lt;&lt; 5, int: 1890614912, binary: 
   01110000101100000111111010000000
i &gt;&gt; 5, int: 1846303, binary: 
   00000000000111000010110000011111
(~i) &gt;&gt; 5, int: -1846304, binary: 
   11111111111000111101001111100000
i &gt;&gt;&gt; 5, int: 1846303, binary: 
   00000000000111000010110000011111
(~i) &gt;&gt;&gt; 5, int: 132371424, binary: 
   00000111111000111101001111100000

The binary representation of the numbers is referred to as signed two’s complement .

Ternary if-else operator

static int ternary(int i) {
  return i &lt; 10 ? i * 100 : i * 10;
}

You can see that this code is more compact than what you’d need to write without the ternary operator:

static int alternative(int i) {
  if (i &lt; 10)
    return i * 100;
  return i * 10;
}

The comma operator

String operator +

There’s one special usage of an operator in Java: the + operator can be used to concatenate strings, as you’ve already seen. It seems a natural use of the + even though it doesn’t fit with the traditional way that + is used. This capability seemed like a good idea in C++, so operator overloading was added to C++ to allow the C++ programmer to add meanings to almost any operator. Unfortunately, operator overloading combined with some of the other restrictions in C++ turns out to be a fairly complicated feature for programmers to design into their classes. Although operator overloading would have been much simpler to implement in Java than it was in C++, this feature was still considered too complex, so Java programmers cannot implement their own overloaded operators as C++ programmers can.

Common pitfalls when using operators

One of the pitfalls when using operators is trying to get away without parentheses when you are even the least bit uncertain about how an expression will evaluate. This is still true in Java.

An extremely common error in C and C++ looks like this:

while(x = y) {
    // ....
}

Casting operators

The word cast is used in the sense of “casting into a mold.” Java will automatically change one type of data into another when appropriate. For instance, if you assign an integral value to a floating-point variable, the compiler will automatically convert the int to a float. Casting allows you to make this type conversion explicit, or to force it when it wouldn’t normally happen.

To perform a cast, put the desired data type (including all modifiers) inside parentheses to the left of any value. Here’s an example:

void casts() {
  int i = 200;
  long l = (long)i;
  long l2 = (long)200;
}

As you can see, it’s possible to perform a cast on a numeric value as well as on a variable. In both casts shown here, however, the cast is superfluous, since the compiler will automatically promote an int value to a long when necessary. You can still put a cast in to make a point or to make your code more clear. In other situations, a cast is essential just to get the code to compile.

Literals

//: Literals.java
 
class Literals {
  char c = 0xffff; // max char hex value
  byte b = 0x7f; // max byte hex value
  short s = 0x7fff; // max short hex value
  int i1 = 0x2f; // Hexadecimal (lowercase)
  int i2 = 0X2F; // Hexadecimal (uppercase)
  int i3 = 0177; // Octal (leading zero)
  // Hex and Oct also work with long.
  long n1 = 200L; // long suffix
  long n2 = 200l; // long suffix
  long n3 = 200;
  //! long l6(200); // not allowed
  float f1 = 1;
  float f2 = 1F; // float suffix
  float f3 = 1f; // float suffix
  float f4 = 1e-45f; // 10 to the power
  float f5 = 1e+9f; // float suffix
  double d1 = 1d; // double suffix
  double d2 = 1D; // double suffix
  double d3 = 47e47d; // 10 to the power
} ///:~ 

Exponents use a notation that I’ve always found rather dismaying: 1.39 e-47f . In science and engineering, ‘e’ refers to the base of natural logarithms, approximately 2.718. (A more precise double value is available in Java as Math.E.) This is used in exponentiation expressions such as 1.39 x e -47, which means 1.39 x 2.718 -47. However, when FORTRAN was invented they decided that e would naturally mean “ten to the power,” which is an odd decision because FORTRAN was designed for science and engineering and one would think its designers would be sensitive about introducing such an ambiguity. [16] At any rate, this custom was followed in C, C++ and now Java. So if you’re used to thinking in terms of e as the base of natural logarithms, you must do a mental translation when you see an expression such as 1.39 e-47f in Java; it means 1.39 x 10 -47.

Note that you don’t need to use the trailing character when the compiler can figure out the appropriate type. With

long n3 = 200;

there’s no ambiguity, so an L after the 200 would be superfluous. However, with

float f4 = 1e-47f; // 10 to the power

the compiler normally takes exponential numbers as doubles, so without the trailing f it will give you an error telling you that you must use a cast to convert double to float.

Promotion

Java has no “sizeof”

Precedence revisited

Mnemonic

Operator type

Operators

Ulcer

Unary

+ - ++ – [[ rest...]]

Addicts

Arithmetic (and shift)

* / % + - << >>

Really

Relational

> < >= <= == !=

Like

Logical (and bitwise)

&& || & | ^

C

Conditional (ternary)

A > B ? X : Y

A Lot

Assignment

= (and compound assignment like *=)

A compendium of operators

//: AllOps.java
// Tests all the operators on all the
// primitive data types to show which
// ones are accepted by the Java compiler.
 
class AllOps {
  // To accept the results of a boolean test:
  void f(boolean b) {}
  void boolTest(boolean x, boolean y) {
    // Arithmetic operators:
    //! x = x * y;
    //! x = x / y;
    //! x = x % y;
    //! x = x + y;
    //! x = x - y;
    //! x++;
    //! x--;
    //! x = +y;
    //! x = -y;
    // Relational and logical:
    //! f(x &gt; y);
    //! f(x &gt;= y);
    //! f(x &lt; y);
    //! f(x &lt;= y);
    f(x == y);
    f(x != y);
    f(!y);
    x = x &amp;&amp; y;
    x = x || y;
    // Bitwise operators:
    //! x = ~y;
    x = x &amp; y;
    x = x | y;
    x = x ^ y;
    //! x = x &lt;&lt; 1;
    //! x = x &gt;&gt; 1;
    //! x = x &gt;&gt;&gt; 1;
    // Compound assignment:
    //! x += y;
    //! x -= y;
    //! x *= y;
    //! x /= y;
    //! x %= y;
    //! x &lt;&lt;= 1;
    //! x &gt;&gt;= 1;
    //! x &gt;&gt;&gt;= 1;
    x &amp;= y;
    x ^= y;
    x |= y;
    // Casting:
    //! char c = (char)x;
    //! byte B = (byte)x;
    //! short s = (short)x;
    //! int i = (int)x;
    //! long l = (long)x;
    //! float f = (float)x;
    //! double d = (double)x;
  }
  void charTest(char x, char y) {
    // Arithmetic operators:
    x = (char)(x * y);
    x = (char)(x / y);
    x = (char)(x % y);
    x = (char)(x + y);
    x = (char)(x - y);
    x++;
    x--;
    x = (char)+y;
    x = (char)-y;
    // Relational and logical:
    f(x &gt; y);
    f(x &gt;= y);
    f(x &lt; y);
    f(x &lt;= y);
    f(x == y);
    f(x != y);
    //! f(!x);
    //! f(x &amp;&amp; y);
    //! f(x || y);
    // Bitwise operators:
    x= (char)~y;
    x = (char)(x &amp; y);
    x  = (char)(x | y);
    x = (char)(x ^ y);
    x = (char)(x &lt;&lt; 1);
    x = (char)(x &gt;&gt; 1);
    x = (char)(x &gt;&gt;&gt; 1);
    // Compound assignment:
    x += y;
    x -= y;
    x *= y;
    x /= y;
    x %= y;
    x &lt;&lt;= 1;
    x &gt;&gt;= 1;
    x &gt;&gt;&gt;= 1;
    x &amp;= y;
    x ^= y;
    x |= y;
    // Casting:
    //! boolean b = (boolean)x;
    byte B = (byte)x;
    short s = (short)x;
    int i = (int)x;
    long l = (long)x;
    float f = (float)x;
    double d = (double)x;
  }
  void byteTest(byte x, byte y) {
    // Arithmetic operators:
    x = (byte)(x* y);
    x = (byte)(x / y);
    x = (byte)(x % y);
    x = (byte)(x + y);
    x = (byte)(x - y);
    x++;
    x--;
    x = (byte)+ y;
    x = (byte)- y;
    // Relational and logical:
    f(x &gt; y);
    f(x &gt;= y);
    f(x &lt; y);
    f(x &lt;= y);
    f(x == y);
    f(x != y);
    //! f(!x);
    //! f(x &amp;&amp; y);
    //! f(x || y);
    // Bitwise operators:
    x = (byte)~y;
    x = (byte)(x &amp; y);
    x = (byte)(x | y);
    x = (byte)(x ^ y);
    x = (byte)(x &lt;&lt; 1);
    x = (byte)(x &gt;&gt; 1);
    x = (byte)(x &gt;&gt;&gt; 1);
    // Compound assignment:
    x += y;
    x -= y;
    x *= y;
    x /= y;
    x %= y;
    x &lt;&lt;= 1;
    x &gt;&gt;= 1;
    x &gt;&gt;&gt;= 1;
    x &amp;= y;
    x ^= y;
    x |= y;
    // Casting:
    //! boolean b = (boolean)x;
    char c = (char)x;
    short s = (short)x;
    int i = (int)x;
    long l = (long)x;
    float f = (float)x;
    double d = (double)x;
  }
  void shortTest(short x, short y) {
    // Arithmetic operators:
    x = (short)(x * y);
    x = (short)(x / y);
    x = (short)(x % y);
    x = (short)(x + y);
    x = (short)(x - y);
    x++;
    x--;
    x = (short)+y;
    x = (short)-y;
    // Relational and logical:
    f(x &gt; y);
    f(x &gt;= y);
    f(x &lt; y);
    f(x &lt;= y);
    f(x == y);
    f(x != y);
    //! f(!x);
    //! f(x &amp;&amp; y);
    //! f(x || y);
    // Bitwise operators:
    x = (short)~y;
    x = (short)(x &amp; y);
    x = (short)(x | y);
    x = (short)(x ^ y);
    x = (short)(x &lt;&lt; 1);
    x = (short)(x &gt;&gt; 1);
    x = (short)(x &gt;&gt;&gt; 1);
    // Compound assignment:
    x += y;
    x -= y;
    x *= y;
    x /= y;
    x %= y;
    x &lt;&lt;= 1;
    x &gt;&gt;= 1;
    x &gt;&gt;&gt;= 1;
    x &amp;= y;
    x ^= y;
    x |= y;
    // Casting:
    //! boolean b = (boolean)x;
    char c = (char)x;
    byte B = (byte)x;
    int i = (int)x;
    long l = (long)x;
    float f = (float)x;
    double d = (double)x;
  }
  void intTest(int x, int y) {
    // Arithmetic operators:
    x = x * y;
    x = x / y;
    x = x % y;
    x = x + y;
    x = x - y;
    x++;
    x--;
    x = +y;
    x = -y;
    // Relational and logical:
    f(x &gt; y);
    f(x &gt;= y);
    f(x &lt; y);
    f(x &lt;= y);
    f(x == y);
    f(x != y);
    //! f(!x);
    //! f(x &amp;&amp; y);
    //! f(x || y);
    // Bitwise operators:
    x = ~y;
    x = x &amp; y;
    x = x | y;
    x = x ^ y;
    x = x &lt;&lt; 1;
    x = x &gt;&gt; 1;
    x = x &gt;&gt;&gt; 1;
    // Compound assignment:
    x += y;
    x -= y;
    x *= y;
    x /= y;
    x %= y;
    x &lt;&lt;= 1;
    x &gt;&gt;= 1;
    x &gt;&gt;&gt;= 1;
    x &amp;= y;
    x ^= y;
    x |= y;
    // Casting:
    //! boolean b = (boolean)x;
    char c = (char)x;
    byte B = (byte)x;
    short s = (short)x;
    long l = (long)x;
    float f = (float)x;
    double d = (double)x;
  }
  void longTest(long x, long y) {
    // Arithmetic operators:
    x = x * y;
    x = x / y;
    x = x % y;
    x = x + y;
    x = x - y;
    x++;
    x--;
    x = +y;
    x = -y;
    // Relational and logical:
    f(x &gt; y);
    f(x &gt;= y);
    f(x &lt; y);
    f(x &lt;= y);
    f(x == y);
    f(x != y);
    //! f(!x);
    //! f(x &amp;&amp; y);
    //! f(x || y);
    // Bitwise operators:
    x = ~y;
    x = x &amp; y;
    x = x | y;
    x = x ^ y;
    x = x &lt;&lt; 1;
    x = x &gt;&gt; 1;
    x = x &gt;&gt;&gt; 1;
    // Compound assignment:
    x += y;
    x -= y;
    x *= y;
    x /= y;
    x %= y;
    x &lt;&lt;= 1;
    x &gt;&gt;= 1;
    x &gt;&gt;&gt;= 1;
    x &amp;= y;
    x ^= y;
    x |= y;
    // Casting:
    //! boolean b = (boolean)x;
    char c = (char)x;
    byte B = (byte)x;
    short s = (short)x;
    int i = (int)x;
    float f = (float)x;
    double d = (double)x;
  }
  void floatTest(float x, float y) {
    // Arithmetic operators:
    x = x * y;
    x = x / y;
    x = x % y;
    x = x + y;
    x = x - y;
    x++;
    x--;
    x = +y;
    x = -y;
    // Relational and logical:
    f(x &gt; y);
    f(x &gt;= y);
    f(x &lt; y);
    f(x &lt;= y);
    f(x == y);
    f(x != y);
    //! f(!x);
    //! f(x &amp;&amp; y);
    //! f(x || y);
    // Bitwise operators:
    //! x = ~y;
    //! x = x &amp; y;
    //! x = x | y;
    //! x = x ^ y;
    //! x = x &lt;&lt; 1;
    //! x = x &gt;&gt; 1;
    //! x = x &gt;&gt;&gt; 1;
    // Compound assignment:
    x += y;
    x -= y;
    x *= y;
    x /= y;
    x %= y;
    //! x &lt;&lt;= 1;
    //! x &gt;&gt;= 1;
    //! x &gt;&gt;&gt;= 1;
    //! x &amp;= y;
    //! x ^= y;
    //! x |= y;
    // Casting:
    //! boolean b = (boolean)x;
    char c = (char)x;
    byte B = (byte)x;
    short s = (short)x;
    int i = (int)x;
    long l = (long)x;
    double d = (double)x;
  }
  void doubleTest(double x, double y) {
    // Arithmetic operators:
    x = x * y;
    x = x / y;
    x = x % y;
    x = x + y;
    x = x - y;
    x++;
    x--;
    x = +y;
    x = -y;
    // Relational and logical:
    f(x &gt; y);
    f(x &gt;= y);
    f(x &lt; y);
    f(x &lt;= y);
    f(x == y);
    f(x != y);
    //! f(!x);
    //! f(x &amp;&amp; y);
    //! f(x || y);
    // Bitwise operators:
    //! x = ~y;
    //! x = x &amp; y;
    //! x = x | y;
    //! x = x ^ y;
    //! x = x &lt;&lt; 1;
    //! x = x &gt;&gt; 1;
    //! x = x &gt;&gt;&gt; 1;
    // Compound assignment:
    x += y;
    x -= y;
    x *= y;
    x /= y;
    x %= y;
    //! x &lt;&lt;= 1;
    //! x &gt;&gt;= 1;
    //! x &gt;&gt;&gt;= 1;
    //! x &amp;= y;
    //! x ^= y;
    //! x |= y;
    // Casting:
    //! boolean b = (boolean)x;
    char c = (char)x;
    byte B = (byte)x;
    short s = (short)x;
    int i = (int)x;
    long l = (long)x;
    float f = (float)x;
  }
} ///:~ 

//: Overflow.java
// Surprise! Java lets you overflow.
 
public class Overflow {
  public static void main(String[] args) {
    int big = 0x7fffffff; // max int value
    prt("big = " + big);
    int bigger = big * 4;
    prt("bigger = " + bigger);
  }
  static void prt(String s) {
    System.out.println(s);
  }
} ///:~ 

The output of this is:

big = 2147483647
bigger = -4

and you get no errors or warnings from the compiler, and no exceptions at run-time. Java is good, but it’s not that good.

Compound assignments do not require casts for char, byte, or short, even though they are performing promotions that have the same results as the direct arithmetic operations. On the other hand, the lack of the cast certainly simplifies the code.


[16] John Kirkham writes, “I started computing in 1962 using FORTRAN II on an IBM 1620. At that time, and throughout the 1960s and into the 1970s, FORTRAN was an all uppercase language. This probably started because many of the early input devices were old teletype units that used 5 bit Baudot code, which had no lowercase capability. The ‘E’ in the exponential notation was also always upper case and was never confused with the natural logarithm base ‘e’, which is always lower case. The ‘E’ simply stood for exponential, which was for the base of the number system used – usually 10. At the time octal was also widely used by programmers. Although I never saw it used, if I had seen an octal number in exponential notation I would have considered it to be base 8. The first time I remember seeing an exponential using a lower case ‘e’ was in the late 1970s and I also found it confusing. The problem arose as lowercase crept into FORTRAN, not at its beginning. We actually had functions to use if you really wanted to use the natural logarithm base, but they were all uppercase.”



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

  • As mobile devices have pushed their way into the enterprise, they have brought cloud apps along with them. This app explosion means account passwords are multiplying, which exposes corporate data and leads to help desk calls from frustrated users. This paper will discover how IT can improve user productivity, gain visibility and control over SaaS and mobile apps, and stop password sprawl. Download this white paper to learn: How you can leverage your existing AD to manage app access. Key capabilities to …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds