Development
After the wringer that is threads, this is a light read.
javac and java
The commands javac and java are used to compile and run java files. The command-line options that are available include -classpath (-cp), -d and -D. Multiple options and files must be separated by spaces.
The -classpath or -cp option are paths that will be searched for classes. The dot (.) indicates searching the current directory. Any subdirectories listed will be searched but not the directories above them. With packages, classpaths become trickier. The root of the package must be in a subdirectory that is in the classpath, making it visible. It is important to understand the relative and absolute paths. With JAR files, one must include the name at the end of the path.
The -d option tells the compiler where to put the class file that is generated. A compile error occurs if the destination directory does not exist. On the other hand, with a java file in a package, javac will build the package directories if none exist.
The -D option creates a system property with the syntax of name=value following immediately with no spaces in-between, eg -Dkey=property. Should the value have spaces, it should be enclosed with quotes. Java provides a java.util.Properties class to obtain information on the system as well as adding more user properties. Two methods, setProperty() and getProperty() facilitate setting and getting a property. setProperty() takes two strings for the name-value pair and getProperty() returns a string while either taking one string for the name value or two strings, a name value and a default value if the property does not exist (similar to setProperty()).
For java, exactly one class file is executed every time it is invoked, with no need for the extension. The argument declaration for the main method is no longer fixed to String[] args. The name of the string array can be changed and the var-args syntax may be used. String... x, String port[] and String[] ___ are all legal.
Static Imports
Static imports are a convenience-only feature that allows less typing. Beginning with the syntax: import static, which can only be used on static object references, constants (which are static and final) and static methods. Beware of ambiguously named static members, two classes may have a constant with the same name. If used, Java will not know which class to refer to.
Friday, 22 December 2006
Wednesday, 20 December 2006
SCJP Chapter 9
Threads
There are two ways to define, create and run threads. One is to extend from the Thread class, overriding the default run() method with another implementation. The other is to implement the Runnable interface, which only contains one method run(), and put in the run() code. With a Runnable implementor, a Thread instance needs to be created with the implementor in the argument, allowing the code to be threaded.
States
When a thread is created but not started using the start() method, it is in the new state. Once started, it will graduate to the runnable state and enters the runnable pool where threads wait for the scheduler to pick them and run them. The run order of runnable threads is not guaranteed. What is guaranteed is that each thread will start and each thread will run to completion. The running state is what all threads aspire to. However, a thread may be pulled off by the waiting, blocking or sleeping states (If the scheduler does not pull it off first). In which case, the thread would still be alive but not eligible for running. Once a thread's run() method is done, the thread is dead. And once the thread has been started, it can never be started again (Runtime exception).
Priorities
Thread priorities are positive integers ranging from 1 to 10, though it is better to use the constants Thread.MIN_PRIORITY, Thread.NORM_PRIORITY and Thread.MAX_PRIORITY. The scheduler guarantees that the thread running will always have a greater or equal priority to the highest priority threads in the runnable pool.
Synchronization
Race condition is a problem where multiple threads vie for access to a single object and in the process, corrupt the state of the object, producing data which is of no use to any of the threads.
The solution to this is to lock methods or code blocks so that only one thread can access the object. Only code that can change the object needs be synchronized. Threads trying to get the object's lock, but discover it has already been taken, is said to be blocked on the object's lock. They go into a pool for that object and wait until the lock is released, whereby any of them can get it. Care has to be taken that deadlock does not occur. Deadlock is when two threads are blocked, with each waiting for the other's lock.
Thread Interaction
wait(), notify() and notifyAll() are methods that deal with objects' locking status. These methods can be called on any objects, within a synchronized context. A thread cannot call these methods on an object unless it owns that object's lock. The thread that calls the wait() method on an object goes into a waiting room, i.e. sleeps, until the notify()/notifyAll() method is invoked on the same object. Thus wait() and notify() must be used on the same object.
There are two ways to define, create and run threads. One is to extend from the Thread class, overriding the default run() method with another implementation. The other is to implement the Runnable interface, which only contains one method run(), and put in the run() code. With a Runnable implementor, a Thread instance needs to be created with the implementor in the argument, allowing the code to be threaded.
States
When a thread is created but not started using the start() method, it is in the new state. Once started, it will graduate to the runnable state and enters the runnable pool where threads wait for the scheduler to pick them and run them. The run order of runnable threads is not guaranteed. What is guaranteed is that each thread will start and each thread will run to completion. The running state is what all threads aspire to. However, a thread may be pulled off by the waiting, blocking or sleeping states (If the scheduler does not pull it off first). In which case, the thread would still be alive but not eligible for running. Once a thread's run() method is done, the thread is dead. And once the thread has been started, it can never be started again (Runtime exception).
Priorities
Thread priorities are positive integers ranging from 1 to 10, though it is better to use the constants Thread.MIN_PRIORITY, Thread.NORM_PRIORITY and Thread.MAX_PRIORITY. The scheduler guarantees that the thread running will always have a greater or equal priority to the highest priority threads in the runnable pool.
Synchronization
Race condition is a problem where multiple threads vie for access to a single object and in the process, corrupt the state of the object, producing data which is of no use to any of the threads.
The solution to this is to lock methods or code blocks so that only one thread can access the object. Only code that can change the object needs be synchronized. Threads trying to get the object's lock, but discover it has already been taken, is said to be blocked on the object's lock. They go into a pool for that object and wait until the lock is released, whereby any of them can get it. Care has to be taken that deadlock does not occur. Deadlock is when two threads are blocked, with each waiting for the other's lock.
Thread Interaction
wait(), notify() and notifyAll() are methods that deal with objects' locking status. These methods can be called on any objects, within a synchronized context. A thread cannot call these methods on an object unless it owns that object's lock. The thread that calls the wait() method on an object goes into a waiting room, i.e. sleeps, until the notify()/notifyAll() method is invoked on the same object. Thus wait() and notify() must be used on the same object.
Tuesday, 19 December 2006
SCJP Chapter 8
Inner Classes
There are four types of inner classes, your 'regular joe' inner class, method-local inner class, anonymous inner class and the static nested class (which is not really an inner class at all).
Regular Inner Class
Beginning with the regular inner class, which is coded alongside the outer class's variable and method members, one needs to understand that the inner class has access to all of the outer class members. Even private ones. The outer class needs to create an instance of the inner class before it can begin accessing the inner class members. Also when calling from another class, the outer class need to be instantiated before one can instantiate the inner class:
MyOuter.MyInner myinner = new MyOuter().new MyInner();
When in the inner class, the object self-reference is this. The outer class reference is NameofOuterClass.this. The same modifiers (final, abstract, public, private, protected, strictfp and static - though that changes it into a static nested class) that can be applied to instance variables and methods can also be applied to inner classes.
Method-Local Inner Class
An inner class defined within a method, it requires instantiation (below the class definition) inside the method to be available for use. No other method or class can create the method-local inner class aside from the method which holds that class. Like regular inner classes, it can access the outer class members, however it cannot access the local variables inside the method containing it unless these variables are marked final. Local variables exist on the stack and within the method lifetime only. Only two modifiers for the method-local inner class, abstract or final.
Anonymous Inner Class
Nameless inner classes with two versions, regular and argument-defined.
The regular version has two flavors, creating a subclass of any class type and creating a class implementing an interface. Subclassing is used to override the methods of a superclass, however creating a new method is legal though there is no way to invoke it short of a compilation error. The syntax for both flavors is unusual:
class Popcorn {
public void pop() {
System.out.println("popcorn");}
}
interface Cookable{
public void cook();
}
class Food {
Popcorn p = new Popcorn(){
public void pop(){
System.out.println("anonymous popcorn");} };
Cookable c = new Cookable(){
public void cook(){
System.out.println("anonymous cookable implementor");} };
}
Note the semicolon indicating the end of the statement. The semicolon is not present in argument-defined anonymous inner classes as the definition is located in the argument portion of the method call.
Static Nested Classes
A class that is simply a static member of the enclosing class. Meaning it can be accessed without instantiating the enclosing class. Naturally being static, it can only access other static variables and methods. Syntax for it:
MyOuter.StaticInner sinner = new MyOuter.StaticInner();
Or within the enclosing class, MyOuter:
StaticInner sinner = new StaticInner();
There are four types of inner classes, your 'regular joe' inner class, method-local inner class, anonymous inner class and the static nested class (which is not really an inner class at all).
Regular Inner Class
Beginning with the regular inner class, which is coded alongside the outer class's variable and method members, one needs to understand that the inner class has access to all of the outer class members. Even private ones. The outer class needs to create an instance of the inner class before it can begin accessing the inner class members. Also when calling from another class, the outer class need to be instantiated before one can instantiate the inner class:
MyOuter.MyInner myinner = new MyOuter().new MyInner();
When in the inner class, the object self-reference is this. The outer class reference is NameofOuterClass.this. The same modifiers (final, abstract, public, private, protected, strictfp and static - though that changes it into a static nested class) that can be applied to instance variables and methods can also be applied to inner classes.
Method-Local Inner Class
An inner class defined within a method, it requires instantiation (below the class definition) inside the method to be available for use. No other method or class can create the method-local inner class aside from the method which holds that class. Like regular inner classes, it can access the outer class members, however it cannot access the local variables inside the method containing it unless these variables are marked final. Local variables exist on the stack and within the method lifetime only. Only two modifiers for the method-local inner class, abstract or final.
Anonymous Inner Class
Nameless inner classes with two versions, regular and argument-defined.
The regular version has two flavors, creating a subclass of any class type and creating a class implementing an interface. Subclassing is used to override the methods of a superclass, however creating a new method is legal though there is no way to invoke it short of a compilation error. The syntax for both flavors is unusual:
class Popcorn {
public void pop() {
System.out.println("popcorn");}
}
interface Cookable{
public void cook();
}
class Food {
Popcorn p = new Popcorn(){
public void pop(){
System.out.println("anonymous popcorn");} };
Cookable c = new Cookable(){
public void cook(){
System.out.println("anonymous cookable implementor");} };
}
Note the semicolon indicating the end of the statement. The semicolon is not present in argument-defined anonymous inner classes as the definition is located in the argument portion of the method call.
Static Nested Classes
A class that is simply a static member of the enclosing class. Meaning it can be accessed without instantiating the enclosing class. Naturally being static, it can only access other static variables and methods. Syntax for it:
MyOuter.StaticInner sinner = new MyOuter.StaticInner();
Or within the enclosing class, MyOuter:
StaticInner sinner = new StaticInner();
Monday, 18 December 2006
SCJP Chapter 7
The last of the big chapters, Chapter 7 deals with collections and generics.
Collections
There are four interfaces (Lists, Sets, Maps and Queues) encompassing the eleven classes that implement them (ArrayList, Vector, LinkedList; HashSet, LinkedHashSet, TreeSet; Hashtable, LinkedHashMap, HashMap, TreeMap and PriorityQueue). Basically have to know what methods are available and how they are used, most of which is in the code done for the chapter.
One thing to note is the equals() and hashCode() methods and their contracts. The methods come from the Object class and thus is present in every class afterwards.
The default equals() consider two objects equal if the two references point to the same object, thus to get proper equality of objects (datawise), the equals() need to be overridden with the desired implementation. Otherwise, Sets will allow duplicates in and objects with the default cannot be used as keys in a hashtable. Read the contract (reflexive, symmetric, transitive, consistent and that for any non-null x x.equals(null) will return false) and watch out for questions on this and below.
With equals() overridden, hashCode() must be overridden as well. The default will allow duplicates in a Set. The contract for this one is a bit trickier:
For: x.equals(y) == true
Required: x.hashCode() == y.hashCode()
For: x.hashCode() != y.hashCode()
Required: x.equals(y) == false
For: x.hashCode() == y.hashCode()
Allowed but not required: x.equals(y)
For: x.equals(y) == false
Allowed but not required: No hashCode requirements
Generics
Generics enforce compile-time type safety on Collections (and classes and methods declared with generic type parameters). A generic type (eg. Animal) can accept subtypes (eg. Dog, Cat, Wolf). One advantage to type safety is no cast is required when the element type is known. Passing type-safe code into non-type code is dangerous, the compiler will compile it under duress (giving out a warning).
Polymorphism applies to base type, not generic type parameter:
Right: List aList = new ArrayList();
Wrong: List aList = new ArrayList();
The generic type must always be the same for declaration and initialization. Another problem is an ArrayList will not accept any collections of an Animal subtype. This requires further reading.
Wildcard ? allows any type to be assigned, but nothing can be added to the list referred to as List.
With the extends keyword, List ? extends Animal will be able to assign any subtype of List and typed for or anything that extends Animal (either subclasses or implementations), but still nothing can be added.
With the super keyword, List ? super Animal will be able to assign any subtype of List and typed for or any supertype of Dog, and allows a restricted way of adding to the collection.
One last thing to note is the use of generic typing in classes and methods, this allows type-checking to be built into the classes and methods, does away with the need to cast and the need to create several subclasses for different types.
Collections
There are four interfaces (Lists, Sets, Maps and Queues) encompassing the eleven classes that implement them (ArrayList, Vector, LinkedList; HashSet, LinkedHashSet, TreeSet; Hashtable, LinkedHashMap, HashMap, TreeMap and PriorityQueue). Basically have to know what methods are available and how they are used, most of which is in the code done for the chapter.
One thing to note is the equals() and hashCode() methods and their contracts. The methods come from the Object class and thus is present in every class afterwards.
The default equals() consider two objects equal if the two references point to the same object, thus to get proper equality of objects (datawise), the equals() need to be overridden with the desired implementation. Otherwise, Sets will allow duplicates in and objects with the default cannot be used as keys in a hashtable. Read the contract (reflexive, symmetric, transitive, consistent and that for any non-null x x.equals(null) will return false) and watch out for questions on this and below.
With equals() overridden, hashCode() must be overridden as well. The default will allow duplicates in a Set. The contract for this one is a bit trickier:
For: x.equals(y) == true
Required: x.hashCode() == y.hashCode()
For: x.hashCode() != y.hashCode()
Required: x.equals(y) == false
For: x.hashCode() == y.hashCode()
Allowed but not required: x.equals(y)
For: x.equals(y) == false
Allowed but not required: No hashCode requirements
Generics
Generics enforce compile-time type safety on Collections (and classes and methods declared with generic type parameters). A generic type (eg. Animal) can accept subtypes (eg. Dog, Cat, Wolf). One advantage to type safety is no cast is required when the element type is known. Passing type-safe code into non-type code is dangerous, the compiler will compile it under duress (giving out a warning).
Polymorphism applies to base type, not generic type parameter:
Right: List
Wrong: List
The generic type must always be the same for declaration and initialization. Another problem is an ArrayList
Wildcard ? allows any type to be assigned, but nothing can be added to the list referred to as List.
With the extends keyword, List ? extends Animal will be able to assign any subtype of List and typed for
With the super keyword, List ? super Animal will be able to assign any subtype of List and typed for
One last thing to note is the use of generic typing in classes and methods, this allows type-checking to be built into the classes and methods, does away with the need to cast and the need to create several subclasses for different types.
Wednesday, 13 December 2006
SCJP Chapter 6
Of strings, I/O, formatting and parsing.
Starting with Strings, which are immutable, there are also mutable StringBuffers and StringBuilders (added as of Java 5). The latter two are the same except that StringBuilders are faster because they are not synchronized (and therefore thread-unsafe). Important stuff to remember; know the methods of all three classes, Strings have length() method and arrays have length variable, know the differences between Strings and the other two, and beware chained methods.
For I/O, there are six classes to contend with; File, FileReader, BufferedReader, FileWriter, BufferedWriter and PrintWriter. Know about File's existential and creation problems. Know about wrapping classes up to enable higher-level usage and which ones are legal. Be aware that PrintWriter, as of Java 5, can now be used by wrapping a File or String.
Serialization is the saving of state of objects into a file for them to be read later (deserialization), reconstituting back the class objects. ObjectOutputStream and ObjectInputStream with their respective writeObject() and readObject() methods are the ones that allow the saving and loading of objects. Being higher-level classes, they need to be wrapped around the lower-level classes FileOutputStream and FileInputStream. A few things to note: all the classes to be serialized must either implement the Serializable interface or have a superclass that did. Any non-serializable object will cough up a runtime exception by the JVM. The transient keyword tells the serialization to skip a variable, not saving it. However, if there is need for that variable's state to be saved, one can override the writeObject() and readObject() to include it (the overriding code has to be in the class to be serialized). Constructors do not run for serialization, however a non-serializable superclass will run its constructor, potentially changing inherited variables. Lastly, serialization does not work for statics.
To work with dates, numbers and currencies, these classes are available: Date, Calendar and Locale from java.util.*, DateFormat and NumberFormat from java.text.*. Calendar, DateFormat and NumberFormat use factory methods to initialize. Knowing how to use all the classes is essential.
The last section of Chapter 6 deals with finding stuff (through regular expressions, of which there are two ways, pattern matching and searching using the Scanner class), tokenizing stuff (through String.split and using the Scanner class) and formatting stuff (Using printf and format, which are identical, C-style).
Chapter 7, the return of the Generics!
Starting with Strings, which are immutable, there are also mutable StringBuffers and StringBuilders (added as of Java 5). The latter two are the same except that StringBuilders are faster because they are not synchronized (and therefore thread-unsafe). Important stuff to remember; know the methods of all three classes, Strings have length() method and arrays have length variable, know the differences between Strings and the other two, and beware chained methods.
For I/O, there are six classes to contend with; File, FileReader, BufferedReader, FileWriter, BufferedWriter and PrintWriter. Know about File's existential and creation problems. Know about wrapping classes up to enable higher-level usage and which ones are legal. Be aware that PrintWriter, as of Java 5, can now be used by wrapping a File or String.
Serialization is the saving of state of objects into a file for them to be read later (deserialization), reconstituting back the class objects. ObjectOutputStream and ObjectInputStream with their respective writeObject() and readObject() methods are the ones that allow the saving and loading of objects. Being higher-level classes, they need to be wrapped around the lower-level classes FileOutputStream and FileInputStream. A few things to note: all the classes to be serialized must either implement the Serializable interface or have a superclass that did. Any non-serializable object will cough up a runtime exception by the JVM. The transient keyword tells the serialization to skip a variable, not saving it. However, if there is need for that variable's state to be saved, one can override the writeObject() and readObject() to include it (the overriding code has to be in the class to be serialized). Constructors do not run for serialization, however a non-serializable superclass will run its constructor, potentially changing inherited variables. Lastly, serialization does not work for statics.
To work with dates, numbers and currencies, these classes are available: Date, Calendar and Locale from java.util.*, DateFormat and NumberFormat from java.text.*. Calendar, DateFormat and NumberFormat use factory methods to initialize. Knowing how to use all the classes is essential.
The last section of Chapter 6 deals with finding stuff (through regular expressions, of which there are two ways, pattern matching and searching using the Scanner class), tokenizing stuff (through String.split and using the Scanner class) and formatting stuff (Using printf and format, which are identical, C-style).
Chapter 7, the return of the Generics!
Monday, 11 December 2006
SCJP Chapter 5
This chapter covers flow control; conditional if and switch statements, looping do-while, while and for statements, even exceptions and assertions.
What's there to know about the if statement? It encompasses the if, if-else and if-elseif constructs. Things to beware of: unbraced statements (only one is in the if construct), lost else clause (belongs to the innermost if statement), misleading indentation and illegal expressions (must resolve to a boolean).
switch statements can simulate multiple if statements. A switch's expression takes an enum or variables that can be implicitly promoted to an int (char, byte, short, int) and it can use boxing. The value for a case must be a compile time constant. Cases with the same value will not compile. Neither will a case with a value larger than the type variable in the switch. Without break statements, the code will run top down starting from the execution entry point (the first case constant matched). This dropping down is called "fall-through". default is executed when there is no match and its location does not matter; end, middle or top.
do-while and while loops are the same except for these differences: do-while must run at least once (while may not run at all) and construction is different (note do-while ends in a semicolon).
for loops has a second structure as of Java 5. The "enhanced for loop", a.k.a for-each and for-in, makes it easier to iterate through arrays and collections. The for-each loop has two components (declaration and expression) versus the three components of a for loop (initialization, condition and iteration).
break and continue statements are used to halt a loop, either the entire loop (break) or the current iteration (continue). Labels are added when nested loops are involved, they enable the break/continue statement to apply to the labelled loop, not necessarily the loop the statement is in.
Exceptions are handled by coding in try and catch blocks. A finally block can be added and will always run after the try block, regardless whether an exception is thrown or not. A try block cannot be used on its own, either a catch or a finally block must be present. An exception must find a match with a catch block or its supertype. A method must declare an exception if it throws (directly or indirectly) the exception otherwise it must handle the thrown exception. This is the "handle or declare" rule. Runtime exceptions are exempt from this rule. Need to know which exceptions are thrown by the Java Virtual Machine (JVM exceptions) and which are thrown explicitly by application programmers (Programmatic exceptions).
Assertions test assumptions during development. Their default mode is off and must be specifically turned on. If an assertion is true, the code runs smoothly. If an assertion is false, an AssertionError is thrown, shutting down the application. Assertions have two versions, both versions have an expression that results in a boolean value, the difference is a second expression to be added to the stack for debugging purposes. Inappropriate uses of assertions are validating public method arguments, validating command-line arguments and assert expressions that cause side effects. Appropriate uses of assertions are validating private method arguments and, even in public methods, checking for cases that are not supposed to happen.
What's there to know about the if statement? It encompasses the if, if-else and if-elseif constructs. Things to beware of: unbraced statements (only one is in the if construct), lost else clause (belongs to the innermost if statement), misleading indentation and illegal expressions (must resolve to a boolean).
switch statements can simulate multiple if statements. A switch's expression takes an enum or variables that can be implicitly promoted to an int (char, byte, short, int) and it can use boxing. The value for a case must be a compile time constant. Cases with the same value will not compile. Neither will a case with a value larger than the type variable in the switch. Without break statements, the code will run top down starting from the execution entry point (the first case constant matched). This dropping down is called "fall-through". default is executed when there is no match and its location does not matter; end, middle or top.
do-while and while loops are the same except for these differences: do-while must run at least once (while may not run at all) and construction is different (note do-while ends in a semicolon).
for loops has a second structure as of Java 5. The "enhanced for loop", a.k.a for-each and for-in, makes it easier to iterate through arrays and collections. The for-each loop has two components (declaration and expression) versus the three components of a for loop (initialization, condition and iteration).
break and continue statements are used to halt a loop, either the entire loop (break) or the current iteration (continue). Labels are added when nested loops are involved, they enable the break/continue statement to apply to the labelled loop, not necessarily the loop the statement is in.
Exceptions are handled by coding in try and catch blocks. A finally block can be added and will always run after the try block, regardless whether an exception is thrown or not. A try block cannot be used on its own, either a catch or a finally block must be present. An exception must find a match with a catch block or its supertype. A method must declare an exception if it throws (directly or indirectly) the exception otherwise it must handle the thrown exception. This is the "handle or declare" rule. Runtime exceptions are exempt from this rule. Need to know which exceptions are thrown by the Java Virtual Machine (JVM exceptions) and which are thrown explicitly by application programmers (Programmatic exceptions).
Assertions test assumptions during development. Their default mode is off and must be specifically turned on. If an assertion is true, the code runs smoothly. If an assertion is false, an AssertionError is thrown, shutting down the application. Assertions have two versions, both versions have an expression that results in a boolean value, the difference is a second expression to be added to the stack for debugging purposes. Inappropriate uses of assertions are validating public method arguments, validating command-line arguments and assert expressions that cause side effects. Appropriate uses of assertions are validating private method arguments and, even in public methods, checking for cases that are not supposed to happen.
Thursday, 7 December 2006
SCJP Chapter 4
A nice small focused chapter on operators. It starts with the assignment operator "=" and its compound kin (+=, -=, *=,/=). With the compound operators, the right side of the = will always be evaluated first, something to take note of.
Relational operators (<, <=, >, >=, == and !=) are relatively straightforward. Comparison of characters use their Unicode value. Equality (==) for objects only check whether variables point to the same object, not whether two objects are the same.
The instanceof operator is used with object reference variables and checks for type. It returns true if the variable IS-A particular class (or superclass) or interface (indirectly or directly implemented). It is legal to test null eg. null instanceof String, but it will always return false. It will not compile if testing a class with no relationship to the other class eg. Dog instanceof Cat. Last note: array IS-A Object.
Arithmetic operators (+, -, *, /, %) are simple to understand. The remainder operator (%) divides two operands and returns the remainder as the result. The + operator can be used to concatenate strings and care must be taken to verify which role it is in. If either one of the operands are strings then it concatenates, otherwise it is a normal addition operator.
Increment and decrement operators (++ and --) have two modes, prefix and postfix. Below shows a code example. These operators can not be used on final variables, doing so results in a compiler error.
int x = 1;
if(++x == 2) // prefix, x is incremented to 2 and
{ return true; }// the expression is evaluated,
// returning true
if(x++ == 2) // postfix, the expression is
{ return true; }// evaluated first, returning true
// then x is incremented to 3
The conditional operator is a ternary operator (with three operands). It is a miniature if statement. The structure is as below.
x = (boolean expression) ? value if true : value if false
Logical operators (&, |, ^, !, &&, ||) are the last topic in the chapter. Short-circuit operators (&& and ||) evaluate up to the operand they need to make a conclusive decision. AND (&&) will return false upon the first operand resolving to false, and does not bother to check any other operands after that. Similarly, OR (||) will return true for the first operand resolving to true.
The non-short-circuit operators (& and |) evaluate all the operands. This is a key point to remember. XOR (^) returns true when EXACTLY one operand is true. The ! operator returns the opposite of the boolean value.
Onto chapter 5, where flow control rules.
Relational operators (<, <=, >, >=, == and !=) are relatively straightforward. Comparison of characters use their Unicode value. Equality (==) for objects only check whether variables point to the same object, not whether two objects are the same.
The instanceof operator is used with object reference variables and checks for type. It returns true if the variable IS-A particular class (or superclass) or interface (indirectly or directly implemented). It is legal to test null eg. null instanceof String, but it will always return false. It will not compile if testing a class with no relationship to the other class eg. Dog instanceof Cat. Last note: array IS-A Object.
Arithmetic operators (+, -, *, /, %) are simple to understand. The remainder operator (%) divides two operands and returns the remainder as the result. The + operator can be used to concatenate strings and care must be taken to verify which role it is in. If either one of the operands are strings then it concatenates, otherwise it is a normal addition operator.
Increment and decrement operators (++ and --) have two modes, prefix and postfix. Below shows a code example. These operators can not be used on final variables, doing so results in a compiler error.
int x = 1;
if(++x == 2) // prefix, x is incremented to 2 and
{ return true; }// the expression is evaluated,
// returning true
if(x++ == 2) // postfix, the expression is
{ return true; }// evaluated first, returning true
// then x is incremented to 3
The conditional operator is a ternary operator (with three operands). It is a miniature if statement. The structure is as below.
x = (boolean expression) ? value if true : value if false
Logical operators (&, |, ^, !, &&, ||) are the last topic in the chapter. Short-circuit operators (&& and ||) evaluate up to the operand they need to make a conclusive decision. AND (&&) will return false upon the first operand resolving to false, and does not bother to check any other operands after that. Similarly, OR (||) will return true for the first operand resolving to true.
The non-short-circuit operators (& and |) evaluate all the operands. This is a key point to remember. XOR (^) returns true when EXACTLY one operand is true. The ! operator returns the opposite of the boolean value.
Onto chapter 5, where flow control rules.
Wednesday, 6 December 2006
SCJP Chapter 3
A lot of information in this one, a 'foundation' chapter indeed.
Starting off with a look into assigning primitives and object references, which then necessitates casting types to the correct one. Implicit casts are done when there is no loss of data, conversely explicit casts are needed when data is at risk of being truncated.
A brief explanation on scope and lifetimes of variables followed by default initialization of instance and local variables. Watch out for shadow variables that exist in their own scope and override any other mimics.
Arrays: declaration, construction and initialization. 1-D, 2-D or n-D; a lot of logic needed for this one.
Wrapper classes for the primitives, enabling them to used as objects. Autoboxing is a new feature in Java 1.5, and is involved when overloading methods with wrappers. Overload paths - widening, autoboxing and var-args, in that order. Beware combination questions.
Lastly is garbage collection. Once an object has no references to it, it is ripe for collection. A request for garbage collection may be made but execution is not guaranteed. Similarly, the finalize method is also not guaranteed. If finalize runs, it does so only once for an object and may save the object from deletion.
Onto chapter 4!
Starting off with a look into assigning primitives and object references, which then necessitates casting types to the correct one. Implicit casts are done when there is no loss of data, conversely explicit casts are needed when data is at risk of being truncated.
A brief explanation on scope and lifetimes of variables followed by default initialization of instance and local variables. Watch out for shadow variables that exist in their own scope and override any other mimics.
Arrays: declaration, construction and initialization. 1-D, 2-D or n-D; a lot of logic needed for this one.
Wrapper classes for the primitives, enabling them to used as objects. Autoboxing is a new feature in Java 1.5, and is involved when overloading methods with wrappers. Overload paths - widening, autoboxing and var-args, in that order. Beware combination questions.
Lastly is garbage collection. Once an object has no references to it, it is ripe for collection. A request for garbage collection may be made but execution is not guaranteed. Similarly, the finalize method is also not guaranteed. If finalize runs, it does so only once for an object and may save the object from deletion.
Onto chapter 4!
Tuesday, 5 December 2006
Blackout
Power went out for the whole building around 3 o'clock. It seems that TNB (electric company) is to blame. Hopefully, by tomorrow it will be resolved.
Monday, 4 December 2006
SCJP Chapter 2
Overriding and overloading, potential mix-up. Overriding methods to replace the original code with something different. Overloading methods to increase the number of methods available.
Constructors and their (default) construction. No-arg constructors, particularly default ones, are needed else beware of pitfalls in the super callers.
A brief look on static, static methods cannot access non-static variables and methods.
Misreading questions is hazardous to one's answers.
Constructors and their (default) construction. No-arg constructors, particularly default ones, are needed else beware of pitfalls in the super callers.
A brief look on static, static methods cannot access non-static variables and methods.
Misreading questions is hazardous to one's answers.
SCJP Chapter 1
Dealing with access modifiers and visibility of classes, methods and variables. default (package), public, private, protected are the access modifiers with final and static thrown into the mix.
Shows correct declaration forms, especially for interfaces and abstract classes. Warns of 'compilable' questions.
Shows correct declaration forms, especially for interfaces and abstract classes. Warns of 'compilable' questions.
First day
The room is fine, comfortable for one person working. The table takes up a third of the space but on the upside, there's more space to place stuff down. Two extra chairs here that won't see much use.
For two persons, will need to replace the table with a smaller one or two. Would be a tighter fit at that time.
Very quiet environment, good for concentration and focus.
For two persons, will need to replace the table with a smaller one or two. Would be a tighter fit at that time.
Very quiet environment, good for concentration and focus.
Tuesday, 28 November 2006
Subscribe to:
Posts (Atom)