Java Programming Language
It is expected that the reader will gain knowledge and comprehension of the Java language such that application of the learning can be demonstrated by writing progressively more complex programs. The approach to the Java syntax is to build from the lowest level to the higher orders of program structure. The main division of the syntax will be made between logic statement syntax and program structure statement syntax. Logic statement syntax encompasses data types, expressions, variable-declarations, if/else and switch conditional statements, while, do and for looping statements, and simple method calling and I/O calls. Program structure statement syntax encompasses arrays, Strings, class and object definition, complex method calling, exception handling, and packaging. How the Object-Oriented paradigm is incorporated into the Java syntax will also be explored.
Java Logic Statement Syntax
These statements are concerned with performing operations within a program to implement the logic of an algorithm. These are very basic statements and are quite common in programming languages. They are not concerned with how the pieces of the program and other modules are put together, nor how they interact with one another. This is in the [Java Structure Statements Syntax] section.
Unicode
Java uses the Unicode standard for encoding character. The Unicode standard is based upon ASCII but uses 16-bits for each character. With 16-bits, 65,536 different characters can be encoded into the scheme. This allows for nearly all of the Western language characters to be available for processing by Java programs. The character codes for the 8-bit ASCII scheme is the same in Unicode except the high-order 8 bits of the 16-bit value are all zeros. Java handles all of this for you so you rarely have to be concerned that this is occurring. One of the rare instances will reveal itself when you see blank spaces between every character that should be put together. For example, t h i s i s a u n i c o d e p r o b l e m . This is most likely to occur when you output from a Unicode system and then view the output on a non-Unicode system.
To included characters that are not a part of the 8-bit ASCII standard but are a part of the Unicode standard, the \uxxxx escape sequence is used within a Java program. Typically this would be in a character or string declaration, however it is valid anywhere, like in comments and variable names. The xxxx represents the hexadecimal code for the character you want. For example \u0020 is the space character and \u03c0 is the Pi (p ) character. The u in the \u portion must be in lower case. More in-depth information can be access at www.unicode.org. To look up character code values see http://charts.unicode.org/Web.html.
Comments
Comments in a Java program consist of three different styles. The first is the single line style which begins with a // and is follow by a comment on the same line. For example,
// this is a single line comment.
The second is the multi-line or block comment. It begins with /*, is followed by one or more lines of comments and ended with */. For example,
/*
This is a multi-line
comment.
*/
or
/* this is
also
multi-line */
The third style is used for generating web pages documentation from the source code comments. This style is just like the multi-line or block comment except the beginning starts with /**.
See the Java Coding Conventions Source Code Comments sections for more information.
Variable names (Identifiers)
An identifier is any symbolic name that refers to something in a Java program (variable name, class name, method name, and parameters). Identifiers can be any length but must begin with a letter, an underscore (_) or a Unicode currency symbol ($, British Pound symbol, Japanese Yen symbol). Any number of letters, digits, underscores and currency symbols can follow as subsequent characters. The first character cannot be a digit and punctuation characters are not allowed anywhere in the identifier name. By convention currency symbols and non-English alphabetic characters should be avoided. Furthermore an identifier cannot be a Java Reserved Word which is covered in the next section.
Valid identifier names example:
I, j, MyVar, anotherVar, _hours, Var3, $_12
Invalid identifier names example:
123, 9Var, [var)
These are keyword in the Java language and cannot be used as identifiers. The keywords are categorized and listed below.
Used for built-in types:
boolean
char
byte short int long
float double widefp* strictfp*
void
Used as values:
true false
null
Used in expressions:
new this super
Use in statements:
Selection Statements
if else
switch case break default
Iteration Statements
for continue
do while
Transfer of Control Statements
return
throw
Exception Statements
try
catch
finally
Thread Statements
synchronized
Used to modify declarations (visibility, sharing, etc…):
static
abstract final
private protected public
Used for other method or class-related purposes:
class instanceof throws native
transient volatile
Used for larger-than-class building blocks:
extends
interface implements
package import
Reserved for possible future use:
const goto
Primitive data types (platform independent)
Java has eight primitive (built-in), non-object data types. These primitive types hold only one value at a time in each particular variable that is declared as one of these types. Java has a strict definition of the size of each of these primitive data types. This is one of the necessary conditions to achieve platform independent portability of "executable" code. These primitive data types are: boolean, char, byte, short, int, long, float, double. Good programming practice uses the best data type for the range of values a particular variable may need to be.
Contains: true or false, Default: false, Size: 1 bit, Range: NA
The boolean type represents a truth value (true/false, on/off, yes/no). Java reserves the keywords true and false to be used as values to assign to a boolean type variable or in expressions that test the value of a boolean variable. The size of a boolean variable is 1-bit but the JVM may use a larger size internally for performance reasons. The boolean values do NOT translate into integer values that can manipulated or used instead of the true/false keywords. This is different from the C syntax. This means that boolean variables can not be converted to or from other data types and implicit logic syntax shortcuts, that translate an integer value of zero to false and non-zero to true, can not be taken in Java. For example, the following code fragment is not allowed in Java:
if (o) { while(i) { } }
Instead Java forces you to write clearer code by explicitly stating comparisons as the following code fragment example reflects:
if (o != null) { while(i != 0 ) { } }
The following example shows how to achieve the same affect as converting a boolean value to another type.
if (bool) i=1; else i=0 //set int i according to bool value. bool = (i==0? false:true) //set bool according to int value of i.
Contains: Unicode character, Default: \u0000, Size: 16 bits, Range: \u0000 to \uFFFF
The char type represents a single unsigned 16-bit integer value; this is a Unicode value and corresponds to one character. You should only use char for character and bit value data. Don’t use char if you want a 16-bit integer value that will have arithmetic operations done upon it. In that case use short which is a signed 16-bit integer value. This will help avoid problems of a variable’s value appearing to change due to the unsigned verses signed interpretation of the value in a variable. A char type variable can be assigned a literal value by enclosing the single literal character in between single quotes (apostrophes). For example, char c = ‘A’;
Other special values can be assigned to a char type variable through Java understood escape sequences. These escape sequences are as follows:
\b – backspace
\t – horizontal tab
\n – newline (next line down but same column position)
\f – form feed
\r – carriage return (go to 1st column)
\" – double quote
\’ – single quote
\\ - backslash
\xxx – the Latin-1 character with octal coding
\uxxxx – Unicode characters
When coding these literals also put them with single quote marks. For example, ‘\n’, ‘\r’, ‘\u03c0’.
Casting is possible and sometimes necessary. Be aware that the char data type is an unsigned value and all the Java integer data types are signed values.
Integer Types
Integer values in Java are always signed; they contain positive (+) and negative (-) values. The range is based upon the size of the data type. Java offers a unique type for the four common sizes of integer data types. See the Numeric Literals and Arithmetic: Integer section below for the rules on coding integer literals and doing arithmetic.
Contains: signed integer, Default: 0, Size: 8 bits, Range: -128 to 127
Examples:
byte b1 = 127, b2 = 1; //largest byte = 127 byte b3 = -128; //smallest byte = -128 byte sum = (byte) (b1 + b2); //sum wraps to -128
The last line in the examples above reveals that Java does all arithmetic with at least 32 bits. Therefore casting is needed to assign the value to sum variable. This same line shows that casting is needed even when all the values involved are of the byte data type.
byte sum = b1 + b2; //this will not compile byte sum = (byte) b1 + b2; //this will not compile only b1 is cast
The b1 & b3 assignments in the examples above did not need a cast because these variables are assigned with literals that fit into 8 bits.
Contains: signed integer, Default: 0, Size: 16 bits, Range: -32768 to 32767
Examples:
short s1 = 32767, s2 = 1; //largest byte = 32767 short s3 = -32768; //smallest byte = -32768 short sum = (short) (s1 + s2); //sum wraps to -32768
The last line in the examples above reveals that Java does all arithmetic with at least 32 bits. Therefore casting is needed to assign the value to sum variable. This same line shows that casting is needed even when all the values involved are of the short data type.
short sum = s1 + s2; //this will not compile short sum = (short) s1 + s2; //this will not compile only s1 is cast
The s1 & s3 assignments in the examples above did not need a cast because these variables are assigned with literals that fit into 16 bits.
byte and short should only be used to match external values already present in a file or for space economy. For the most part the space economy is not worth gaining, since arithmetic is done with 32 bit values, performance is not enhanced with these smaller values. Therefore, use int when what you need is an integer value and it doesn’t have to match an external data type that is not of type int.
Contains: signed integer, Default: 0, Size: 32 bits, Range: -2147483648 to 2147483647
Examples:
int i1 = 2147483647, i2 = 1; //largest byte = 2147483647 int i3 = -2147483648; //smallest byte = -2147483648 int sum = i1 + i2; //sum wraps to -2147483648
Contains: signed integer, Default: 0, Size: 64 bits, Range: -9223372036854775808 to 9223372036854775807
Use the L character at the end of integer literals that are to be treated as long (64 bits). The lowercase L (l) is valid but it is too similar to the one (1) character so avoid using it.
Floating-Point Types
Floating-point (real) values in Java are always signed; they contain positive (+) and negative (-) values. The range is based upon the size of the data type. Java offers a unique type for the two common sizes of floating-point data types. See the Numeric Literals and Arithmetic Floating-Point section below for the rules on coding floating-point literals and doing arithmetic.
Contains: IEEE 754 floating point, Default: 0.0, Size: 32 bits, Range: ± 1.4E-45 to ± 3.4028235E+38
This data type is for signed 32-bit real numbers with at least 6 significant decimal digits.
Contains: IEEE 754 floating point, Default: 0.0, Size: 64 bits, Range: ± 4.9E-324 to ± 1.7976931348623157E+308
This data type is for signed 64-bit real numbers with at least 15 significant decimal digits.
Numeric Literals & Arithmetic
Integer variables can be assign values using a literal string of digits. The unary minus (–) and unary plus (+) operator can precede a string of digits to indicate a negative or positive value.
Literals are most commonly expressed in decimal notation.
Literal format: [+|-](digits…)[L|l]
Examples:
0
1
123
-42000
Literals can also be expressed in hexadecimal or octal notation. The hex notation begins with 0x and followed by hex characters (0-0,A-F or a-f).
Literal format: 0x(A-F|a-f|digits…)[L|l] or 0X(A-F|a-f|digits…)[L|l]
Examples:
0xff
0xCAFEBABE
The octal notation begins with 0 and followed by octal digits (0-7).
Literal Format: 0(digits0-7…)[L|l] //octal digits. 8 & 9 not allowed.
Examples:
0377
Binary notation is not allowed.
All integer literal values are 32-bit int values unless they end with the character L or l in which case they are 64-bit long values. For example,
1234 // int value
1234L // long value
0xffL // long value in hex notation
Integer Arithmetic
All arithmetic on integers is modular meaning that there is never overflow or underflow. The values "wrap around" from the top to bottom of the value range or visa-versa. The Java environment doesn’t give you any warning or exception when this occurs. Therefore, care must be taken when coding to use the correct sized integer data type that corresponds to the range of values the variable should be allowed to have. Division or Modulo by zero does cause an exception to occur.
Floating-point variables can be assign values using an optional literal string of digits, followed by a decimal point and another string of digits. The unary minus (–) and unary plus (+) operator can precede a string of digits to indicate a negative or positive value.
Literal format: [+|-][(digits…)].(digits…)[F|f|D|d] //normal notation
Scientific or exponential notation is allowed by properly using the E character. Refer to the Java textbooks for more detailed information about coding using the E character. Floating-point literals are of type double unless they end with F or f. The D or d character at the end of a floating-point literal can be used to indicate explicitly that the literal is of type double.
Examples:
123.45
123.45D
123.45f
0.0
.01
-43.607F
1.2345E02 //1.2345 * 102 or 123.45
1e-6 // 1 * 10-6 or 0.000001
1. //this is valid to represent 1.0, but it is not good practice.
Java can handle overflow to infinity and underflow to zero and has a special NaN (Not-a-Number) value. Therefore, Java floating-point arithmetic never throws an exception, even when performing illegal operations, like dividing by zero or taking the square root of a negative number. The following code shows how to code some special floating-point values. Other textbooks should be referenced to gain more extensive knowledge of floating-point numbers and is recommended if you are going to use any floating-point data types. The "wrapper" classes for float and double as will be discussed later should also be investigated.
Special Values:
double inf = 1.0/0.0; // Infinity double neginf = -1.0/0.0; // -Infinity double negzero = -1.0/inf; // Negative Zero double NaN = 0.0/0.0; // NaN
Particular Java textbook references for floating-point are:
Java in a Nutshell 3rd Edition, Pg. 25-26.
Just Java 2 4th Edition, Pg. 93-96.
The trouble with floating-point numbers and arithmetic is that they are not mathematically accurate real numbers, but are often (mis)used as if they are. The main problem is that floating-point numbers have a limited accuracy – only so many digits to the right of the decimal point. If all of the numbers in a calculation are of approximately the same magnitude, then the calculation won’t lose accuracy. But if the numbers are of very different magnitude, then a floating-point calculation sacrifices accuracy. The IEEE-754 floating-point standard used by Java goes a long way to avoid this type of problem, but you should carefully consider this problem when you numbers of different magnitudes that may exceed the number of significant digits for the floating-point data type. The other problem with floating-point is that we code the numbers as decimal numbers (base 10), but the computer uses binary (base 2). The necessary conversion may cause problem with accuracy. For example, 0.1 is a repeating fraction when translated into binary. Because the computer cannot store the infinite number of repeating digits in this fraction, the number 0.1 cannot be stored accurately. Therefore, you have a problem like added 0.1 together 10 time not producing 1.0, but 1.0000001.
Boolean is the only primitive type you cannot convert to or from another primitive type. You can convert integers to floating-point, characters to integers or floating-point because characters are 16-bit numbers in the Unicode encoding scheme. Converting other types requires casting. There are two basic types of conversion, widening and narrowing.
Widening
This conversion is from one primitive data type to another primitive data type that has more bits, wider. Widening conversions are always safe and Java may do it for you automatically when, for example, you assign an int literal to a double variable or a char literal to an int variable.
Narrowing
This conversion is from one primitive data type to another primitive data type that has fewer bits, narrower. Narrowing conversions are not always safe. If the wider type variable value fits within the range of the narrower type variable then all is well. If it does not fit in the range then loss of data will occur. The Java compiler complains whenever you attempt a narrowing conversion even if the wider type variable value does fit within the range of the narrower type variable. The one exception to this rule is that you can assign an int literal to a byte or short variable if the literal falls within the range of narrower type variable.
For example,
int i = 13; byte b = i; // the compiler does not allow this short s = 24000; //int literal in the range of short data type variable
When converting characters remember that they are 16-bit unsigned values and all the integer types are signed values. This conversion is of particular importance when converting a char to short. Both are 16-bits wide but the high bit of short is treated as a sign bit. In this case data interpretation confusion could ensue.
To force the type conversion in the narrowing direction, casting can be used. Casting is done by preceding the value, variable or expression to be converted to a narrower data type with the narrower data type in parentheses ( ). Casting should only be done when you are confident that loss of data or precision will not occur or not be a problem. You should only employ casting when it is truly necessary and you understand what is happening.
Examples:
int i = 13; byte b = (byte) i; // this is valid
The following is a good type conversion and casting reference table.
Convert From: |
Convert To: Boolean |
Byte |
Short |
Char |
Int |
Long |
Float |
Double |
Boolean |
- |
N |
N |
N |
N |
N |
N |
N |
Byte |
N |
- |
Y |
C |
Y |
Y |
Y |
Y |
Short |
N |
C |
- |
C |
Y |
Y |
Y |
Y |
Char |
N |
C |
C |
- |
Y |
Y |
Y |
Y |
Int |
N |
C |
C |
C |
- |
Y |
Y* |
Y |
Long |
N |
C |
C |
C |
C |
- |
Y* |
Y* |
Float |
N |
C |
C |
C |
C |
C |
- |
Y |
Double |
N |
C |
C |
C |
C |
C |
C |
- |
N = no conversion allowed
- = NA
Y = widening conversion and done automatically and implicitly by Java
Y* = same as Y except some least significant digits of value may be lost
C = narrowing conversion and explicit cast is required
Composite data types (or reference types)
Java also has composite data types. These data types are also known as reference types because they refer to a starting memory location (a pointer) that contains a data structure. There are three composite data types (strings, arrays and classes), each with their own unique characteristics. In contrast too primitive data types, composite data type holds multiple values that are referred to by one variable. Depending on the composite data type, the variable has a secondary mechanism to address the individual values. For example, an array is a composite data type that is addressed by a variable that refers to the entire array and an index that refers to the individual elements of the array. A composite data type can have a value of null when it has no actual reference yet. The keyword null can be used to assign or compare a reference type to the value null.
Strings
Strings are a series of characters grouped together. The String data type is actually an Object. Since strings are so often used, Java provides some features such that the String object can be treated as though it is a primitive data type. Most notably is that a String data type can be assigned a literal value. Enclosing a series of characters within double quote marks ("…") does this. Anything that can be coded as char data is done the same way within a String. Strings can also be joined together (concatenated) using the + operator. The Java language will know by the context that the + operator means to concatenate the strings together. The + sign is used because concatenating is most analogous to adding.
Other than these special means of using strings, they are treated as objects. This means that other operations on string are done through methods like testing for string equality or addressing a substring within the string. A String object reference variable that hasn't been created yet has a value of null. This is different from a String that has the value "" which is called an empty string. The use of the String object will be explored in more detail in a later section.
Arrays
An array is an ordered collection or numbered list of values, all of which are of the same data type, either primitive or composite. This means that an array can contain values that are any of the primitive data types, values that are strings, other arrays or Objects. Arrays in Java are actually Objects thus they are more dynamic. In Java an array is not limited to being a fixed size at compile time, the array can grow and shrink during runtime. Bound checking on the index to the elements in an array occurs at runtime and an exception occurs if the index is out of bounds. While arrays are Objects they also have some peculiar differences. Arrays will be explored in more detail in a later section.
Classes & Objects
In Java, a Class is like a type - a definition of the data value, and an Object is like a value – a specific instance of a type with a value. Classes are a composite type that contains both code and data that define a new type. Objects are instances of a class and are referenced through an identifier, which is the variable name for the object that contains an address of where the code and data of that particular object is stored. Classes are defined so that you can have objects that contain the data and the operations that are germane to the entity the class models. Since Java is an Object-Oriented language, its fundamental structural unit is a class. When you write a "program" in Java, you are really defining a class and this class can use other classes. The details about declaring, creating and using classes and objects is explored in a later section.
Constants
Constants should be used in programs when you have a value that doesn’t change and you want to associate this value with an identifier (instead of "hard-coding" the value within the program logic). Declaring constants in Java is easy but not intuitive. Constants in Java are declared like variables, with a type, identifier and initial value but with an extra preceding modifier. This modifier tells the Java compiler that the variable declaration is to be modified in a particular way. This allows the compiler to make certain optimizing assumptions about the use of the "variable". The modifier (one of several that will be discussed later) that signifies that an identifier has a constant value is the keyword final. Final means that the value cannot change. It can be applied to data ("variable" declarations) and code (classes and methods) - more on this later. The static keyword is usually used with constants so that the compiler can make compile-time optimizations. The static keyword indicates that the item exists only once for the class no matter how many instances there are. Each instance uses the single item that is global to all objects of the class. In terms of coding constants simply precede the declaration with the keywords final and static, for example:
final static boolean yes = true; final static char activeCode = ‘A’; final static int i1 = 99; final static float pi = 3.14159; final static String myString = "This is a constant string";
It is important to note the when an object is made final, it is the reference to the object that cannot change, not the values of any data that is contained in the object.
Expressions & Operators
Primitive data types, variables and literals are the elements that have been discussed so far that are used to build Java programs. The next level up in the Java language is expressions and operators. With these elements we can write program logic to "calculate" values using other values and a rich set of operations. An expression is a set of operands and operators grouped together in a programming statement such that they are evaluated together. Operands are the values (variables, literals and intermediate values calculated during the expression evaluation) that are operated on by an operator. An operator is a symbol that denotes a specific mathematical or logical manipulation to do on one or more operands. Each operator has a defined number and types of operands that it needs to function correctly. For example the subtract symbol - needs two numbers to work correctly. The boolean not symbol ! needs one boolean operand to work correctly.
Primary Expressions
Primary expression are those expression that assign a variable a value from a literal or another variable. For example,
int i1 = 3; int i2 = i1; float sum = 1.7;
These are not very interesting themselves, but when combined with other values through the rich set of operators, powerful program logic can be employed. These are known as compound expressions.
Compound Expressions
Compound expressions are constructed by combining primary or other compound expressions together with an operator. This can be extended such that the expression is a whole collection of other expressions and operators. For example,
double sum = 1 + 2 + 3*1.2 + (4 + 8)/3.0; int sumint = (int)(sum + 33);
The combining of expression together through various operators introduces the problem of what order should the operations be done in. This problem is dealt with by three properties. The first is precedence, which gives an order of importance to the operators so that the order of evaluation can be determined. The second property, associativity, is necessary because two or more operators may have the same precedence value so another means must be used to break these ties. Associativity defines which direction left-to-right or right-to-left should be used to break the ties. The third property, order of evaluation, means that operands associated with the operator(s) are evaluated first. The goal of these three concepts is to allow a natural coding of expressions in the Java language. There are various nuances related to these concepts which are discussed next.
Precedence
All the operators in Java have a specific precedence value. The greater the value the higher the precedence the operator gets when an expression is evaluated. This allows for a natural coding of expression to get the correct result. For example, the multiplication operator has a higher precedence than the addition operator does. Therefore, the evaluation of the expression a + b * c will follow the conventional mathematical order. Hence, the multiplication will be done first and then the addition. Parentheses can be used to make the precedence clear or to alter the natural precedence. Thus a + b * c is the same as a + (b * c). To change the precedence we could code (a + b) * c. It is good programming practice to use parentheses to explicitly state the expression evaluation order so that the precedence, associativity and order of evaluation rules do not have to be remembered to read the Java expression. There are a few common places in which parentheses are required in Java; these are shown in the following examples.
Class cast combined with member access
int o; //o is primitive data type int //cast o into the int wrapper class Integer and then call the //intValue() method. ((Integer) o).intValue(); (Integer) o.intValue(); //this would not work because the method call would be attempted while //o was still the primitive data type int.
Assignment combined with comparison
while((line = in.readLine()) != null) {…} //the assignment of the variable line from the return value of the //in.readLine() method call is done first and then the value of line is //compared with null to see if they are not equal. The while would //proceed based on the true/false boolean value that is the result of //the comparison and line would contain the value from the //in.readLine() method call. while(line = in.readLine() != null) {…} //this would not work because in.readLine() return value would be //compared with null and this value (true or false) would be assigned //to line.
Bitwise operators combined with comparison
if ((flags & (PUBLIC | PROTECTED)) != 0) {…} //the constants PUBLIC and PROTECTED are bitwise Or’d together and then //that value is bitwise And’d with the flags variable and then this //value is compare with 0 to test if it is not equal. if (flags & PUBLIC | PROTECTED != 0) {…} //this would not work the constant PROTECED would first be compared //with 0 to return a true or false result, then an attempt would be //made to bitwise or this boolean result with the result of the bitwise //& of flags and the PUBLIC constant.
Each of these examples will not work correctly without parentheses because of the precedence order of the operators involved.
Associativity
Associativity deals with determining which operands go with which of a sequence of adjacent operators of equal precedence. Most operators have a left-to-right associativity, however there are some that have a right-to-left associativity. The direction is defined so that the natural way of writing an expression can be done without parentheses. Parentheses can be used to alter the default associativity direction and it is good programming practice to use parentheses in expression to make the code explicit.
For example, given the expression 3 * 5 % 3, the associativity property of the operators * and % (modulus) which both have the same precedence defines the direction to be left-to-right. Therefore the expression is evaluated as (3 * 5) % 3 = 15 % 3 = 0.
Order of Evaluation
The order of evaluation applies to the operators of a particular operand and is always evaluated left-to-right. For example, (i=2) * i++ is evaluated as first, i=2 because of the parentheses, second i * i, (2 * 2) is done and results in the value 4. Lastly, i which is still 2 is incremented by 1 to become 3. Even though ++ had a higher precedence that * it is still evaluated afterwards because the order of evaluation is left-to-right. In another example we will see the opposite happen. Given int a =2; int v = ++a + ++a * ++a; the value in v will be 23. This happens because a starts at 2 and the first ++a is encountered which increments a to 3 (the order is still left-to-right and the increment is before the operand) this is also the value of the first operand. Then the next ++a is encountered and a is incremented to 4, this is also the value of the second operand. Next the last ++a is encountered and a is incremented to 5, this is also the value of the third operand. Now the expression is 3 + 4 * 5. The precedence rules for operators takes affect now and the expression is evaluated as 3 + (4 * 5) which is 23.
Expression Evaluation Summary
The three properties of expression evaluation: precedence, associativity and order of evaluation are difficult to clearly explain. Just remember that Java has defined these properties for the operands and operators of expressions such that an expression is evaluated in a natural order in terms of mathematics and common programming languages. Java accomplishes this through the basic evaluation order of left-to-right but it also defines the order of everything precisely so that exceptions to the basic rule are consistently dealt with. Particular things to note about expression evaluation are:
ObjectInstance.methodName(arguments);
The objectInstance is fully evaluated before the methodName and arguments. This can make a difference if the objectInstance is given to you by a method that has side effects. In that case, any arguments are evaluated one by one from left to right.
It is still best to liberally use parentheses to make the expression explicitly clear and to test the expression so that the desired result is being achieved.
In Java in a Nutshell, table 2-5 on page 30-31 documents the expression evaluation order.
In Just Java 2, table 5-2 on page 123 documents the expression evaluation order.
Return Type
When expressions are evaluated intermediate values and the final value is returned during the evaluation of the expression. The data type that is returned is based upon the "largest" type of operand that is encountered in the expression evaluation at the particular point a value is returned. The order of data type from "greatest" to "least" is double, float, long and then int. If the data types of the operand(s) is short, char or byte the return type is promoted to int. Expressions involving boolean operands always returns boolean data types.
Side Effects
Some operators have additional side effects than just their basic evaluation. This means that they change the state of the Java program in some way such that evaluating the expression again may yield a different result. This is most easily seen with the increment (++) or decrement (--) operators. If these operators are associated with an operand that is a variable then the variable’s value are changed as well as the effect of the expression being evaluated. If the expression is evaluated again, the result will be different because the value of the variable has changed and the second evaluation will again change the value of the variable. Other common operators that have side effects are the assignment with operation type operators (*=, /=, +=, -=, <<=, &=, etc…). Method invocation, methodName(…), may or may not have side effects based on what the method does. The new operator to create a new object obviously has a profound side effect on the state of the program.
Arithmetic Operators
The arithmetic operations can occur on integer, floating-point and character data types (all the primitive data types except boolean). If any of the operands is floating-point then floating-point arithmetic is used otherwise integer arithmetic is used. This matters in terms of division and underflow, overflow handling.
Addition (+)
Adds two numbers together.
Subtraction (-)
Subtracts second number from first number. 7 – 3 = 4
Multiplication (*)
Multiplies two numbers together.
Division (/)
Divides the first number by the second number.
If both numbers are integers then the result is an integer truncated towards zero and the remainder is lost. Integer division by zero throws an ArithmeticException. If either number is a floating-point number then the result is floating-point. Floating-point division by zero yields an infinite result or NaN (Not a Number). See the floating-point arithmetic section.
Examples:
7/3 // evaluates to 2 7/3.0f // evaluates to 2.333333f 7/0 // throws an ArithmeticException 7/0.0 // evaluates to positive infinity 0.0/0.0 // evaluates to Nan
Modulo (%)
Return the remainder of the first number divided by the second number. The sign of the result stays the same as that of the first operand. This operations work with both integer and floating-point numbers. Integer modulo by zero throws an ArithmeticException. Floating-point modulo by zero and Infinity modulo anything yields a NaN result.
Examples:
7 % 3 = 1
4.3 % 2.1 = 0.1
If you did not have a modulus operator the following statements could be used to calculate the remainder for integer numbers.
q = x/y r = x – (q * y) For Example, x = 5 y = 2 q = 5/2 = 2 //quotient r = 5 – (2 * 2) = 1 //remainder
Unary Minus (-)
When used before a single number it reverses the sign of the number. Negative become positives and positives become negatives.
Unary Plus (+)
When used before a single number it makes the number positive no matter the original sign.
Accuracy
In Just Java 2, 4th Edition, pages 129-135 is a simple explanation of the issues of accuracy with integer and floating-point arithmetic. Much of this information is documented in this document at various places where it applies most directly.
String concatenation
Concatenate (+)
If either operand is a string then this operator will if necessary convert the other operand to a string and then join the two strings together. Care should be taken when you combine string concatenation with arithmetic addition. Use parentheses to get the desired results. For example, "Total: " + 3 + 4 will form the string "Total: 34", not 7! To get "Total: 7" the expression should be "Total: " + (3+4). Note this only applies to the arithmetic addition operator and not other operators. For example, "Quotient: " + 7/3.0f will form the string "Quotient: 2.3333333".
toString() method
The Java interpreter has built string conversion for all primitive types. Objects invoke the toString() method to convert the object to its string representation. All objects have a toString() method by default because it is inherited from the parent object named "Object". This default method can be overridden by coding your own toString() method (it must be named exactly this way). In your customized method you can cause the string representation of you object be whatever you want. For some objects it may be obvious what the string representation should be, for other objects this may be complex or unpractical. The default toString() method for objects seems to print the starting memory address of the object when the string representation would be impractical to be something else. This can be used in some cases for rudimentary debugging. Even an array can be converted to a string using the toString() method. The default toString() method for an array is not a useful string representation of the array contents though.
Increment/Decrement
Increment (++)
Increments a variable, array element or a field in an object by one. This can be either a post or pre increment operation depending on whether the operator is after or before the operand. With post-increment the expression operand value is evaluated and then the operand incremented. With pre-increment the operand is incremented and then used in the expression.
Examples:
i = 1; j = ++i; //both j and i are set to 2 after this statement executed. i = 1; j = i++; //j equals 1 and i equals 2 after this statement executed.
Decrement (--)
Decrements a variable, array element or a field in an object by one in a similar fashion as the increment operator.
The expression x++ and x— are equivalent to x=x+1 and x=x-1 respectively, except that when using the increment and decrement operators, x is only evaluated once. If x is itself an expression with side effects, the makes a big difference. For example, these two expressions are not equivalent:
a[i++]++; //increments an element of an array and then increments i. a[i++] = a[i++] + 1; //adds one to value at array element i, //increments i and stores the new value at that index //and then increments i again. //Example, a[0]=10; a[1]=20; a[2]=30; i=0; a[i++] = a[i++] + 1; //a[0] stays at 10 //a[1] = a[0] + 1 -> 11 //i = 2 now
Increment and decrement in both pre and post forms are most commonly used as counters in a control loop.
Comparison
Comparison operators are divided into two categories. The first is equality operators (== and !=) and the second is relational operators (<, <=, >, >=). The equality operators can work on all primitive and reference data types. The relational operators can work on numbers and character data types (not boolean, objects or arrays). Both categories return a boolean value.
Equals (==)
This operator returns true if both operands are equal, otherwise it returns false. If the operands are primitive types then the test is that are both values are identical. If the operands are reference types then the test is that do both operands refer to the same object or array. It does not test for the value equality of two distinct objects or arrays. Since a String is really an object this is why this operator cannot be used to test that two different string have the same sequence of characters. It would test if the two strings referred to the same String object. Type conversion will occur as necessary so that the comparison can be done.
Not Equals (!=)
This operator is the opposite of the == operator. It returns true if the two operands are not equal, otherwise it returns false. It returns true if two primitive types have different values and if two reference types refer to different objects or arrays.
Less Than (<)
Evaluates to true if the first number is less than the second number, otherwise false. When comparing characters, the Unicode number that represents the character is used in the evaluation.
Less Than or Equal (<=)
Evaluates to true if the first number is less than or equal to the second number, otherwise false. When comparing characters, the Unicode number that represents the character is used in the evaluation.
Greater Than (>)
Evaluates to true if the first number is greater than the second number, otherwise false. When comparing characters, the Unicode number that represents the character is used in the evaluation.
Greater Than or Equal (>=)
Evaluates to true if the first number is greater than or equal to the second number, otherwise false. When comparing characters, the Unicode number that represents the character is used in the evaluation.
Boolean
These operators work on boolean operands and returns a boolean result. These operands are used to form complex logical comparison operations.
Conditional AND (&&)
This operator returns true if and only if both operands are true, otherwise it returns false. This is referred to as a conditional AND because it always evaluates the first operand, however if it is false then the second operand is not evaluated because it is already known that the AND operation will yield a false value. This is done for efficiency reasons but since the second operand may be skipped beware that if that operand was an expression with side effects, they may not be produced.
Conditional OR (||)
This operator returns true if either one or both operands are true, otherwise if both operands are false then it returns a false value. This is a conditional operator in a similar fashion as the AND operator but it skips the second operand if the first operand is true because in that case it is known that the comparison will yield a true value. The same cautions about the second operand’s side effects apply.
Boolean NOT (!)
This operator applies to one boolean operand and reverse the boolean value. If the operand is true then false is returned. If the operand is false then true is returned. This unary NOT (!) operator has a high precedence so parentheses are often needed to code a statement correctly. For example, if ( ! ( x > y && y < z ) )
Boolean AND (&)
This is used with two boolean operands and is the same as the && operator except that it always evaluates both operands.
Boolean OR (|)
This is used with two boolean operands and is the same as the || operator except that it always evaluates both operands.
Boolean XOR (^)
This is used with two boolean operands and is equivalent to the != operator. The boolean value returned is true if exactly (only) one of the two operands is true. If both operands are true or both are false then false is returned.
Bitwise and Shift
These operators work on integer values to do low-level bit manipulation. This is most common for testing and setting individual bit flags in a value. An understanding of binary numbers and the powers of two are necessary to accurately use these operators. The operands cannot be floating-point numbers, boolean values, objects or arrays. If either operand is a long then the result is a long otherwise it is an int. If the left operand of a shift operator is a long then the result is a long otherwise it is an int.
Bitwise Complement (~)
This operator reverses the bits of one operand. All the ones become zeros and all the zeros become ones.
Examples:
byte b = ~12; // ~00001100 -> 11110011 = -13 flags = flags & ~f; //clear flag f in a set of flags
Bitwise AND (&)
This operator combines two operands together by ANDing the individual bits together. The ANDing value is determined by the AND truth table. Refer to [Programming Basic: Construct, AND truth table]. A bit is set (1) only if both bits are set in the same bit position in the operands.
Examples:
10 & 7; // 00001010 & 00000111 -> 00000010 – 2 if ((flags & f) != 0) //test whether flag f is set
Bitwise OR (|)
This operator combines two operands together by ORing the individual bits together. The ORing value is determined by the OR truth table. Refer to [Programming Basic: Construct, OR truth table]. A bit is cleared (0) only if both bits are cleared in the same bit position in the operands.
Examples:
10 | 7; // 00001010 | 00000111 => 00001111 = 15 flags = flags | f; //set flag f
Bitwise XOR (^)
This operator combines two operands together by XORing the individual bits together. The XORing value is determined by the XOR truth table. Refer to [Programming Basic: Construct, XOR truth table]. A bit is set (1) only if both bits are different in the same bit position in the operands. If both bits are the same then the bit is cleared (0).
Examples:
10 & 7 // 00001010 ^ 00000111 => 00001101 = 13
Left Shift (<<)
This operator shifts the bits in the left side operand to the left by the number of bits specified by the right side operand. The bits that are shifted out of the left side operand are lost and zeros are shifted into the least significant bit of this same operand. Shifting an integer by n places is the same as multiplying that number by 2^n. The right side operand should be in the range of 0 to the number of bits – 1 that the left side operand occupies. Longs are 0 to 63 and ints are 0 to 31.
Examples:
10 << 1 // 00001010 << 1 = 00010100 = 20 (10 * 2) 7 << 3 // 00000111 << 3 = 00111000 = 56 (7 * 8) -1 << 2 // 11111111 << 2 = 11111100 = -4 (-1 * 4) 2’s complement format
Signed Right Shift (>>)
This operator shifts the bits in the left side operand to the right by the number of bits specified by the right side operand. The low-order bits are lost and the high-order bit shift into the left side operands is the same as what the high bit originally is, i.e. its sign. This technique is known as sign extension and is used to preserve the sign of the left side operand. If the left side operand is a positive integer then shifting an integer by n places to the right is the same as dividing that number by 2^n.
Examples:
10 >> 1 // 00001010 >> 1 = 00000101 = 5 (10 / 2) 27 >> 3 // 00011011 >> 3 = 00000011 = 3 (27 / 8) remember it is integer -50 >> 2 // 11001110 >> 2 = 11110011 = -13 != (-50 / 4)
Unsigned Right Shift (>>>)
This operator is the same as >> except a zero is always shifted into the high bit. This is known as zero extension; it is appropriate when the left side operand is being treated as an unsigned value (despite the fact that Java integer types are all signed).
Examples:
-50 >>> 2 // 11001110 >>> 2 = 00110011 = 51 0xFF >>> 4 // 11111111 >>> 4 = 00001111 = 15 (255/16)
This works on longs and ints. For short and byte is doesn’t work because these values are promoted to int with sign extension first and then the zero extension is done starting at bit 31 and not at bit 15 or 7 as expected. If you want to do an unsigned shift on a short or byte use the following technique that masks the bits you want and then does the shift.
byte b = -1; //b will = 0xFF b = (byte) ((b & 0xFF) >> 4); //0xFFFFFFFF & 0x000000FF = 0x000000FF //then right shifted by 4 to get 0x0000000F //then cast it to a byte to get 0x0F
Assignment
Assignment operators store in the left side operand a value evaluated from the right side operand. The left side operand can be a variable, an array element or a field in an object. The right side operand must evaluate to a data type that is compatible with the left side operand. The assignment operator is evaluated from right-to-left meaning that the statement a=b=c is equivalent to a=(b=c).
Assignment (=)
The = operator, "is assigned a value" operator should not be confused with the equal operator (==) and visa versa. This is a common source of bugs.
Assignment with operations (op= [op = +,-,*,/,%,&,|,^,<<,>>,>>>])
The op= operator allows for a short-hand way of coding an expression in the form var = var op value as var op= value. The allowable ops are +, -, *, /, %, &, |, ^, <<, >>, >>>. These have the same meanings as documented above for each of these operators. The one difference is that with the op= operators the var is only evaluated once and this could cause a different program effect if the long-hand form of the expression has side effects from the evaluation of var each time it is evaluated. In this case the short-hand form will only apply the side effect once whereas the long-hand form would apply the side effect twice.
For example the following two statements are not equivalent.
a[i++] += 2; // a[i] = a[i] + 2 and then i is incremented by 1 a[i++] = a[i++] + 2; //adds two to an array element at i, increments i by 1 and stores the //value at that index and then increments i by 1 again.
Conditional
Conditional (?:)
This is an obscure three-operand operator that allows you to embed a conditional within an expression. The operator is like an if/else statement. The first and second operands of the conditional operator are separated by a ?, while the second and third operands are separated by a :. The first operand must evaluate to a boolean value. The second and third can be any type but they must be the same type. The first operand is evaluated and if it is true then the second operand is evaluated and is used in the expression, otherwise the third operand is evaluated and used. Only one of the second or third operands is going to be evaluated so be careful of side effects.
Examples:
int max = (x > y) ? x : y; //if x > y then max = x else max = y String name = (name != null) ? name : "unknown"; //if name != null then it keeps its value otherwise the variable name //gets the value "unknown".
This is sometimes easier to code than the simple corresponding if/else statement. The general format of the conditional operator expression is:
someCondition ? trueExpression : falseExpression
The corresponding if/else format is:
if (someCondition) truePart else falsePart
Use parentheses as necessary to make the code clear. This operator has the lowest precedence other than assignment (=) so parentheses are generally not needed but their use can make the code easier to read and understand.
Special
These "operators" may also be considered part of the language syntax. They represent the performance of special operations and logic in a Java statement.
Instanceof
This operator is used to determine if an object or array value is of a specified type. The type can only be an array or object type, it does not work with primitive types. The operand of the left side must be an object or array value and the right side is a reference type. If the left side operand is an instance of the right side type then true is returned other false is returned. If the left side operand is null then false is always returned. If true is returned it means that the left side operand can be safely cast or assigned to a variable of the type of the right side operand.
Examples:
"string" instanceOf String //True: all strings (literals or variables) are instances of String "" instanceOf Object //True: strings (even null strings) are also instances of Object new int[] {1} instanceOf int[] //True: the array value is an int array new int[] {1} instanceOf byte[] //False: the array value is not a byte array new int[] {1} instanceOf Object //True: the arrays are instances of Object null instanceOf String //False: null is never instanceOf anything //Use instanceOf to make sure that it is safe to cast an object if (object1 instanceOf Point) { Point p = (Point) object1; }
Array Element Access ([])
An array is a numbered list of values. Each element of an array can be referred to by its number, or index. The [] operator allows you to refer to the individual elements of an array. The index value must be an integer type of int or smaller. It is best to use the int type only; long, float, double, char and boolean are not allowed. More on arrays in a later section.
Examples:
a[i] //access array element i in array named a
myArray[3*2] //access array element 6 in array named myArray
Object Member Access (.)
This operator is used to access members of an object. An object contains a collection of data and code. These are called fields and methods and are known as members of an object. If o is an expression that evaluates to an object reference and f is the name of a field in the object, o.f evaluates to the value contained in the field. If m is the name of a method, o.m refers to that method and allows it to be invoked using the () operator shown later.
Examples:
myObject.phonenum //get value of the field phonenum in the object named myObject
"string literal text".length() //get the length of the string literal object
Method Invocation (())
A method is code within an object. It can be invoked by calling the method name of an object reference with zero or more comma-separated arguments within parentheses. These arguments are values of expressions. A method can optionally return a value of the declared return type.
Examples:
myObject.method1(); //no arguments
myObject.method2(a, b+c, 2^8); //3 arguments that are evaluated and passed to method
Object Creation (new)
Instances of a class - an object - is created with the new operator. The new operator or keyword is followed by the object type and a list of arguments within parentheses. These arguments are passed to a special method called the object constructor. This constructor has the exact same name as the objects class and is code that is invoked when a new instance of an object is created. This is useful for doing some initialization of an object instance. Constructors will be discussed more fully later.
Examples:
new ArrayList(); //create a new ArrayList object, it has no constructor arguments
new Point(1, 2);
//create a new Point object, it has 2 constructor arguments, the x & y coordinates.
Type Conversion or Casting (())
As we learned in the Type Conversion and Casting section above, we "force" a value into another compatible type. Placing the type to convert to within parentheses and the expression after the closing parentheses does this.
Examples:
(byte) 28; //an integer literal cast to a byte type
(int) (x + 3.14f); //a floating-point sum value cast to an integer value
(String) h.get(k); //a generic object cast to a more specific string type
Statements
A statement is a command in the Java language to cause the computer to do a specific task. By default statements are executed one after the other. There are commands that alter the default order of execution in well-defined ways. These are flow-control types of statements such as conditional and looping statements. There are a few different types of statements and several commands that form the Java language. This section will discuss the statement types and commands most closely related to coding program logic. The more Java specific and advanced structuring type statements are discussed in a later section.
Types
The different types of statements are really a variation of the single statement theme. A single statement is a single command in the Java language. This theme is varied to produce the following statement types.
Expression
Expression statements are those expressions as described earlier that have a side effect of changing the state of a Java program. These statements are assignments, increments and decrements, method calls and object creation. Each of these are followed by a semicolon (;). Expression statements are to be distinguished from expressions in general. Expressions in general may be placed within the syntax of keyword commands like if, switch, while and do. In those cases the expression is not necessarily followed by a semicolon – the keyword syntax will determine if a semicolon is required or not.
General Syntax:
var = expr; expr++; method(); new Type();
Examples:
a = 2; x *= a; i++; --c: System.out.println("text"); Point p = new Point(x, 0);
Compound
A compound statement is any number and kind of statements grouped together within curly braces { }. They can be used anywhere a statement is required in the Java syntax.
General Syntax:
{ statements }
Examples:
//The body of this for loop has two expression statements that form a //compound statement. for (int i = 0; i < 10; i++) { a[i]++; b[i]--; }
Empty
An empty statement does nothing but can be used to signify that the body of some command syntax is empty. Strict coding conventions would advise against using this technique in favor of using an compound statement with no statement within the curly braces (ie { }).
General Syntax:
;
Examples:
//increment array elements value, loop body is empty for (int j = 0; j < 10; a[j++]++) /* empty */ ;
Labeled
Any keyword command, but not an expression, variable declaration, compound or empty statement can be labeled by pre-pending an identifier and a colon (:) to the statement. Labels are used by the break and continue statements as a type of transfer of control statement; it is more restrictive than a goto statement.
General Syntax:
label : statement
Examples:
rowLoop: for(int r = 0; r < rows.length; r++) { colLoop: for(int c = 0; c < columns.length; c++) { break rowLoop; } }
Variable Declaration
This type of statement is really a local variable declaration. Variables with a different scope than the local method or compound statement are really an object or class field. These will be discussed later. For local variable usage, they must be declared first and have a specific type. During the declaration a variable can be given an initial value. Multiple variables of the same type can be declared and optionally initialized in the same statement if the variable names and values are separated by commas. This is not a good programming practice though. A variable can have the pre-pended modifier final to indicate that once the variable is declared and initialize in the same statement, it cannot be changed during the particular execution of the program. Variables do not necessarily have to be declared at the beginning of a method, though it is good programming practice. They can be declared and initialized anywhere in the Java code and also within the initializer of a for loop. Where a variable is declared in relation to compound statements (curly braces) will determine the scope of the variable. A variable is in scope as long as the statement being executed in the program are within the same set or enclosed set of curly braces.
General Syntax:
[final] type name [= value] [, name [= value]] …;
Examples:
int counter; String s; int j = 0; String s = readLine(); int[] data = {x+1, x+2, x+3}; //array init, discussed later int i, j, k; float x = 1.0, y = 1.0; String question = "Really Quit?", response; final String greeting = getLcalLangaugeGreeting(); void method() // a generic method { int i = 0; //declare var i while (i < 10) //i is in scope { int j = 0; //declare j; i & j in scope } //j no longer in scope System.out.println(i); //i still in scope, print it out } //i no longer in scope
Keywords
This section describes the command and basic logic keyword statement’s syntax.
if/else
The if/else statement is a conditional that executes the statement(s) after the if when the expression evaluates to true. Otherwise the else or the next statement after the if block is executed. There is no "then" keyword and the parentheses must surround the expression following the if keyword. There are several variations to this statement as shown in the following examples.
General Syntax:
if ( expr ) statement [ else statement]
Examples:
if (username == null) Username = "John Doe"; if ((address = null || address.equals("")) { address = "[undefined]"; System.out.println("WARNING: no address specified."); } if (username != null) System.out.println("Hello " + username); else { username = askQuestion("What is you name?"); System.out.println("Hello " + username + ". Welcome!"); } if (i == j) if (j == k) System.out.println("i equals k"); else //bad indenting, goes with closer if statement System.out.println("i doesn’t equal j"); if (i == j) if (j == k) System.out.println("i equals k"); else System.out.println("i doesn’t equal j"); if (i == j) //braces make code clearer but the logic is bad { if (j == k) { System.out.println("i equals k"); } else { System.out.println("i doesn’t equal j"); } } if (i == j) //finally clear and correct code { if (j == k) { System.out.println("i equals k"); } } else { System.out.println("i doesn’t equal j"); } //else if clause if (n == 1) { //execute block #1 } else if (n == 2) { //execute block #2 } else if (n == 3) { //execute block #3 } else //if all else fails { //execute block #4 } //the above is the same as the following if (n == 1) { //execute block #1 } else { if (n == 2) { //execute block #2 } else { if (n == 3) { //execute block #3 } else //if all else fails { //execute block #4 } } }
switch
The switch statement is an efficient way to code a nested if/else statement if the branches depend on the value of a single variable. Based on the value of the expression evaluated in the switch statement’s parentheses, the corresponding case block will be executed. If there is no corresponding case block then the default block is executed. If this is not present then the entire switch block is skipped. At the end of each of the case blocks and the default block a break, return or throws (discussed later) statement should be present so that the transfer of control to the proper statement to execute next is done. If they are not present then the next statement to execute is the next statement in the switch block, even if it belongs to another case. In other words the default action is to "fall-thru" to the next statement; the case or default labels act only as a starting point within the switch block to execute code. Since the default action is to execute the next statement regardless of case and default labels curly braces are not necessary in the case and default blocks but do help in readability. There are some restrictions on the expression of the switch statement and the case labels. The expression must evaluate to a byte, char, short or int value only. The case labels must be a constant value or expression the compiler can evaluate. It cannot be a runtime expression such as an expression involving a variable or method call. The case label values have to be in range of the data type used in the switch expression. It is not legal for two or more labels to have the same value, nor for there to be more than one default label.
General Syntax:
switch ( expr ) { [ case expr : statements ] … [ default : statements ] }
Examples:
switch (n) { case 1: //start here if n == 1 //execute code block #1 break; case 2: //start here if n == 2 //execute code block #2 break; case 3: //start here if n == 3 //execute code block #3 break; default: //start here if n == anything else //execute code block #4 break; } //note more than one case clause for a statement, does a fall-thru //also the return transfers control out of switch as does break. //also shows that an exception could be thrown to transfer control out //of switch. boolean parseYesOrNoResponse(char response) { switch (response) { case ‘y’: case ‘Y’: return true; case ‘n’: case ‘N’: return false; default: throw new IllegalArgumentException("Y or N"); } }
while
The while statement tests the conditional value of the expression at the beginning of the loop. This means that the loop can execute zero or more times. If the while expression evaluates to true then an iteration of the loop body will be done and then the while expression will be re-evaluated. If and when the while expression evaluates to false then the statement after the while loop will be executed.
General Syntax:
while ( expr ) statement
Examples:
int count = 0; while (count < 10) { System.out.println(count); count++; }
do
The do statement tests the conditional value of the while expression at the end of the loop. This means that the loop can execute at least one and maybe more times. If the while expression evaluates to true then an iteration of the loop body will be done again. If and when the while expression evaluates to false then the statement after the do loop will be executed.
General Syntax:
do statement while ( expr );
Examples:
int count = 0; do { System.out.println(count); count++; } while (count < 10);
for
The for statement allows you to put all the important aspects of the loop control on a single line; the initial state, loop terminating test condition, and iteration update step. The syntax allows for multiple variable declarations and initializations and multiple iteration update step statements. The examples clarify what is possible with the for loop.
General Syntax:
for ( initialize ; test ; increment ) statement
Examples:
int count; for (count = 0; count < 10; count++) System.out.println(count); for (int count = 0; count < 10; count++) System.out.println(count); for (int count = 0, j = 10; count < 10; count++, j--) sum += count * j; for (Node n = listHead; n != null; n = n.nextNode()) process(n);
break
A break statement causes Java to skip immediately to the end of a containing statement. If used with label, that named block is exited when the break statement is encountered.
General Syntax:
break [ label ] ;
Examples:
for (int j = 0; j < data.length; j++) //loop thru data { if (data[j] == target) //if a data value is found then { index = j; //remember the location, j will lose scope. break; //stop looking, exit for loop. } } //the break causes this to be the location of the control transfer testfornull: if (data != null) { for(int r = 0; r < numrows; r++) { for(int c = 0; c < numcolumns; c++) { if (data[r][c] == null) break testfornull; } } } //the break testfornull transfers control to here
continue
The continue statement quits the current iteration of a loop and start the next one. This is different from the break statement which does a complete exit. Continue can only be used within a while, do, or for loop. When used without a label the innermost loop starts a new iteration. When used with a label then that loop is started with a new iteration, possibly skipping out of other more inner loops. The continue statement starts a new iteration of the while, do, and for loops in slightly different manners.
With a while loop, Java simply returns to the top of the loop, tests the loop condition and proceeds accordingly.
With a do loop, Java jumps to the bottom of the loop, tests the loop condition and proceeds accordingly.
With a for loop, Java jumps to the top of the loop where it first evaluates the increment expression and then evaluates the test expression to decide whether another iteration is to be done or not.
General Syntax:
continue [ label ] ;
Examples:
for (int j = 0; j < data.length; j++) //loop thru data { if (data[j] == -1) //if a data value is missing then continue; //skip to the next iteration process(data[j]); //process valid data values }
if (data != null) { testfornull: for(int r = 0; r < numrows; r++) { for(int c = 0; c < numcolumns; c++) { if (data[r][c] == null) continue testfornull; //start new iteration of the r (row) loop } } }
return
The return statement tells Java to stop executing the current method and return control to the caller. If the method is declared to return a value then a value of the defined type must be returned. If a method is declared to not return a value, it returns a void, then the return statement is optional and if present will not have a return expression following the return keyword. Also with void methods you can use the return statement to exit the method at a point other than the implied return at the closing brace } of the method.
General Syntax:
return [ expr ] ;
Examples:
return; return (3 * 2); return 1;
Where to put the Semicolon (;)
The semicolon (;) should be placed after expression statements (distinguished from expressions), variable declaration and where ever the keyword syntax form specifies. Placing semicolons where they do not belong and not placing them where they do belong will cause compilation errors but they can be very hard to decipher. Some common errors related to misplaced semicolon follows.
Semicolons present where they do not belong
Semicolon after the increment/update step in a for loop:
for ( i=0; i<10; i++;)
Compiler error message:
for ( i=0; i<10; i++;)
’)’ expected
for ( i=0; i<10; i++;)
illegal start of expression
for ( i=0; i<10; i++;)
; expected
Semicolon after a method or constructor declaration:
public static void main(String args[]);
Compiler error message:
missing method body, or declare abstract
public static void main(String args[]);
Semicolon after a class definition:
public class BinDriver;
Compiler error message:
’{‘ expected
public class BinDriver;
[and other errors]
Semicolon after while ( expr ) in a while loop:
while (! (binnum = bd.getBinInput()).equals("") );
{
//supposed body of while loop
}
Compiler error message:
no error message, but what is thought to be the body of the loop is not.
Semicolons absent where they do belong
No semicolon after while ( expr ) is a do loop:
do
{ //statements
} while (a < 10)
Compiler error message:
’;’ expected
} while (a < 10)
No semicolon after return ( expr ):
return (3*2)
Compiler error message:
‘;’ expected
return (3*2)
Missing semicolon after import statement
import java.io.*
Compiler error message:
’;’ expected
import java.io.*
Simple Method and I/O Calls
Method is the name given to subroutines (procedures and functions) in the Object-Oriented paradigm. In Java, they are very similar to C-style function calls. The method declaration consists most basically of a return type, name and an argument list. The return type is the primitive or composite data type of the "value" that the method returns. If the method doesn’t return any "value" then the return type used is called void. The name of the method is used to identify the method and should be of such that gives a simple name to what the method does. The argument list is a set of optional parameters enclosed within parentheses. Even if there are no parameters the opening a closing parentheses are required. The optional parameters are specified as a parameter type and parameter name pair. A comma separates each parameter. There is no comma after the last parameter.
Examples:
void method1() //a method with no parameters or return value
int method2(int x, int y) // a method with 2 int parameters x & y and returns an int
String method3(int a, float b) //a method with int & float parameters and returns a String
The body of a method is enclosed in brackets { }. If the method is declared to return a particular type then it must have a return ( expr ); statement in the body at the exit point of the method.
To call a method you reference the instance of the object that contains the method. This will be in a composite variable type that is of that object type. It will have been created with the new operator or passed to your program as a reference to the instance of an object – more on this later. After the object instance variable a period (.) is placed and then the method name and the required parameters in the same order as the method declaration all within parentheses are placed.
Examples:
myObject.method1();
i = myObject.method2(3, 4); //i is of type int
s = myObject.method3(5, 3.14); //s is a reference to a object of type String instance.
The method name and argument list portions of the method declaration are known as the method signature.
Some of the most common methods you will use are for doing input from the keyboard or output to a screen. These methods are part of the Core API of the Java platform. They are available on all installations of the Java Runtime Environment.
To do output to the screen the method call System.out.println(string); is used. The Java language has an object called System which contains a PrintStream object called out which has a method called println. The println method can take a string as a parameter and it prints the string to the default output device (usually the screen).
To get a line of input from the keyboard the following pieces of code are needed.
//declare a variable to be a BufferedReader object // <1> BufferedReader console; //declare a string variable to get a line of input from keyboard String keyboardInput; //create a BufferedReader object that is wrapped around a created //InputStreamReader that uses the standard input System.in object. // <2> console = new BufferedReader(new InputStreamReader(System.in)); //try to get a line of input from the keyboard. If error then ignore
try { //get a line of input & put into string var. keyboardInput = console.readLine(); } catch (IOException e) { //ignore any exceptions }
The BufferedReader object buffers input and thus provides a method to read a line of input at a time. A line of input is all the characters typed in until the "enter" key is pressed. To create a BufferedReader object a character input stream object must be passed to it. The InputStreamReader is such an object that converts a byte input stream into Unicode characters. The InputStreamReader is created with a byte input stream. The default input stream for Java is System.in which is a byte input stream. All of this is accomplished at the line of code below the // <2> comment. The variable console gets the reference to the BufferedReader object that is created. This variable was declared after the // <1> comment. With this type of input, an exception could occur so we must place the readLine method call within a try/catch block. The details of this will be covered later. The readLine method, which is in the BufferedReader object that has an instance in the console variable, is called to get a line of keyboard input. Once the "enter" key is pressed on the keyboard the line of characters inputted is stored in the string variable keyboardInput.
[Exercise: Java Logic Statement Syntax Exercises 1]
[Exercise: Java Logic Statement Syntax Exercises 2]
[Programming Assignment 1: Find Max]
[Programming Assignment 2: Book and Chapter Model]
[Programming Assignment 3: Numeric Conversions]
[Programming Assignment 4: Student Vitals]