CSCI213 (aka ITCS907, alias MCS9213)
Autumn Session, 2007

Lab Exercise 2

This exercise is intended to improve your understanding of Java and gain a little more experience with the class libraries before you start Assignment 2.

The primary objective of Assignment 2 is to give you some experience of classes and inheritance. You will be creating classes that can be used with supplied framework code. Your classes will all implement a standard interface - they all do the same things, they just have their own individual ways of performing.

These preparatory exercises include:

  1. Farmyard program Working with an ultra simplified class hierarchy (thanks to the Perl development community, this is their introductory OO example).
  2. Basic graphics Side-task - primarily a graphic exercise (assignment 2 will involve limited use of graphics) but you do extend it to have a hierarchy of "Shape" classes
  3. Space Invaders You implement the SpaceInvaders program used as an example in lectures.

Farmyard: an ultra simplified "inheritance" exercise (thanks to Perl developers whose provided this example)

Overview

Inheritance allows a programmer to exploit similarities in behaviours to simplify a program's design. In the simplest case, the programmer defines an interface that specifies the common functionality and then creates a number of classes that all implement this interface.

In more complex cases, you can have an elaborate hierarchy that may have an interface or an abstract base class specifying the essential commonalities together with subclasses and/or partially implemented abstract classes that have code implementing some or all of the inheritted functions and which define additional more specialized functionality.

The example for the exercise is a little program that maintains a "farmyard" where there may be many different kinds of animal. All animals eat, make noises, etc etc. The example here is a simplified version of the one used for Perl OO.

The essence of being an "animal" in this program is defined via the interface animal:

public interface animal {
	void getResources();
	void identifySelf();
}

(An "animal" is something that can identify itself. There is also a getResources method; some animals used by the program need to do things like load sound files when they are created.)

Actually, this is NOT a good example of inheritance (sorry Perl guys, but you really have messed this up). The implementations defined for the different kinds animals are essentially all the same - there are no real differences in behaviour! Each kind of animal identifies itself in the same way - it plays a sound file (or prints a message if there is no sound file). Of course, the sounds differ - cows "moo", sheep "baah" (or vice versa if you get the sound files mixed up). But the code is essentially the same. These "classes" differ parametrically - they have different sound file parameters. One should NOT define different classes in such cases; a single class with some data members to hold the parametric data will suffice. However, if you were really trying to simulate a farmyard, then there would be many additional behaviours defined for the classes. These would have different implementations (think of behaviours - methods of the class - that express how the different kinds of animals eat or move).

The program is a little "menu select" style program offering commands that allow a user to add animals to the farmyard, or inspect the farmyard allowing animals to identify themselves. The "farmyard" is the heterogeneous collection of polymorphic references; it is simply a java.util.Vector holding references to different animals. The dynamic binding part in the "doVisit" function where each animal in the farmyard gets to identify itself.

The application is provided as a compressed NetBeans project (in /share/cs-pub/csci213/Lab2). The main project folder includes a "sounds" folder with a number of sounds in .au format. (The AudioClip class in the Java libraries can be used to play audio files in ".au", ".wav", ".midi", and ".aiff" sound formats. It cannot play most of the other sound formats. With ".wav" files, it is limited to simply "PCM" encoding - no compression, no stereo, no high sampling rates. Most of the sound files that you find on the Internet cannot be played. There are converters that will convert a high quality sound file to (a much larger) ".au" file that can be played.) The files making up the application are shown in NetBeans Files view:

Files in NetBeans project

Farmyard - main program

Driver program

The main program is:


public class A2a {
	private static Vector<Animal> theFarmYard;

	public static AudioClip getSound(String name) {
		// code discussed below
		...
	}
	
	private static void doVisit() {
		if(theFarmYard.size()==0) System.out.println("Silence, the farmyard is empty");
		else {
                System.out.println("Each animal in farm will identify itself and play sound");
                System.out.println("Multiple sounds may play concurrently");
                for(Animal a : theFarmYard ){
				       a.identifySelf();	
				}	
		}
	}
(The highlighted loop in doVisit is where we get "polymorphism" and "dynamic binding". The object reference variable Animal a is polymorphic - many shaped - sometimes referencing a "Cow" object, sometimes a "Rooster" object etc. The identifySelf method call involves dynamic binding. At run-time, the Java system determines whether to call Cow.identifySelf or Rooster.identifySelf.)
	private static void doCreate(BufferedReader input) {
		// code discussed below
		
	}
	
	private static void menuSelect(BufferedReader input) {
		try {
			System.out.println("Commands:" +
				"\nc\tCreate animal from specified class" +
				"\nv\tVisit farmyard" +
				"\nq\tQuit");
			for(;;){
				System.out.println("->");
				String line = input.readLine();
				if("q".equals(line)) break;
				else
				if("v".equals(line)) doVisit();
				else
				if("c".equals(line)) doCreate(input);
				else {
					System.out.println("Don't understand, see commands as above");
				}
			}
		
		}
		catch(Exception e) {
			System.out.println("Failed because " + e);
			System.exit(1);
		}
	}
	public static void main(String[] args) {
		try {
			BufferedReader input = new BufferedReader(
				new InputStreamReader(System.in));
			theFarmYard = new Vector<Animal>();
(Vector<Animal> theFarmYard is the heterogeneous collection; it contains Animals of different (hetero) kinds (genus).)
			menuSelect(input);	
			System.out.println("finished");
		}
		catch(Exception e) {
			System.out.println("Failed because " + e);
		}
	}
}

Farmyard hierarchy

Two Animal classes are defined in the NetBeans project supplied - Rooster and Cow. The Navigator/Inheritance view can show hierarchies - not that interesting in this trivial case:

Rooster's base classes

As shown in the diagram, class Rooster inherits from the Animal interface and, as always, from java.lang.Object.

The Rooster class is:

Rooster - functionality
public class Rooster implements Animal {
    
    private static int counter = 0;
    private static AudioClip theSharedSound;
    private int myId;
    /** Creates a new instance of Rooster */
    public Rooster() 
	{
        myId = ++counter;
    }

    public void getResources() {
	     if(theSharedSound==null)
            theSharedSound = A2a.getSound("Rooster.au");
    }

    public void identifySelf() {
        System.out.println("Rooster #" + myId + " crowing");
        if(theSharedSound!=null)
            theSharedSound.play();
    }
    
}

Each rooster gets its own identifier. Only one copy of the sound resource gets loaded; these roosters all crow the same way.

Creating the animals

The program has to be able to create animals that are instances of the different classes. Obviously, this part of the code has to know about the different possible classes. An initial version of the code is:

	private static void doCreate(BufferedReader input) {
		try {
			// Modify this to create your animal types
			System.out.print("Enter class of animal:");
			String line = input.readLine().trim();
			if(line.equals("Rooster")) {
				Rooster aRooster = new Rooster();
				aRooster.getResources();
				theFarmYard.add(aRooster);
				System.out.println("done");
			}
			else
			if(line.equals("Cow")) {
				Cow aCow = new Cow();
				aCow.getResources();
				theFarmYard.add(aCow);
				System.out.println("done");
			}
			else System.out.println("Don't know how to create a " + line);
			
		}
		catch(Exception e) {
			System.out.println("Failed because " + e);
			System.exit(1);
		}
		
	}

This code can be extended as more animal sub-classes are defined.

Really it is better to separate such code out from the main program so that there are no explicit references to the different animal subclasses. This can be done by defining a separate helper "factory" class that has a static method like "createAnimal". This factory class would be something along the following lines:

public class AnimalFactory {
	public static Animal createAnimal(BufferedReader input) { ... }
}

The main line code could simply make an invocation on this static class function.

Loading and playing sounds

Java can handle sound files in some formats. The code for loading sounds is all supplied by classes in the standard Java libraries. However, the required functions are distributed among a number of classes and the coding is a bit low level. The following fragment packages the code:

	public static AudioClip getSound(String name) {
		String baseName = System.getProperty("user.dir");
		String fileURL = "file:" + baseName + "/sounds/" + name;
		AudioClip theClip = null;
		try {
			theClip = Applet.newAudioClip(
					new java.net.URL(fileURL));
		}
		catch(Exception e) {
			System.out.println("Failed to load sound '" 
				+ name +"'");
			System.out.println(e);
			System.exit(1);
			}
		return theClip;
	}

This static function loads a file from a "sounds" subdirectory of the current working directory (by default, the working directory of a NetBeans program is the main project directory). Although a function rather than a complete class, this is a fragment of reusable code. You can cut and paste it into some helper class in any Java program where you need to load sound files. Here, for convenience, it was added to the A2a main class.


