Alexei B Khorev
February-March 2013
| A man (lecturer) | Java (technology) | Students |
|---|---|---|
|
|
|
|
|
|
Course web site: http://cs.anu.edu.au/Student/comp6700
Email: comp6700@cs.anu.edu.au
Consultations and other help: http://cs.anu.edu.au/Student/comp6700/index.html#consult-time
Schedule of all important events: http://cs.anu.edu.au/student/comp6700/schedule.html
Reading references and other valuable resources: http://cs.anu.edu.au/student/comp6700/references.html
Doing a university course can be fun, but it should not be a walkover!
What we will do in this part:
Introduction to the course
Java’s story
Java as a language; Java SDK commands
Program structure
First Java Program
Types and values (literals)
Variables and operators; String s and arrays
Expressions and statements, methods and return
Control structures
Top-down design, algorithms
More intensive than a normal course: lectures will run only for first 9 weeks
The course is introductory in nature for the MITS (and other degrees). It aims to provide a firm foundation for programmers in the Java language as well as a perspective on the computing discipline and computer science.
We will be taking a spiral approach to studying Java. Although the course will march along a generally linear trajectory (from easy to complicated), we will need to use some advanced concepts before we are ready to understand them fully. Later on we will come back to these concepts and describe them in more detail. Experience has shown that this is a good way of working with mature students who need to assimilate material in a short period of time.
All of you, and especially beginners, should take the lab and homework exercises seriously as they will help you to feel comfortable when programming in Java (and in other languages).
… and Java is going to change, but how?
| Rest in peace, dear friend |
|
|
Different Java for different hardware platforms:
Java EE (Enterprise Edition) — for secure and scalable distributed server-based applications in large enterprises.
Java SE (Standard Edition) — for desktop applications and applets for browsers (applet problem with security). The one we use.
Java ME (Mobile Edition) — for applications run on devices with “limited” computing power (mobile/smart phones, PDA, GPS-like navigators, digital players/recorders, eBook readers) Based on a scaled-down version of JVM and subsets of JavaSE (dead?).
Java Card — for applications running on SmartCards (considerably scaled down and optimised JVM and APIs, specialised packages).
Java TV — for interactive applications for new generation TVs and TV services (with play-back and programmable recording functions, user-controlled camera view, video-on-demand…). Includes JavaSE and Java Media Framework API (dead?).
JavaFX — for Rich Internet Applications, RIA: with a scripting language (no more!), API and SDK tools (now is a part of Java API, supplement and alternative to Swing).
![]() |
A program is just a text with instructions. How to make computer to execute them? And how best to write programs? Use tools — special applications to create software.
Baby tools : a text editor (better with the syntax support & colouring and automatic layout) + SDK commands run on the terminal (console). For the SDK commands see later. The available editors are:
Mini IDE — Integrated Development Environment, a lightweight version of a real IDE with many functions and capabilities not present. Good for learning, not for professional work. Two useful mini-IDEs: DrJava and BlueJ.
Proper IDE: These are formidable tools. All are quite similar in what they can do. The difference is in how they do it, and how their capabilities can be extended. The difference is important depending on the kind of work which you are doing, but this has to be a professional work. Require a steep learning curve. Benefits are enormous. Even a novice can get great benefits. The IDEs which are currently popular are:
Eclipse and Netbeans are free (both available in labs), IntelliJ has a slim-down community edition free version.
Java Software Development Kit, SDK
The Java programming environment includes a set of programs (tools), which is needed by everyone who wants to create software in Java. The list of these tools is ever growing (Java 1.5 had 36; Java 1.6 has half a dozen more). The most important SDK tools are the following:
javac — Java compiler, usage : javac MyProgram.java; output : MyProgram.class (the byte code)java — Java interpreter, usage : java MyProgram; output : running the programjavadoc — Java documentation generator, usage: javadoc MyProgram.java ; output : a bunch of html filesjdb — Java debuggerjar — Java archive toolappletviewer — Java applet viewerjavap — Java class file disassembler(Wiki says): “A programming language is a language used to write computer programs, which involve a computer performing some kind of computation or algorithm and possibly control external devices (printers, censors, robots, etc)”. Several hundred programming languages exist. Why so many? How do they differ apart from syntax features (which may or may not be accidental)? There are several programming paradigms and styles:
Modern Java is procedural and object-oriented language. It does not have features of a functional programming language (but wait till Java 8).
A language definition is not enough. To be used in practice a language must be implemented (realised, adopted for creating software). Depending on implementation, a programming language can be
compiled — a special program, called a compiler, translate the whole program into the machine code. The machine code is a set of instructions executed directly by the processor; such execution is fastest.
interpreted — a special program, called an interpreter, reads the program text, statement by statement, and executes them successively. This eliminates the need for a separate compilation phase. The program can be run on any platform which has the required interpreter, but the code executes (much) slower.
Java is a hybrid : javac produces a bytecode, which is like an assembly code, but architecture neutral. Java code compiled on a Linux machine will run on a Windows machine without recompilation (was important for the applet technology — the server could send a required applet which the client browser could execute immediately). The Java interpreter (java command) executes Java bytecode. The same bytecode runs on any platform if it has a JVM. Some compilers can produce a binary code, which is faster but platform specific, like any binary code; modern JVMs, like Sun’s Java HotSpot are very efficient, and such translation into machine-specific code — Just in Time Compilation — are rarely needed. Such combination of compilation and interpretation is what distinguishes Java from other common languages.
Java provides support for creating robust distributed systems. The design principles which Java strives to implement are:
Various definitions of computation exist (including very abstract and powerful, namely using λ-calculus by A. Church). We shall adopt a simple one: A computation is a dynamical process, and a program is a definition of such process.
Create stars, planets, Earth, plants, animals, humans… name them… define how they behave (move, interact, change)… set them loose, let them try and repeat whatever they need to do… until you decide that the time is up (or a “catastrophe” ensues).
What describes a process?
| A computation is a dynamical process, and a program is a definition of such process. Compare it to a mechanical phenomenon of stone throwing, or a chemical reaction, when the initial state of the agents involved is given, and the evolution is determined by the corresponding laws (Newton’s laws, etc). But! In a computer program you can define your own laws (following some rules, though)! | Let’s calculate! (When Creator strikes the ball, the main method is called, and execution begins.) |
“All programming languages have building blocks for description of data and processes or transformations applied to them” (Wiki).
That’s how my favourite Java text “The Java Programming Language” characterises a program text:
A program starts as a sequence of characters contained in a file — the source code.
Interpreting those characters according to the rules of a given language, is a job of the compiler, or interpreter.
Some characters will represent the names of variables, others will be special keywords used by the language, still others will be operators or “punctuation” characters used to separate the other elements.
These textual constructs form the lexical elements of the program. The lexical elements must be identified as keywords, comments, literals, variables, operators, or whatever else is appropriate…
The path from an empty file to a correctly functioning program can tortious. Things can go wrong, and they can even go wrong when it appears the thing are right. Remember, a program code is aimed for human eyes as much as for machine.
At the beginning, your programs inevitably will be:
Syntactically incorrect — grammar violation: wrong or undefined symbols, sub-blocks grouped in wrong way. “Yoda speak”, incomplete sentences. Syntax errors are revealed at compile time.
Semantically incorrect — impossible to assign a meaning to a syntactically correct sentence. “Colourless green ideas sleep furiously.”
Logically wrong — produce results which cannot be true). “John is married bachelor.” This is a “famous” GIGO principle — Garbage-in–Garbage-out.
Poorly written — “Of two cars on the road: one was green, and another had turned left.” OK for computer to executer, but pain for programmers to read and use. This is called a code smell (if code smells, it needs refactoring).
A (single-threaded) computer program is a sequence of instructions (statements) which are executed sequentially, one after another when the program is run. Many modern programs are multi-threaded. Their structure is (significantly) more complex. We shall have a glimpse of multi-threaded programming in Block-7 (time permitting).
|
|
|
To discuss the basic structure of a Java program, we need compromise.
Although one cannot use explicitly object-oriented (OO) features in a however simple Java program, we will pay minimum attention to them, instead focusing it on the program run-time structure.
Enough to say, that the world of a running Java program is populated by objects, which are created according to rules defined in a corresponding class, which can be thought as a template.
First application will have only one class (one cannot write Java code without classes altogether).
In such case, a Java program is similar to a program written in a procedural programming language (C, Fortran, …). The focus of our interest will be the fundamental building blocks of Java, which you will have to use when practicing writing simple programs in the labs.
Yet, Java is cluttered with object oriented terms, it is not possible to avoid mentioning them along the way (particularly when you need to call library methods in other classes). For the moment, consider those elements of syntax which look obscure as a linguistic pattern that you just copy and that you will try to understand later on.
Loosely speaking, the world of a Java program is populated with:
Data (values and references) which are actual information carriers; the program uses this information and generates a new one
Types and Variables (each variable has certain type)
Declarations, Assignments an Initialisations statements
Operators (the act on data and variables)
Control Flow Keys
Class/Object Fields and Class/Object Methods
Arrays and Strings (they are particular types, used very often)
Comments (irrelevant for program behaviour, but crucial for human reader)
We shall start with the bare essentials: studying the syntax and semantics of the core language. We will type code into an editor, compile it and run the byte code interpreter. Our simple programs will read and write text to a terminal (shell) window (or, using simple pop-up windows).
Type the following text (never mind the white space) into the editor window
1 public class FirstSample {
2 public static void main( String[] args ) {
3 System.out.println( "Hello World" );
4 }
5 }
(the line numbering is fictitious, it’s not part of the code!)
Save it as FirstSample.java; the name of the file must match the name which follows the word “class” on the first line — Java is case-sensitive!)
Type javac FirstSample.java in your terminal (don’t forget to hit “return” button, ↩)
If you entered the code correctly the compiler will produce the FirstSample.class file
Run (execute) the FirstSample.class file by typing java FirstSample ↩ . You should see the following output:
Hello World
After that the program will exit (terminate).
Line 1: public is an access modifier. It determines what other parts of the program can have access to the class (or class members).
Line 1: class defines a code grouping. Everything in Java lives inside classes.
Line 1: FirstSample is the name of our class.
The file for the source code must be of the same name as the public class with the extension .java added (FirstSample.java)
Line 1 (the last symbol): Left hand curly bracket (or brace) defines the beginning of a code block. Matching { and } is important, the indentation is not (but the code layout is important for readability).
Line 2 : When you compile a class and run it, execution always starts at the beginning of the method main.
public, static and voidmain — the data passed to this method.
main must have a single argument of type String[ ] (array of strings).public static void main( String[] args )Line 3 : This sentence is the body of the method and is a Java statement.
Line 3: The System.out object is contacted and asked to use its println(...) method. The syntax for method calls is o.mname(parameters), where o is the name of the object (or class).
Line 3: The single argument of System.out.println is a string constant (enclosed in double quotes).
Line 3: Finally, the word static in the main method declaration is a scope modifier, meaning that the method belongs to the whole class, and does not require an instance of the class to be called (unlike not-static methods which need an instantiated object to be called). Other modifiers exist in Java, and we shall meet them in due time.
/** The very first Java program with added comments
* @author abx
* @version 1.0 */
public class FirstSample {
/* we need a main method to run it as a standalone app */
public static void main( String[] args ) {
// to say "G'day mate" is more authentic
System.out.println( "Hello World" );
}
}
What we can add to the code without changing its behaviour :
A very important part of every code in every language are comments. They are totally irrelevant to the compiler, but they mean a great deal to a human who writes and reads the code. Comments do not affect the executable code at all but can increase the readability of the source. You can add as many comments as you like but too many comments can make the code unreadable as well. Look at good code examples to get a feel for appropriate comments. There are three kind of comments in a Java code :
// to the end of line./* ... *//** ... */ ; they may contain special javadoc tags, which begin with @ symbol. We shall study the use of Doc comments later; they are used to generate the class documentation automatically using javadoc commandBlock and Javadoc comments do not nest!
Try adding some comments of these kinds to the above code — neither compilation, nor execution should change.
Computer programs operate on values, which are sequences of bits. These values can be represented as symbols (variables or constants), or as literals. Symbols must be declared with a type. The type determines the number of bits representing a given symbol in memory and how these bits are interpreted (its value). There are primitive types and references. Each type has literals (set of constant values of this type). The only literal of reference type is null (it represents an invalid or uncreated object). Primitive type literals are various.
Java is a strongly typed language — every symbol must have a declared type (such as the String array args in main).
boolean (1-bit, true/false) |
byte (8-bit signed) | char (16-bit unicode) |
| short (16-bit signed) | int (32-bit signed) | float (32-bit FP numbers) |
| long (64-bit signed) | double (64-bit FP numbers) |
Variables of different type store either different types of data or a different range of possible values. byte, short, integer and long are signed (their range includes negative values from -2bitsize -1 to +2bitsize -1 -1 (one bit is used for the sign, +/-). For instance, the int values change from -2,147,483,648 to +2,147,483,647. |
public class PrintNumber { // usage of the primitive type variables
public static void main( String[] args ) {
int i = 90000; short s = 20000; // one can have 2 statements on one line
byte b = -100; double d = 1.5; // but normally it's not good for readability
long l = 4000000000L; // note the `L` here
float f = 1.5f; // note the f here
char c = 'a'; boolean bn = true;
int i2; i2 = -90000; // delayed initialisation
System.out.println( "The value of s is " + s ); // display the value
}
}
1, use only the upper-case “L”.floats and doubles (the real numbers) have mantissa and exponent parts, according to “scientific” representations: x = α * 10β. The same value can be given different representation by consistently changing the mantissa (α) and the exponent (β) — “the point can float”. Storage for the values of mantissa, exponents and the sign is allocated separately.
For FP literals either decimal or hexadecimal format is used:
18.=1.8e1=.18E2=180.0e-1 — decimal point is optional; e or E is optional, and if used is followed by optional signed integer0x12p0=0x1.2p4=0x.12P+8=0x120p-4 — the binary exponent, p or P, is mandatory; it’s followed by an optionally signed integer; the exponent represents scaling by two raised to a power (the shown number is decimal 18.0)float has 7 decimal digits of precedeision, the trailing suffix “f” is mandatory. double has 16 decimals of precision, he trailing suffix “d” is optional. Normally, use double unless you have a good reason.
FP literals include two zeros, 1d * 0 = 0.0 (positive) and -1d * 0 = -0.0 (negative); 0.0==-0.0 evaluates to true, but some expressions evaluate to different values: 1d/0.0=∞, yet 1d/(-0.0)=-∞
64-bit IEEE 754 standard stipulates the number of bits used for mantissa and exponents: 52 and 11 (plus one for sign). There are special floating point values:
isNaN() to test a FP valueJava 7 has introduced enhanced syntax for numeric literals to make it more readable for humans.
These new features include:
Numeric constants (that is, one of the integer primitive types) may now be expressed as binary literals
int x = 0b1100110Underscores may be used in integer constants to improve readability:
long anotherLong = 2_147_483_648L;
int bitPattern = 0b0001_1100__0011_0111__0010_1011__1010_0011;A variable of one type is often assigned a value of a different type: var1=var2 (var1 is of type1, var2 is of type2). This is a type conversion: type2→type1. Java is a strongly typed language — it checks for type compatibility at compile time in almost all cases — all “fishy” assignments are forbidden. To allow assignment of types with compatibility only known at run time, or to explicitly force conversion for primitive types that would otherwise loose range (double→float), Java provides cast operator: var1 = (type1)var2;
A “narrower” type, represented by a fewer number of bits, is implicitly widened, cast is not required
A “wider” type must be explicitly narrowed to control the change of value: (“high” bits are chopped off).
![]() |
![]() |
![]() |
The explicit cast is also needed if two types use equal number of bits, but their memory layout is different, eg conversion double→long requires a translation from a 64-bit floating-point value to the 64-bit integer representation (depending on the run-time value, information may be lost, hence the cast).
Hierarchy of widening and narrowing conversions
|
Conversion and round-off effects
If we declare double d = 1.3237 and int i = 10, then d * i, if assigned to a double, its value is 13.237, but if assigned to an int, the value will be 13, and if d is first converted to int, (int) d, the product (int)d * i is 10. To converted float to int:
(int)x, this truncates the fractional part of x, ORMath.round(x) from the Math classCasts (including the effect of losing the fractional parts of floats) are illustrated by the following code:
public class ConversionTest {
public static void main(String[] args) {
int i1 = 10;
float f1 = i1;
double d1 = i1;
double d2 = 1.5;
float f2 = (float) d2;
int i2 = (int) d2;
// call print method to display the values
}
}
Add printing statements for all the variables involved, compile and run this code to see the values generated by conversion (alternatively, use DrJava’s interaction pane).
What do you get when you print the value of i2 ? It turns out that roundoff errors are a fact of life with floating point numbers:
double f = 4.35; int n = (int) ( 100 * f );
System.out.println( n ); // prints 434: Wrong!
What’s wrong? In the binary number system, there is no exact representation for the number 4.35, it is actually stored as 4.349999999... so 100 * f = 434.99999... (etc) and when cast to an integer the fractional component is dropped (not rounded!) thus n = 434. The proper way to do rounding is to use a function call from the Math class:
double f = 4.35; int n = (int)Math.round( 100 * f );
System.out.println( n ); // prints 435! (Good)
Characters are represented by primitive char s. Strings are not characters — strings are instances of the class String. But it may help to think of them as sequences of characters (which is what they are in term of values). Character literals appear between single quotes: 'a', string literals appear between double quotes: "a" ('a' and "a" are literal values of different type!). The character literals can be expressed either by a single symbol, or by an escape sequence inside the pair of single quotes, including an octal character constant (\ddd, where each d is one of 0-7, but the code cannot exceed \377).
char literals are what you can find in the Unicode character table (used to be — and still is for C — the plain old ASCII with 128 entries; not for Java!): characters (Latin and other scripts), digits and many special symbols. Any valid Unicode character can be represented using ASCII symbols only with an escape sequence \uxxxx, where x is a hexadecimal digit (0,…,F). The number of u symbols can be more than one. (BTW, the Unicode character mapping escape sequence can be used anywhere inside Java code as a part of an identifier, not only in char and String literals). Here is a portability problem for Java: despite you can manipulate all Unicode characters in your code, you may not be able display them because many terminal and other output devices are yet to adopt the Unicode standard. Some special ASCII characters: \n = \u000A (new line), \t = \u0009 (tab), \\ = \u005C (backslash) etc.
Two chars can be added: their code values are added, and this int value must be cast back to char: char c = (char)(a + b). Study these programs:
Character strings are implemented as instances of the class String. All string literals in Java programs, such as “abc”, are implemented as instances of this class. Strings are immutable (constant) — their values cannot be changed after creation. (StringBuffer objects support mutable strings). Because String objects are immutable they can be shared.
String str1 = "abc";
char data[] = {'a', 'b', 'c'};
String str2 = new String(data);
System.out.println(str1.equals(str2));
// should print "true", the objects str1 and str2 are identical
// But, str1 and str1 references were independently assigned, therefore
System.out.println(str1 == str2); // should print "false"
More examples of how strings can be used:
System.out.println("abc"); String cde = "cde";
System.out.println("abc" + cde); String c = "abc".substring(2,3);
String d = cde.substring(1, 2);
The String class includes methods for examining individual characters of the sequence, for comparing strings, for searching strings, for extracting substrings, and for creating a copy of a string with all characters translated to uppercase or to lowercase, etc (string operations are important, and String is a large class).
Three strings defined with the same content: What is the difference?
String s1 = "abc";
String s2 = new String("abc");
String s3 = "abc";
Strings (as other same type objects) are compared by state (content) and by reference. Because of same content: s1.equals(s2) is true. Yet s1 and s2 are different objects: s1 == s2 is false. Why then s1 == s3 is true? When a string is initialised to a literal, it’s stored in the literals’ pool (in the same part of memory where primitives are stored). String’ pool literals are unique. Strings from the literals’ pool can be content compared by their references; it is much faster then calling s1.equals(s3). The strings which are created with new operator are references to the proper objects (they are placed in the heap, with all other objects).
One way to make a faster program with a lots of string operations is to avoid string creations by using one (or few) StringBuffer object, manipulate its content as required, and create a String object at the end. To get additional performance boost (in case of many “text comparing operations”), internalise every string:
str = str.intern(); // str variables is recycled
returns a string from the literals’ pool which has the same content as str.
String’s pool of literals crosses class and package boundaries. But different JVMs (my guess) maintain their own pools (DrJava flaw?!)
An array is an ordered collection of elements indexed by an integer starting from 0 (first element) up to length - 1. All elements have the same type. The declaration and instantiations can be done differently:
//standard instantiation with elements set to default values
int[] ia = new int[3];
// creation of an array with initialisation of its element values (no new!)
int[] ia = { 2, 3, 5, 7, 11, 13 };
// next C-like syntax is also legal, but don't do it!
int ia[] = { 2, 3, 5, 7, 11, 13 };
// as above, but note absence of the array size! (array size is inferred)
int[] ia = new int[] { 17, 19, 23, 29, 31, }; // last comma is optional
Dimension of the array variables in the type declaration is omitted. The symbol new is the creation operator (used when an object is created). When it’s called, an array is instantiated (ie, an array object is created) and its dimension is set. As every reference, ia can be later assigned to a different array of a different size (example ArrayTest.java).
System.arraycopy() method.arr[i] returns the i-th element in the array arr, and str.charAt(i) returns the character at the i-th position.length field, strings have length() method.Arrays of arrays, which are standard one-dimensional arrays, each element of which is an array itself, are supported in Java. This is an examples of how a matrix (two-dimensional array) can be declared and used:
float[][] mx = new float[3][3];
// initialises every element of mx
setupMatrix(mx);
// loop through rows
for (int row = 0; row < mx.length; row++) {
// loop through columns
for (int col = 0; col < mx[row].length; col++)
// prints every element in the row
System.out.print(mx[row][col] + '\t');
// print new line after a row is printed
System.out.println();
}
On initialising, only the overall size of the arrays must be specified, therefore, only the number of leftmost indices must be provided. The inner array sizes can be initialised later, and can, in fact, be of different size:
float[][] mx = new float[4][];
for (int row = 0; row < mx.length; row++) {
mx[row] = new float[row + 1]; // (row + 1) is the size of inner 1D array
Declarations, initialisation, assignment
Variables of char, int, double, String and an array type, discussed above, are just names which determine storage locations, “boxes” in which one can “place” a value of the right type (can fill in), then swapped with another similar typed value and so on (assign and reassign a value of the correct type). Variables can appear as: class fields, local variables inside a block of code, and method parameters.
Variables are introduced as program’s entities through declaration:
[@annotation-type] [modifier]* type identifier
where (we skip the @annotation here) modifiers can be optional and possible few (like private, static, or final), type is absolutely essential (it determines the size of allocated memory location and behaviour supported by the declared entity), and identifier is the actual name of this location. The declaration can be accompanied by initialisation (assigning a value for the first time), or the initialisation can be performed later in the code. The variables of the same type can be declared in a comma separated list, but the variables of different type must be declared in separate statements.
float x, y; // declared but not initialised
int z, u = 2; // two declared, one initialised
A variables must be initialised before it can be used (except for formal method parameters). Once a variable is initialised, its value can be used in expressions or assignments.
int x; //not initialised, cannot be used
int y = 10;
x = y * y; // now x has a value
int z = x; // now x can be used and given a new value
Field variables are declared inside the class definition. Non-static fields exist during the life of an object to which they belong. If not initialised explicitly, they are given the default values when the object is created.
Local or block variables are declared anywhere inside the block (method’s body is a typical example of a block); unlike field variables, local variables must be initialised explicitly before they can be used; they cease to exist when the flow of control (the computation path which the program execution goes through) reaches the end of the block or method body in which they were declared. Local variables can have only one modifier, final.
All variables can change their values during their lifetime unless they were declared final (they are essentially constants). The final variables of the reference type (object’s name) are called immutable. String instances are such immutable objects — any would be manipulation with the String object content actually involves creation of a new String object with modified value.
An attempt to change the value of a final variable will result in a compile error:
final int x; // if not initialised during the declaration,
// the final variable is a blank constant
x = 2; // ok, now the constant x is given a value
x = 4; // can't do!
final variables are normally initialised right where they are declared. It is legal to postpone assignment (which must precede the use), but not recommended (all modern IDEs issue warning in such case). Blank finals are mostly used if the field value is determined by the constructor arguments:
class Student {
//below is the Student class constructor
final String name;
Student(String name) { this.name = name; }
}
Parameter variables are the parameters declared in methods, constructors or catch part of the try-catch-finally blocks. A parameter declaration includes an optional modifier (an @annotation or final), type and a single identifier. During the method declaration or its invocation in the bodies of other methods, the method variable is a formal parameter (it has no value), but during the actual invocation of the method, it is an actual parameter (it must have value). Parameter variables cease to exist when the method body in which they appear completes (by reaching its logical end, or terminating with error).
Variables are subject to various operations which use their values and produce new ones. The program operations which involve primitive type variables and Strings feature the variables themselves and operator, much like mathematical operators which together with variables and parameters form a part of the mathematical expression. These are five binary operators which can be applied to a pair of any primitive numeric type variables (called operands)
+ addition- subtraction* multiplication/ division% remainder (modulo division)and two unary operators
- negation+ unary (included for completeness)Integer arithmetic is modular two’s complement arithmetic, ie, if the value exceeds the range of its type (int or long), it is reduced modulo the range (it wraps), so that the integer arithmetic never results in an overflow or an underflow.
Integer division truncates toward zero (5/2 = 2, and -5/2 = -2). Division and remainder obey this rule:
(x/y)*y + (x%y) == x // hence 5%2 = 1 and -5%2 = -1
Division (or remainder) by zero is illegal for integer arithmetic and throws ArithmeticException.
Character arithmetic is an integer arithmetic after the char is implicitly converted to int.
Floating point arithmetic is more lengthy subject (you may explore this subject yourself, or take COMP2300/6300 course).
String concatenation: The addition operator also applies to two String objects, returning a string with the joint content:
String s1 = "French Connection"; String s2 = " UK";
System.out.print("s1 + s2 = " + s1 + s2);
Unlike for primitive types, the string “addition” is not commutative (ie, the result depends on the order of the two operands).
Comparing test operators: < , >, <=, >=, ==, !=
Logic operators:
| & (bitwise AND) | | (bitwise inclusive OR) | ^ (bitwise exclusive, XOR) | ||
| && (conditional AND) | || (conditional OR) | ! (logical negation) |
The difference between the bitwise and conditional operators: the latter “short-circuit”.
Increment and decrement operators (see below)
Conditional ternary operator (see below)
Type check: (reference) instanceof (ClassName/InterfaceName) (evaluates to boolean)
Bit manipulation operators: first three of the logical operators plus three shift operators
Assignment operator — x = 5;
Type conversion operator (convert-to-type)var: char c = (char)x;
Objects (wait till Block 2 for more detail) need to be created and operated upon.
Object creation operator:
String str = new String("I love Perignon!");
The operator new allocates memory for a new object and return a reference to it
Method invocation: Objects represent chunks of data and operations which involve these data. These operations are carried out by invoking or calling a corresponding method by using the object reference o — the invocation operator is a dot which follows the object reference:
o.methodname(x); // x is a parameter passed to the method
System.out.println("I am confused"); // the object here is output stream "out"
Field access: Data associated with the object o (if allowed) can be read by using the same dot operator followed by the name which represent this data inside the object, so called field:
int age = o.age;
System.out.println("The object name is " + o.name);
Unary bitwise operator (applies to ints):
~ toggles each bit in its operand: ~0x00003333 → 0xffffcccBinary bitwise operators (also apply char) : perform their operation on each pair of bits in operands:
& bitwise AND: 0xf00f & 0x0ff0 → 0x0000| bitwise inclusive OR: 0xf00f | 0x0ff0 → 0xffff^ bitwise exclusive or (XOR): 0xaaaa ^ 0xffff → 0x5555Bit shift operators (the bit pattern is given by the left-hand operand, and the number of positions to shift by the right-hand operand):
<< signed left shift operator — shifts bits left filling with 0 bits on the RHS: 14<<2→56>> signed right (arithmetic) shift operator — shifts bits right filling vacated bits with the highest (sign) bit value: 14>>2→3 and -14>>2→-4 (the leftmost position after >> depends on sign extension)>>> unsigned right (logical) shift operator — shifts bits right, filling vacated bits with zero: 14>>>2→3 and -14>>2→1073741820| Precedence | Type | Operators |
| 1. | postfix operators | [] . (params) expr++ expr-- |
| 2. | unary operators | ++expr --expr +expr -expr ~ ! |
| 3. | creation or cast | new (type)expr |
| 4. | multiplicative | * / % |
| 5. | additive | + - |
| 6. | shift | << >> >>> |
| 7. | relational | < > <= >= instanceof |
| 8. | equality | == != |
| 9. | bitwise AND | & |
| 10. | bitwise exclusive OR | ^ |
| 11. | bitwise inclusive XOR | | |
| 12. | conditional AND | && |
| 13. | conditional OR | || |
| 14. | conditional ternary | ?: |
| 15. | assignment | = += -= *= /= %= >>= <<= >>>= &= ^= |= |
It is vital to control the data used and produced by a program, so called input-output, I/O. The simplest way to get your data into the program is to use the command-line arguments. They are picked up by those parameters args (the array of Strings) which are passed to the main:
public class ArgsTest {
public static void main( String[] args ) {
System.out.println("first arg = " + args[0]);
System.out.println("second arg = " + args[1]);
System.out.println("third arg = " + args[2].toUpperCase());
}
}
output produced by 'java ArgsTest I love perignon'
first arg = I
second arg = love
third arg = PERIGNON
args are Strings, but if you need to provide an integer (or float), the input string can be converted:
String s = "123"; int i = Integer.parseInt(s);
String s = "-123.45"; double d = Double.parseDouble(s);
The command-line arguments is a very rigid way to control input. It can be done only once, at the start of program execution.
Reading text from the terminal’s standard input (stdin, or console) provides a better way: It allows you to control the moment of execution when the program will receive input data. One convenient way to provide such input channel into the program, is to create a Scanner object to read form stdin stream (stdin is the object System.in, and stdout is System.out — standard input and standard output). Details will be discussed in Block 3.
import java.util.Scanner;
public class ConsoleTest {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// the user is expected to type something and hit "return"
System.out.print("Enter some text: ");
String input = sc.next();
System.out.println("You entered " + input +
", it has " + input.length() + " characters");
}
}
demo! (the code is available in ConsoleTest.java)
A simple input from the user can be alternatively provided using a Graphical User Interface:
import javax.swing.JOptionPane; // to make JOptionPane available
public class GUITest {
public static void main( String[] args ) {
String input = JOptionPane.showInputDialog("The answer to Exam's Q5");
System.out.println( "Your answer to Q5 is " + input );
}
}
Running the program:
abx% java GUITest
Your answer to Q5 is 42
An expression consists of operators and their operands, which are evaluated to produce a result, which may be a single value, or a variable (“lvalue”), or nothing when the expression is an invocation of a method that is declared void (in which case, the result is implicit change in the computational environment, like a state of an object or something else). An expression may be simple (atomic), like a single variable, or complex — a sequence of subexpressions like method invocations, variable access, object creations, and combination of results of various subexpression involving arithmetic and other operators, more method invocations and variable access etc, etc, etc.
// simple (atomic) expressions
int x = 2;
int y = 2 * x;
double θ = Math.PI / 6;
// expressions with sub-expression
double d = Math.sqrt(Math.pow(Math.cos(θ),2) + Math.pow(Math.sin(θ),2));
boolean b = !(d == 1);
// really concocted one
// notice that the wite space between token does not matter
String str = new String((new Integer(x)).toString() + " + " + y + " = " +
new String((new Scanner(System.in)).
next().equals("smart") ? "4" : "5"));
Note: the last statement above is completely gratuitous — do not write you code with unnecessary expression, break them into shorter ones and combine at the last step.
Which expressions are legal and what do they evaluate to?
3 << 2L - 1;
(3L << 2) -1;
10 < 12 == 6 > 17;
10 << 12 == 6 >> 17;
13.5e-1 % Float.POSITIVE_INFINITY;
Float.POSITIVE_INFINITY + Double.NEGATIVE_INFINITY;
Double.POSITIVE_INFINITY - Float.NEGATIVE_INFINITY;
0.0 / -0.0 == -0.0 / 0.0; // very important why the answer is what it is?
Integer.MAX_VALUE + Integer.MIN_VALUE;
Long.MAX_VALUE + 5;
(short) 5 * (byte) 10;
(i < 15) ? 1.72e3f : 0; // assuming int i is declared, but not initialised
i++ + i++ + --i; // i = 3 before statement executed, what is i value after?
(hint the precedence table on Operator Precedence_ slide can help; if perplexed, type them into a program, run and try to understand why the results are what they are)
Statements are what a program is built from — a sequence of expressions and declarations interleaved with control flow statements. Compared to a natural language, statements in a computer program correspond to sentences in an English (Chinese, Turkish, Martian,…) text. In Java, statements are terminated with semicolons (the semicolon itself is a statement, an empty one). The following colon-terminated expressions constitute statements:
x++A block statement is a zero or more statements grouped inside the braces (curly brackets), { block-statement }. Whenever a single statement can be used, a block statement can be used too. The scope of variables declared inside a block is bounded by the delineating curly brackets. Blocks occur in:
static (for initialisation of object fields other than through a constructor)class BlockDemo {
public static void main(String[] args) {
boolean condition = true;
if (condition) { // begin block 1
System.out.println("Condition is true.");
} // end block 1
else { // begin block 2
System.out.println("Condition is false.");
} // end block 2
}
}
return key word — 1A method is a separate piece of code which can be called from a main program or another method to achieve some well defined computational task. In Java, each method must be defined within the body of a class and each class can have several methods. Copies of the data from the calling program are passed down to a method in its argument list. In Java, each method returns a single value (the result) to the calling program. This returned value can be a value of a primitive type, or an object, including an array (discussed below). Methods can do all sorts of other things, such as writing to files or databases, and often they do not return any value at all — in this case the return type is given the special designation void.
The syntax of method definition
[@annotation-type] [modifier]* return-type method-name(argument list) {
... method-body (block statement) ....
[return expr;]* // where expr type must be compatible with the method return-type
}
The method return-type can be a name of any primitive type, or a class name (standard Java API class or user’s defined), or void, in which case the method does not return any value. If a method has non-void return-type (it does return a value), its body must contain one or more return statements: return expression.
Unlike in some languages, in Java one cannot define a method inside a body of another method (this can be circumvented by using local inner classes).
return key word — 2Modifiers of methods (just like modifiers of variables) determine their characteristics in terms of relationship with other parts of the program: can they be used by other classes (private, protected, public), can they change implementation (final), can they be called without creating an object of the class in which they are defined (static). The argument list is a comma separated list of parameter declarations surrounded by a pair of matching parentheses:
methodname(type1 name1, type2 name2, type3 name3, ...)
Method parameters are used to pass data to the method during the invocation, and they are used inside the method body.
public class SquareInt { // Calculates the squares of the numbers from 1 to 10
// main method
public static void main(String[] args) {
for (int i =1; i<=10; i++) {
System.out.println(" number= " + i + " square= " + square(i));
}
}
// that other method
static int square(int x) { // static because is called by static (main)
return x*x;
}
}
Here is an example of a class which contains a method definition:
Well designed methods (and classes) reduce the amount of effort required on a large programming project. Their benefits include
Independent testing of sub-tasks. Each sub-task can be coded up in a separate method and tested using an appropriate “harness” before combining it with the rest of the program.
Reusable code. It could turn out that methods that you write can be used in a number of different classes in a number of different programs. In some cases you might want to construct a library of useful methods. The Math class in the java.lang package is an example of such a library. (Note that library methods are declared as static.)
Isolation from unintended side-effects. If a calling program communicates with a method only through the list of values passed to it as arguments (and makes no modification to the class fields), then thereafter the method executes independently of the main program. In such case the method can not inadvertently corrupt data in the main program. This makes your programs easier to debug and maintain. As a safety layer for avoiding side effects, the passing parameters (in the method declaration) can be defined with the modifier final.
public int square(final Object x) { … }If the object x is an instance of a class X, which contains methods doThis(), doThat(type-name param) and returnSmth(), where the last method is declared to return a value of the type return-type, then other parts of the program which are aware of the object x, can call these methods:
X x = new X(…);
type-name y = expr; // expr must have compatible type with type-name
return-type z;
... and z ...
x.doThis();
x.doThat(y);
z = x.returnSmth();
Depending on the type of the object of the X class, and the type of the client (the object from within which the method call is made) Y, the calling is possible if:
publicprotectedprivateWhen methods with parameters are invoked, how the argument values are treated by the method during its execution?
|
|
|||
| The actual parameter is a reference to a value, The method affects the value if the actual parameter is modified by the method. (Happens for functions with pointers as parameters in C/C++). | The actual parameter is a copy of the passed value. Even if the actual parameters is modified by the method, the original value is not affected. |
Java uses pass-by-value rule, but its effect is different for primitive and reference types of parameter.
a value modified (squared) by the method square(i) in SquareInt.java has no relation to the value of i (the value used by the method does not live outside method’s body, and disappear after its execution). How to make use of the data calculated by the method? Make the method to return value: SquareIntV2.java.
a copy of the value passed as an actual parameter is the alias of the same object! The method modifies the object all the same (side effect): SquareIntV3.java. The original reference cannot be modified (unlike for pass-by-reference). If a method returns a reference to an object which was constructed anew during the method execution, take care of the object components — are they new or recycled objects? A newly created object made of recycled components may open a possibility (which did not exist prior to the method call) to change the environment. (Swing library has design defects of such kind.)
As general principle — when designing a method try to make it cause as less changes in other variables of your program as possible. If a methods disturbs its environment, it can (against programmer’s knowledge, or intention, or both) break it, what will cause serious problems, and may result in execution failure. Such side-effects are dangerous. In older languages, functions with side-effects were a common thing (due to undisciplined use of global variables), creating difficult debugging problems. Ideally, methods should not change their surrounding at all; such methods are called side-effects free (or, pure). In functional programming all methods are pure (and FP have no variables, only values).
if-elseOften (almost always, and many times over) one needs to organise the program execution in different ways depending on the data. The selection expression, as it’s known, is the if-else statement with the syntax:
if (b) // b is a boolean expression
statement_1;
else // this part is optional, it covers all logic alternatives to b, !b
statement_2;
If the set of logical alternatives is more complex, then the extended form if-[else if]*-else is used:
if (b1)
statement_1;
else if (b2)
statement_2;
...
else if (bn)
statement_n;
else // the remaining alternatives go — !(b1 ∨ ... ∨ bn)
statement;
Statements statement_i can be one-liners or multi-line blocks ({…}). if-else statements can nest — if-else statement can be a part of a statement_i. You should not nest too deep, and to avoid the dangling else error (like in DanglingElses.java).
switchIf there are too many else-ifs in a situation when the boolean expression is an equality test for integer values, int_expr==valuei, i=1,2,…k,, the switch statement can be used:
int var = int_expr; //can be number type, or Enum (and String in JDK 7)
switch (var) {
case value_1: //this is a statement label
statement_1; // label is just a label, not a condition for execution!
break; // to prevent execution of default_statement
case value_2: case value3: ...
statement_23; // multiple case labels may require the same statement
break;
case value_k: // not all range of values needs to be explicitly checked
statement_k;
default:
default_statement;
// flow control jumps here if no matching case found. default is
// optional: in its absence, the entire switch statement is skipped
}
The case or default label does not force the break out of the switch, and it does not imply the end of execution of statements. The falling through can be avoided explicitly by break statement, or, if the switch is used inside a method which returns a value, by return. Some people advise not to use switch, but it may be useful for processing multiple command-line options. Traps of the control flow falling through in switches (unintentionally) exemplified in IfTest.java.
while and do-whileNeed to repeat the same statement multiple times depending on a value of the boolean expression, and the number of repetitions isn’t known in advance (indeterminate loop)? Use either while loop
while (boolean-expr)// if boolean-expr evaluates to true, then execute
statement;
or, to execute the statement once, no matter what, use do-while loop
do
statement;
while (boolean-expr);
statement (especially in do-while case) is almost always a block.
class WhileDemo {
public static void main(String[] args){
int count = 1;
while (count < 11) {
System.out.println("Count is: " + count);
count++;
}
}
}
forWhen the number of repetitions is known in advance (determinate loop), it can be controlled by a counter, most often, when the looping is done over a range of values, like an array elements, from beginning to end, usage of for loop is recommended:
for (init-expr; boolean-expr; incr-expr) // all expressions are optional
statement;
which is equivalent to
{ //mind this block !
int-expr;
while (boolean-expr) {
statement;
incr-expr; // it often has that (in/dec)cremental form i++ / j--
}
}
Java 1.5 has a simplified form of for loop, the so called for-each loop (though there is no such keyword):
for (ElementType var: objectOfCollectionType)
statement;
break and continuebreak is often important to control the exit from a loop or a block:
while (years <= 100) {
balance += payment;
double interest = balance * interestRate/100;
balance += interest;
if (balance >= goal) break; //at this moment, while loop is exited
years++;
}
System.out.println("No of years = " + years);
continue: instead of breaking out of the loop, it only skips remaining statements in a current iteration and goes to the next one.
|
|
A version of break (and continue) with a label transfers the control flow to the statement immediately outside the labeled statement which can be a nested block (eg, a loop). Unlike an ordinary break which terminates the innermost block, a labeled break transfers the control flow to the statement immediately outside the labeled statement (one can escape a nested loops at once.)
label_one:
for (int i = 0; i < 99; i++) {
... break label_one ...
}
When labeled break and continue are used in multi-nested blocks, they violate the basic tenet of structured programming known as Single Entry — Single Exit principle:
Every block of code must have a single entry point and a single exit point. It should be impossible to enter or exit such a block in the middle. Entry is at the top, exit is at the bottom (like in the program text).
Labeled break allows you to exit deeply nested blocks (like double for-loop) from the middle. This is bad because the enclosing blocks (outer loop) may not know that their are being exited; if such outer blocks assume that they control their execution and exit (which is normal), this can lead to errors which will be hard to pinpoint. The example — Labeled.java .
Labeled break and continue considered harmful (if possible, avoid simple break and continue, too).
returnreturn statement terminates execution of a method and returns to the invoker.
double nonNegative(double val) {
if (val < 0) return 0; //an int constant, but it's promoted to double
else return val; // a double
}
Even if the methods does not return a value, a simple return; with no following expression will terminate execution of the method code (leaving whatever is left out of execution):
void reportIfNegative(double val) {
if (val < 0) {
System.out.println("It's negative, alright.");
return;
}
System.out.println("This will not seen if val < 0");
}
For primitive numerical types, the operators
+, -, *, / and %
can be combined with the assignment operator = when used to increment (decrement, multiply, divide,remainder) an old value :
value = value + delta; // is same as
value += delta; // same for other -, *, /, %
In a particular case of incrementing (decrementing) by 1, commonly used in determinate for-loops, a more cryptic expression is used :
i = i + 1; // is identical to
i++; // similarly, i = i - 1 is identical to i--;
The expression i++ is post-incrementing (first evaluates then increments), the expression ++i is pre-incrementing (first increments then evaluates).
Quirks like ++i++ are syntax errors.
The + operator also works for strings (concatenation), so it’s also possible to use it like this:
String s1 = "French Connection "; String s2 = "UK"; s1 += s2; // s1 evaluates to …
Often the content of the conditional if-else statement is assigning a value to the variable, depending on the state of the system. A familiar statement
if (wonAOFinal)
prize = 1000000;
else
prize = 500000;
can equivalently be written in a more succinct but expressive form :
prize = (wonAOFinal ? 1000000 : 500000);
The ? : operator has a value and can be used in an expression. The general form :
prize = (booleanExpr ? value1 : value2);
where value1 and value2 must have compatible types (if types are not the same, the return type depends on assignable (on LHS) type; automatic in-boxing and out-boxing can be carried out; in the case of incompatible references, the first common parent is returned, up to the Object type — brrrr! Error definitely occurs if the type is void).
In JDK 7: Elvis Operator helps to avoid testing for nullity of a reference used in assignment:
Value v = val1 ?: val2; // if val1 is null, val2 will be used to assign v
To write a method with an arbitrary number of parameters requires usage of an array. The new Java 1.5 feature variable arguments (varags), which allows an alternative way of dealing with such situation. The last parameter in a method (or constructor) can be declared as a sequence — finite but otherwise undetermined set of parameters — of a given type. The syntax :
public static R foo(T1 t1, T2 t2, T... t);
(only one varags is allowed which must be the last declared parameter) which is treated by the compiler as
public static R foo(T1 t1, T2 t2, T[] t);
the access and scope modifiers are arbitrary; T1, T2, T and R are parameters and return type. When the method foo() is called, the third parameter can be replaced by any number of actual parameters of the same type T, including no parameters, ie, passing no arguments is a legal (while the “old-fashioned” array form assumes that the last argument is always included, and it’s an array).
foo(a1, a2);
foo(a1, a2, v1);
foo(a1, a2, v1, v2,...,vn);
main can be also declared with varags : public static void main(String... args);
printf(): “March of Progress” (after Cay Hortsmann)printf(String format, Object... args) // the syntax of printf() formatting function:
System.out.printf("%s: %d, %s%n", name, idnum, address); // the usage
1980: C |
1988: C++ |
1996: Java
java.text.NumberFormat formatter = java.text.NumberFormat.getNumberInstance();
formatter.setMinimumFractionDigits(2);
formatter.setMaximumFractionDigits(2);
String s = formatter.format(x);
for (int i = s.length(); i < 10; i++)
System.out.print(' ');
System.out.print(s);
2004: Java |
2008 and beyond: Java.next |
When a computing problem gets more complex, the ad hoc approach to solving it may not be possible. Therefore, you have to postpone coding and first design the solution. The most often used design method is top-down approach:
|
Abstraction, decomposition and step-wise refinement make the most essential abilities for writing computer programs, regardless of a language. So far, we have been learning syntax rules (and a few other things), but we hardly learnt how to apply those rules for creating programs! But how can you teach it?
The pseudo-code is a loose mixture of Java and English (or your native tongue). As you refine your design more and more, the “English” statements get replaced by Java’s. Your pseudo-code algorithms should clearly show the steps which are sequential, those which are conditional (if blocks) and those which are iteration (loops). Any algorithm can be composed of these three elements. A computer language which has these constructs is Turing-complete. Thinking about algorithms in this way is called structured programming (E. Dijkstra).
A step-wise refinement approach to a problem of finding roots. The problem: Find a point x inside the interval between points a and b, x∈[a,b], where the function f(x) turns zero; it is given that f(a) < 0 and f(b) > 0.
|
The next step in implementing the root-finding function — the pseudo-code:
Pseudocode for the method bisect(f,a,b,ε)
bisect(f, a, b, ε)
if sign f(a) = sign f(b) then return fail
while |b - a| >= ε
m ← (a+b)/2
if sign f(m) = f(a)
then b ← m else a ← m end if
end while
return a
end bisect Finally, the pseudo-code is converted into the language code (whatever the language is used for implementation). In Java, one cannot pass a function to a method as argument. We shall ignore the f argument, assuming that f(x) denotes an already defined function (method), and bisect calculates its root:
double bisect(final double a, final double b, final double e) {
// complete this code as a exercise
}
This is a more mundane problem (used for one of the yesteryear assignments): Create a program which reads in a file containing a list of student names and IDs, then asks the user (“lecturer”) to enter marks which every student earned in a course, calculates final mark and grade for every student, and displays the results in different order (alphabetical, ranking etc).
Input: a plain text data file with student (one per line) information, and user prompted input during execution
Output: sorted lists of students, and program messages which guide the user
Define a Student class, initialise a list structure to contain all student objects, open an input file for reading, read in line by line, determine names and ids, use them to create a student object, add it to the student list.
Go through the list, prompt user to enter correct marks (this is an indefinite loop because the user should be given a choice of asking for help, he can enter a wrong value and the program must be able to correct him etc — the dialog must be robust against incorrect input). When all marks are collected, calculate final marks and grades.
Sort the list in alphabetical or in ranking order.
Prompt user to choose a student to correct marks; repeat until user terminates the program (this dialog must be equally robust).
Save data in a file.
The program skeleton (high-level pseudocode expressed in Java) may look like this:
List students = new ArrayList<Student>(); // Define Student class separately
Scanner sc = new Scanner(infile);
while (sc.hasNextLine()) {
studentData = sc.nextLine();
student = new Student(studentData); // Define appropriate constructor in Student class
students.add(student);
}
procureMarks(); // first dialog session with the user
process(students); // represent every method first by its stab
collectErrata(); // second dialog to collect corrections
saveData(outfile, students);
First, methods can be defined as stabs; stabs have empty body or single return statement. When the top-level structures are complete, implement every stab method as necessary.
Another approach to designing a complex program is a bottom-up, which is better known as data-driven. It is not full alternative — in real-life programming problems, the top-down and data-driven approaches do not exclude each other. They are often used simultaneously; problems appear when the two finally meet (glue layers are often necessary; the modern software bane is too many glue layers).
Data-driven programming makes strong separation between code (operations) and data structures on which the code acts. In design, the data are given priority, they are defined as the first step, after which the necessary operations follow. Change in logic of the program is achieved by editing the data structures, not the code. Transformation of data structures in the process of computation determine the program control flow (here is the difference between data-driven and object-oriented programming; also, if OO is also concerned with encapsulation, DD’s goal is to minimise writing the actual coding instructions; in this aspect, DD programming is related to functional programming).
Data-driven approach often involves automatic code generation (writing code using programs). DD programming with code generation is used to produce visual components of graphical user interfaces (applications which allow the user to interact with it through a set of graphical elements, so called widgets, and other means of receiving “physical” input). Java was late to adopt such technology — XAML for Microsoft Windows, Glade for X/Gnome; Qt had QtDesigner, and recently added QtQuick and QLM language; Apple uses this technology heavily in InterfaceBuilder to create GUI for MacOSX/iOS applications/apps. Java has finally caught up with them with JavaFX GUI library and tools. We shall learn about it later in the course.
Three types of errors:
logical errors (eg, dangling if-else and other kinds) — most insidious, eliminated by
Advice on how to avoid the logical errors: