Download doc - CodeSchool.org

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
Java miscellany
literals
An integer literal in Java normally denotes an int value, but an integer literal with the suffix l or L
denotes a long:
35
35L
// an int
// a long
A non-integer number literal in Java normally denotes a double value, but a non-integer number literal
with the suffix f or F denotes a float:
2.3
2.3F
// a double
// a float
Integer literals can be represented in hex using the prefix 0x or 0X and represented in octal using the
prefix 0 (zero):
0x6AC2
0x6AC2L
// the int value hex 6AC2
// the long value hex 6AC2
027
027L
// the int value octal 27
// the long value octal 27
Non-integer number literals can be represented in “engineering” notation denoted with an e or E:
662.25E-4
662.25E-4F
// the double value 662.25 * 10 -4
// the float value 662.25 * 10 -4
A char literal is represented as a single character enclosed in single-quote strings:
‘A’
‘\t’
// the char value A (the integer value 65)
// the char value tab (the integer value 9)
We can represent Unicode characters in a string or char literal with \u or \U followed by a four-digit
code point:
‘\U0065’
“\U0065pple”
// the char A
// the string Apple
string concatenation with +
In Java, when both of the + operator’s operands are strings, the operation does string concatenation,
not addition:
3+5
// adds of 3 and 5, returning 8
“foo” + “bar”
// concatenates “foo” and “bar”, returning “foobar”
When only one operand is a string, + first gets the string equivalent of the other operand:
32.2 + “bar”
// concatenates “32.2” and “bar”, returning “32.2bar”
Java knows how to get the string equivalent of its primitive types, but for reference types, the string
equivalent is gotten with the toString method (which is a method of java.lang.Object, so all reference
types have this method).
Cat c = new Cat();
“foo” + c // returns “foo” concatenated with string returned by c.toString()
Of course, for toString of a type to return anything useful, toString should be overridden in that type.
pre-populated multi-dimensional arrays
Recall that arrays in Java are objects on the heap. We create an array with the new operator by
specifying the type of the array and the number of items:
new Cat[3]
new Dog[5][][]
// return a 3-member array of Cats
// return a 5-member array of arrays of arrays of
Dogs
The inconvenience here is that populating arrays with values can get quite verbose:
int[] arr = new int[4];
arr[0] = 35;
arr[1] = -2;
arr[2] = -20;
arr[3] = 80;
It’s especially bad with multi-dimensional arrays, because first we must create the component arrays
themselves before finally populating them with the actual values:
Cat[][] c = new Cat[2][];
c[0] = new Cat[3];
c[1] = new Cat[3];
c[0][0] = new Cat();
c[0][1] = new Cat();
c[0][2] = new Cat();
c[1][0] = new Cat();
c[1][1] = new Cat();
c[1][2] = new Cat();
The above can be condensed by populating the multi-dimensional array with sub-arrays at creation:
Cat[][]
c[0][0]
c[0][1]
c[0][2]
c[1][0]
c[1][1]
c[1][2]
c
=
=
=
=
=
=
= new Cat[2][3];
new Cat();
new Cat();
new Cat();
new Cat();
new Cat();
new Cat();
The rule is that we always, of course, must specify the size of the outer dimension, but then we can
optionally fill in sizes from left-to-right:
new Cat[2][3][7]
new Cat[2][3][7][][]
// needn’t specify all dimensions
What we can’t do is leave gaps:
new Cat[2][][7]
// illegal
This seems to be saying, ‘create a 2-element Cat array array array, don’t populate it with any Cat array
arrays, and populate those non-existent Cat array arrays with 7-element Cat arrays’. This of course
doesn’t make sense because you can’t populate arrays that don’t exist.
Another limitation with this convenience is that it doesn’t help us when we want the sub-arrays to have
different sizes, so there’s no shorter way to write this:
Cat[][] c = new Cat[2][];
c[0] = new Cat[7];
c[1] = new Cat[9];
array literals and array initialization
When creating a Java array, we can populate it with a comma-separated list of items in {}:
new type[] {items}
We don’t specify the array size because the size is determined by the number of listed items:
// return a new 3-element int array with 3, 5, and -4
new int[] {3, 5, 2 - 6}
// return a new 2-element Cat array with two new Cat instances
new Cat[] {new Cat(), new Cat()}
We can use this syntax with multi-dimensional arrays simply by providing items of the proper array type:
// return a new 2-element short array array with two short arrays
new short[][] {new short[6], new short[] {6, 2, 8}}
When an item itself uses the new type[] {items} syntax, we can omit the new type[] part:
// return a new 2-element short array array with two short arrays
new short[][] {new short[6], {6, 2, 8}}
The compiler doesn’t need to be told that {6, 2, 8} is meant to be a new short array because it can
infer from the context.
This array syntax can get a bit dense, especially with multi-dimensional arrays, so it’s often best to
spread them out onto multiple lines. For example:
// return a new 2-element Cat array array
new Cat[][] {
{new Cat(),new Cat(),new Cat()},
{new Cat(),new Cat()}
}
When initializing an array reference, we can also omit the new type[] part of new type[] {items}:
int[] arr = {3, 6, 7};
// int[] arr = new int[] {3, 6, 7};
This isn’t allowed in a regular assignment, however:
int[] arr;
arr = {3, 6, 7};
// compile error
The compiler could perfectly well infer the type of the array from the reference’s type in the
assignment, but this syntax is disallowed because, it’s argued, not being able to see the array type
makes the code more bothersome to read:
// bad style: the programmer can’t see the type in this line
arr = {3, 6, 7};
// acceptable: the programmer sees the type in this line
int[] arr = {3, 6, 7};
{} as a statement
Within a function (or method), a pair of curly braces that enclose a “body” are actually what’s called a
compound statement, a statement comprised of other statements. Wherever the language syntax allows
a statement, you can put {} with one or more other statements inside.
So whereas previously I’ve defined the syntax of, say, while as:
while (condition) {body}
…the true syntax is:
while (condition) statement
It’s just that we usually want a loop to contain multiple statements, so we use {} with while most of the
time.
In fact, many programmers insist that {} should always be used in these constructs for stylistic
consistency. So though this is legal:
if (bla) foo();
…you should probably just always write:
if (bla) {
foo();
}
I personally prefer when if’s and while’s follow this uniform indentation and always use {} whether
they contain just one statement or many.
Be clear that not all pairs of {} are compound statements. The {} of a method definition, for instance, are
just a part of the syntax for a method definition, not a statement of their own.
subscopes
A variable exists in the scope in which it is declared and in all subscopes thereof.
In Javascript, each function is a unique scope, and the other functions contained therein are subscopes
of that function. So a variable declared within a function exists in that function and all other functions
therein.
In Java, functions and methods can’t be nested, but each control structure within a function constitutes
a subscope. So if you, say, declare a variable inside a while block, that variable exists only in that while
block and its subscopes, not the rest of the function. So this method will give a compile error:
void april() {
if (something) {
int december = 3;
}
// compile error: december doesn’t exist here
this.june(december);
}
The solution here is to simply declare the variable in the outer scope where it can be used everywhere it
is needed:
void april() {
int december;
if (something) {
december = 3;
}
this.june(december);
// OK
}
This rule of subscopes serves two purposes:
1) Though writing long, complicated functions is generally frowned upon, it’s done often enough
that some programmers would like to be able to reuse variable names within a function,
especially for commonly used names like i as a loop counter. The rule of subscopes helps in such
cases because it effectively divides a function into multiple namespaces.
2) When execution leaves a scope, the variables in that subscope are no longer needed and can be
discarded. Discarding variables as soon as possible may allow the compiler to do certain
optimizations, so dividing a function into multiple scopes may increase the number of
opportunities for such optimizations.
As just discussed, a compound statement {} can be used any place a statement can. A {} constitutes its
own subscope, so variables declared within a pair of {} don’t exist outside that pair of {}:
{
int december;
}
june(december);
// compile error: december doesn’t exist here
While using {} just to create a subscope is legal, doing so at best only serves a stylistic purpose, so it’s
rarely done.
Within a subscope, you can declare a variable with the same name as a variable from an outer scope.
When you do this, the outer scope variable of that name is “hidden” in that subscope and so can’t be
used there:
{
// in this scope, january refers to the int variable declared
here
int january = 3;
…
{
// in this scope, january refers to the char variable
declared here
char january;
…
}
}
If you need an outer scope variable, simply use different names for your inner scope variables so as not
to hide that outer scope variable.
boolean operators and conditions
In Java, the comparison operators (>, <, ==, etc.) and equality operators (== and !=) return booleans.
In Java, the && and || operators have a special “short-circuit” behavior. This means their right operand
is not always evaluated. For example:
x.foo() && x.bar()
Here, if foo() returns true, the && operation will return false whatever bar() returns. In fact, when any
&& operator’s left operand returns false, we can skip the evaluation of the other operand entirely.
Similarly:
x.foo() || x.bar()
Here, if foo() returns true, the || operation will return true whatever bar() returns. So when any ||
operator’s left operand returns true, we can skip the evaluation of the other operand entirely.
When evaluation of the right operand is skipped, this is called “short-circuiting”. Short-circuiting doesn’t
change the end result, but it skips doing unnecessary work.
In Java, we have variants of && and || that don’t short-circuit: & and |. They are occasionally useful
when you want the right operand to evaluate regardless of the left operand.
In Java, condition expressions (such as in an if statement or an ?: operation) must always return
booleans. The compiler will object to any expression returning another kind of value in those places.
while (x + 3) {…}
// compile error
bitwise operators
Java’s | and & operators are overloaded: with boolean operands, they are the logical OR and AND
operators that don’t short-circuit; but with integer operands, they are the bitwise OR and AND
operators. The complete set of bitwise operators are:
~
|
&
^
>>
<<
bitwise negation (unary)
bitwise OR
bitwise AND
bitwise OR exclusive
bitwise right shift
bitwise left shift
For example, the int 1024 in bits is:
0000_0000 0000_0000 0000_0100 0000_0000
~1024 returns an int with each bit flipped:
1111_1111 1111_1111 1111_1011 1111_1111
For another example, the ints 204 and -124 in bits are:
0000_0000 0000_0000 0000_0000 1100_1100 (204)
1111_1111 1111_1111 1111_1111 1000_0100 (-124)
204 | -124 returns each corresponding pair of bits OR’d together:
1111_1111 1111_1111 1111_1111 1100_1100
Similarly, 204 & -124 returns each corresponding pair of bits AND’d together:
0000_0000 0000_0000 0000_0000 1000_0100
And 204 ^ -124 returns:
1111_1111 1111_1111 1111_1111 0100_1000
(In |, &, and ^ operations, when one integer operand is of a smaller type than the other, it is padded out
with zeroes, and the operation returns the larger type. For instance, the bitwise OR of a char and an int
pads out the char and returns an int.)
The shift operators shift the bits of their left operand by the number of places speficied by the right
operand. So given 1024 in bits is:
0000_0000 0000_0000 0000_0100 0000_0000 (1024)
1024 << 2 returns:
0000_0000 0000_0000 0001_0000 0000_0000 (4048)
…and 1024 >> 2 returns:
0000_0000 0000_0000 0000_0001 0000_0000 (256)
conditional operator
The conditional operator is a bit unusual because it takes three operands—a condition and two
expressions---and is denoted by two separate symbols, a question mark and a colon:
condition ? expression1 : expression2
In a ?: operation, the condition is evaluated, and if true, expression1 is evaluated and its value returned;
otherwise, expression2 is evaluated and its value returned.
Notice that the conditional operator is also unusual because, unlike with normal operators, not all of its
operands are evaluated: either expression1 evaluates or expression2 evaluates, never both. Basically, a
?: is like an if-else in expression form, allowing for writing some compacter code. For instance,
instead of writing:
if (something) {
x = 3;
} else if {
x = 5;
}
…we can instead write:
x = (something ? 3 : 5);
(The parens here are not necessary, but I recommend always surrounding a ?: in parens for clearer
style.)
Many programmers frown upon the use of ?: because it can be hard to read, especially in complex
expressions. When all branching is done with just statements like if and while, the flow of code tends
to stand out more and thus be a bit easier to follow.
(Because the conditional operator takes three operands, it’s also known as the “ternary operator”.)
compound assignment operators
Very commonly, we perform an operation upon a variable’s value and then assign the result to that
same variable:
foo = foo + 3;
Because this is so common, we have a more compact syntax:
foo += 3;
The general form is:
variable op= expression;
…which converts to:
variable = variable op (expression);
This works not just for the + operator but for several other operators, e.g.:
foo /= 3;
// foo = foo / (3)
increment and decrement operators
Because incrementing and decrementing variables is so common, we have the unary ++ (increment) and
-- (decrement) operators:
foo++;
bar--;
// (foo = foo + 1)
// (bar = bar – 1)
The odd thing about ++ and -- is that they come in prefix forms as well as postfix:
++foo;
--bar;
// prefix form (foo = foo + 1)
// prefix form (bar = bar – 1)
The difference between the prefix and postfix forms is what value gets returned: the postfix forms
return the value of the variable before the increment or decrement; the prefix forms return the value of
the variable after the increment or decrement:
int x = 3;
int foo = x++;
// x++ returns 3
int x = 3;
int foo = ++x;
// ++x returns 4
In both cases here, x increments from 3 to 4; just the values returned differ.
The use of increment and decrement can get confusing in complex expressions. To avoid this confusion,
most programmers simply only use ++ and -- in isolated statements where it doesn’t matter whether
you use the prefix or postfix forms:
++foo; // just as well might write foo++ here instead
Be clear that the increment and decrement operators only work on “lvalues” (things to which you can
assign):
int x = ++3;
// compile error: ++3 is an invalid expression
break
A break statement is written:
break;
Within a loop, a break statement jumps execution out of the loop:
while (bla) {
…
}
foo();
// execution jumps here after a break in the loop
It never makes sense to execute a break unconditionally because any statement(s) immediately after a
break would never execute:
break;
bar();
// this would never execute
So break only makes sense as the last statement within an if or else:
if (something) {
break;
}
bar();
continue
A continue statement is like break, except continue jumps execution back to the conditon for retesting instead of terminating the loop:
continue;
A continue basically says to skip the rest of the current iteration.
If we didn’t have break and continue, we could get the same control flow just using regular while, ifelse, and variables, but break and continue often express the same logic more elegantly.
switch
The switch construct is an alternative to if-else for mutual exclusion. Instead of branching based on
one or more conditions, a switch branches based on a value. So for instance, a switch might have one
branch corresponding to the value 3, another branch corresponding to the value -45, and another
corresponding to the value 100:
switch valueExpression
case 3
body1
case -45
body2
case 100
body3
default case
body4
Which body gets executed depends upon the value returned by valueExpression. If the value doesn’t
match any case, the default case is executed. This is really just an alternative way to express the same
thing as:
x = valueExpression
if (x == 3)
body1
else if (x == -45)
body2
else if (x == 100)
body3
else
body4
In Java, the form is:
switch (valueExpression) {cases}
…where each case is written:
case value: body
…and the default case is written:
default: body
A quirk of switch, however, is that execution “falls through” from one case to the next until a break is
encountered. So the above example would be written as a switch like so:
switch (valueExpression) {
case 3:
body1
break;
case -45:
body2
break;
case 100:
body3
break;
default:
body4
break;
}
(The default case needn’t be written last, but this usually makes most sense.)
Above, if we were to omit the break after body2, then when valueExpression returns -45, body2 would
execute and then continue through body3 until execution encounters a break. Very occasionally, you
might find this fall through behavior desireable, but mostly it just annoys programmers who forget to
include break statements. (Also note that the break after the last body isn’t necessary, but I write it
there just for stylistic consistency.)
In Java, the value of a case must always be an integer, and the value must be expressed as an expression
whose value can be determined at compile-time (so the expression can’t have variables and function
calls):
case 35 + 9:
case foo() + x:
return
// valid
// invalid: compiler can’t know what foo() + x will
A switch is really just a more constrained form of if-else and doesn’t really do anything an if-else
doesn’t (although, with some compilers, a switch might produce slightly more efficient code). I
recommend you always favor if-else over switch because it’s just not worth the hassle deciding when
a switch might make your code minutely more elegant or efficient.
do-while loop
The do-while loop is a simple varient of while that simply puts the condition at the end of the loop
such that the loop is entered the first time unconditionally:
do statement while (condition);
(Note the semi-colon at the end.)
A do-while is intended for those times when you want a loop to always execute at least once. The
thinking is that, rather than writing:
doStuff();
while (something) {
doStuff();
}
…you can just write:
do {
doStuff();
} while (something);
…thus sparing the repition of the body of the loop. In practice, though, I find that do-while, like switch,
is not terribly useful.
for loop
The for loop is another variant of while, but it tends to get used much more often than do-while:
for (expression1 ; condition ; expression2) statement
As you can see, for differs from while by the addition of two expressions surrounding the condition,
separated by semi-colons (which you should think of as unrelated to the use of semi-colons to terminate
a statement: the semi-colons here just divide the expressions from the condition). The first expression is
evaluated only once, before the condition is first tested. The second expression is evaluated after each
run through the body. The while loop equivalent would be:
expression1;
while (condition) {
… // body statements
expression2;
}
So the for loop really just exists to compact our code into fewer lines. Consider this while:
int i = 0;
while (i < 5) {
foo(i);
i++;
}
The for equivalent is written:
int i;
for (i = 0; i < 5; i++) {
foo(i);
}
The expression before the condition can be a declaration, so we can make this even more compact:
for (int i = 0; i < 5; i++) {
foo(i);
}
Basically, any time you need to iterate a counter, a for is the preferred choice of loop.
“enhanced” for loop
Many languages contain a loop specifically for iterating over every member of a collection, such as a list
or array. This loop is usually called “foreach”, but in Javascript it is called for-in, and in Java it is just
called the “enhanced” for loop. In C, there is no “foreach”, only the regular for.
In Java, the “enhanced” for loop is written:
for (variable : expression) statement
…where variable is the name of a variable (or a declaration of one) and expression returns a Java array
or some other Java collection object.
String[] messages = new String[] {“apple”, “banana”, “orange”};
for (String s : messages) {
foo(s);
}
Above, the loop calls foo three times, first with argument “apple”, then “banana”, then “orange”.
The “enhanced” for loop works with arrays and any type that implements the interface
java.lang.Iterable.
java.lang.Iterable
compound declarations
In Java, we can declare multiple variables in one statement by listing multiple names:
int april, may, june; // declare three ints: april, may, and june
String a, b, c;
// declare three String references: a, b, and c
This syntax can’t be used for parameters, but it does work for fields, in which case the visibility modifier
applies to each variable:
private char foo, bar; // declare two private char fields: foo and bar
We can initialize any variable in a compound declaration:
int april = 35,
may,
june = 7;
Above, april and june are given initial values, but may is left uninitialized.
varargs
A “varargs” function is a function which can take a variable number of arguments—hence “varargs”. In
Java, a varargs method is declared with a last parameter written with … ellipses after the type:
void foo (int a, int b, String ... c)
In truth, this is the same as making the last parameter an array:
void foo (int a, int b, String[] c)
…but a method declared with the ellipses can take a variable number of arguments. We either invoke
foo with an array of Strings for its last argument, or we invoke foo with one or more Strings for its last
argument(s):
public class Cat {
public static void foo (int a, int b, String ... c) { /* do
stuff*/ }
public Cat() {
// passes a new 3-String array
Cat.foo(3, 5, “apple”, “orange”, “banana”);
// passes a new 1-String array
Cat.foo(3, 5, “apple”);
// also passes a 3-String array
Cat.foo(3, 5, new String[] {“apple”, “orange”, “banana”});
// also passes a 1-String array
Cat.foo(3, 5, new String[] {“apple”});
}
}
Essentially, varargs methods in Java simply spare us from some occasional bother writing array literals.
strictfp
Some processors perform floating-point operations using more than 32 or 64 bits for intermediate
values before truncating the result down to 32 or 64 bits. The x86, for example, will use 80 bits for these
intermediate values. The problem is that this extra precision means the result of the same floating-point
operation may differ from platform-to-platform. If this variance is unacceptable for your purposes, you
can suppress this extra precision using the strictfp modifier.
When the strictfp modifier is applied to a method, floating-point operations in that method will not use
extra precision. When applied to a class, floating-point operations in that class will not use extra
precision.
Understand that using strictfp will actually make floating-point operations slower on most systems
because it requires performing more, slower operations when we can’t just use the processor’s built-in
floating-point operations.
array reference declarations
Normally a Java array reference is declared with the [] immediately after the type:
Cat[] x;
// declare x, a reference to a Cat-array
Because, in the early days, most programmers coming to Java came from C and C++, Java also allows the
C-like syntax of putting the [] after the reference name:
Cat x[];
// declare x, a reference to a Cat-array
Annoying, stupid, and morally wrong, but legal.
break and continue labels
In C and Javascript, break and continue always apply to the loop in which they are most immediately
contained. So if you have a loop within a loop, you can’t write a break statement in the inner loop that
will break out of the outer loop.
In Java, you can get around this limitation by applying labels to outer loops and then specifying the outer
loop by name in a break or continue statement. A label has the form:
identifier:
…and immediately precedes the loop it labels:
ian:
// labels the following loop as ian
while (something) {
…
}
(I prefer writing these labels on the preceding line, though most often you’ll see them placed on the
same line as the statement they label.)
Now we can break out of the loop ian from interior loops:
ian:
// labels the following loop as ian
while (something) {
while (something) {
if (something) {
break ian;
// break out of ian
}
}
}
Without the use of the label, the break would break out of the interior loop, not the outer loop.
Complicated nesting of loops within loops is to be generally avoided in the first place, but when we can’t
avoid such logic, these labels can occasionally help simplify things a bit.
the final modifier
Java’s modifier final is useable in several contexts, but it always has the general sense of “cannot
change”:
A final class cannot be inherited from.
A final interface cannot be inherited from.
A final method cannot be overridden in any subclasses.
A final field must be initialized in construction and thereafter cannot be reassigned.
A final static field must be initialized and thereafter cannot be reassigned.
A final local variable must be initialized and thereafter cannot be reassigned.
(The reasons for declaring classes, interfaces, and methods as final are highly debated. Mainly, it is a
point of style.)
Final fields needn’t be initizlied directly in their declarations, but they must be initialized at some point
in construction. The compiler is halfway clever about detecting when a final field might not get
initialized in some branches:
class Cat {
private final int x;
public Cat (boolean b) {
if (b) {
x = 3;
}
}
}
Here, the compiler objects that x may not get initialized in the Cat constructor (because b might be
false).
Basically the same rule applies to final local variables: they must be initizliaed at some point in the
method, and the compiler will complain if there are branches where they might not get initialized.
implicit this and superfluous super
The reserved word this can often be left implicit:
void adam() {
foo(); // this.foo()
}
Above, no local foo exists, so foo must resolve to this.foo. (If no this.foo exists, the compiler objects.)
void adam(String foo) {
this.foo() ;
foo = “asdf“;
}
Here we must leave this explicit to distinguish between the local foo and the class member foo.
The super reserved word is necessary for explicitly invoking superconstructors and invoking overridden
methods. More generally, though, super can be used to refer to any inherited member, even when this
suffices. So where an inherited member bar exists, you can write super.bar instead of this.bar. Doing so
doesn’t really serve a purpose though. Stylistically, it’s best to only use super when it’s really needed.
try-catch-finally
A try-catch in Java can have multiple catch clauses. An exception in the try is caught by the first catch
clause with an appropriate variable to receive it:
try {
…
} catch
…
} catch
…
} catch
…
}
// thrown Gonzo exception
(Automobile ex) {
(Muppet ex) {
(Humanoid ex) {
Assuming Gonzo is a descendant of both Muppet and Humanoid but not Automobile, a Gonzo exception
in the try is caught by the Muppet catch clause because that is the first catch from the top that matches.
A try-catch in Java can end with an optional finally clause. The finally clause is executed:
1) immediately after the try executes without throwing an exception, or
2) immediately after a catch clause executes, or
3) immediately after an exception is thrown from within a catch (in this scenario, the exception
continues to propogate after the finally clause completes).
Basically, once a try is entered, its finally clause will execute in all circumstances: the only question is
when.
package imports with *
Rather than importing each individual type from a package by name with its own import statement, you
can import all the types from a package in one statement using glob (*):
import pig.tiger.*;
// import all types from the package pig.tiger
What about name collisions? What happens if I import from different packages two types of the same
name? What happens if I import a type with the same name as a type in the current package? The rules
are simply:
1. You cannot import a type with the same name as a type in the current package. A non-* import
that conflicts is a compile error. A * import that conflicts will simply not import the type in
question.
2. Non-* imports take precedence over * imports:
import pelican.*;
import zebra.Manatee;
zebra.Manatee is imported, not pelican.Manatee (assuming it exists)
3. * imports do not import conflicting names:
import pelican.*;
import zebra.*;
If both pelican and zebra contain a type Manatee, neither Manatee is imported by these
statements.
the default package
If the package statement is omited from the top of a source file, the file belongs to the “default”
package, a nameless package that really only exists to make it easier to write trivial example code. In
practice, you shouldn’t use the default package, as you can’t import anything from it because it doesn’t
have a name.
static imports
Normally when using a static field, we qualify it with its class name:
Cat.foo
// static field foo of the class Cat
Because some class names are bothersomely long, Java allows static fields to be imported much like
classes themselves so that we can write a static field name without qualifying it by its class:
// import static field foo of the class Cat of the package pelican
import static pelican.Cat.foo;
The field Cat.foo can now be refered to as just foo in this file (at least, in any place in this file where no
other thing named foo interferes).
shadow fields
If a class inherits a field foo, it can declare its own field named foo. Instances of the class effectively have
two separate fields that just happen to share the same name. The new field “shadows” the inherited
field, meaning that the inherited field is inaccessible by name in the subclass.
Assume three classes, A inheriting from B inheriting from C, each declaring a field foo. C thus has one
foo, B has two, and A has three. Inside B, you can refer to the foo it inherits from C as super.foo, and
inside A you can refer to the foo it inherits from B as super.foo, but inside A you cannot refer to the foo
it inherits from C. If this is a problem, simply give the fields different names.
Shadowed fields are allowed simply because it’s sometimes nice to reuse generic names like message, x,
y, and the like. Shadowed fields are rarely worth this convenience and tend to just cause confusion.
Consider:
Cat c = new Cat();
Mammal m = c;
c.foo = 3;
// foo declared in Cat
m.foo = true;
// foo declared in Mammal
If Cat inherits from Mammal and both Cat and Mammal declare a public int field named foo, then a Cat
object has two separate int fields named foo. Above, which foo is refered to depends upon the compiletime types of the object expressions, c and m. So if you’re not cognizant in such scenarios, you can easily
end up referring to the wrong field.
(Be clear that a field that shadows another is truly a separate field that just happens to share the same
name; it needn’t have the same type or visibility.)
the instanceof operator
The instanaceof operator, not surprisingly, tests whether an object is an instance of some type and
returns true or false:
object instanceof type
…where object is an expression evaluating into a reference type instance and type is some reference
type (a class, an interface, or an array type). For example:
“hello” instanceof String
new Cat() instanceof Cat
new Cat() instanceof Mammal
// true
// true
// true
Notice though, that the above expressions are pointless because they will always return true. The
instanceof operator is only useful in cases where it might possibly return either true or false, e.g.:
void foo(Mammal m) {
if (m instanceof Cat) {…}
}
The above use makes sense because the reference m might hold a Cat or it might not.
Because the Java compiler knows the compile-time type of all expressions, it will reject a use of
instanceof that can’t possibly be true at runtime:
new Mammal() instanceof Cat
Assuming Cat inherits from Mammal, a Mammal object can’t possibly be an instance of Cat, so the
compiler won’t allow this operation.
initializer blocks and field initialization
Surprisingly, constructor code can actually be written outside of any constructor. This can be done in
two ways:

inside initializer blocks, which are pairs of {} in the class but outside any constructor or method

in the direct initialization of a field
For example:
class Cat {
public int lives = 9;
{…}
// direct initialization of a field
// an initializer block
public Hairball hb = new Hairball(); // direct initialization of
a field
public Cat() {…}
{…}
// another initializer block
}
(Note that the initialization of a field can use any expression that returns the proper type.)
The question then is, in what order does this code run and where? The way to think of it is that all of the
code outside the constructors gets rolled up, top-to-bottom, and placed in each constructor
immediately after its call to super() or this() (recall that the calls to super() or this() are often left
implicit).
What’s the point? Quite simply, it’s often more neat and compact to initialize a field where it’s declared
than inside the constructors. Initiazlier blocks aren’t used often, but they occasionally help us initialize
fields with code that’s too compilcated to put in just one expression.
Be clear that these features are really just conveniences. We could simply put the same code in the
constructors.
static initializer blocks and static field initialization
Static fields can be directly initialized like non-static fields, and static initializer blocks can be written in a
class like so:
static {…}
// a static initializer block
The static field initializations and static initializer blocks are run top-to-bottom when the class itself is
loaded. They are the only way to initialize the static fields of a class before the first instance of the class
is created.
assert
In the development process, we often want to include checks in our code to make sure things are going
as we expect. For this purpose, Java has assert statements:
assert condition : arguments;
assert condition;
// no arguments
When an assert executes, the condition is evaluated: if true, execution simply continues on, but if false,
an AssertException is thrown with arguments passed to its constructor. So for example:
assert isTuesday() : “Oh crap, it’s not Tuesday.”;
If isTuesday() returns false, then a new AssertException(“Oh crap, it’s not Tuesday.”) is thrown.
You might think that an assert is just a different way of writing:
if (!condition) throw new AssertException(arguments);
…but the special thing about assert statements is that they can be disabled by the JVM such that no
processing time is wasted doing these checks. During development, we sometimes want to run the code
with these checks turned on, but other times we want to turn them off. The assert mechanism allows us
to do so without having to keep separate versions of the code.
abstract classes
A class declared with the modifier abstract:
1) Cannot be instantiated.
2) Can include abstract methods.
Abstract methods you’ve already seen in interfaces: abstract methods specify a name, parameter types,
and a return type, but don’t specify an actual body.
If we can’t instantiate a class, what good is it? Well we can still inherit from it. When extending an
abstract class, you must provide actual implementations of all its abstract methods (just like you must
implement the methods of an interface).
We don’t strictly need abstract classes, but they can help codify the programmer’s intent in certain
circumstances.
interface methods are implicitly abstract and public
All methods in an interface are abstract and public, but these modifiers can be left implicit:
interface Hugh {
void foo();
}
// abstract public void foo();
Whether to explicitly write abstract public for each method is a matter of style.
interface inheritance
An interface can extend other interfaces:
interface Mitch extends James, Rose {…}
This means that Mitch includes all the methods included in both James and Rose. Consequently, any
Mitch object is also a valid James and Rose object:
Mitch m = new Laurie();
James j = m;
// assume Laurie implements Mitch
// a Mitch is a valid kind of James
When an interface extends other interfaces, name collisions are only a problem when two or more
methods share the same name and parameters but have different return types. For example:
int foo(char, String);
void foo(char);
int foo(char);
An interface can’t contain both the second and third foo here because a class can’t be overloaded with
two methods that differ only in their return type.
public static fields in interfaces
Oddly, an interface may contain public static fields. Just like the static fields of a class, static fields in an
interface have no real relationship with the interface that contains them; rather, the interface really just
serves as their containing namespace.
This allowance was added to Java simply because some people like the idea of organizing some public
static fields into interfaces rather than classes. It’s kinda dumb, really.
enums
In Java, an enum (short for “enumeration”) is simply a class with a fixed set of instantiations. Each
instantiation is given a name and referred to outside the enum as enum.instance. For example, we might
create an enum Season with four instances: WINTER, SPRING, SUMMER, and FALL (by convention, enum
instance names are all-caps because they are like constants). These instances are then referred to as
Season.WINTER, Season.SPRING, Season.SUMMER, and Season.FALL.
An enum is written:
enum name { instances; fields/methods/constructors}
This is just like a class but with the addition of the list of instances. (Also, an enum never has an extends
clause because all enums implicitly extend from java.lang.Enum.) So for example:
enum Season {
WINTER, SPRING, SUMMER, FALL; // 4 instances
// no fields, methods, or constructors
}
(Because the above enum has nothing after the instances, we could omit the trailing semi-colon.)
When an enum has one or more constructors, arguments to the constructor(s) are listed after each
instance:
enum Season {
WINTER(“cold”), SPRING(“warm”), SUMMER(“hot”), FALL(“cool”);
public String climate;
Season(String climate) {
this.climate = climate;
}
}
(Because all instances are created within the enum itself, it doesn’t really matter whether the
constructor is private, public, or default.)
The instances of an enum aren’t actually instances of that enum type itself: rather, each instance is its
own one-off subtype of the enum. This allows each enum instance to add its own members and override
its inherited methods:
enum Season {
WINTER {public void foo() {…}},
SPRING {public void foo() {…}},
SUMMER {public void foo() {…}},
FALL {public void foo() {…}};
}
Above, each instance is its own subtype of Season, and each implements its own method foo. Given the
above, we can write:
Season.WINTER.foo();
…but not:
Season s;
… // assign s some Season instance
s.foo(); // illegal
The compile-time type of s is Season, but the Season type itself has no foo method: Season’s instances
each have their own unrelated foo methods. To fix this we simply declare an abstract foo in Season:
enum Season {
WINTER {public void foo() {…}},
SPRING {public void foo() {…}},
SUMMER {public void foo() {…}},
FALL {public void foo() {…}};
public abstract void foo();
}
Now we can invoke foo via Season references because it is part of the type though each instance still
implements its own version of foo.
The last trick with enums is that we can use them as switch values:
Season s;
…
switch (s) {
case Season.WINTER:
…
break;
case Season.SUMMER:
…
break;
case Season.SPRING:
…
break;
case Season.FALL:
…
break;
}
generic methods and constructors
In Java, individual methods and constructors can be generic, even in classes which are not themselves
generic.