Tasks for exercise

  1. Get a copy of the "farmyard" project supplied and make it work in NetBeans.
  2. Define at least one additional class of animal and integrate it into the program.
  3. Reorganize the program so that it works with a separate "animal factory" class so removing all details of the special animal subclasses from the main program.

Basic "graphics" exercise

Assignment 2 involves some graphical and, optionally, audio output. Instances of the classes that you develop for A2 have to display themselves. The different classes could define different display code, though in this year's assignment that part of the code is probably the same for all your classes (and so should be defined in a base class).

The lectures will not have started exploring the Java classes for constructing graphical user interfaces. That does not matter. All the complex parts of the graphical output are supplied for you.

But graphics applications do allow for simple experimentation with hierarchies. In this laboratory class, you should try defining a hierarchy of different shape classes whose instances can be displayed graphically. Your main code will work entirely in terms of collections of shapes; at run time you can have rectangle, circle, polygon etc shapes.

Java has two graphics libraries: java.awt (AWT) and javax.swing (Swing). Both use the same Graphics class for application specific graphical output. Both provide collections of standard graphical components (windows, buttons, textfields, etc). A program using AWT may be a little faster; a Swing program probably looks better. At the base level, java.awt.Component, they share some commonalities. Many of their subclasses are also similar; for example there is a java.awt.TextField class for text input that matches with the javax.swing.JTextField class. You pick whichever library is more convenient (don't try to mix them, it doesn't always work). Some exercises and assignments use AWT, some use Swing.

A graphical user interface (GUI) will consist of:

The hard part of writing a GUI is the creation of all the objects and allocation of specific visual areas to these objects. Integrated development environments (IDE) often have visual editors that allow you to construct the GUI using "point-and-click" style operations (the IDE then creates the necessary Java code). The actual layout of different elements within a window is the most tiresome part; you have to consider issues such as what happens when the window is resized (which, if any, of the elements should grow or shrink - and by how much).

Once a GUI has been constructed, it is easy to use. Does it have an "action button"? You don't need to be concerned about how or when the button gets drawn, or how it appears when the mouse cursor moves over it. All you need to do is arrange for an instance of one of your classes to be told to handle an "action event" that gets generated when there is a mouse-click on the button.

Do you have a text input field? Again, you don't need to be concerned about how it appears, how it handles input and editing of character data. Your code simply asks the textfield for the input text data when these data are needed.

The only point where you must write graphic related code is where your program must display some application specific data. In these situations, a part of the GUI display is reserved as an area where your data will be shown. If you are using java.awt your GUI will have an instance of a specialized java.awt.Canvas; with javax.swing your GUI will contain an instance of an application defined subclass of javax.swing.JPanel. These specialized classes are quite standard in their forms. The GUI object (Canvas or JPanel) occupies a defined area of the window (relying on standard behaviours as defined in the Java library code). It will have an extra data member that is a reference to your "data object" and an extra function that allows your code to set this data member. The paint function for the class is overridden so that the request to perform the "painting" can be passed to your data object.

Actual output to the window ("painting") is done using an instance of the class java.awt.Graphics. The Graphics class defines a number of simple functions for drawing lines, ovals, rectangles, and some complex functions for drawing polygons and even images (read from "gif" or "jpg" files). These functions include:

(Smart students will notice that java.awt.Graphics is an interface not a class. One cannot instantiate an interface. The methods declared in an interface are not defined. So, how can a Graphics object exist and draw rectangles etc? The way it works is that the Java AWT code linked into your program creates an instance of some class that implements Graphics. When running on a PC, you would get a "PCGraphics" object, on a Macintosh you would get a "MacGraphics", and you would get instance of other classes on Solaris or Linux systems.)

(The coordinate system for Graphics is not the same as that which your learnt at school! Coordinate 0,0 is the topleft corner, and the "y" value increases as you go down, not up.)

Colors (sic) are instances of the class java.awt.Color. A color is defined in terms of the intensities of its red, green, and blue components. The color (255,0,0) is pure red; the color (255, 165, 0) is orange; the color (255, 255, 224) is a light yellow; and color (65, 105, 225) is "royal blue".

Example Graphics code

The code supplied for this exercise builds a GUI that has two panels as shown below. There is a "data panel" for display of application data, and a "control panel" that contains an action button. Each time the action button is clicked, the data panel gets redrawn with the data slightly changed (the coloured rectangle changes its size and colour).

GUI display

The code comprises the following classes:

