Java's primitive types, character and numeric literals. How to declare variables, arrays of variables, and arrays of objects. How to initialise variables. Java expressions and type conversions. How to access members of a class, including overloaded methods. Integer and floating-point arithmetic. Java's ++, relational, bitwise, conditional, assignment operators and their precedence. Java's flow control
boolean | either true or false | |
---|---|---|
char | 16-bit Unicode 1.1.5 character | |
byte | 8-bit signed two's-complement integer | |
short | 16-bit signed two's-complement integer | |
int | 32-bit signed two's-complement integer | |
long | 64-bit signed two's-complement integer | |
float | 32-bit IEEE 754-1985 floating-point number | |
double | 64-bit IEEE 754-1985 floating-point number |
short and byte are always promoted to int before being evaluated. They are only stored, never operated upon.
MIN_VALUE = minimum value pre-defined for the type concerned MAX_VALUE = maximum value pre-defined for the type concernedfloat and double can also have stored values of:
NaN means 'not a number'. It indicates that the number is an invalid result such as the result of dividing by zero. This can be tested for by the method (function) isNan().
null | an as yet uncreated, or an invalid, Object Reference | |
---|---|---|
true, false | the two possible values of a boolean variable | |
29 | int constant - decimal representation | |
035 | int constant - octal (base 8) representation | |
0x1D | int constant - hexadecimal (base 16) representation |
When an int constant (as above) is assigned to a byte variable or a short variable, it is automatically converted to byte or short as appropriate (provided its value lies within the valid range for that type).
29L long constant - decimal representation 035L long constant - octal (base 8) representation 0x1DL long constant - hexadecimal (base 16) representation 18.0F float constant - the .0 is optional, the F is mandatory 1.8E1F float constant - exponent form, the F is mandatory .18E2F float constant - exponent form, the F is mandatory 18.0D double constant - the .0 and the D are optional 1.8E1D double constant - exponent form the D is optional .18E2D double constant - exponent form the D is optional
Zero can be positive or negative. These are numerically equal but give different results in certain calculations eg:
A non-float constant such as a double cannot be assigned to a float variable. You must use a float literal viz:
float x = 18.9F;
Character literals appear between single quote marks viz:
char c = 'a'; //make character variable c contain the letter 'a'
Certain special characters are represented by the following escape sequences (which are also placed between single quote marks in assignment statements).
\n | \u000A | new-line | |||
---|---|---|---|---|---|
\t | \u0009 | tab | |||
\b | \u0008 | back-space | |||
\r | \u000D | return | |||
\f | \u000C | form-feed | |||
\\ | \u005C | back-slash | |||
\' | \u0027 | single quote | |||
\" | \u0022 | double quote | |||
\ddd | d = 0 to 7 | character's octal value | |||
\udddd | d = 0 to F | character's hexadecimal value |
String literals appear between double quote marks viz:
string s = "help"; /* make string variable s point to a string array containing the letters 'help' */
To break a line within a quoted string, you must insert a \n into the string, not simply start a new line in your source file. Be careful of how you place octal character entities in strings. For instance:
\0116 is rendered as \t6 (ie a tab followed by a character 6)
\116 is rendered as N.
private | variable is visible only within the immediate class in which it is declared. | |
---|---|---|
public | variable is visible from anywhere from which the class within which it is declared is visible. | |
protected | variable is visible to classes which extend the class in which it is declared. |
static | variable retains its value after the method in which it is declared terminates. | |
---|---|---|
synchronized | variable is locked from access by other program threads while a particular thread is updating it. | |
final | variable can only be assigned a value once, that is, when it is initialised just before the first pass of the method code. |
The order in which the modifiers are placed in the declaration of a variable are, by convention:
The form of a complete variable-declaration is therefore as follows:
The following statement declares three interger variables x, y, z:
private static final int x, y, z;
A variable can be declared anywhere in a Java source file. It takes effect from the point at which it is declared to the end of the 'namespace' within which it was declared.
function( ) { }
A namespace is the space between the braces {} of a source block. This is deemed to include the space between the brackets ( ) of the function to which the source block pertains where present. Here, the term function is used as a generic term for method, constructor, exception handler etc..
An array is declared and created as follows:
int[] a; /* declares a reference 'a' to an interger array of unspecified dimension */ a = new int[3]; /* creates an array object with 3 interger elements */
The declaration and creation can be combined as follows:
This creates an array of integers as follows:
A different size of interger array object can be assigned to the array reference 'a' at any time as follows:
a = new int[256];
Java's built-in automatic garbage collection service gets rid of the old dis-referenced 3-element array.
The size or length of the object currently referenced by reference variable 'a' is always available in the length variable a.length. This is illustrated in the program below which prints out the contents of the array.
for(int i = 0; i < a.length; i++) System.out.println(i + ": " + a[i]);
Arrays can contain objects other than Java's primitive types like int, float etc. For instance, you can create an array of objects of a class called X as follows:
X[] a = new X[32];
Suppose that X is a name and address class containing a person's name, job title, company, premises name, street name, town, county, postcode. Now suppose that Class Y extends X to define a Contact Record class which includes all of the above plus phone and fax numbers, Email address and Web Site. You can create an array of objects of Class Y as follows:
Y[] b = new Y[1024];
Each element of b[] contains everything that each element of a[] contains and more. The X-fields of each element of an array of Y objects can therefore be processed by any method() which has been designed to process arrays of pure X objects. Therefore a reference to a Y object can be used anywhere that a reference to an X object can be used.
The sense in which an array is an object is limited in that an array cannot itself be extended. You can only extend objects which can later become elements of an array of the extended objects. For example, to create an array of actual objects of type X, you have to proceed as follows:
X[] x = new X[12]; /*create a 12-element array of pointers to objects of Class X. The elements of this array initially contain null pointers. */ for(int i = 0; i < x.length; i++) x[i] = new X(names[i], jobtits[i], ... );
Each pass of the for loop creates an object of Class X which becomes an element of the array x[]. During this process, the address of each new object is put in the i^{th} element of the array of references (pointers) x[].
Only single-dimensional arrays of primitive types or objects exist in Java. Multi-dimensional arrays are effected by creating arrays of pointers to sets of single-dimensional arrays. For example, to declare a 4 x 5 array of float variables, m, you write:
float[][] m = new float[4][]; for(int i = 0; i < m.length; i++) m[i] = new float[5];
The first statement generates a 4-element array of pointers to floats whose elements are all initialised to null. Each pass of the for loop then generates a 5-element array of floats, the start address of which is placed in the appropriate element of the pointer array created earlier. The structure thus generated is shown below:
It is not necessary that all the arrays of floats be the same length. You can for example create a 2-dimensional array of objects whose rows are of different lengths. To print out the elements of the above array write:
for(int i = 0; i < m.length; i++) { for(int j = 0; j < m[i].length; j++) System.out.print(m[i][j] + " "); System.out.println(); }
A variable can be initialised in its declaration statement: eg.
final double p = 3.14159d float r = 1.0f
Variables which are fields of a class are automatically initialised to default values as follows:
boolean: | false | |
---|---|---|
char: | 'u0000' | |
interger (byte, short, int, long): | 0 | |
floating-point (float, double): | +0.0f, +0.0d | |
object reference: | null |
However, Java doesn't initialise the local variables in a block to the above defaults. You must initialise them explicitly. Local variables are initialised each time their declarations are executed. Object fields and array elements are initialised when they are created with a new. All static variables within the bounds of a class are initialised before any code in that class is run.
To declare and initialise a string array you can write for example:
String[] d = new String[3]; d[0] = "Lions"; d[1] = "Tigers"; d[2] = "Bears";
But this can be short-handed to:
String[] d = {"Lions", "Tigers", "Bears"};
To initialise a 4 x 4 matrix of doubles using this short-hand form, write:
double[][] m = { {1.0, 0.0, 0.0, 0.0}, {0.0, 1.0, 0.0, 0.0}, {0.0, 0.0, 1.0, 0.0}, {0.0, 0.0, 0.0, 1.0} };
postfix operators: | x[] x. (x,y,z) x++ x−− | |
prefix operators: | ++x −−x +x −x ~x !x | |
creation operator: | new x | |
cast operator: | (type)x |
multiplicative operators: | * / % | |
---|---|---|
additive operators: | + − | |
shift operators: | << >> >>> | |
relational operators: | < > > = <= instanceof | |
equality operators: | == != | |
bitwise AND: | & | |
bitwise exclusive OR: | ^ | |
bitwise inclusive OR: | | | |
logical AND: | && | |
logical OR: | || | |
conditional operators: | ? : |
= += −= *= /= %= >>= <<= &= ^= |=
Use parentheses to override precedence where necessary eg:
while((v = stream.next()) != null) process(v);
Expressions are evaluated strictly left-to-right. For example, x + y + z is evaluated in the following order.
This order of evaluation is guaranteed. This can matter when the operands are results returned by embedded function calls.
The type of the result of an expression evaluation is determined as follows:
int + int = int int + long = long float + int = float float + long = float float + float = float float + double = double
where + represents any arithmetic or bit-wise operator.
string + any type = string string += any type = string
Any interger type less than 32 bits long is promoted to a 32-bit int before it takes part in the evaluation of an expression as follows:
byte: top 24 bits filled with the value of the sign bit short: top 16 bits filled with the value of the sign bit char: top 16 bits set to zero
int x = 5; //x is a 32-bit interger int y = 40; //y is another 32-bit interger long z = x + y; //z is a 64-bit interger
Implicit type conversions take place automatically in the third line above as illustrated below:
long z = (long)x + (long)y; float x = 29.5; //x is a 32-bit IEEE floating point value float y = .00037; //y is a 32-bit IEEE floating point value double z = x + y; //z is a 64-bit IEEE floating point value
Implicit type conversions take place automatically in the third line above as illustrated below:
double z = (double)x + (double)y; long x = 0x7effffffffffffffL; //x has 64-bit precision float y = x; /* but y's mantissa has only 24-bit precision */y can accommodate the range, but not the precision of x. Some of x's 64-bit precision therefore is lost when its value is assigned to y.
double x = 7.99; //48-bit mantissa + 16-bit exponent long y = (long)x; //cast to a 64-bit interger
Although it can accommodate greater precision, y holds only the truncated value of x, namely 7. That is why this conversion is not automatically allowed, but must be explicitly written into the code.
double x = BigNum; /* double has a greater range and precision than float */ float y = (float)x; /* so this conversion must be forced explicitly */
Can result in a serious loss of both range and precision if you aren't careful.
long x = 0x0f0fffff; //a long whose lower 16 bits are all set char y = (char)x; //lower 16-bits only used so y = \uffff short c = (short)y; //same length but the value in c = -1 int z = (int)y; //top 16 bits filled with zeros z = 65535
An object reference (what most of us know as a pointer) can be converted to refer to different types of object.
Obgekt p = null; ExtendedObgekt q = p; //this assignment conversion is implicit ExtendedObgekt p = null; Obgekt q = (Obgekt)p; //but this conversion must be explicit
However, the above can be forced by explicitly typecasting the extended object as an object, for example:
teeshirt p = TeeShirts[x]; clothing q = (clothing)p;
This is valid when you want to perform a function on a teeshirt object which could be equally well performed on an item of clothing of any kind, such as reading its washing label viz:
ReadWashingLabel(q = (clothing)p);
where the above is a method within the Class Clothing{ } of which the Class TeeShirt{ } is an extension.
To test whether teeshirt is an extension of clothing, thus avoiding a possible error use the test:
if(teeshirt instanceof clothing) q = (clothing)p;
long x = 23.789; string y = "Output Value = " + x;
This is interpreted as:
string y = "Output Value = " + toString(long x);
A toString() method is defined for all primitive types and one can be written into the class code for any object.
Members (or elements) of an array are accessed by using the [] operator. The following accesses the i^{th} member of the interger array X:
int x = X[i];
Members (fields) of objects are accessed using the . operator as follows:
int lat = NavStn.lat; //access latitude of a navigation station int lng = NavStn.lng; //access its longitude also
where NavStn is an object reference pointing to an instance of the class of objects called NavStns{ }.
Members of the class itself - ie fields which pertain to the class as a whole rather than to individual objects of that class - are accessed in the same way. But, this time you use the class name instead of an object reference:
int NumStns = NavStns.NumStns; /*access the number of navigation stations currently on file */
Methods are also members of classes. Consequently they are accessed in the same way. The following example finds a station's current bearing:
int brg = NavStns.GetBrg(lat, lng);
Exceptions: If you access an object or array with a reference whose value happens to be null, then a NullPointerException is thrown. If you specify an array element number which is out of range (eg you ask for the 23rd element of an array which has only 16 elements), then an IndexOutOfBoundException is thrown. Java omits this check if at compile time it can be determined that the index cannot ever go out of range (eg in a loop of defined limits).
A chocolate cake is a specific kind of cake. A cake is a specific kind of dessert. A buttered scone is a specific kind of scone. A scone is a specific kind of dessert.
Suppose we have three methods all with the same name and each with the same number of parameters. However, the parameters are of different object types:
Which version of the method will the following statements invoke?
arrange(dessertRef, sconeRef);
It invokes No 1 because the parameter types are an exact match.
arrange(chocolateCakeRef, dessertRef);
This invokes No 3 because chocolate cake is a more specific form of cake and scone is a more specific kind of dessert.
arrange(chocolateCakeRef, butteredSconeRef);
This invokes No 3 because chocolate is an exact match and buttered scone is a more specific type of scone.
arrange(cakeRef, sconeRef);
This would tend to invoke Nos 1 and 2 equally and therefore cannot invoke either. It is an invalid invocation.
The same rules apply to primitive types. Eg. an int is assignable to a float just as a buttered scone is assignable to a scone. But if you declare two methods which both compute the distance to a navigation station:
int dist(int lat, int lng){ } short dist(int lat, int lng){ }
and then you invoke one of them by the statement:
double d = dist(lat, lng);
the compiler has no way of knowing which one to invoke here - even if the two methods throw different exceptions.
The moral is that when in doubt use the method's complete class reference.
0111 | +7 | |
0110 | +6 | |
0101 | +5 | |
0100 | +4 | |
0011 | +3 | |
0010 | +2 | |
0001 | +1 | |
0000 | +0 | |
1111 | −1 | |
1110 | −2 | |
1101 | −3 | |
1100 | −4 | |
1011 | −5 | |
1010 | −6 | |
1001 | −7 | |
1000 | −8 |
The figures at the left illustrate the principle of two's complement arithmetic on which all interger arithmetic in JAVA is based.
Notice how the bits change around zero. Minus 1 is all bits set, zero is no bits set, plus 1 is the least significant bit set. Notice too that two's complement arithmetic wraps round from + 7 to −8! One more numerically.
The full range of values is shown for only a 4-bit two's complement register so that the full range can be illustrated in half a page.
To convert a negative number to its positive equivalent and vice versa, invert all the bits then add 1.
Java's int is a 32-bit register. Its critical wrapping at
zero and at its numerical maxima are shown below.
Around zero it behaves as follows:
00000000000000000000000000000010 +2 00000000000000000000000000000001 +1 00000000000000000000000000000000 +0 11111111111111111111111111111111 -1 11111111111111111111111111111110 -2Around its maximum values it behaves as follows:
10000000000000000000000000000010 -4294967294 10000000000000000000000000000001 -4294967295 10000000000000000000000000000000 -4294967296 01111111111111111111111111111111 +4294967295 01111111111111111111111111111110 +4294967294
A short (16-bit), int (32-bit), and long (64-bit) arithmetic all follow the same rules. Arithmetic performed on a char (16-bit Unicode) converts implicitly to int before applying the actual arithmetic operations.
The arithmetic operations which can be performed on integers are:
x + y | add x to y (always wraps) | |
x - y | subtract y from x | |
x * y | multiply x by y | |
x / y | divide x by y | |
x % y | gives the remainder when x is divided by y |
The action of the remainder operator is to repeatedly subtract y from x until it cannot get a complete y. It then returns what is left. Eg: 15 % 4 works out as:
15 - 4 = 11 11 - 4 = 7 7 - 4 = 3 3 - 4 cannot go so it returns 3 as the answer.
The remainder function is defined formally by the identity:
( x / y ) * y + x % y == x
Interger multiplication wraps. Interger division truncates towards zero: eg:
+7 / 2 = +3 and -7 / 2 = -3
x / 0 and x % 0 are invalid and throw an ArithmeticException. Java arithmetic also supports the 'minus' unary operator to negate the value to which it is applied eg: -3
For symmetry it also supports the unary 'plus' operator eg: +3
An IEEE 754-1985 subset:
Underflows to zero: | +0 −0 | if decrementing to zero from the positive side if incrementing to zero from the negative side | ||
---|---|---|---|---|
Overflows to infinity: | +∞ −∞ |
if incrementing upwards to infinity if decrementing downwards to minus-infinity |
You can initialise a variable to infinity for example:
float x = float.NEGATIVE_INFINITY;initialises the IEEE 32-bit floating-point variable x to minus-infinity.
A Java floating-point variable can also hold a value called NaN which stands for 'not a number'. It results when the outcome of a floating-point arithmetic operation is what mathematicians call 'indeterminate'. The behaviour of Java floating-point arithmetic in operations involving signed zeros and infinities is illustrated by the following identities:
+∞ + +∞ ≡ ∞ | +∞ − +∞ ≡ NaN | |
---|---|---|
−∞ + −∞ ≡ −∞ | −∞ − −∞ ≡ NaN | |
+∞ + −∞ ≡ NaN | +∞ − −∞ ≡ ∞ | |
x ÷ ±0 ≡ ±∞ | x % 0 ≡ NaN | |
x ÷ ±∞ ≡ ±0 | x % ±∞ ≡ x | |
0 ÷ 0 ≡ NaN | 0 % 0 ≡ NaN | |
±∞ ÷ x ≡ ±∞ | ±∞ % x ≡ NaN | |
±∞ ÷ ±∞ ≡ NaN | ±∞ % ±∞ ≡ NaN |
Java floating-point arithmetic does not throw exceptions like invalid operations, division by zero, overflow, underflow or inexact. Nor does it signal the occurrence of a NaN result. It just carries on by rounding results of operations towards zero or the nearest representable value, preferring values with the least-significant bit set to zero.
Also, you cannot use things like <= or >= in Java floating point. Eg you must use if(!(x < y)) instead of if(x >= y).
The statements i++; and ++i; both increase the value of i by 1.
The statements i--; and --i; both decrease the value of i by 1.
Therefore both i++; and ++i; are equivalent to i = i + 1;
and both i--; and --i; are equivalent to i = i - 1;
However there is a difference:
x = a[++i]; is equivalent to i = i + 1; x = a[i];
x = a[i++]; is equivalent to x = a[i]; i = i + 1;
If instead of incrementing the index of an array, we increment the contents of one of its elements, then both a[i]++; and ++a[i] are equivalent to a[i] = a[i] + 1; and a[i]--; and --a[i] are equivalent to a[i] = a[i] - 1;
However, if i is obtained from a method m( ) which returns a different value each time it is called as in a[m(i)]++; , then a[m(i)]++; is not equivalent to a[m(i)] = a[m(i)] + 1; and a[m(i)]--; is not equivalent to a[m(i)] = a[m(i)] - 1;
The statement a[m(i)]++; increments a particular element of a[] while the statement a[m(i)] = a[m(i)] + 1; sets the value of one element of a[] to one more than the value of some other element of a[].
Illustration of the effects of ++i and i++:
class IncOrder { public static void main( String[] args ) { int i = 16; System.out.print(++i + " " + i++ + " " + i); } }
Output is 17 17 18.
These operators return a boolean result true or false.
> | greater than | |
---|---|---|
> | less than | |
>= | greater than or equal to | |
<= | less than or equal to | |
== | equal to | |
!= | not equal to |
Only the last two can be used in expressions relating boolean variables. The prefix operator ! means 'not'. It inverts the boolean result of what immediately follows it.
These operators return a boolean value indicating whether a particular relationship between two variables is true or false.
For example, in the test: if(x < y) Java evaluates the expression x < y as a behind-the-scenes boolean variable b whose value is either true or false. It then uses the value of b in the implied statement if(b == true) to determine which route to take.
Test boolean values directly:
If x and y are boolean variables, use a test of the form: if(x || !y ){ ... }
instead of: if(x == true || y == false){ ... }
Single expressions which evaluate to a boolean value can be joined to form a composite expression using: && (conditional AND) and || (conditional OR). For example:
if( x && y ) { ... }
Java evaluates as little as it has to in order to obtain a valid result. For instance, in
if(i >= 0 && i < a.length && a[i] != 0){ ... }
if i is negative, the other two expressions are not evaluated.
Because == and != can only relate boolean values, they can be used to construct an exclusive OR or XOR test as follows:
if(x < 0 == y < 0) SameSign(); else DiffSign();
x != NaN always returns true
x # NaN always returns false
where # is any relational operator other than !=.
If s1 and s2 are string pointers, the expression s1 == s2 is true only if s1 and s2 point to the same string array: it does not test to see if two different string arrays have the same content.
& | bitwise AND | |
---|---|---|
| | bitwise OR | |
^ | bitwise exclusive OR ie XOR | |
~ | bitwise inverter, eg ~1101 becomes 0010 |
<< | Shift left (shifting zero bits in from the right) | |
---|---|---|
>> | Shift right (shifting with sign bits in from the left) | |
>>> | Shift right (shifting zero bits in from the left) |
1101 | << | 2 becomes 0100 | ||
1101 | >> | 2 becomes 1111 | ||
---|---|---|---|---|
1101 | >>> | 2 becomes 0011 |
The number of bit-places shifted (the right operand) is the number you provide masked by (the size of the type of the left-hand operand) - 1. So if the left-hand operand is a 32-bit int, the mask is 31
so a shift of | +35 | 00000000000000000000000000100011 | ||
---|---|---|---|---|
or a shift of | −29 | 11111111111111111111111111100011 | ||
masked by | 31 | 00000000000000000000000000011111 | ||
both yield | +3 | 00000000000000000000000000000011 |
Bitwise operators & and | can be used on boolean variables in place of && and ||. However unlike && and ||, & and | always evaluate both operands before returning their result - so beware.
A converse logical XOR test can be constructed using the bitwise XOR operator ^ which returns a true if the expressions evaluate to opposite boolean states and false if to the same boolean state as in:
if((x < 0)^(y < 0)) DiffSign(); else SameSign();
is equivalent to:
if(UserSetIt) value = UsersValue; else value = DefaultValue;Consider the types of the variables involved.
double scale = halveIt ? 1 : 0.5;
The types of the second and third operands must be the same as, or assignable to, the type of the value (ie the left side of the = sign).
The most basic assignment operator is '='.
However, in Java, any arithmetic or bitwise operator can be joined to '=' to form a composite assignment operator. For example:
a[func()] += 1; is equivalent to a[func()] = a[func()] + 1;
However, in the first case, func() is called only once. Since it may return a new value when called a second time, we cannot be sure whether or not it is the same array element that is being referred on both the left and right hand sides of the '=' sign. The composite '+=' operator is therefore clearer and more predictable in operation when a function is used as an array indexer.
If var is a variable of type Type, then
var op= expr is equivalent to var = (Type)((var) op (expr))
Note that the whole of expr is bound tightly. For example:
a *= b + 1; is equivalent to a = a * (b + 1); not a = a * b + 1;
Although a += 1; is the same as ++a; the ++ is traditionally the preferred coding.
A statement: | assignment; | |
A block: | {assignment; assignment; ... } |
if(boolean condition) statement; or {block} else //an 'else' is always associated with the most recent 'if' statement; or {block} switch x { case A: statement; or {block} //fall through case B: statement; or {block} //fall through case C: statement; or {block} break; //skip default default: statement; or {block} } while(condition) statement; or {block} do statement; or {block} while(condition) for(initializers; condition; incrementers) statement; or {block} label: statement; or {block}
break; | exit the inner-most block in which it occurs | |
---|---|---|
break label; | exit the labelled block called 'label' | |
continue; | immediately start the next pass of the inner-most loop | |
continue label; | immediately start the next pass of the labelled loop | |
return; | return from the current method to its caller | |
return(x); | return from the current method, passing the value of x to its caller. The 'type' of 'x' must be the return type declared for this method. |
NOTE: There's no 'goto' statement in Java because 'break' and 'continue' can express more formally what 'goto' is used for in other languages.