Sun Certified Java Programmer Pre-Exam Essentials

©1999, 2000, 2002 Dylan Walsh.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being the disclaimer, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
The exam objectives below are quoted from Sun Microsystems website. Also there are parts quoted from the Javadocs, also the property of Sun Microsystems.


No claims are made about the accuracy of this document and no responsibility is taken for any errors.
If you find any errors contact the maintainer of the site where you found this document, or better still submit a newer version with a correction.
The original author cannot control, and may not be aware of, derived versions of this document.


The purpose of this document is to provide a basis for revising for the Sun Certified Programmer examinations, not to teach the Java language or the topics required for the exam. It is designed to collect all the essential information you need to retain, in one place, and is designed for someone who has already finished their own study and is about to take the exam.

This version is written according to the objectives of the "Sun Certified Programmer For Java™ 2 Platform 1.4" exam. For those taking the 1.2 exam, the objectives no longer required for 1.4 have been retained towards the end of this document. The sections have "1.2 Exam Only" at the start of their titles. I would recommend doing the 1.4 exam instead, as it is more current and the only major new subject is assertions.

Aside: There is no 1.3 exam, as such. The 1.1 exam exam was replaced by a Java2 exam, which at the time seemed like it would suffice for all Java2 releases. However in September 2002 Sun released seperate 1.2 and 1.4 exams. The 1.2 syllabus is for 1.2 and 1.3.

Thank you to everyone who has emailed me with compliments and/or corrections for previous versions of this document, it is appreciated. On the other hand, if you have questions on the exam or Java, I am afraid I do not have time to answer all of these, so please try either the certification discussion groups on the web, or the Java newsgroups.

Having obtained my certification, I have neither time or reason to maintain this document in the future and keep it current with changes in the exam. Rather than let it stagnate, I have decided to release it as a resource to the Java certification community under the Free Documentation License. This will allow others to change, correct, expand and update it. The accompanying XML file has more information on how to do this.

If you are just starting to prepare for the exam, then I recommend the following steps:

  1. Buy a good certification book, read the chapters, and do all of the example questions and exercises. This is advisable even if you are already experienced in Java. According to Barry Boones book, Java instructors have failed this exam. Apparently, many people have to take the exam more than once. For experienced programmers, there are things which are examined that you may not encounter in everyday programming, or would lookup in the Javadocs as needed. On the other hand, there are many things experienced developers ought to know but often do not. This exam is regarded as being more difficult than most other certifications.
  2. The books I used to study for the exam were versions of the Roberts, Heller et al book. and the book by Barry Boone. You can find more details on the latest versions of these on the web, and there are more recent books which may be worthwhile.
    You don't need to buy both, but I found that a good way to study was to do one chapter from each every evening. I did chapters on different topics, which made it more interesting and meant that I revised the same topic later in the other book.
  3. There are courses available, which I cannot comment on, as I studied for the exam without sitting any courses. While these may be worthwhile, I suggest you still follow step 1, as they may be general Java courses and not cover some of the specific issues which come up in the exam.
  4. When you are finished studying, do any and all mock exams available. In addition to those which come with the books, there many available online and you can find lists of links to them on some of the websites below. Do not sit the exam until you are getting marks comfortably above the pass level. I suggest studying until you get atleast 10% more in practice exams than the minimum required for the real exam. A habit you may find useful is when you are marking yourself, for every question you get wrong, hit yourself over the head with a baseball bat... Just kidding, for every question you get wrong, find out what the right answer is, and why, and write that down in a notebook for future revision. Make sure you do the free Sun sample questions as they most accurately reflect the exam.
  5. Read this document again the night, or morning, before you take the exam.

Useful links

Suns own certification site, has FAQs, objectives and sample questions:

Marcus Greens Site has exams, FAQs, tutorials:

A discussion forum:

The JavaRanch, has a nice rules exam, and lots of cows:

More links:

The Exam

Here are just a few comments on the exam itself. Firstly, time should not be a problem, as you should have time to answer all the questions, review them, and possibly be able to leave early. It will really be an issue of whether you know/understand what is required. That said, as with any exam, do not get bogged down in any one question, mark it and come back to it later if it is taking too long.

Generally, the questions in the real exam are a lot better than the mock exams which are going around: They less ambiguously worded, the sample code is usually short, and the slant of the questions is towards whether you understand the principles rather than whether you have memorized a lot of methods etc. The majority of the questions are on the core language syntax rather than its APIs. Of course, you do need to be familiar with the APIs mentioned in the objectives to get a pass mark.

About This Document

This document is structured around the objectives for the exams. For each objective, I have attempted to list all the bare-bones information you may need to remember for the exam.

The "1.2 Exam Only" sections are based on a subset of the 1.1 objectives with additions where needed, as they were taken from an older version of this document. This may seem strange but they provide good information for the 1.2 exam. At the time, the objectives for the Java 1.1 exam were more detailed and descriptive than those for the Java 2 exam, but the exams were very similar. I took the 1.2 exam (it was called just "Sun Certified Programmer For Java™ 2 Platform" at the time) in 1999, and released the first version of this document afterwards.

Declarations and Access Control

Write code that declares, constructs and initializes arrays of any base type using any of the permitted forms both for declaration and for initialization.

Declaring arrays:

For example,use either

	int[] x;


	int x[];

Both are legal positions for the brackets. To declare a multidimensional array, use multiple sets of brackets, e.g. int i[][]; declares a two dimensional array. Note that, for example, int[]i[]; is also legal, and means the same thing. In Java, multidimensional arrays can be "not square" i.e. they are just arrays of arrays, and each of those constituent arrays can be of a different size.

Construct arrays:

Arrays are objects. Use the new keyword to construct them. For example having declared an array i:

int[] i;

you then construct the array and assign it to i as follows:

i = new int[10];

The size of the array is in the brackets (in this example 10). It is common to do both operations in one statement:

int i[] = new int[10];

To initialize an array using loop iteration:

An example:

	int array[] = new int[15];
	for(int j=0; j<array.length;j++){

Write code to initialize an array using the combined declaration and initialization format:

An example:

char c[]= new char[] {'a','b','c','d','e'};

or you can use

char c[]= {'a','b','c','d','e'};

Declare classes, nested classes, methods, instance variables, static variables and automatic (method local) variables making appropriate use of all permitted modifiers (such as public, final, static, abstract etc.). State the significance of each of these modifiers both singly and in combination and state the effect of package relationships on declared items qualified by these modifiers.

This is fundamental Java. If you are unsure of this topic, see a textbook.

A good but technical reference for these kinds of issues is the Java Language Specification (not for beginners). It is available at the following URL:

Some terms and their synonyms:

Scope/Visibility: Where something can be seen / is accessable from.

Nested/Inner Class: A class whose code sits inside the body of another 'outer' class. It exists 'inside' the class in that it can see the private methods and variables.

Instance/Member: Means the method/variable/nested class belongs to each object which is an instance of the class. The term 'a member' is often used to refer to both methods and variables of this type. Cannot be accessed by statically i.e. without having an instance of the class.

Class/Static: The method/variable/nested class belongs to the class as opposed to the instances of the class. Can be used without creating an instance of the class, but static methods/nested class cannot use instancts variables/methods.

Local/Automatic variable: A variable which is declared within a method or as a parameter to the method. Cannot be seen outside the method.

Scoping Types

"default" or "friendly" - this is where no modifier is used. Means something is only visible to classes in the same package.

protected - can only be accessed by classes in the same package or subclasses of this class. This frequently surprises experienced developers, especially those with a prior backround in the mutant hybrid of object orientated programming and assembly language known as 'C++' :-) To fair the name is misleading, as it sounds like it restricts access, where as it in fact it adds subclasses outside the package to the list of things that can access the item in question, as compared to using "default" access.

public - can be accessed by any other class.

private - can only be accessed from inside the class.

Declare classes using the modifiers public, abstract or final:

public - is visible outside of its package. Without this modifier, the class cannot be accessed outside its package.

abstract - cannot be instantiated, is allowed to contain abstract methods.

final - cannot be subsclassed.

Using the modifiers private, protected, public, static, final, native or abstract:

private - can only be accessed from inside the class. Private members are not inherited by subclasses. Inner classes can be declared private.

protected - can only be accessed by classes in the same package or subclasses of this class.

public - can be accessed by any other class.

