Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
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.