|
 |
Java concepts (for Lightning Java, Fall 2015)
|
|
|
 |
Expressions
|
|
|
 |
Low-level / atomic expressions
|
|
|
 |
Java has a few atomic constants, i.e., language forms which signify a specific value; these include: null, true, false
|
|
|
 |
Literals are similar to constants, but their values vary with the particulars of how they are written out; for example, numerals (which represent numbers), strings, characters, etc. (Note that strings are actually an object type in Java, not a primitive type.)
|
|
|
 |
Java variables might seem simple, but they are actually fairly complex: variables can be distinct because of “where they live”, how they are typed, whether they can be changed, etc.
|
|
|
 |
Unlike variables in math, Java variables change over time: we over-write the current variable with a new one when we use an assignment statement of the form “var = expr;” where var is a variable and expr is an expression
|
|
|
 |
A variable may be a simple name (like x) or a more complex expression which evaluates to a variable (i.e., a location)
|
|
|
 |
for example: foo.labels[i+3] = 7
|
|
|
 |
The four main types of variables in terms of their scope and lifetime are:
|
|
|
 |
instance variables: (associated with a specific object, i.e., stored “inside” the object)
|
|
|
 |
class variables: (marked as static) which live in a class, rather than a specific object (for example, we might use a class variable to generate a unique “serial number” for objects that are created through it)
|
|
|
 |
local variables: which are specific to a method, and which go away when the method finishes computing
|
|
|
 |
parameters: similar to local variables, but used to pass information to a method (for the other direction, passing information out of a method, we can either return a value or modify other state via a side effect)
|
|
|
 |
All Java variables have a type, which is declared at the point in the program where the variable is introduced: the type is written first, then the name of the variable (e.g., “int x”)
|
|
|
 |
Java variables may have several kinds of types: primitive types, object types (references), or interface types
|
|
|
 |
when a variable of a primitive type is assigned, the value is over-written and thus gone
|
|
|
 |
when a variable of object type is assigned, the reference is over-written, but other references to the same object may still exist
|
|
|
 |
when we make changes to a referenced object, the object itself changes, and all references to it can "observe" those changes
|
|
|
 |
having a reference to an object, via a variable, allows us to call methods on that object
|
|
|
 |
example: myMinion.doSomething();
|
|
|
 |
a variable of interface type must hold a reference to an object whose class implements the interface in question
|
|
|
 |
See the link below for a summary of primitive Java data types
|
|
|
 |
Operator expressions
|
|
|
 |
We use expressions built from atomic values and operators to express numbers and other “unstructured” values
|
|
|
 |
We also have a “relational” level of expression which computes boolean values (true or false) from numeric and other ones
|
|
|
 |
The two prior “levels” can be tied together with the ternary (three-operand) conditional expression (_ ? _ : _)
|
|
|
 |