static - belongs to the class rather than any particular instance of the class. For variables, effectively, there is just one copy of this variable for all instances of the class, and if an instance changes the value, the other instances see that new value. For methods, it means the method can be called without having created an instance, but within the methodd you cannot use the this keyword, or refer to instance variables and methods directly (without creating an instance and referring to the variable/class in that instance). For inner classes, it means they can be instantiated without having an instance of the enclosing class, but as with static methods, the methods of the inner class cannot refer to instance variables or methods of the enclosing class directly.

final - cannot be changed. Variables are constants, methods cannot be overridden, classes cannot be subclassed. Since Java1.1 you can declare the variable without assigning a value. Once you assign a value, you cannot change it. The are known as 'blank' finals, are frequently used for things like constants you wish to initialise from a configuration file.

native - a method which is not written in java and is outside the JVM in a library.

abstract - a method which is not implemented. Must be implemented in a subclass if that subclass is to be 'concrete' and allow people to instantiate it.

Nested Classes

To define a non-static nested class either in a class or method scope:

Place the class definition (for the nested class) inside another class definition (the outer class) or a method.

To define, in method scope, an anonymous nested class that implements a specified interface:

An anonymous nested class is defined where is it instantiated (in a method). An anonymous nested class must either implement an interface or extend a class, but the implements or extends keywords are not used. For example the following line causes the method to return an object which is an instance of an anonymous nested class:

return new SomeClass() { /*body of the anonymous class goes here*/ };

You might like to think of an anonymous nested class as part of a really long new statement, which happens to contain a class definition, and which is why it has a ";" at the end. The following example calls someMethod(), passing an instance of the anonymous nested class:

someMethod(new SomeClass() { /*body of the anonymous class goes here*/ });

In both cases SomeClass() is not the name of the anonymous class (anonymous means it has no name) rather is it the name of the class that you are extending or the interface you are implementing. These classes cannot define a constructor, as they do not have a name that you can use to declare the constructor method. If SomeClass() is a class, the default constructor of that class is called, if you want to use a non-default constructor instead, you supply arguments e.g.:

return new SomeClass(12) { /*body of the anonymous class goes here*/ };

will call the SomeClass constructor which takes an int.

Write code in a non-static method of the outer class to construct an instance of the nested class.

Inner x = new Inner(); constructs an instance of Inner where Inner is a nested class defined in the current class.

Write code to construct an instance on a nested class where either no this object exists, or the current this object is not an instance of the outer class.

You must create an instance of the outer class first. Given a class, Outer, containing a nested class Inner:
Outer.Inner y = new Outer().new Inner();

The above creates an instance of Inner called y, but it had to construct an instance of Outer first. The following example creates the Outer instance on a separate line, the syntax in the second line is the one you use when you already have an instance of the outer class.

Outer x = new Outer();
Outer.Inner y = Inner();
If Inner is static, you can use:
Outer.Inner I= new Outer.Inner();

State which variables and methods in enclosing scopes are accessible from methods of the inner class.

A non-static inner class has access to all member variables and methods of the containing class. If the inner class is defined inside a method, it has access to those method (a.k.a. automatic or local) variables which are declared final, in addition to the above.

A static inner class is restricted in the same way as a static method: it cannot refer to instance variables and methods of the containing class directly (without creating an instance and referring to the variable/class in that instance).

For a given class, determine if a default constructor will be created and if so state the prototype of that constructor.

The default constructor takes no arguments e.g. classname() where classname is the name of you class. A default constructor is automatically created only if you do not create any constructors in your class. If you create a non-default constructor (i.e. one that takes an argument), then you may have to create a default one yourself, if you want there to be one.

All constructors call the default constructor of its parents class (if there is one), and so on up the hierarchy to Object, unless you specify a different constructor using super(...) (to use a constructor in the parent) or this(...). If you use such calls, they must be the first thing in the constructor.

Flow Control, Assertions and Exception Handling

Write code using if, and switch statements and identify legal argument types for these statements.

Consult a text book for the basics of these, if unsure. If you do know the basics, here are a few things to watch out for:

Number 1
Be wary where if is used without braces, and with an else statement. This is referred to as the dangling else problem, and occurs in C and C++ aswell. The following example is from Boone (p.154):

if (result >=0)
	if (result > 0)		

The indentations are misleading. It might appear that the above code will print "positive" if result is greater than 0, "negative" if is less than zero, and print nothing if result is equal to 0. In fact, it will print "positive" if result is greater than 0, it will print nothing if is less than zero, and print "negative" if result is equal to 0. This is because the else statement belongs to the second if statement, and only executes if result >=0 but not if result>0, in other words if result equals 0. Using braces (curly brackets) would fix this and is the best practice if you want readable code that is not potentially misleading. The following indentation shows the real relationship, of course the compiler doesn't care about indentation:

if (result >=0)
	if (result > 0)

A good practice is to use blocks with all your if and else statements to avoid this ambiguity.

Number 2
After switch/case construct has executed the correct case, it will continue to execute all those after it (including default: if it is there, after the selected case) on to the end of the switch/case block, unless you put in a break, return or throw statement. This is referred to as "falling through". For example:

switch (a) 
	case 1:
	case 2:
	case 3:
		System.out.println("some other number");

If a is 1, this will print "one", "two", "three" and then "some other number" in succession. If a is 3, this will print "three" and then "some other number".

In some cases, this kind of behaviour might desirable, but here it isn't. This block of code fixes it:

switch (a) {
	case 1:
	case 2:
	case 3:
		System.out.println("some other number");

Note that in switch blocks, it is legal to put default: anywhere in the block, it doesn't have to be the last one.

Number 3
The argument for an if() statement must be a boolean or an expression which evaluates to a boolean. A common beginners mistake is to use a single = rather than == to compare values. The following gives a compiler error:

public static void main(String[] args) {
	int a=1;
	if(a=2) System.out.println("a is two");

a=2 assigns the value 2 to a.

The argument for a switch statement must be a byte, a char, a short or an int, or an expression which evaluates to one of those.

Write code using all forms of loops including labeled and unlabeled, use of break and continue, and state the values taken by loop counter variables during and after loop execution.

An loop example:

for(int i=0;i<3;i++){ 
	System.out.println("i is "+i); 
	for(int j=0;j<3;j++) { 
		System.out.println("j is"+j); 

Will print:

i is 0
j is 0
j is 1
j is 2
i is 1
j is 0


Note: Don't get thrown if the for loop uses pre-increment instead of post-increment i.e. for(int i=0;i<3;++j) instead of for(int i=0;i<3;j++). This does not make any difference to the behavior of a for loop.

break will cause the current loop to be abandoned. continue causes execution to skip the rest of the code in the current iteration of the loop, and start at the top of the loop with the next iteration.

These (unlabeled) versions will only affect the execution of loop that they are in. If you have nested loops, and want to break out of, or skip to the next iteration of, an outer loop, from an inner loop, you used the labeled versions. These jump to the wherever the label is, allowing you to break out of, or skip several nested loops, by placing the label in front of the outer loop.

Labels are a name followed by a colon, i.e. ":", placed before the loop.

Write code that makes proper use of exceptions and exception handling clauses (try, catch(), finally) and declares methods and overriding methods that throw exceptions.

Place code which is likely to throw an exception inside a try { } block. Create one or more catch() { } blocks with code to deal with the types of exceptions that might be thrown in the try block. The type of exception goes inside the round brackets of the catch() statement, as you would when declaring a method.

Exceptions are objects, which belong to a class hierarchy. If the exception thrown is an instance of the class, or a subclass of the class, specified in catch(), that catch block is the one executed. Your catch() block therefore can handle a range of exceptions if they are subclasses of the class specified. If you want to handle one specific subclass one way, and all the other subclasses differently, put a catch statement for the specific subclass first, and a more general catch block for the superclass second. Then when an exception is thrown, if the exception is a member of the subclass, that catch block only executes, but if it is a different subclass of the parent class, the general catch block executes. If you put the superclass first, you will get a compiler error.

Declaring exceptions in a method

For example:

void mymethod(int i) throws Exception { }

Methods can throw more than one class of exception, you list all the exceptions after the throws keyword, separated by commas.

Overriding and exceptions

Essentially, a method can throw the same or fewer, but not more, exceptions than the superclass method it is overriding. This has to do with object orientation, if you could broaden the number of exceptions in the subclass method, anything which can deal with the superclass might not be able to deal with the subclass, because the are new exceptions there that it was not designed to handle. You want all members of subclasses to be able to be handled as if there were members of the parent class ("upcasting" or polymorphism).

The hierarchy of exceptions is important here, you can replace a exception class in the overridden method with one or more of its subclasses, but you can't replace it with its superclass. To do this would be to broaden the range of exceptions the method could throw.

To create and throw a specified exception

For example:

throw new SomeKindOfException();

Recognize the effect of an exception arising at a specified point in a code fragment. Note: The exception may be a runtime exception, a checked exception, or an error (the code may include try, catch, or finally clauses in any legitimate combination).

If an exception is thrown, then the rest of the code in the try block is not executed. If any catch block matches the exceptions class or a super class of the exception, that block executes. If the exception is not caught correctly, then after the finally block executes, the rest of the code in the method is not executed.

finally blocks execute no matter what, in other words, whether the code executes without an exception, or an exception is thrown and successfully caught, or an exception is thrown and not caught. This is useful because, except for code in a finally block, if an exception is thrown and not caught, the execution of the rest method is abandoned.

It is legal to have a try block, followed by a finally block, without any catch blocks.

One thing (probably not important for the exam) which could stop a finally clause from executing, is a call to System.exit(0).

RuntimeExceptions differ from normal exceptions in that they are not required to be declared or handled within a try block. An Error indicates serious problems that a reasonable application should not try to catch. Examples are the VM running out of memory.

Write code that makes proper use of assertions, and distinguish appropriate from inappropriate uses of assertions.

For more information see:
The simplest form of assertion consists of the assert keyword followed by an expression which evaluates to a boolean. Example:

	assert someObject != null;
If the expression evaluates to false then an AssertionError is thrown. The other form adds a colon followed by a second expression which evaluates to a value of some kind. If the assertion fails, the value is included in the AssertionError for information. Example:
	assert someObject != null : "someObject should not be null at this point!";

Assertions may be enabled or disabled. Your code should work correctly in either cases. Do not do anything in an assertion which will have side affects on the behaviour of other parts of your code (with the possible exception of other assertions). Sun do not recommend the use of assertions to verify the correct use of the public API of you classes. Instead a more specific exception such as IllegalArgumentException should be used. However you can perform checks using assertions on the arguments provided to non-public methods, so long as those assertions should remain true regardless of how other classes invoke your API. In other words use assertions to confirm your is doing internally what you intended, rather than to check that it is being used correctly by others.

Identify correct statements about the assertion mechanism.

See the previous objective, or look in a textbook or on the web.

Garbage Collection

State the behavior that is guaranteed by the garbage collection system.

How and when garbage collection occurs is left up to the implementation of the JVM, all you can know is that an object becomes eligible for garbage collection when there are no longer any references to it. At that stage, no variable points to the object, therefore your code has no way of accessing it and the object can safely be removed by the system.

Finalize Method
The finalize() method of a class is called before it is garbage collected. The method takes no arguments and must return void, and throws Throwable. Do not confuse the finalize() method (garbage collection) with the finally keyword (exception handling).

Write code that explicitly makes objects eligible for garbage collection.

My definition of explicit differs, as I would categorize this as implicit:

Date d = new Date();
d = null;
There is no longer a reference pointing to the Date created on the first line, therefore that object is eligible for garbage collection. No matter what you do, there is no way your code could get back a reference to that object, so your code cannot be affect by its removal. If on the other hand you had passed the object into a Collection, or it was pointed to by some class, instance or local variable somewhere, then it is not ready for garbage collection (for example you could pass it to a method or constructor before setting d to null).
Date d = new Date();
d = new Date(0);
The new Date created on the second line holds the value midnight, January 1, 1970. The previous object referred to by d, which held the current Date is now eligable for garbage collection, but the new value of d (1/1/1970) is unaffected.

Recognize the point in a piece of source code at which an object becomes eligible for garbage collection.

An object is eligible for garbage collection when there are no longer any references to it. This can happen if a variable is assigned to a newly created object, but later is assigned to null or a different object, so there is nothing pointing to the original object. Or when a method exits, all the local variables go out of scope, so all the objects are eligible for garbage collection, unless there is a reference to them somewhere outside the method.

Language Fundamentals

Identify correctly constructed package declarations, import statements, class declarations (of all forms including inner classes) interface declarations, method declarations (including the main method that is used to start execution of a class), variable declarations, and identifiers.

Order of package declarations, import statements, public class declarations

The order is as follows: package declarations, import statements, then class definitions. The order of public vs. non-public class definitions does not matter. However, note that with Suns JDK, you can only have one top-level public class per source file, and the name of the file must be the same as the name of the public class. For example;

	package acme.applications.userinterfaces;
	import java.awt.*;
	public class SomeClass {
 // etc.

Correct declaration for a main() method.

[The following rules apply to creating a main() method, which allows you to run the class as an application. You can create any number of methods called 'main' in a class (overloading), which take other arguments, are not public and static, or return a value. To be able to run the class, however, there must be one method called main that takes an array of Strings as an argument, and that method must be static, and not return a value. Otherwise you will get a runtime error when you try to execute the class]

The main() method must be static, not return a value, and take an array of strings.

Whether it is declared public is something of a controversy. The Java Language Specification clearly states "The method main must be declared public, static, and void. It must accept a single argument that is an array of strings." On the other hand, 'The Complete Java2 Certification Study Guide' by Roberts, Heller et al. states. "The main method is declared public by convention. However, it is a requirement that it be static...". Tests indicate that in Java 1.2 (and 1.3), you can execute a program whose main() method is private, as bizarre as that may seem. You cannot do this in Java 1.1. or in Java 1.4.1.
Exam candidates should follow the Java language specification, and assume public is required, as far as answering questions is concerned. Use the signature described below, in your code and in your exam answers.

Either of the following two examples are accepted ways of declaring main:

	public static void main(String[] args) 
	public static void main(String args[])

Note that 'args' is just a common name for the array, and you can call it anything you like. Also:

static public void main(String[] args)

is perfectly legal and will compile.

Legal and illegal identifiers

Identifiers may contain only letters, numbers, dollar signs, i.e. "$", or underscores, i.e. "_". The first character cannot be a number. Obviously an identifier cannot have the same spelling as a keyword or the boolean and null literals (true, false and null) as described earlier.

State the correspondence between index values in the argument array passed to a main method and command line arguments.

The command line arguments, after the java command and the name of the program, are placed in the String array, starting at array element zero. The first command line argument is at position 0 in the array, the second at 1 etc. The java command and its various settings are not included in the list.

Identify classes that correctly implement an interface where that interface is either java.lang.Runnable or a fully specified interface in the question.

There is only one method in the interface, run(), which must be implemented in the class.

public void run() {
	//Do something

Identify all Java programming language keywords. Note: There will not be any questions regarding esoteric distinctions between keywords and manifest constants.

Java keywords (check that you know what each of these does, if not, find out before you take the exam!):

abstract assert boolean break byte
case catch char class const
continue default do double else
extends final finally float for
goto if implements import instanceof
int interface long native new
package private protected public return
short static strictFP super switch
synchronized this throw throws transient
try void volatile while

The keywords goto and const are reserved but not used.

While they are not keywords, the boolean and null literals (true, false and null) cannot be used as identifiers. So fear ye not the "manifest constants" mentioned in the objective.
[Note that Roberts & Heller lists these as keywords, but they are literals, not keywords]

A new keyword in Java2 is strictFP. strictFP is not in versions of Java prior to Java2. It is included here for completeness, however, I cannot say whether it is examinable and you will have to look elsewhere for information on its syntax. This modifier tells the compiler to use a strict form of floating point calculations, which ensures an identical result on every platform. In Java 1.1, this strict form is always used, but in Java2, when you don't specify strictFP, the Java virtual machine can do a fast calculation, which makes the most of the performance of the native processor without the overhead of converting to a consistent cross-platform format.

[Technical aside, for those interested in microprocessors:
I believe part of the reason for its introduction is that the X86 CPUs have an 80bit FPU, versus 64bits used in the Java standard and on Suns processors. Intel complained that this gave their CPUs a disadvantage in Java]

assert is new to 1.4 - covered elsewhere in this document.

A brief note on some of the more obscure keywords:

transient - indicates a field should not be serialized when the class is serialized.
volatile - indicates the field may be changed frequently by different threads. Somewhat like a weak form of synchronization.

State the effect of using a variable or array element of any kind when no explicit assignment has been made to it.

Class level variables (variables declared in the class, but outside of any methods, i.e. static and instance variables) are automatically initialised to a default value, if no explicit assignment has been made. The defaults are 0 for numerical types, '\u0000' for chars, false for booleans, and null for objects.

The elements of an array are always (even inside a method) initialised to their default values when no explicit assignment has been made to the element.

If you do not initialise a variable declared in a method (local variables) to some value, you will get a compiler error.

State the range of all primitive formats, data types and declare literal values for String and all primitive types using all permitted formats bases and representations.

Range of primitive data types.

A guide to understanding the ranges is to remember that if a type is x bits in size, then it can hold 2x possible combinations of bits. 0 must be represented somehow, so that is why 1 is subtracted from the maximum value. Negative values must be represented, so you must take one away from the exponent. For example, a byte is 8 bit (28), but one bit is needed for the positive/negative, so the exponent is 7 (-27 to 27). Then we subtract one for zero, giving -27 to 27-1.

chars are an exception as they are unsigned, so there are no negative values. floats and doubles are more complex. They use some bits to store the binary digits and others to store the exponent, like a binary version of scientific notation (e.g. 1.2354*105). It is unlikely you will be required to know these for the exam.

Primitive Type Size Range of Values
byte 8 bit -27 to 27-1
short 16 bit -215 to 215-1
int 32 bit -231 to 231-1
long 64 bit -263 to 263-1
char 16 bit '\u0000' to '\uffff'(0 to 216-1 )
float 32 bit Max. positive value: (2-2-23)*2127. Min. positive value: 2-149
double 64 bit Max. positive value: (2-2-52)*21023. Min. positive value: 2-1074

Constructing literal numeric values using decimal, octal and hexadecimal formats.

Octal literals begin with zero e.g. 013042 (and obviously only digits 0-7 are allowed). Hexadecimal literals begin with zero and an 'x' e.g. 0x23e4A (digits allowed are 0-9 and a to f, the 'x' and the letters can be upper or lower case).

Construct literal String values using quoted format.

String duh = "This should be obvious, but I'll include an example anyway.";

Construct a literal value of char type using Java's unicode escape format for a specified character code.

Use \u followed by four hexadecimal digits representing the 16 bit unicode character e.g.

char x='\u1234'

Java also supports certain escape codes for special characters such as '\n' for newline. See a textbook for more.

Operators and Assignments

Determine the result of applying any operator (including assignment operators and instance of) to operands of any type class scope or accessibility or any combination of these.

Most of the operators are fundamental Java, so consult a textbook or tutorial if you are unsure of them. Here is information on some them, and notes on promotion rules.

Applying the '>>', '>>>' and '<<' operators to an int value specified as a bit pattern.

These operators are followed by a number which defines by how many bits the number is shifted.

>> performs a signed right-shift, that is, if the most significant (i.e. first on the left) bit is 1, then when it right-shifts the bits, it fills in a 1s on the left. If the most significant bit is 0, then when it right-shifts the bits, it fills in a 0s on the left. As the first bit represents the sign of a number (positive or negative), this preserves the sign of the number.

>>> performs an unsigned right-shift, it always fills in 0s on the left.

<< performs a left-shift (it always fills in 0s on the right).

The + operator

Adds. If any of the variables are Strings, it converts the non-Strings to Strings, and concatenates (joins) them.

Using the '==' comparison operator with two objects of any type.

With Objects, == determines whether the variables reference the same object in memory, rather than comparing their contents in a meaningful way. If you assign a=b, then a==b will evaluate to true, where a and b are objects.

Note that if you construct two Strings with the same String literal, without using the new keyword, e.g.

	String a = "Hello"
	String b = "Hello"

, then Java creates only one String object, so a==b evaluates as true.

Using the ternary operator.

This consists of a boolean expression, followed by question mark, then two expressions, seperated from each other by a colon. If the boolean expression before the question mark evaluates as true, the value from the expression before the colon is used, otherwise the value from the last expression is used. Example:

int a = true ? 1 : 0;
int b = false ? 1 : 0;
a will be 1. b will be 0.

When assignment is permitted between any two variables of possibly different types.

Primitives: You cannot assign booleans to any other type. With the exception that you cannot assign a byte to a char, you can assign a variable of type X to type Y (i.e. Y=X) only if Y is 'wider' than X. 'Wider' means that the primitive type can contain a wider range of values. The primitives, in order of 'width' are char/short, int, long, float, double. Note that you cannot assign a char to a short or vice versa.

Objects: You can assign object X to object Y (i.e. Y=X) only if they are of the same class, or X is a subclass of Y (called "upcasting").

Effect of assignment and modification operations upon variables .

In an arithmetic statement, variable may be widened automatically, to evaluate the expression (note the variables themselves aren't change, i.e. byte b is still a byte afterwards, but for its calculations Java uses a widened value). This is called promotion. Bytes, shorts and chars are always converted to ints, in unary (e.g. x++)or binary operations (e.g. x*y). For binary operators, if one operand is wider, the other is widened to the same type.
This has import consequences. For example:

byte b = 3;
b = b + 5;
will not compile, because the result of b+5 is an int.

Determine the result of applying the boolean equals() (Object) method to objects of any combination of the classes java.lang.String, java.lang.Boolean, and java.lang.Object.

Unless it is overridden, the equals() method behaviour in Object, and therefore inherited from it, performs the same reference comparison as the == operator. However, the Boolean and String classes override this with a more meaningful comparison. equals()returns true, in Booleans if the two objects contain the same Boolean value, and in String if the Strings contain the same sequence of characters.

Note that StringBuffer does not override the equals() method, so if you use this method, it will not compare the actual text characters that the StringBuffers contain.

In an expression involving the operators &, |, && and ||, state which operands are evaluated and determine the resulting value of the expression.

Just a quick note on the second two operators: && (AND) and || (OR) are the short circuit operators, which do not evaluate the second operand if it is not necessary. AND is only true if both values are true, therefore if the first is false, the result is false, and there is no need to evaluate the second part. OR is true if either value is true, therefore if the first is true, the result is true, and there is no need to evaluate the second part.

Determine the effect upon objects and primitive values of passing variables into methods and performing assignments or other modifying operations in that method.

When passed to a method, primitives may be promoted, and objects may be upcast, to the type/class that the method takes, if possible. For example, if a method takes an int as an argument, and you pass it a byte, the value passed to the method is the byte converted to an int. The rules for when this is allowed are as per the objective "Determine if an assignment is permitted between any two variables of possibly different types" above.

Note that primitive variables, in the calling method, are unaffected by modifications made in the target method. This is called passing by value. Look at this code:

void methodA() {
	int x=5;

void methodB(int x) {
	int x=10;
If you execute methodA(), it will print out "5". This is because it is the value of the variable x (which is 5), that is passed to methodB(). methodB() accepts this value into a local variable, which is coincidentally named x aswell. But it is a different variable. Subsequent changes inside methodB() will not change the value of x in methodA().
This issue is more complex where objects are concerned. Strictly speaking, they too are passed by value, like primitives. However, the value in question, where object variables are concerned, is in fact the memory location of the object that the variable points to. First, lets look at an example which acts like the above, but with objects. The Price class just stores the value assigned to it (using its constructor) as a member variable. The toString() method has been overwritten to return this value. It also has a setPrice() method to change its value.
void methodA() {
	Price x = new Price(5);

void methodB(Price x) {
	Price x = new Price(10);
This example, just like the one above, will print out "5". Note that the value being passed here to methodB() is in reality the location of the Price object in memory. In methodB(), the local variable x is re-assigned to point to a new Price object, but what methodA() sees is unaffected. This is made obvious here by the use of the new keyword. On the other hand, if you do operations which modify the object in the target methods, those changes will be visible in the calling method, as in the following example:
void methodA() {
	Price x = new Price(5);

void methodB(Price x) {
This will print out "10". Again methodB() is passed the location of the Price object, which it holds in a local variable x. However, when you call the setPrice() method, this change goes out to the actual object in memory, and hence methodA() sees the same new value, 10.

Overloading, Overriding, Runtime Type, and Object Orientation

State the benefits of encapsulation in object oriented design and write code that implements tightly encapsulated classes and the relationships "is a" and "has a".


Encapsulation is the principal of keeping the internal details of a classes state and behaviours hidden from the other classes that use it. This allows you to change those details where necessary, without breaking compatibility. The interconnectness between pieces of code is called 'coupling', and minimising it makes for more reusable and maintainable classes. The expectated behaviour of a class or method is referred to as its 'contract'. You do not want to expose any more than the minimum required to support the contract, or you can end up with tangled interdependent code with poor maintainabilty, and in all likelyhood, poor quality.

Encapsulation also aids polymorphism and inheritance - if the internal details of a class are exposed and used, it is very hard to substitute a different implementation of the same contract.

To achieve good encapsulation, make everything have the tightest possible access control so if something should not be used in a given context, limit the access to prohibit it. In particular, class instance variables should be private, and accessed only through 'get'/'set' methods (a.k.a accessors and mutators). This would allow you to do some work or checking before reading/writing a value to the variable. You could remove the variable entirely and derive it or obtain it from another source. If the variable was public, and was accessed directly, should changes would break client code.

Write classes that implement object oriented relationships specified using the clauses 'is a' and 'has a'.

Briefly, 'is a' should be implemented using inheritance, 'has a' should be implemented using containment (i.e. 'composition' or sometimes 'aggregation'). This objective is conceptual, consult a textbook for more.

Determine at run time if an object is an instance of a specified class or some subclass of that class using the instanceof operator.
SomeObject instanceof SomeClass

evaluates as true if SomeObject is an instance of SomeClass, or is an instance of a subclass of Someclass. Otherwise it evaluates as false.

SomeObject instanceof SomeInterface

evaluates as true if SomeObject is an instance of a class which implements SomeInterface. Otherwise it evaluates as false.

Write code to invoke overridden or overloaded methods and parental or overloaded constructors; and describe the effect of invoking these methods.

Overloaded and overridden methods.

What identifies a method in the Java is not merely the name of the method, rather it is the name of the method and the arguments it takes. This is called the methods signature. You must watch for any differences in the arguments list. If the arguments are different, you are dealing with a separate method. This is overloading. The fact that they have the same name is only of significance to humans.

The return type is not part of a methods signature. You are only overriding if there is a method in the parent class with same name which takes exactly the same arguments. Then the method replaces the one from the superclass.

You cannot define two methods within a class with the same name and the same arguments.

A related point is that you cannot override variables or static methods, rather you hide them when you declare, in a subclass, a variable of the same name or static method with the same signature. This is one of these things that occasionally surprises experienced developers. It is an important distinction as the polymorphic behaviour which happens when overriding (see below) does not apply here.

Legal return types for an overloading method

Overloaded methods are really completely different and separate methods, so they can return completely different types.

State legal return types for an overriding method given the original method declaration.

The overriding method must return the same type as the method in the superclass. This is because in the subclass, the overriding method replaces the method in superclass, and polymorphism or "upcasting" would not work they returned different types (i.e. you should get the same return type as you would with the parent class, or else you can't treat the subclass as the superclass).

Invoking overridden method in base and derived classes.

If the object is an instance of the derived class, then the overridden version defined in the derived class is the one that is invoked. Look at this example:

class Animal {
	void sayHello() {
		System.out.println("Hello, I'm an animal.");
class Dog extends Animal {
	void sayHello() {
		System.out.println("Hello, I'm a dog.");

We are overriding the sayHello method with behaviour more specific to a dog.

The straightforward method calls are as follows:

Animal i = new Animal;

prints "Hello, I'm an animal."

Dog j = new Dog();

prints "Hello, I'm a dog." because we have overridden sayHello, and this instance is a dog.


Animal k = new Dog();

prints "Hello, I'm a dog." Here we are creating an instance of Dog, but we are assigning it to an Animal reference [referring to an object as the base class is called "upcasting" and is useful as you can write methods that deal with instances of any of the subclasses]. Because the underlying object is really a Dog, the method in the derived class is the one that executes.

Note: This behavior only applies to instance (i.e. not static) methods. Variables and static methods do not "override", instead they "hide" the variable/method in the parent class. This means that it is the class of the reference that matters, rather than that of the underlying object.

Write code for any overridden method that invokes the parental method, using super.

super.someMethod() will call the version of someMethod() in the immediate super class. You cannot use something like super.super.someMethod() to call the method two steps up the object hierarchy.

Write code to construct instances of any concrete class including normal top level classes and nested classes.

This is fairly straight forward, just use the new operator. Nested classes are covered in another section.

Creating constructors which use this() and super() to access overloaded or parent-class constructors.

These calls must be at the very start of the code in the constructor, therefore you can only make one of these types of calls. A call to the default constructor in the parent class is made by default, so super(arguments) is useful when you want to call a version of the constructor which takes an argument instead (and there may not be a default constructor in the parent class, in which case this call is necessary, or the code won't compile).

this() with or without arguments in brackets is used to call any of the other (overloaded) constructors defined in the class, before performing actions specific to current constructor being defined.


Write code to define, instantiate and start new threads using both java.lang.Thread and java.lang.Runnable.

To define a class that implements Runnable, you must define a run() method in the class e.g.

class MyClass implements Runnable {
	public void run () {
		// Insert the code you want to run in a thread here

run() must be public, with a void return type, and not take any arguments. Note that if run() is defined to take any arguments, then that is a different method and the Runnable interface hasn't been implemented!

Create an instance of the class, and construct a thread, passing the instance of the class as an argument to the constructor i.e.

MyClass mc = new Myclass();
Thread t = new Thread(mc);

To start the thread, use the start() method:


Alternatively, you can create a subclass of the Thread class, and override the run() in the subclass:

class MyThread extends Thread {
	public void run () {
		// Insert the code you want to run in a thread here

Then create an instance of the subclass, and start it running:

MyThread mt=new MyThread();

Recognize conditions that might prevent a thread from executing.

A thread can be in one of the following states:
a) Running
b) Ready - Waiting for a chance to run on the CPU. All threads enter this state before running. Threads with higher priorities may be preventing the thread from getting a chance to run (this depends on how priorities have been implemented in the particular Java Virtual machine)
c) Various waiting states (Waiting, Sleeping, Suspended & Blocked)

d) Dead - the run() method has completed (the thread can never be restarted)
Note that the suspend() and resume() methods of the thread class are deprecated in Java2.

Write code using synchronized wait, notify and notifyAll to protect against concurrent access problems and to communicate between threads.

The use of these methods, and synchronized methods or code blocks, ensure only one thread at a time can access parts of a class, and control how and when threads get this access. This is a complicated area, if it is new to you, consult a textbook.

Using the synchronized keyword to require a thread of execution to obtain an object lock prior to proceeding.

Using the synchronized keyword in the method declaration, requires a thread obtain the lock for this object before it can execute the method.

synchronized void someMethod() { }

You can also make a block of code synchronized by using the synchronized keyword followed by the object or class, for which a thread must obtain the lock before it can execute this block of code:

// .. some code before the synchronized block
synchronized (someObject) {
	// synchronized code
// more code...

The wait() method.

The wait() method must be used in synchronized code. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up (i.e. move into Ready state) either through a call to the notify() method or the notifyAll() method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

The notify() or notifyAll() methods of an object.

notify() moves one thread, that is waiting the this objects monitor, into the Ready state. This could be any of the waiting threads; the choice of which thread is chosen is an implementation issue of the virtual machine.

notifyAll() moves all threads, waiting on this objects monitor, into the Ready state.

Define the interaction among threads and object locks when executing synchronized wait, notify or notifyAll.

Consult a textbook or the web.

Fundamental classes in the java.lang package

Write code using the following methods of the java.lang.Math class: abs(), ceil(), floor(), max(), min(), random(), round(), sin(), cos(), tan(), sqrt().

See the JavaDocs for more detail.

abs() - Returns the absolute value of the argument. Overloaded for ints, longs, floats and doubles.

ceil(double) - Returns the smallest (closest to negative infinity) double value that is not less than the argument and is equal to a mathematical integer.

floor(double) - Returns the largest (closest to positive infinity) double value that is not greater than the argument and is equal to a mathematical integer.

max(value1,value2) - Returns the greater of two values.

min(value1,value2) - Returns the smaller of two values.

random() - Returns a random number between 0.0 and 1.0.

round(double) - Returns the closest long to the argument.

sin(double) - Returns the trigonometric sine of an angle. The angle is in radians.

cos(double) - Returns the trigonometric cosine of an angle. The angle is in radians.

tan(double) - Returns the trigonometric tangent of an angle. The angle is in radians.

sqrt(double) - Returns the square root of a double value.

Describe the significance of the immutability of String objects.

String objects cannot be changed. They are assigned a sequence of characters when they are constructed. Look at the following code:

String message = "Good";
message= message + " morning";

When the first line executes, a string object containing "Good" is assigned to message. A new String object is constructed, unless the literal "Good" was used somewhere else, in which case the existing String is re-used. A consequence of this is the behaviour of the == operator which was describe under Language Fundamentals above, here is that information again:

[Note that if you construct two Strings with the same String literal, without using the new keyword, e.g.

String a = "Hello"
String b = "Hello"

, then Java creates only one String object, so a==b evaluates as true.]

On the second line, " morning" is appended. But the String object containing "Good" cannot be changed, so a new String, containing "Good morning" is created and assigned to message.

Describe the significance of wrapper classes, including making appropriate selections in the wrapper classes to suit specified behavior requirements, stating the result of executing a fragment of code that includes an instance of one of the wrapper classes, and writing code using the following methods of the wrapper classes (e.g., Integer, Double, etc.):

There are two fundamental types of variables in Java: primitives and objects. Sometimes a method or a constructor takes objects, but what what you have is a primitive. To cope with this situation, Java has a set of 'wrapper' classes which exist to hold the value of a primitive. For example, you might want to use a float as a key in a Map e.g. keying some business objects representing a loan by their respective interest rates. Using the Float class will allow you to do this. You would create a Float object for each value, and place that object into the Map.

Primitive Type Corresponding Wrapper class
boolean Boolean
byte Byte
short Short
char Character
int Integer
long Long
float Float
double Double

The names are entirely obvious, except for the two that have been highlighted. Take a look at these classes in the JavaDocs. The pattern tends to be pretty similar: For a primitive type x, there is one constructor taking x and another that takes a String, which is parsed to get the value. There is a static parseX() method, which parses a string and returns the primitive type x. An xValue() method returns the underlying primitive value held by wrapped object. The toString() method from Object is overridden appropriately.

toHexString() is a static method on Integer and Long which creates a hexadecimal string representation of the argument as an unsigned integer in base 16.

The Collections Framework

Make appropriate selection of collection classes/interfaces to suit specified behavior requirements.

As far as I can gather, and this is implied by the objective itself and the sample examination question on the Sun website, this objective just requires a general knowledge of the classes/interfaces, in other words which to use for a given purpose. If you want a more in-depth treatment of the subject, try the Sun tutorial:


Implementations: Hashset

A Set is a collection which cannot contain any duplicate elements and has no explicit order to its elements (unlike, for example, an array, where every element exists at a particular index i.e. MyArray[15]).

Implementations: TreeSet

A SortedSet is a Set which maintains its elements in ascending order.


Implementations: LinkedList, ArrayList, Vector, Stack

A List is a collection which can contain duplicate elements, and the elements are ordered (like an array, you can add an element at a specific position, and the elements are accessed using their index).

[Stack has come up in the examination in the past, so here is some info. on it.

Stack is a subset of Vector, which contains some extra methods. The idea of a stack is like its name, it acts like a piled up stack of items: When you add an element, it goes to the top of the stack, and when you extract an element, it is taken off the top. In other words, this is a last-in, first-out system. The methods are:

push(object) - Add an element onto the top of the stack.

pop() - Removes the object at the top of this stack and returns that object as the value of this function.

peek() - Looks at (i.e. returns) the object at the top of this stack without removing it from the stack.. ]


Implementations: HashMap, Hashtable, WeakHashMap

Maps keys to values. In other words, for every key, there is a corresponding value, and you look up the values using the keys. Maps cannot have duplicate keys, and each key maps to at most one value.

Note: A Map does not implement the Collection interface.

Implementations: TreeMap

A SortedSet is a Set which maintains its mapping in ascending key order.

Object Ordering

Implementations of the SortedSet and SortedMap interfaces sort their elements. To determine what criterion is used to sort the elements you can use either the Comparable or Comparator interfaces. Using Comparable means that the classes that you put in your SortedSet or SortedMap implement the Comparable interface, which just means that they contain a method compareTo() which determines whether the object is "greater" or "less than" another object. To use the Comparator interface, you pass an object which implements Comparator to your SortedSet or Sorted map, and it will use the Comparator for ordering.

For completeness, here is some information on BitSet, which isn't part of the Collections Framework, but may be examinable:
A BitSet contains elements which are bits, i.e. of the boolean primitive type. Like, for example, a Vector, and unlike an array, a BitSet does not have a fixed size, and grows as needed when you add elements to it.

Distinguish between correct and incorrect implementations of hashcode methods.

The hashcode value of an object gives a number which can be used to in effect to 'index' objects in a collection. A collection class can group its objects by their hashcodes, and if you called e.g. contains() (or get() in case of a Map), it can first find the group based on the hashcode, then search that group. This avoids the need to check every object in the collection.

The requirements of a hashCode() implementation are that if two objects are equal as determined the equals() method, their hashcodes should be the same. This is why wherever you override the equals() method, you should override hashCode() aswell.

The reverse is not required i.e. it is permitted for two objects that are not equal to have the same hashcode. This makes sense, as it is not always possible to ensure unique hashCodes. The method returns an int and there are only an finite number of int values to use. However, where possible, it is desirable to have the hashcodes be distinct as this can improve performance. If the hashcodes can be well distributed (i.e. scattered throughtout the int range) aswell, all the better.

1.2 Exam Only: The java.awt package - Components and Facilities

Write code to demonstrate the use of the following methods of the java.awt.Component class: setVisible(boolean), setEnabled(boolean), getSize(), setForeground() and setBackground().

The names of these methods are self-explanatory. Note that after you create a frame, there are not visible by default, you must use setVisible(true) before it can be seen.

getSize() returns a dimension object, which contains two integer member variables, height and width.

Obviously setForeground() and setBackground() take a color argument.

Construct a java.awt.TextArea or java.awt.List that prefers to display a specified number of columns.

As there is no mechanism to set the number of columns a list has, I would assume that the above objective contains a typo, and should refer to rows, not columns.

The constructors for a List are as follows:

List(int rows)
Creates a new scrolling list initialized with the specified number of visible lines. By default, multiple selections are not allowed.
List(int rows, boolean multipleMode)
Creates a new scrolling list initialized to display the specified number of rows. If the value of multipleMode is true, then the user can select multiple items from the list. If it is false, only one item at a time can be selected.

The constructors for a TextArea are under the next objective.

Construct a java.awt.TextArea or java.awt.TextField that prefers to display a specified number of columns.

A TextArea object is a multi-line region that displays text. The following constructors allow you to specify the preferred number of rows and columns:

TextArea(int rows, int columns)
Constructs a new empty TextArea with the specified number of rows and columns.
TextArea(String text, int rows, int columns)
Constructs a new text area with the specified text, and with the specified number of rows and columns. This text area is created with both vertical and horizontal scroll bars.
Note: It is the number of rows, then columns. This is counter-intuitive, as usually in programming you specify a horizontal dimension, then a vertical one, so this exception can catch you out.

A TextField has a single line of text:

TextField(int columns)
Constructs a new empty TextField with the specified number of columns.
TextField(String text, int columns)
Constructs a new text field initialized with the specified text to be displayed, and wide enough to hold the specified number of characters.

State the significance of a "column" where one of the text components is using a proportional (variable) pitch font or a fixed pitch font.

The width of a "column" is equal to the width of a character, in the particular font being used. In the case of fixed pitch fonts, where all the characters are the same size, this is straightforward. For proportional fonts, the width of a column is equal to the average of the width of all the characters in the font. This means, for example, if you had text component with a proportional font and a width of 10 columns, if you use a narrow letter such as 'i', more than 10 'i's could be displayed.

1.2 Exam Only: The java.awt package - Layout

Demonstrate the use of the methods add(Component) and add(String, Component) of the java.awt.Container class and recognize which classes in the java.awt and java.awt packages are valid arguments to these methods.

Note that the second form, as far as the certification exam scope is concerned, is used with BorderLayout. The String is one of "North", "South", "East", "West" or "Center", which specifies which area of the component is to added to. However, the JDK documentation strongly advises the use of the Java 1.1 form. Which is asked in the exam I can't say, but it is probably wise to be familiar with both. The 1.1 form, which is the only one used in Roberts & Heller (Boone only uses the above form) is as follows:

public void add(Component comp, Object constraints)
Adds the specified component to the end of this container. Also notifies the layout manager to add the component to this container's layout using the specified constraints object.

For BorderLayout, the second parameter is a String as above, or you can use one of the constants defined in the BorderLayout class, BorderLayout.NORTH, BorderLayout.SOUTH etc.

Distinguish between AWT classes which are directly responsible for determining component layout and those which are responsible for implementing that layout.

You set the layout manager that a container should use. That layout manger then implements the layout.

Write code to change the layout scheme associated with an AWT container.
public void setLayout(LayoutManager mgr)
Sets the layout manager for this container.

For example, in the code of an applet:

setLayout(new GridLayout()); 

creates an instance of the GridLayout class, and makes it the layout manger for the applet.

Worth noting are the default layout managers: FlowLayout for applets and panels, BorderLayout for windows, frames and dialogs. Containers always use these defaults after being created, unless you specify otherwise, they do not get the default layout manager from the containers they themselves are placed in, unlike other properties such as colour, font etc.

Use BorderLayout, FlowLayout, and GridLayout to achieve required dynamic resizing behavior of a component.

BorderLayout divides the container into 5 regions, and one component at a time can be placed in each (that component can be another container, which itself can contain multiple components). If you add a component to a region which already contains another component, only the latest component will be displayed. Components in the North and South are placed at the top and bottom respectively, and are allowed to be their preferred heights but are as wide as the container. East and West are on the right and left sides respectively, and are allowed to be their preferred widths. Their heights stretch from the North and South regions (if there is nothing in North or South, they go from the bottom to the top of the container). Center fills the remaining space, left after the other regions, in the middle. The component is stretched to fill this area.

How to add components to the regions of a container using BorderLayout is described above, in the first object of this section. Note that if you don't specify a region, the component is added to Center. Therefore, for example, if you create a frame (default layout is BorderLayout), and add a button to it using the default add(Component) method, the button will fill the entire frame.

FlowLayout always allows components to be their preferred size [This is true even if the component is wider or taller than the container. A centered portion of the component is shown, with as much of the component being made visible as can be]. It places many components as it can on a line, with the first component starting on the left, until there is no more room horizontally, then it starts another line. By default the rows are centred in the container. You can specify how the rows are aligned using this constructor:

new FlowLayout(int alignment)
Constructs a new Flow Layout with the specified alignment and a default 5-unit horizontal and vertical gap. The value of the alignment argument must be one of FlowLayout.LEFT, FlowLayout.RIGHT, or FlowLayout.CENTER

GridLayout divides its area into equally sized rectangles. When components are added to it, they are made the same size as their cell. Two constructors to be aware of are:

public GridLayout(int rows, int cols)
Creates a grid layout with the specified number of rows and columns. All components in the layout are given equal size. One, but not both, of rows and cols can be zero, which means that any number of objects can be placed in a row or in a column.
Remember: it is rows, then columns. If the number of components added is more than the number of cells (i.e. rows * columns) then more columns are added as needed, but the number of rows remains the same.
public GridLayout()
Creates a grid layout with a default of one column per component, in a single row.


While it is not mentioned in the objectives, GridBagLayout is examined in SCJP 2.

There is a tutorial which includes using GridBagLayout, with AWT, at

GridBagLayout is like GridLayout, except that components can be different sizes (i.e. take up more than one cell in the grid) and you have a lot more options, hence it is more complex. To use GridBagLayout, you create an instance, and set it as the layout manager for your container, as with the other layout managers. You don't specify the number of cells when constructing an instance of GridBagLayout, the constructor is just the default GridBagLayout().

The twist is that you specify how the component is to be displayed using an instance of GridBagConstraints. The fields in this class contain all the options. For each component, you use the GridBagLayout setConstraints() method:

setConstraints(Component comp,GridBagConstraints constraints)

The values for each component are read from the GridBagConstraints object, so you can change the values after each component, and reuse the same GridBagConstraints instance for the next one. Then you add the component to the container e.g.

MyGridBag.setConstraints(componentX, MyConstraints); 

GridBagConstraints Fields:
[The constants (uppercase) listed below for use with certain fields are static fields in the GridBagConstraints class, so you refer to them as GridBagConstraints.EAST for example.]


This field is used when the component is smaller than its display area. It determines where, within the display area, to place the component.
This field is used when the component's display area is larger than the component's requested size. It determines whether to resize the component, and if so, how.
Specifies the number of cells in a column for the component's display area.
Use REMAINDER to specify that the component be the last one in its column. Use RELATIVE to specify that the component be the next-to-last one in its column.)
Specifies the number of cells in a row for the component's display area.
Use REMAINDER to specify that the component be the last one in its row. Use RELATIVE to specify that the component be the next-to-last one in its row.
[Here REMAINDER acts somewhat like a carriage return. You add elements to the row, and when you use REMAINDER that marks the end of the line, and then the next components are added to the next row. Look at the examples in the tutorial or in the JavaDocs. Alternatively, you can use gridx and gridy to specify grid position when adding a component.]
gridx, gridy
Specifies the position of the component. The constant RELATIVE (which is the default) specifies that this component be placed next (i.e. to the right for gridx, and below for gridy) to the previously added component. The top left cell has gridx=0, gridy=0.
Specifies the external padding of the component, the minimum amount of space between the component and the edges of its display area. This field belongs to the Insets class which uses the following constructor:
Insets(int top, int left, int bottom, int right)
Creates and initializes a new Insets object with the specified top, left, bottom, and right insets.
Specifies the internal padding of the component, how much space to add to the minimum width of the component.
Specifies the internal padding, that is, how much space to add to the minimum height of the component.
weightx, weighty
Specifies how to distribute extra vertical/horizontal space respectively. This is a double, typically between 0 and 1, the default value is 0. If all the weights are zero, and there is extra space, all the components are bunched together in the middle.

1.2 Exam Only: The java.awt package - Event Handling

Write a non-abstract class that implements a specified Listener interface, given the interface definition.

Listener Interfaces
For information and comparison purposes, here is the list of Listener interfaces, and their methods. You do not need to memorize this table. Note the relationship between the Listener interface name, the Event type (which is the argument to the methods) and the 'add' method which belongs to the component classes.

Interface Name Event Methods in interface Add method
ActionListener ActionEvent actionPerformed() addActionListener()
AdjustmentListener AdjustmentEvent adjustmentValueChanged() addAdjustmentListener()
ComponentListener ComponentEvent componentHidden()
ContainerListener ContainerEvent componentAdded()
FocusListener FocusEvent focusGained()
InputMethodListener InputMethodEvent caretPositionChanged()
ItemListener ItemEvent itemStateChanged() addItemListener()
KeyListener KeyEvent keyPressed()
MouseListener MouseEvent mouseClicked()
MouseMotionListener MouseEvent mouseDragged()
TextListener TextEvent textValueChanged() addTextListener()
WindowListener WindowEvent windowActivated()

Select methods from the classes in the java.awt.event package that identify the affected component, mouse position, nature, and time of the event.

The only Event class that has methods for all of the above is MouseEvent.

Affected Component:
There are two methods that fit this description. From looking at the source code, it appears they perform the exact same function.

All ComponentEvent subclasses implement this method:

public Component getComponent()
Returns: the Component object that originated the event

All events implement this method:

public Object getSource()
Returns: the object on which the Event initially occurred.

Mouse Position:
MouseEvent implements these methods:

public Point getPoint()
Returns: a Point object containing the x and y coordinates relative to the source component
public int getX()
Returns: x an integer indicating horizontal position relative to the component
public int getY()
Returns: y an integer indicating vertical position relative to the component

All AWTEvent subclasses implement this method:

public int getID()
Returns the event type. This can be compared with the event mask constants defined in the class for the different types of event e.g. MOUSE_EVENT_MASK.

InputEvent subclasses (KeyEvent and MouseEvent) implement this method:

public long getWhen()
Returns the timestamp of when this event occurred.

Demonstrate correct uses of the Listener methods in the Component, TextArea, and TextField classes.

For examples, see a text book, or the sites at the start of this document. Briefly, you create a class which implements one of the listener interfaces listed above. Important listener interfaces for the components mentioned in this objective are, for example, MouseListener, FocusListener, KeyListener or TextListener.

Create suitable methods for the events you are interested, for the rest of the methods in the interface, just create an empty method body. For example, if you have a button, and you just need it to respond to being clicked, in your listener class you would put code in the mouseClicked() method, and implement the rest of the methods with empty bodies.

Then create an instance of the listener class and add it to the component in question using the components appropriate add method, which is the last column in the table above.

[To save typing, there is the option of using Adapter classes, e.g MouseAdapter for the MouseListener interface. These are classes which have empty methods for all the methods in the Listener. You extend them, and then overwrite the methods you are interested in, saving the effort of typing other methods.]

For any listener in the java.awt.event package, state the argument type and return type of a specified listener method, given the name of the interface that declares the method and the name of the method itself.

The argument types are listed in the "Listener Interfaces" table above, under the "Event" heading. The return type for all methods of all listener interface methods is void.

1.2 Exam Only: The package

For this section, you may find the following tables useful. They list the various kinds of Streams and Readers/Writers, along with what they "connect to", typically their constructor arguments.

Low Level Streams
Input Streams Arguments Output Streams Arguments
FileInputStream File FileOutputStream File
ByteArrayInputStream byte[] ByteArrayOutputStream (Creates an array)

High Level Streams
Input Streams Arguments Output Streams Arguments
FilterInputStream InputStream FilterOutputStream OutputStream
Implements DataInput
InputStream DataOutputStream
Implements DataOutput
BufferedInputStream InputStream BufferedOutputStream OutputStream

Low-level Readers/Writers
Readers Arguments Writers Arguments
FileReader File FileWriter File
CharArrayReader char[] CharArrayWriter Creates an array of chars
StringReader String StringWriter Creates a String

High-level Readers/Writers
Readers Arguments Writers Arguments
BufferedReader Reader BufferedWriter Writer
InputStreamReader InputStream OutputStreamWriter OutputStream

Write code that uses objects of the file class to navigate a file system.

Methods that support navigation:

boolean exists() - Tests if this File exists.

String getAbsolutePath() - Returns the absolute pathname of the file represented by this object.

String getCanonicalPath() - Returns the canonical form of this File object's pathname.

String getName() - Returns the name of the file represented by this object.

String getParent() - Returns the parent part of the pathname of this File object, or null if the name has no parent part.

String getPath() - Returns the pathname of the file represented by this object.

boolean isDirectory() - Tests if the file represented by this File object is a directory.

boolean isFile() - Tests if the file represented by this File object is a "normal" file.

String[] list() - Returns a list (an array of Strings) of the files in the directory specified by this File object.

Note: The File class does not provide a method to change the current working directory.

Other methods worth being aware of:

mkdir() - Creates a directory whose pathname is specified by this File object.

delete() - Deletes the file specified by this object.

length() - Returns the length of the file represented by this File object.

renameTo(File) - Renames the file specified by this File object to have the pathname given by the File argument.

canRead() & canWrite() - Return true if the file is readable/writeable.

Write code that uses objects of the classes InputStreamReader and OutputStreamWriter to translate between Unicode and either platform default or ISO 8859-1 character encodings.

The following constructor for InputStreamReader allows you to specify the encoding scheme:

InputStreamReader(InputStream in, String enc)

Create an InputStreamReader that uses the named character encoding.
in - An InputStream
enc - Name of encoding to be used

Encoding scheme string "8859_1" is ASCII.

The constructor for OutputStreamWriter is similar, but the first argument is an OutputStream, obviously.

Readers (such as InputStreamReader) have the following methods for reading characters:

read() -Read a single character.

read(char[] cbuf) -Read characters into an array.

read(char[] cbuf, int off, int len) -Read characters into a portion of an array.

Writers (such as OutputStreamWriter) have the following methods for writing characters:

write(char[] cbuf) -Write an array of characters.

write (char[] cbuf, int off, int len) -Write a portion of an array of characters.

write(int c) -Write a single character.

write(String str) -Write a string.

write(String str, int off, int len) -Write a portion of a string.

Destinguish between conditions under which platform default encoding conversion should be used and conditions under which a specific conversion should be used.

To avoid corruption, text should be read using the same encoding scheme that it was written with. Therefore, in general platform default encoding may be used if the data is written on the same computer as it is going to be read. However if the text is going to be sent from one computer to another across a network, or if a text file is created on a different computer (for example files that are installed as part of an application), then a specific encoding scheme should be used at both ends.

Select valid constructor arguments for FilterInputStream and FilterOutputStream subclasses from a list of classes in the

The correct constructor argument for FilterInputStream is an instance of a subclass of InputStream. Similarily, the correct constructor argument for FilterOutputStream is an instance of a subclass of OutputStream.

Write appropriate code to read, write and update files using FileInputStream, FileOutputStream, and RandomAccessFile objects.

See a textbook, the Java tutorials or Javadocs if you are unsure of this area. Here is some brief information on RandomAccessFile:

RandomAccessFile is not part of the i/o stream or reader/writer hierarchies. The constructors are:

RandomAccessFile(String file, String mode)
RandomAccessFile(File file, String mode)

Important: For the RandomAccessFile constructor, the mode argument must either be equal to "r" or "rw". Do not be fooled in the exam by credible sounding, but non-existent options like "r+w" or "w".

RandomAccessFile has a pointer which determines where to read/write in the file. getFilePointer() returns the current position in the file, in bytes, and seek(long position) sets the pointer to a specific location. There is also a length() method. This class implements the DataInput and DataOutput interfaces, which have read or write methods (respectively) for all the primitives, strings, and byte arrays ( readFully and write() respectively). The DataInput interface also has skipBytes() method. Consult the Java API Docs for more on these interfaces.

Describe the permanent effects on the file system of constructing and using FileInputStream, FileOutputStream, and RandomAccessFile objects.

RandomAccessFile will create an empty file if it is constructed as "rw". Constructing a FileOutputStream can create an empty file.

FileInputStream of course, never causes a file to be created or modified.

1.2 Exam Only: Miscellaneous

Write code to demonstrate the use of the following methods of the java.lang.String class: length(), toUpperCase(), toLowerCase(), equals(), equalsIgnoreCase(), charAt(), concat(), indexOf(), lastIndexOf(), substring(), toString(), trim().

length() - Returns the length of this string.

toUpperCase() - Converts this string to uppercase.

toLowerCase() - Converts this String to lowercase.

[Note that toLowerCase() and toUpperCase() will return a reference to original string object if the it is already in lower case or upper case respectively. This is important for questions on the == operator and strings. There appears to be a bug in Suns JDK 1.1 which causes this not to occur, but this is the behaviour described by the JavaDocs. Java2 behaves as described in the JavaDocs.]

equals(Object) - Compares this string to the specified object. Returns true if the strings contain the same sequence of characters.

equalsIgnoreCase(String) - Compares this String to another object. Returns true if the strings contain the same sequence of characters, ignoring case.

charAt(int) - Returns the character at the specified index. An index ranges from 0 to length() - 1.

concat(String) - Concatenates the specified string to the end of this string.

indexOf(int) - Returns the index within this string of the first occurrence of the specified character.

indexOf(int ch, int fromIndex) - Returns the index within this string of the first occurrence of the specified character, starting the search at the specified index.

ch - a character.
fromIndex - the index to start the search from.

indexOf(String) - Returns the index within this string of the first occurrence of the specified substring.

indexOf(String, int) - Returns the index within this string of the first occurrence of the specified substring, starting at the specified index.

lastIndexOf(int) - Returns the index within this string of the last occurrence of the specified character.

lastIndexOf(int ch, int fromIndex) - Returns the index within this string of the last occurrence of the specified character, searching backward starting at the specified index.

ch - a character.
fromIndex - the index to start the search from.

lastIndexOf(String) - Returns the index within this string of the rightmost occurrence of the specified substring.

lastIndexOf(String, int) - Returns the index within this string of the last occurrence of the specified substring.

substring(int) - Returns a new string that is a substring of this string. The substring begins at the specified index and extends to the end of this string.

substring(int beginIndex, int endIndex) - Returns a new string that is a substring of this string. The substring begins at the specified beginIndex and extends to the character at index endIndex - 1.

Remember: substring gives you the substring from the first index, to the second index minus one.

trim() - Removes white space from both ends of this string.

toString() - Returns a string representation of the object. This method is inherited by all classes from Object. By default, the toString method for class Object returns a string consisting of the name of the class of which the object is an instance, the at-sign character '@', and the unsigned hexadecimal representation of the hash code of the object. You can override it in your own classes to produce some useful string e.g. for debugging info. For the Strings, toString() returns the String object itself.

State all operators that are legal in String expressions.

The + operator to concatenate strings.

©1999, 2000, 2002 Dylan Walsh.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being the disclaimer, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".