All display classes are specialized subclasses of javax.swing.JPanel. The control and data panels have links to the data object. The control panel needs a link to tell the data object to change itself whenever the action button is activated. The display panel needs a link to the data object so that it can forward the paint command.

The ControlPanel's "pedigree", as shown in the Navigator "inheritance view", is a little more interesting than the previous inheritance example:

As shown in the diagram:

  1. a ControlPanel is a subclass that extends the class javax.swing.JPanel and implements the ActionListener interface
  2. a JPanel is a subclass that extends the class javax.swing.JComponent and implements the Accessible interface
  3. a JComponent is a subclass that extends the class java.awt.Container and implements the Serializable interface
  4. a Container is a subclass that extends the class Component
  5. a Components is a subclass that extends the class Object and implements the ImageObserver and MenuContainer interfaces

(Single inheritance through the extends sequence, multiple interfaces - not the same as C++ multiple inheritance.)

If you want to know what any of the base classes or interfaces does, moving the mouse over the entry in the inheritance panel will cause a pop-up to appear with information.

The data object, instance of A2ExData, has to have a link back to the display panel. Whenever the data object is changed, it needs to request a "repaint" operation. (In normal usage, a data object requests a "repaint" operation and expects to receive a subsequent command to "paint"; the paint operation will be scheduled by code in the Java graphics libraries. It is only in special circumstances, such as animations, that a data object goes ahead and paints directly to the GUI.)

The code for these classes is:

class A2Ex
public class A2ex {
    
	private static Toolkit toolkit;
	private static Component displayer;
	public static Image getImage(String name) 
	{
		// Not used in the example
		String baseName = System.getProperty("user.dir");
		String fileName = baseName + "/images/" + name;
		Image anImage =  toolkit.getImage(fileName);

		MediaTracker waitForIt = new MediaTracker(displayer);
		waitForIt.addImage(anImage, 1);
		try { waitForIt.waitForID(1); }
		catch (InterruptedException ie) { }
		return anImage;
	}

	public static AudioClip getSound(String name) 
	{
		// Not used in the example
		String baseName = System.getProperty("user.dir");
		String fileURL = "file:" + baseName + "/sounds/" + name;
		AudioClip theClip = null;
		try {
			theClip = Applet.newAudioClip(
					new java.net.URL(fileURL));
		}
		catch(Exception e) {
			System.out.println("Failed to load sound '" 
				+ name +"'");
			System.out.println(e);
			System.exit(1);
			}
		return theClip;
	}
	
	public static void main(String[] args) 
	{
		try {
			// Ignore this Java hacking that defines
			// an "anonymous inner class".  It is just
			// a short cut way of generating code
			// that handles "Window" events coming from
			// the operating system.
			WindowListener l = new WindowAdapter() {
				public void windowClosing(WindowEvent e) {
					System.exit(0);
				}
			};
		
			// Create the "data object"
			A2ExData someData = new A2ExData();
			
			// Create the window
			JFrame aFrame = new JFrame("A2Exercise");
			aFrame.addWindowListener(l);

			// Create the GUI display that goes in the window
			// and link it to the data object
			A2ExGUI displayComponent = new A2ExGUI(someData);
			
			// set references needed by helper functions that
			// can be used to load sound and image files
			displayer = displayComponent;
			toolkit = displayer.getToolkit();	
			
			// Organize the GUI
			aFrame.getContentPane().add(displayComponent);
			aFrame.setSize(new Dimension(640,640));
			// Get it displayed
			aFrame.setVisible(true);
			
			// Main program now ends!
			// Don't worry, there is a new thread running in
			// the awt graphics library.  it is this thread
			// that keeps the program running.
	   }
	   catch(Exception e) {
		System.out.println("Failed because : " + e) ;
	   }
	}
    
}
class A2ExData
public class A2ExData {
	// Data object - would normally own some
	// collection of shapes; here there is only
	// one shape - the colored rectangle defined
	// by the private data members that set its (r,g,b) color
	// etc
	private DataPanel myDataPanel;
	private int dx;
	private int dy;
	private int rcol;
	private int gcol;
	private int bcol;
	private Random rgen;

	public A2ExData() 
	{
	    // Create some data.
		// In your "shapes" version, you should create a
		// LinkedList<Shape> and then create
		// a random collection of Shape objects of
		// different kinds
		rgen = new Random();
		dx = 0;
		dy = 0;
		rcol = rgen.nextInt(255);
		gcol = rgen.nextInt(255);
		bcol = rgen.nextInt(255);
	}
	
