CSCI213
Autumn Session, 2007

Lab Exercise 1

This exercise is intended to help you get used to the Java development environment before you start assignment 1. It has lots of parts with little programs; you should enter and run each of these example programs so as to gain experience with the Java environment.

Laboratory classes for CSCI213 are normally held in the "Java" lab. in building 3. This lab. (3.229) is equipped with Dell workstations configured for either Linux or Windows use. We will run them in Windows mode.

(Well, that was what was promised! But the updating of the laboratory didn't take place. So we are to move to a different lab. Hope things work there!)

Although an attempt is made at the start of session to allocate you to specific lab. classes, this allocation is not that important. All that matters is that you don't all try to use the lab. at the same time. There is no permanent association between your lab. class and the tutor who will mark your submissions - all submissions are pooled and randomly allocated to markers.

You will be able to do most of your work on your own computer if you prefer. You will have to install some software - Java systems and usually a database system. The best way to work is to duplicate, as far as is practical, the laboratory environment on your own computer and then do preliminary work on your own computer coming to the labs with partly completed programs that you can then reference when discussing any problems with a lab-tutor.

You will need a USB flash-drive to store your code. When developing code at a workstation, you arrange that the necessary files are kept on your flash drive. Do not leave any files on the laboratory workstations.

We use the JDK 1.5 ("Java 5") dialect of Java. The older JDK (Java Development Kit) 1.4 dialect is still quite commonly deployed. Most of the books in the library will use JDK 1.4 or older dialects of Java; all books now in bookstores will use JDK 1.5.

In previous years, we have used the simple JDK environment for development. With the JDK, you use an ordinary text editor when preparing code, and you work at the command line (cmd.exe in Windows) using commands like javac to compile Java and java to run programs.

We will be using Sun's NetBeans 5 development environment. This should be installed on the workstations in 3.229. You can download the latest version from Sun for your own use; to save download costs, there is a copy on the banshee Unix machine in the /share/cs-pub/csci213 directory (you can access this directory via secure ftp or when logged in on a Unix terminal in one of the labs in building 17.)

(Again, the fact that the lab has not been updated may stop the planned switch to using NetBeans. The assignments as set assume NetBeans to be available and many of examples in lecture notes have been changed to take advantage of NetBeans. If it proves impractical to use NetBeans in the alternate lab then the old lecture notes can be put back; some parts of the assignments will need to be changed - Assignment 2 currently is set to use the NetBeans tool that helps create JavaDoc documentation, a task that is too painful to do without good tool support.)

NetBeans has been around for several years but it used to be very SLOW. Possibly the current version of NetBeans is better, more likely it is simply that current machines are so much faster. The current NetBeans is quite usable.

An integrated development environment like NetBeans has several advantages:

Such features generally improve your productivity - you will find it easier to get your programs to compile and run correctly when using an integrated development environment as compared with conventional editor/command-line environments.

The disadvantage of an integrated development environment is that you must learn to use its features. In the initial stages, it is more complex than the simpler environment of text-editor and javac, java commands.


  1. Login at one of the workstations; your user-identifier and password should be the same as used in the main labs in building 17.
  2. "Mount" your USB flash drive and create a folder "213Examples"; inside "213Examples" create a folder "Greetings".
  3. Check the desktop environment - there should be icons for shortcuts to the NetBeans application and to the main Java HTML documentation.
    NetBeans Java docs
  4. Open the Java documentation in Internet Explorere or Mozilla
    Java docs main page
    The top level page has links to extensive documentation. Some of the links go back to the Sun site - these include links to Sun's own Java tutorials. Use these links sparingly when in the laboratory, their use counts against your WWW quota. Other links are to local documentation files.
    The local documentation includes instructions in how to use the javac (compiler) and java (execution system) tools that are used when developing without an IDE.
    The main part of the local documentation - accessed via the link to the API (Application Programmer Interface) - consists of descriptions of the 3000+ classes in the various class libraries.
    Use the API links to open a window to the class documentation (Internet Explorer may raise a security dialog asking whether you want to activate a feature on the documentation page - give IE permission to activate the control). The API web page lists the names of all the classes as hypertext links; clicking a link results in display of information about a particular class (the information always includes a list of methods, sometimes there is introductory material explaining the use of the class and providing example code).
    API - class details
    You will be checking on details of particular classes later in this exercise.
  5. Click on the NetBeans icon to start the IDE. NetBeans should display a window divided into a number of panes, something like the following:
    Empty project window
  6. Use the File/New Project menu option to bring up a dialog that lets you define a new project. Select "Java Application" as the project type:
    Create new project
    Define a project named HelloWorldDemo, located in the 213Examples\Greetings folder on your flash disk. Set the main class name to be helloworlddemo.HelloWorld (rather than the default helloworlddemo.Main).
    Create Hello World
    (Your flash drive may not be mounted as drive G, use the "browse" dialog to find the correct location for your files).
  7. NetBeans will create the project and supply some default template code, when it is complete it will display this code. The display should appear like the following:
    Generated outline code
  8. The demonstration HelloWorld program will consist simply of a single main() function; we won't be creating any HelloWorld objects.
    Edit the generated code int the HelloWorld.java window in the NetBeans IDE; select and delete the (empty) constructor that was generated, and replace the line // TODO code application logic here with the line System.out.println("HelloWorld");
    The HelloWorld.java window should look like:
    Edited HelloWorld
    Use the File/Save menu command to save your edits.
  9. Use the Build/Compile "HelloWorld.java" menu command to compile the code. It should compile without errors:
    Compilation output
  10. Use the Run/Run Main Project menu command to run the program. The output window should display the results
    init:
    deps-jar:
    compile:
    run:
    HelloWorld
    BUILD SUCCESSFUL (total time: 1 second)
    
    (The program output from this little program - HelloWorld - is almost lost among all the administration output that lists the steps taken and the total time.)
  11. Practice stopping and resuming development.
    Select (left click on) the HelloWorldDemo project in the Projects pane of the NetBeans window (the line with the coffee cup icon). Then use either the File/Close Project menu option, or the pop-up "close project" option you get with a right click.
    Use File/Exit to terminate the NetBeans session.
  12. Restart NetBeans; use the File/Open Project menu option to get a dialog that lets you find and re-open a project:
    Re-open a project
  13. Command line arguments are accessible via the args String[] passed to main(). Their use is similar to but not quite identical to the use of command line arguments in C/C++. You can use a command line argument in your code as follows:
    public class HelloWorld {
    	public static void main(String[] args) {
    		String name = null;
    		if(args.length==0) name = "nobody";
    		else name = args[0];
    		System.out.println("Hello " +
    				name );
    	}
    }
    
    Arrays, like the array args here, have a read-only data member length. The if test checks whether there were any command line arguments provided (array would have a size greater than zero if there were some arguments). If there were arguments, the code takes the first as the name of a person to be greeted. (The first argument is in element [0] of the array; like C++, Java starts its arrays with element 0).
    The println() function is actually being invoked with a single String argument. The code:
    "Hello " + name
    
    uses String concatenation to build a new String object that contains the value of name appended to the constant "Hello ". String concatenation is only one of many, many things that you can do with Strings (just look at the String section in the class documentation to find out some more things you can do.)
    Rather than using the correct code immediately, try some incorrect code to see how NetBeans highlights errors in the editor and at compile time.
    The following code has some errors (missing close quote, missing semi-colon, misspelt class and method names). Delete the existing line System.out.println("HelloWorld"); from the HelloWorld main() and use copy-and-paste to transfer the following code into the main() function.
    		Sting name = null;
    		if(arg.length==0) name = "nobody"
    		else name = args[0];
    		System.out.prinln("Hello  +
    				name );
    
    If you paste that code in exactly as shown, the NetBeans editor should (after a momentary delay) highlight a couple of the errors with red check- marks in the left margin. If you move the mouse over a check-mark, a pop-up window will appear with an explanation of the error.
    Errors in edit window
  14. Fix the errors that NetBeans highlighted - the missing quote (") after the "Hello , and the missing semi-colon (;) after nobody.
    Once those errors have been fixed, NetBeans takes a closer look at the code and flags more errors with red check-marks:
    More errors!
    Fix the remaining errors, when all are fixed use File/Save to save the updated program.
  15. If you are running a javac compiled Java file with the java command, it is easy to supply any command line arguments, e.g.
    java HelloWorld Homer Simpson
    
    results in Homer being in args[0] and Simpson being in args[1].
    NetBeans has a rather more involved approach to supplying command line arguments.
    Select the HelloWorldDemo project in the projects pane of the NetBeans window, and use right-click to get a pop-up menu with options. Pick the options Properties to get the properties dialog.
    In the Properties dialog, pick the Run Category:
    Run time properties
    Supply some command-line arguments in the Arguments text field.
  16. Re-run the application and check the output.
  17. You can also get input from the keyboard. This is slightly more involved.
    An "InputStream" object, System.in, is defined along with the PrintStream object System.out. An InputStream object doesn't have very useful methods; it has got functions that allow you to read input one byte at at time, but that is soooo clumsy.
    Java's approach to i/o is based on the "streams and filters" model. Basically, you get a stream (something that supplies and consumes bytes) and wrap it inside "filter" classes until you get something which supports some useful level of functionality (more on this much later in the lecture course). Normally, when processing keyboard data, you will want to use a BufferedReader, as in the following example:
    public class HelloWorld {
    	public static void main(String[] args) {
    		
    		BufferedReader input = new BufferedReader(
    			new InputStreamReader( System.in));
    
    		System.out.print("Who are you? ");
    		String name = null;
    
    		try {
    			name = input.readLine();
    		}
    		catch(Exception e) {
    			System.out.println("Couldn't read from keyboard because"
    				+ e);
    
    			System.exit(1);
    		}
    
    		System.out.println("Hello " + name);
    
    	}
    }
    
    
    Here we can no longer rely purely on defaults. We want to use some stream filter classes - InputStreamReader, BufferedReader - that aren't in the basic java.lang package.
    If you are programming with just an ordinary text-editor and compiler, you have to note the classes that you use, find (from the API documentation) which packages they belong to, and add extra import statements at the start of your program. You can import classes individually with specific imports like:
    import java.io.BufferedReader;
    
    Often, it's easier to import the entire package:
    import java.io.*;
    

    An IDE like NetBeans can sort out the imports for you. If you replace the previous body of main() with the code shown above, NetBeans will show red check-marks on the lines where you refer to undefined classes:
    Need an import statement
    . Use the Source/Fix imports command to fix up the imports. (Of course, NetBeans has to guess which class you mean. It finds only one BufferedReader among the standard classes, so it guesses correctly that you mean java.io.BufferedReader. Sometimes, there are classes of the same name in different packages - NetBeans finds this confusing and will usually put up a dialog asking which you mean. Occassionally, NetBeans simply makes an incorrect guess and adds the wrong import statement. You can always fix the import statements manually.) In this case, NetBeans guesses correctly and adds a couple of import statements that you will find at the start of the HelloWorld.java file:
    package helloworlddemo;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    
    
    Once you have fixed the imports, save the file and read through the code.
    We start by creating a BufferedReader (useful because it has a handy readLine() method). (We create a new BufferedReader object giving it a newly created InputStreamReader object that works with the underlying System.in object; yeeech. Clumsy, but you will soon get used to this stuff.) Then we print a prompt, and want to read a user input.
    Which is where we hit the next problem.
    Java is obsessive about errors. Maybe something will go wrong when we try to read from the keyboard. We must check! This leads to the try { ... } catch(...) { ... } construct. An exception will be thrown by the library code if something does actually go wrong when reading from the keyboard. We must catch the exception and deal with it. Not much we can do apart from printing an error message and terminating via System.exit().
    When a BufferedReader object is asked to do a readLine() operation it actually creates a new dynamically allocated String in the heap. We get a reference to this new object and hold it in name. The new String contains the entire contents of the line.
    If we read a line successfully, we can print a greeting.
    Try compiling and running the program. (A little "input" text entry field appears at the bottom of the Output pane in the NetBeans window; you type input into this text entry field. Your prompt probably hasn't appeared before you need to supply input.)
  18. Suppose you want numeric input?
    Again, it is rather involved. You read the input into a String, you then explicitly attempt to parse the String to interpret the characters as representing a number. Here is an example:
    
    public class HelloWorld {
    	public static void main(String[] args) {
    		
    		BufferedReader input = new BufferedReader(
    			new InputStreamReader( System.in));
    
    		System.out.print("How many hellos do you want? ");
    		int number = 0;
    
    		try {
    			String data = input.readLine();
    			number = Integer.parseInt(data);
    		}
    		catch(Exception e) {
    			System.out.println("Couldn't read from keyboard because"
    				+ e);
    
    			System.exit(1);
    		}
    
    	
    		for(int i=0;i<number;i++)
    			System.out.println("Hello World");
    
    	}
    }
    
    
    Here we use a static member function in class Integer to do the parsing of the String "data" into an integer value. (It is a static member function, so we don't need to create an instance of class Integer and ask an Integer object to do the parsing). You can check the definition of the function in the Java API documentation:
    Look up Integer.parseInt
    If you respond to the prompt with data that cannot be interpreted as representing an integer then you will be hit with a format error.
    Edit the program to use an integer input. The output in NetBeans is a little messy (the prompt and input text field don't work well together - it is actually better when run using java command at the command line interface).
    init:
    deps-jar:
    Compiling 1 source file to G:\213Examples\Greetings\HelloWorldDemo\build\classes
    compile:
    run:
    6
    How many hellos do you want? Hello World
    Hello World
    Hello World
    Hello World
    Hello World
    Hello World
    BUILD SUCCESSFUL (total time: 6 seconds)
    
  19. Close the HelloWorldDemo project and all its files.
    Create a new WordCounts project, located in a different subdirectory of your 213Examples directory.
  20. If you get a String made up from separate words, you can chop it into individual words with a StringTokenizer. If you look up StringTokenizer you will find an explanation of how it works and the following example code fragment:
    StringTokenizer st = new StringTokenizer("this is a test");
    while (st.hasMoreTokens()) {
    	System.out.println(st.nextToken());
    	}
    
    This creates a tokenizer to work on the constant string "this is a test". It will split the string into individual words ("this", "is", ...); by default, string tokenizers split at white space characters.
    Write a short Java program that will prompt for an input line and print the individual words. (Class StringTokenizer is part of the java.util package; you will need an "import" directive for this package in addition to the import directives that you've already had in your programs. NetBeans will add the import line if you ask it.)
    When you are typing in code, rather than pasting existing code, NetBeans will start displaying pop-up windows with suggestions as to what it thinks you want to type! Like this:
    Prompting the typist!
    And like this:
    Guessing the function for you
    Try your new program on inputs like "Tom Dick and Harry" and "Tom, Dick, and Harry!". Read more about StringTokenizer and find how to set one up so that the punctuation marks are treated as delimiters along with white space characters.
  21. Sun added extra functionality to the String class with Java 1.4. The String class now has a split() method that allows a string to be broken up into an array of substrings. Now split() uses "regular expressions" that define the characters that act as substring terminators. "Regular expressions" are not part of the CSCI213 subject content; you may however get an introduction in one of the other CSCI subjects. Regular expressions can be quite sophisticated and rather complex to define.
    If you simply want to break up a long string to get substrings with only alphabetic characters, you can use the following simple example regular expression:
    String aLine = input.readLine();
    String[] words = aLine.split("[^a-zA-Z]");
    
    the regular expression [^a-zA-Z] specifies that all non-alphabetic characters are substring terminators.
    (Translating that regular expression:
    [...]	square brackets delimit a group of characters
    ^	here, the ^ character means "complement", the
    	characters that we are interested in are anything other than
    	those listed following the ^ character
    a-z	a character range, all lower case letters
    A-Z	a character range, all upper case letters
    
    So the expression says all characters that are NOT lower case or upper case letters, i.e. digits, punctuation marks etc. OK?)
    If you have input like -- "here is an example -- text, and numbers 19349 : what result did you get???" -- you will get an array of words with some empty Strings ("") e.g Java finds an empty string between the two hyphen characters. When processing the word array you would typically want to ignore empty strings (for(i=0;i<words.length;i++) { if (words[i].equals("")) continue; ... } )
    Sun encourage developers to use the split() method in the String class rather than a StringTokenizer. Try rewriting your code to use split() rather than a StringTokenizer.
    (Is there any real benefit from using split? Not in simple cases like this. But since split can do anything that StringTokenizer can do, and a lot more as well, Sun's view is that programmers should learn to use the more sophisticated tool. It is highly unlikely that StringTokenizer will be removed - too many existing programs use it.)
  22. The following shows NetBeans display from a buggy trial version of the word counting program that used split.
    Buggy code caught by run-time system
    The output pane of the NetBeans window shows there was a NullPointerException and it has also highlighted the line in the source window.
    The error is in the try {...} catch{...} block above. The code says read in a line, but don't store its value! The code should have been
    try {
    	line = input.readLine();
    }
    catch(IOException ioe) { ... }
    
    You will soon be meeting many such run-time errors!
    (With the code shown, line was initialized to null and never got set to reference an actual string. When execution reached the statement with line.split("[^a-zA-Z"]) the Java interpreter tried to use the value in line to determine which string to split. But there was no value in line, hence the NullPointerException.)
  23. Sun's site has an extensive Java tutorial; there is also a section on the Sun java site that contains simple example code to illustrate almost every class! For example, try http://java.sun.com/docs/books/tutorial/java/data/decimalFormat.html DecimalFormat That page is part of a tutorial on Java output formatting and gives some ideas as to how you can convert numeric values to Strings with specified formats.
    Try writing a little program, as part of another project, that reads in some integer and double numbers - look for Double.parseDouble(Sting), it works like Integer.parseInt(String) - and then prints them in differing neat formats.
  24. The following little program contains a number of new classes. Try it, read up about the classes, work out how it operates:
    
    public class Words {
        
        public static void main(String[] args) {
     		BufferedReader input = new BufferedReader(
    			new InputStreamReader( System.in));
    
    		LinkedList<String> wordList = new LinkedList<String>();
    
    		for(;;) {
    			System.out.println("next line");
    			String line;
    			try {
    				line = input.readLine();
    				}
    			catch(Exception e) { break; }
     
    			if(line.equals("Quit")) break;
    			if("quit".equals(line)) break; 
    			System.out.println("\t"+line);
    			String[] words = line.split("[^a-zA-Z]");
    
    			for(String word: words){
                	if(word.equals("")) continue;
                    word = word.toLowerCase();
                    if(wordList.contains(word)) continue;
    				wordList.add(word);
                }
    		}
    		Collections.sort(wordList);
    		System.out.println("Unique words");
    		for(String word: wordList)
    			System.out.println(word);        
        }
    }
    
  25. Here is a "better" version of essentially the same program (better because it breaks the work down into distinct functions).
    public class Words {
        private static LinkedList<String> wordList ;
        private static BufferedReader input;
        
        private static void initialize()
        {
     		input = new BufferedReader(
    			new InputStreamReader( System.in));
    		wordList = new LinkedList<String>();        
        }
        
        private static void readData()
        {
      		for(;;) {
    			System.out.println("next line");
    			String line;
    			try {
    				line = input.readLine();
    				}
    			catch(Exception e) { break; }
    			if(line.equals("Quit")) break;
    			if("quit".equals(line)) break; 
    			System.out.println("\t"+line);
    			String[] words = line.split("[^a-zA-Z]");
    
    			for(String word: words){
    				if(word.equals("")) continue;
    				word = word.toLowerCase();
    				if(wordList.contains(word)) continue;
    				wordList.add(word);
    			}
    		}      
        }
        
        
        private static void generateReport()
        {
            Collections.sort(wordList);
            System.out.println("Unique words");
            for(String word: wordList)
                System.out.println(word);       
        }
        
        public static void main(String[] args) 
        {
            initialize();
            readData();
            generateReport();
        }
    }
    
    
    Make certain that you understand the use of the "static" data members and "static" member functions.
    If you can characterize a few lines of code in a function with a phrase like "these lines do the initialization", "these read the data" then it is worth separating out those lines into a private auxiliary function that is called from the main function. Function calls are not that costly. Short simple functions that accomplish a clearly defined task are a lot easier to understand than a long complex function that performs many processing steps.
    Such auxiliary functions are always private for they are used only from within other (public) functions defined in the class. Here they have to be static, this is still procedural style code.
  26. Modify the program so that it expects a command line argument that is the name of a file. Explore how to create a BufferedReader that, with the aid of a FileReader, handles input from a file. Change the code so that the data are read from a file that you open. (If you get a file not found, or other exception, your program should terminate gracefully with an error message.)
    You should modify the input loop to change the termination condition. Instead of requiring the file to end with the word "Quit", you should simply terminate when you reach the end of file. The readLine() function will return null when you have reached the end of file. Your code should use this condition to terminate input.
    There is of course an issue of where the data file is located. You can make things work by providing the program with a command line argument that is the full path name for the data file. In NetBeans you would set the command line argument (in the Run section of the Properties dialog for the Words project) to something like:
    G:/213Examples/WordsProject/WordCounts/datafiles/data.txt
    
    or
    G:\\213Examples\\WordsProject\\WordCounts\\datafiles\\data.txt
    
    Note, if you supply a Windows style pathname for a file, you should double every back-slash character in the string (actually, with NetBeans it doesn't matter - NetBeans fixes up the paths; but generally Java programs don't like Window's style pathnames because a backslash character in a String is normally an escape character).
    If there is only one input data file, then it is easy to supply a full pathname as a command line argument. But if your program uses more than one input and/or output file, it is easier to specify the "working directory" used by the program and then just use simple filenames. The Properties/Run dialog has a section that lets you browse through your directories and set a working directory for a program.
  27. Some of the assignments require that you submit reports as PDF files. You are to prepare such reports using a word processor (most likely Word) and convert the report to PDF format. There are a number of free PDF converters available for download, see Google's listing. You will have to download one for your home PC (use your own ISP account, not the quota limited University account). (There may be a PDF converter in /share/cs-pub/csci213; this is not necessarily one of the better ones). Get a converter, prepare a small Word document on your own PC, and convert to PDF.
    There will be an example PDF report in /share/cs-pub/csci213; note that this long report was for a 25-mark assignment in a 400- level subject. It illustrates good presentation style for code and commentary.
    Another way of getting a PDF file is to generate a postscript file, transfer it to Unix, and use the Unix ps2pdf command. ps2pdf is not a supported Sun program. It appears to work but might have problems with complex files. (You can get a postscript file on Windows by the "Print to File" option; you will create a file with a name suffix of .prn; this appears to be more or less standard postscript.
    (And still another way is to use the Adobe site, you can upload a file and get back the PDF - but that approach is a bit clumsy.)