(this is like an if-then-else statement, but it has a value (depending on the boolean first sub-expression), rather than causes a change (like a statement would)
|
|
|
 |
Note that variables can be assigned within expressions, although this can be confusing, so use this feature sparingly.
|
|
|
 |
for example: y = x*(y=2)+3;
|
|
|
 |
Be careful about the use of equality comparison in Java (==): it is a binary operator which compares its arguments for literal equality, i.e., the same value. If the values being compared are object references, then they may not compare as equal even though the two objects are in exactly the same state. (Modern Java often shows Strings equal by this comparison because String literals are kept efficiently in a table of values from the compilation process. Just don’t count on this working for other object types.)
|
|
|
 |
See the link below for a comprehensive list of operators:
|
|
|
 |
Statements
|
|
|
 |
General aspects of statements
|
|
|
 |
The next “level” up of Java syntax is the statement; each statement has as it’s meaning a command, i.e., a (possible) change to the state of the Java system (roughly, a change to the states of the objects in play at the time, i.e., changes to the values of their variables).
|
|
|
 |
The simplest kinds of statements are assignment statements: they set the value of a variable (or other location) to a specified value using the “=” sign between the location (on the left) and the value (on the right).
|
|
|
 |
Statements occur in the context of method (and constructor) bodies: they are performed when the methods are called, in order, with exceptions noted below (loops, conditionals, and branching).
|
|
|
 |
Iteration statements (loops)
|
|
|
 |
standard for loops have a “header” consisting of three parts, typically used for initializing variables, checking for completion (a boolean) and
|
|
|
 |
example: for(int i=0; i<a.length; i++) { … }
|
|
|
 |
the order of execution is: initialization, then test, then body, then update, body, test, update, etc.
|
|
|
 |
“foreach” loops: if an object which supports the Iterator interface is used, a special kind of loop allows iterating through its values using a variable name
|
|
|
 |
example: for(x : MyCollection) { … }
|
|
|
 |
while loops allow for control of looping using only a boolean conditional for termination; any initialization or update must be done elsewhere
|
|
|
 |
example: while(i<3) { … }
|
|
|
 |
do-while loops reverse the order of the termination test and the body of the loop, so that the body is always executed at least once (the braces are required here)
|
|
|
 |
example: do { … } while(x<10)
|
|
|
 |
Conditional statements
|
|
|
 |
Simple if statements perform a body statement conditionally, based on a boolean test
|
|
|
 |
example: if(x<3) { … }
|
|
|
 |
The if-then-else statement adds a second statement body after the else keyword; it is executed if the condition is false
|
|
|
 |
example: if(x<3) { … } else { … }
|
|
|
 |
If statements may be nested, along with if-then-else and other statements—an else clause belongs with the last “unclosed” if that precedes it
|
|
|
 |
The switch statement
|
|
|
 |
the switch statement is like a generalized if-else statement: it allows handling multiple cases in separate clauses
|
|
|
 |
the syntax is:
|
|
|
 |
switch (expression) { case c1: statements ... case cn : statements default : statements }
|
|
|
 |
the semantics is:
|
|
|
 |
evaluate the expression after the switch, then choose a clause based on the value and execute all the statements in that clause; continue on to the next clause unless there is a break statement
|
|
|
 |
the default label is used if no other constant matches the value of the expression
|
|
|
 |
note: very important! remember that you must use a break statement at the end of each clause, or execution will fall through into the next case. This is a common cause of errors.
|
|
|
 |
Branching statements
|
|
|
 |
In general, branching statements allow a “transfer of control” to another part of the program—although Java reserves the “goto” keyword, it does not have a goto statement
|
|
|
 |
The break statement transfers out of a loop to the next statement after
|
|
|
 |
The continue statement transfers to the top of a loop, “continuing” around the loop, but skipping the remainder of the current iteration
|
|
|
 |
The return statement transfers out of a method call, back to the statement which called the method
|
|
|
 |
For methods with a non-void return type, a value must be supplied: return expr;
|
|
|
 |
If nested loops enclose a break or continue, they refer to the innermost loop by default
|
|
|
 |
Loops may be labelled to disambiguate them and a break or continue may be followed by a label in order to break or continue to the specified level of nesting
|
|
|
 |
Arrays and other collections
|
|
|
 |
An array is a linearly-ordered collection (set) of Objects or values
|
|
|
 |
arrays allow us to maintain a large number of related values (called the elements) together
|
|
|
 |
the number of values may be set at run-time (when the array is created)
|
|
|
 |
although any given array is always of a fixed length, we can set an array variable to point to a newly-created, larger array
|
|
|
 |
we can use loops to run through all the elements of an array, using the .length field of the array as a loop limit
|
|
|
 |
if an array is not filled up all the way, we may want to use a separate variable to tell how full it is
|
|
|
 |
Arrays and initialization
|
|
|
 |
an array variable itself may be uninitialized (it will be null)
|
|
|
 |
int[] myArray;
|
|
|
 |
an array variable may be initialized (it refers to the array), but the elements may be uninitialized (they will be null)
|
|
|
 |
int[] myArray = new int[10]
|
|
|
 |
we can initialize all (or some) of the elements of an array (e.g. in a loop)
|
|
|
 |
an array can also be initialized with a set of specific values without telling how many there are (braces notation)
|
|
|
 |
int[] myArray = {1, 2, 3, 4, 5};
|
|
|
 |
Access to elements through indexing
|
|
|
 |
we can get (or assign) an array element by using the bracket notation: myArray[i]
|
|
|
 |
array element indices always start at 0 and run up to (but not including) the length of the array
|
|
|
 |
ArrayLists are objects that encapsulate an array and allow access to it via methods, including ones which seem to extend the size of the collection
|
|
|
 |
In fact, when an ArrayList needs to be enlarged, a new array is allocated internally, and the old values copied over
|
|
|
 |
(For this reason, ArrayLists should be used carefully: the illusion of extendability comes with a cost.)
|
|
|
 |
Other Java objects implementing various forms of aggregates are part of the Collections framework (not on the exam!)
|
|
|
 |
Methods
|
|
|
 |
Methods are like sub-routines, functions, procedures and similar features in other languages: they collect together statements which suit some purpose and are parameterized to allow for greater flexibility
|
|
|
 |
In Java, methods execute in the context of some objects local state (i.e., collection of variables), and thus are normally used to update the variables in that state (but also to communicate with other objects by calling their methods)
|
|
|
 |
Rationale for methods
|
|
|
 |
methods allow us to abbreviate or consolidate common "blocks" of code
|
|
|
 |
parameters allow us to abstract out common structure relative to some systematic variation
|
|
|
 |
Uses of methods
|
|
|
 |
methods may provide an interface to the outside world so an object can be used
|
|
|
 |
methods may provide internal consolidation of repeated code for purposes of convenience
|
|
|
 |
Methods and return values
|
|
|
 |
some methods are called only for their effects: these should have a void return type
|
|
|
 |
others are called to return a value (e.g., in an expression); these have non-void return types
|
|
|
 |
if a method has a non-void return type, it must execute a return statement
|
|
|
 |
Method header syntax: a method header has a return type (possibly void), a method name, a parameter list (each one a typed variable) and an optional throws clause (see Exceptions below)
|
|
|
 |
void foo(int x, int y) { … }
|
|
|
 |
int hoo() throws NullPointerException { … }
|
|
|
 |
local variables may be declared inside a method—they disappear upon return of the method
|
|
|
 |
method input and output: void return type and empty parameters possible
|
|
|
 |
Objects and classes
|
|
|
 |
Objects are encapsulated collections of variables (called a state) and sets of methods which act upon them
|
|
|
 |
Object-oriented programming consists of building interacting groups of objects, possibly created dynamically during program execution, so that their interaction provides a program service
|
|
|
 |
Objects in Java are associated with a class, roughly a template which describes the form of the object (which variables and which methods it will have), but different objects of the same class have their own, individual state
|
|
|
 |
The syntax of a class includes a class name, optional clauses for extension and implementation, and then a body consisting of variable and method declarations
|
|
|
 |
Static and non-static
|
|
|
 |
variables and methods declared static are stored in the class, not in individual objects
|
|
|
 |
variables and methods declared without static are stored/associated with a specific created object
|
|
|
 |
static methods cannot access non-static data: there isn't a specific object involved, so no specific data
|
|
|
 |
non-static methods may access static data (they just access the data stored in their class)
|
|
|
 |
Constructors
|
|
|
 |
Objects are created by calling a constructor, a special “method” with no return type, but named the same as the class
|
|
|
 |
The constructor is called with the new keyword and then the name of the class/constructor, followed by actual arguments
|
|
|
 |
Each class may have several different constructors, but each constructor must have a different signature (i.e. sequence of types) so that they can be distinguished by the compiler
|
|
|
 |
Class hierarchy and inheritance
|
|
|
 |
a (sub) class may be declared so that is extends an existing (super) class; an object or instance of a sub-class contains all parts of the original super-class
|
|
|
 |
syntax: class A extends class B { ... (normal stuff here) ... }
|
|
|
 |
objects of the sub-class have all the variables and methods of the super-class
|
|
|
 |
note carefully! this means that the sub-class properties are a superset of the super-class ones!!
|
|
|
 |
a method (or variable) definition which is available in a sub-class in this way is said to be inherited from the super-class
|
|
|
 |
if a method is defined in the super-class and re-defined in the sub-class, the sub-class definition is given preference: this is called an over-ride of the super-class definition
|
|
|
 |
one class may extend another, which extends another, etc.; however, a given class can extend only one other one directly
|
|
|
 |
this leads to a class hierarchy of classes, their sub-classes, sub-sub-classes, etc., like an organization chart in a business (one at the top, branching out down below)
|
|
|
 |
when we look for a variable or method for some object, we look first in its own class, then in any super-class, then any super-super-class, etc., stopping and using the first definition we find
|
|
|
 |
Interfaces
|
|
|
 |
A good object-oriented design will often include different classes which nevertheless provide some of the same capabilities, in terms of meaningful method calls
|
|
|
 |
Java provides a useful notion of contract which allows these common capabilities to be specified by the programmer and checked by the compiler—this facilitates re-factoring of designs
|
|
|
 |
Interfaces are collections of method names and signatures: but no method bodies!
|
|
|
 |
Thus an interface allows us to specify a set of capabilities without (yet) specifying how they will be implemented
|
|
|
 |
but Java does not allow behavioral specification beyond signatures
|
|
|
 |
Signatures include the name of a method, the type of the result and the number and type of the arguments
|
|
|
 |
argument names don't matter for matching a signature
|
|
|
 |
signatures with different result types, but the same name and argument types, conflict
|
|
|
 |
A class can be declared as implementing an interface: class A implements I { ... }
|
|
|
 |
this will be checked by the compiler, which ensures that the class actually has methods corresponding to the signatures in the interface
|
|
|
 |
One interface may be declared as extending another, so as to provide some (hierarchical) structure, just like with sub-classes and super-classes
|
|
|
 |
Abstract classes are "halfway between" a class and an interface
|
|
|
 |
they may have some signatures and some full definitions
|
|
|
 |
this allows a form of "default implementation" when a full method is defined
|
|
|
 |
classes may extend abstract classes, forming a hierarchy
|
|
|
 |
you may not instantiate (with new) an abstract class!
|
|
|
 |
Exceptions
|
|
|
 |
Exceptions are a program structuring mechanism which allow us to ignore potential errors for the duration of longer blocks of code
|
|
|
 |
Exceptions allow us to concentrate on what is likely to happen, and to restrict attention to errors to specific locations in the code
|
|
|
 |
Exceptions are objects of (a sub-class of) the Exception class in Java
|
|
|
 |
Exceptions are thrown (either by the programmer or the system) when something goes wrong
|
|
|
 |
(as a programmer, we write “throw new Exception()”, i.e., we create a new exception object and throw it)
|
|
|
 |
Exceptions can be caught using a try-catch block
|
|
|
 |
try { ... <body statements> ... } catch (Exception e) { ... <error statements> ... }
|
|
|
 |
the body statements are executed and, if an exception occurs, it is bound to the name e and the error statements are executed: control continues to the statement after the try-catch bock
|
|
|
 |
Methods which might throw an exception without catching it must be marked with a clause “throws Exception” in their header (even if the throw comes indirectly from a method they call)
|
|
|
 |
Some exceptions (array index out-of-bounds, division by zero and null reference access) are so common that they do not need to be explicitly marked with throws clauses: these are called unchecked exceptions
|
|
|
 |
When an event is thrown, it propagates up the chain of called methods, eliminating them until a catch block is found
|
|
|