	public void paint(Graphics g) 
	{
	    // In your "shapes" version, you will iterate through
		// a collection of shapes telling each to paint itself
		System.out.println("Painting the data object");
		Dimension d = myDataPanel.getSize();
		g.clearRect(0,0,(int)d.getWidth(),(int)d.getHeight());
		g.setColor(new Color(rcol,gcol,bcol));
		g.fillRect(40,40,60+dx,80+dy);
		g.setColor(Color.BLACK);
	}
	
	public void setLinkToDataPanel(DataPanel dp)
	{
		// When the data are changed, they have to
		// be redrawn; need to inform display - so
		// first need a link to the display
		myDataPanel = dp;
	}
	
	public void paintAgain() 
	{
		// Change the data and request a repaint
		//System.out.println("Told to change data and repaint");
		dx++;
		dy+=2;

		rcol = rgen.nextInt(255);
		gcol = rgen.nextInt(255);
		bcol = rgen.nextInt(255);
		myDataPanel.repaint(); // Request redraw after change
		if(dx>400) {
			dx = 0;
			dy = 0;
		}
		// In your shapes version, you will modify individual
		// shape objects or you could change the contents of
		// the collection
	}

}

class A2ExGUI
public class A2ExGUI  extends JPanel {
    // Define the contents of Graphical User Interface
	// Placing a control panel on right and a larger data
	// display panel on left
	private DataPanel	dp;
	private ControlPanel cp;

	private A2ExData myData;
	public A2ExGUI(A2ExData someData)
	{
		myData = someData;

		dp = new DataPanel(myData);
		cp = new ControlPanel(myData);
		cp.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
		
		GridBagLayout gbl = new GridBagLayout();
		GridBagConstraints gbc = new GridBagConstraints();
		setLayout(gbl);
		// Layout is 8x8; a 6x8 area for data display and a 2x8 area for controls
		
		// Data gets all of the x- expansion
		gbc.weightx = 100;
		gbc.weighty = 100;
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.gridheight = 8;
		gbc.gridwidth = 6;
		gbc.insets = new Insets(2,2,2,2);
		gbc.anchor = GridBagConstraints.CENTER;
		gbc.fill = GridBagConstraints.BOTH;
		add(dp, gbc);		

		gbc.weightx = 0;
		gbc.weighty = 100;
		gbc.gridx = 6;
		gbc.gridy = 0;
		gbc.gridheight = 8;
		gbc.gridwidth = 2;
		gbc.insets = new Insets(2,2,2,2);
		gbc.anchor = GridBagConstraints.CENTER;
		gbc.fill = GridBagConstraints.BOTH;
		add(cp, gbc);	
	}

}
class DataPanel
public class DataPanel extends JPanel {
	private A2ExData myData;
	public DataPanel(A2ExData theData) {
		myData = theData;
		myData.setLinkToDataPanel(this);
	}
	public void paint(Graphics g){
		myData.paint(g);
	}

}
class ControlPanel
public class ControlPanel extends JPanel implements ActionListener {
	private A2ExData myData;
	private JButton	paintButton;
		
	public ControlPanel(A2ExData theData) {
		myData = theData;

		GridBagLayout gbl = new GridBagLayout();
		GridBagConstraints gbc = new GridBagConstraints();
		setLayout(gbl);
		// Column header
		gbc.weightx = 0;
		gbc.weighty = 0;
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.gridheight = 1;
		gbc.gridwidth = 1;
		gbc.anchor = GridBagConstraints.CENTER;
		gbc.fill = GridBagConstraints.NONE;
		add(new JLabel("Control Panel"), gbc);

		// action button
		gbc.weightx = 0;
		gbc.weighty = 0;
		gbc.gridx = 0;
		gbc.gridy = 1;
		gbc.gridheight = 1;
		gbc.gridwidth = 1;
		gbc.anchor = GridBagConstraints.CENTER;
		gbc.fill = GridBagConstraints.NONE;
		paintButton = new JButton("Paint again");
		add(paintButton, gbc);		
		paintButton.addActionListener(this);
		
		// Filler panel to take space if display enlarged
		gbc.weightx = 0;
		gbc.weighty = 100;
		gbc.gridx = 0;
		gbc.gridy = 2;
		gbc.anchor = GridBagConstraints.CENTER;
		gbc.fill = GridBagConstraints.BOTH;
		add(new JPanel(), gbc);	
				
	}
	
	public void actionPerformed(ActionEvent aev) {
		// User action - request a new set of data and new display
		myData.paintAgain();
   }
    
}

Tasks for exercise

Basic graphic tasks

  1. Take the supplied NetBeans project (/share/cs-pub/csci213/Lab2) and install in NetBeans, get it to run
  2. Experiment with the drawing functions in class Graphics; get your data object to draw rectangles, ovals, and polygons in a variety of colors with the display changing each time the action button is activated.
  3. Try using a predefined image (read from a gif or jpeg file) within your graphic display. The Graphics class has a "drawImage" function that lets you draw a scaled version of your image at a chosen position. (This function requires as an argument a reference to an "ImageObserver". You should organize your code so that you pass a reference to your DisplayPanel. Generally, a GUI component that is to contain an image is the most appropriate "image observer". Image observers are a very low level technical detail. Their use relates to problems associated with downloading images across networks.)

Shapes hierarchy

In this exercise you extend the graphics example.

  1. Define a "shape" interface that has paint and move member functions and a set of shape subclasses that can paint different shapes (e.g. MyRect implements Shape, MyPoly implements Shape etc).
  2. Redefine the "data object" class so that it owns a collection of "shapes" and so that its paint method passes the paint request to each shape in its collection. Get your modified version of the program to work with the shapes changing their positions at each request to redraw the data.
  3. Add controls to the control panel that allow a user to create additional "shape" objects (picked from some menu) and add these to the data object's collection.

Exercise combining graphics and use of inheritance

SpaceInvaders

game image

You will have met this example in lectures. You can play with my version:

java -jar SpaceInvaders.jar

You should find the SpaceInvaders.jar file in /share/cs-pub/csci213/Lab2.

Code for the framework parts is supplied as a Windows zip file ( available in /share/cs-pub/csci213/Lab2). This zip file contains the NetBeans project.

The supplied framework builds a graphical user interface (GUI) and organizes the mechanism for running the game. The GUI display is shown above. The display comprises a "game panel" and a "control panel". The game panel shows a "gun" that can be controlled by the player and the waves of attacking space invaders of various types. The control panel holds a couple of text output fields that contain regularly updated data with the players "score" and "status" and an action button that may be used to start the game, or restart the game after the player's "gun" has been destroyed by invaders.

Supplied code

The code comprises the following classes and interfaces:

JavaDoc documentation has been generated for the supplied classes.

In this version of Invaders:


Exercise

Define, implement, and document some additional invader classes and integrate them with the framework code supplied.

The following are suggestions for possible invaders, but you can invent others with more complex behaviours:

GIF/PNG/JPG images can be loaded and used for invaders. Additional sounds can be loaded - only need to load each sound once - and used when invaders move. Don't overdo sound effects or your game will be intolerable. Invaders can obtain a reference to the GamePanel from the Game; this could be used if special graphical effects are required (effects similar to the gun fire effect). This reference to the GamePanel will be needed by any invader classes that choose to use "gif" or "jpeg" images rather than graphic drawing actions. Again, don't overdo the advanced graphical effects for they can slow your game or otherwise make it unplayable.


JavaDoc comments in NetBeans

Your explorations of the Java libraries should have introduced you to JavaDoc documentation. JavaDoc relies on a highly constrained way of adding comments to your code; the javadoc program reads your code, finds these special comments, and generates HMTL reference pages using the same stylesheets as used for the Java libraries. JavaDoc is tedious to use manually - but once again, an IDE can make things simple.

In the NetBeans/Project pane, right-click on the class that is to be documented and pick Tools/Auto comment from the pop-up menu. The pane used to display code will be hidden by a new pane that allows you to add comments to a class.

Editing the Java docs

Normally, only public members (and public classes) are documented. The editing display should show the class and those members that require documentation. A red check mark by an entry means that it has no documentation; a yellow check mark indicates that documentation is in some way incomplete; a green check mark flags entries for which JavaDoc style comments have been added.

Pick each entry in turn and supply the documentation. The class entry should get a brief description of the role of the class. Functions should get documentation describing their role and detailing any input parameters and returned result. After selecting a function that is to be documented, use the AutoCorrect button to add @param and @return tags (initially empty).

Compose JavaDoc documentation describing your subclasses of SpaceInvader.

Once you have added comments to all your classes, you can generate the HTML pages by selecting the project and using NetBean's Build/Generate JavaDoc for ... menu option.