UCL Logo

COMP1008 Object-Oriented Programming 2006

Programming Notes and Exercises 1

Recommend finishing date: Friday 27th January 2006


Purpose: Some revision and continuing on from COMP1007 by giving further practice of writing programs that using methods and files.

Goal: Complete as many of the exercise questions as you can. If you are keeping up, you need to do at least the core questions. The additional questions are more challenging and are designed to stretch the more confident programmers. Don't worry if you can't do them now, but be prepared to come back and try them later on.

Feedback: It is important that you get feedback on your exercise answers so that you know they are correct, that you are not making common mistakes, that the program code is properly presented and that you are confident you have solved the problem properly. To do this, get your answers reviewed by a lab demonstrator during lab helpdesk sessions.

NOTE: You must keep all exercise answers as they form a record of your progress. After the exams you may be required to hand-in all exercises and coursework answers, as part of the course assessment process.

See the additional notes on the 1008 web page about using files.


Example Questions and Answers — READ THESE CAREFULLY

The following are examples of questions and answers, to show how the Java syntax works, and to illustrate the kinds of programs you should be writing. There are further examples in the text book.

Example 1.1 Write a class that has the following methods:

  • public int sumOfDigits(int n) — to find the sum of the integer parameter's digits and return the result.
  • public void inputAndProcess(KeyboardInput in) — to input an integer, call sumOfDigits
    and display the result.

Hint: % is the division operator that returns the remainder. Try using it to divide by 10.

Answer:

// Written by A.Person, January 2006
// Answer to Ex1 example question 1
class Example1_1 
{
  public int sumOfDigits(int n)
  {
    int sum = 0; 
    n = Math.abs(n); 
    while (n > 0) 
    { 
      sum += n % 10; 
      n /= 10; 
    } 
    return sum;
  }
  
  public void inputAndProcess(KeyboardInput in)
  {
    System.out.print("Type an integer: "); 
    int n = in.readInteger(); 
    System.out.print("The sum of the digits of: " + n); 
    System.out.println(" is: " + sumOfDigits(n)); 
  }
  
  // The main method should do no more than create the objects
  // and call a method to do the work.
  public static void main(String[] args) 
  { 
    KeyboardInput in = new KeyboardInput();
    Example1_1 myObject = new Example1_1(); 
    myObject.inputAndProcess(in);
  } 
} 

Further thoughts:

  • Does the program work correctly with negative integers? Copy, compile and run the program to find out.
  • Why is the KeyboardInput object created in the main method and not the inputAndProcess method? Is this a good decision?
  • Note the use of Math.abs. This is a function implemented as a utility method in class Math (see the online class documentation for more details). The abs function returns the absolute value of a double, basically meaning that it removes the minus sign from any negative number.

Example 1.2 Write a one-class program that has a method to ask for and return the name of a text file, a method taking a file name (a String) as a parameter that reads and displays each line of text in the file on the screen, and a method to call the first two methods.

// Written by A.Person, January 2006
// Answer to Ex1 example question 2
class Example1_2
{
  private void displayFileContent(String filename)
  {
    FileInput fileIn = new FileInput(filename);
    String s = fileIn.readString();
    while (!fileIn.eof())
    {
      System.out.println(s);
      s = fileIn.readString();
    }
    fileIn.close(); // Always close a file after it has been used.
  }


  private String getFileName()
  {
    KeyboardInput in = new KeyboardInput();
    System.out.print("Enter filename: ");
    String filename = in.readString();
    return filename;
  }

  public void showFile()
  {
    String filename = getFileName();
    displayFileContent(filename);
    // Could have written displayFileContent(getFileName());
  }

  public static void main(String[] args)
  {
    Example1_2 myObject = new Example1_2();
    myObject.showFile();
  }
}

This uses the FileInput class. To get a copy of the class and information about how to use it, see the notes on the web at http://www.cs.ucl.ac.uk/staff/G.Roberts/courses2005_6/code/fileinput.html). Look at the way that data is read from the file in the while loop. The eof() method will return true when an attempt has been made to read data from beyond the end of the file (not simply when the end of file has been reached). Hence, before the while loop an attempt is made to read a string so that eof will be set to true before the loop is run if the file is empty.

Note that the methods displayFileContent and getFileName have been made private. The public showFile method, acting as a controller, is the only one that can be called for an object in the main method.

Example 1.3 Write a one-class program that uses a method to display a multiplication table, given an integer between 2 and 12 inclusive. The program should ask the user which table to display and reject any invalid request.

Notes: With most of the previous exercises you really only thought and planned in terms of a single statement sequence. However, now you know how to write methods, you want to update your strategy and first consider the design in terms of methods.

Obviously there will be a method that displays the multiplication table, which can be called displayTable. This will take an integer parameter but does not need to return a result, so can be declared as void displayTable(int n). Equally obvious, there will be a main method, as all programs must have one. However, should the main method ask the user for input and call displayTable, or should that behaviour be put into another method?

The answer to this question lies in an important method design principle: each method should be cohesive — that is, it should focus on doing one, and only one, well-defined chunk of behaviour needed by the program. Following this principle, the main method can focus on initializing the program by creating an object and then call another method to handle the user input. That method can, in turn, then focus on doing one thing before calling the method to display the table. Hence, we now have a method called doTable, and established which method calls another.

With the methods and their behaviour identified, each can now be looked at in turn to design their respective method bodies. Moreover, rather than having to consider the entire statement sequence in the program at one go, we are now concerned only with small portions at a time. This has divided a larger and more complex design problem into a collection of smaller, and more easily designed, components. Further each component has a well-defined interface by which it connects to other components. The interface is the name of the method and its parameter list (i.e. the number and types of the parameters) and the returned value if not void.

The design of each method is now straightforward. Method displayTable needs a loop and output statements to display a multiplication table, while doTable asks the user to enter a value and checks it is within the supported range. If it is then displayTable is called, otherwise an error message is displayed.

// Written by A.Person, January 2006
// Answer to Ex1 example question 3
// Program to display a multiplication table.
public class MultiplicationTable 
{
  // Display multiplication table for n.
  private void displayTable(final int n) 
  {
    int counter = 1;
    System.out.println("The " + n + " times table");
    while (counter < 13) 
    {
      System.out.print(counter + " x " + n);
      System.out.println(" = " + counter * n);
      counter = counter + 1;
    }
  }

  // Input table to be displayed and get it displayed.
  public void doTable() 
  {
    KeyboardInput in = new KeyboardInput ();
    System.out.print("Which table (2-12)? ");
    int x = in.readInteger();
    if ((x < 2) || (x > 12)) 
    {
      System.out.println("Cannot display that table");
    } 
    else  
    {
      displayTable(x);
    }
  }

  public static void main(final String[] args) 
  {
    MultiplicationTable myTable = new MultiplicationTable();
    myTable.doTable();
  }
} 

Further thoughts: 

  • Note where the KeyboardInput object is created in this program. Is this the correct decision? The strategy should be to always minimise the scope within which a name is visible.
  • Is the method doTable actually well-designed and cohesive? Perhaps it would be better to change it so that it only does the input and returns an integer, removing the if statement to a new method.

A method name should be chosen to help describe what the method does. Are the names chosen in these example programs good enough or can you suggest better names? Does doTable properly suggest the role of the method?

Example 1.4 Write a class that has methods to input a collection of integers, storing them as integer objects in an ArrayList<Integer>, to sort the ArrayList and to display the sorted contents of the ArrayList. The ArrayList should be stored using an instance variable.

// Written by A.Person, January 2006
// Answer to Ex1 example question 4

import java.util.ArrayList;   // Need to import the class before it
                              // can be used.
import java.util.Collections; // Class Collections provides a sort
                              // method for collections objects
                              // such as ArrayLists.

class Example1_4
{
  private ArrayList<Integer> numbers = new ArrayList<Integer>();

  private void displayIntegers()
  {
    for (int i = 0; i < numbers.size(); i++)
    {
      System.out.println(numbers.get(i));
    }
  }

  private void inputIntegers()
  {
    KeyboardInput in = new KeyboardInput();
    System.out.print("How many integers to input? ");
    int count = in.readInteger();
    for (int n = 0; n < count; n++)
    {
      System.out.print("Enter integer (" + n + ") :");
      int value = in.readInteger();
      numbers.add(new Integer(value));
    }
  }

  private void sortIntegers()
  {
    // Easy if you can find and use a method in the class library!!
    Collections.sort(numbers);
  }

  public void run()
  {
    inputIntegers();
    sortIntegers();
    displayIntegers();
  }

  public static void main(String[] args)
  {
    Example1_4 myObject = new Example1_4();
    myObject.run();
  }
}

This example makes use of the generic ArrayList class, where the <Integer> notation denotes that the ArrayList can hold Integer objects only. Class Integer is a library class that represents int values as objects, in contrast to primitive values of type int, which are not objects. A mechanism called auto boxing and unboxing, automatically deals with conversions between int values and Integer objects, allowing int values to be stored in an ArrayList<Integer> without needing to do any conversions explicitly.

Notice how easy the sorting turned out to be! A suitable sort method already exists in the class library, the only work lies in finding the class and method - use the index in the Java class documentation. Also notice the line:

System.out.println(numbers.get(i));

This works as many objects have the ability to return a string representation of their value that println requires. Look for class Integer in the Java class documentation and find the toString method.


Example 1.5 Write a one-class program that determines whether a word or sentence is a palindrome. (A palindrome reads backwards the same way as forwards, for example: "Able was I ere I saw Elba".) Spaces are considered as significant.

Notes: The problem statement gives little information about designing the program, so we definitely need to do some thinking and planning before trying to write code. First of all we need an algorithm to test whether a string is a palindrome. This turns out to be easy:

make a copy of the string
reverse the order of the characters in the copy
compare the reversed copy with the original to see if they are the same

Now that we know that it's possible to check for a palindrome, the next step is to consider the overall sequence of events that should take place when the program runs. This gives:

1. input string to test
2. make a copy
3. do the reverse
4. check the reverse copy against the original
5. display answer

Next we want to determine what methods are required and allocate the steps listed above to them. We could bundle everything into a single method but that would violate our cohesion guidelines. Instead, we identify the following methods:

String getInput() // Get the input string to be tested. (1)
String reverse(String s) // Return the reverse of the argument String (2,3)
boolean check(String s1, String s2) // Is s2 the reverse of s1? (4)
void testForPalindrome(String s) // Call the check method and display the answer (5)

This may seem more methods than necessary. That could turn out to be true but at this stage we are deliberately breaking the overall behaviour down into minimally sized methods to get a good feel for the design. Methods can be combined and behaviour re-allocated later if it seems appropriate. Note that making a copy of a String is a basic operation that does not need to put into a separate method on its own.

With the methods identified, each can be considered in turn to design its method body. Method getInput is easy as it simply requests input and returns the resulting string. TestForPalindrome is also straightforward, it just need to call the check method with the same string for both parameter values and display a suitable message giving the result. Check is a bit more interesting as the decision has been made to make it more general purpose than is strictly necessary for this version of the program. The idea for check is that it can actually test any two strings to see if they are the reverse of each other. TestForPalindrome will call it with the same string for both arguments since it checks for self-reversed strings. Check works by reversing one string and then doing a string comparison using the compareTo method in the String class.

This leaves reverse, which requires a bit more work. The basic strategy here is to unpack the original string character by character and reassemble the characters in the reverse order to create a new string. To find out how to extract individual characters from a string we need to return the documentation for class String. This shows that there is a method declared as char charAt(int index), which returns the character at position index, counting from 0 as the position of the first character. The String documentation also shows a method int length() that returns the number of characters in a String. With these methods we can construct a loop to access each character in turn:

// Given some String s
int position = 0;
while (position < s.length()) 
{ 
  // Characters run from 0 to length-1
  ... s.charAt(position); // Get the character and do something with it
  ...
  position = position + 1; // Move to next character
}

Building the new string requires a search of the class documentation to find a method to join a char to a String. The String class does not provide one, so we start looking at other classes. It turns out there is a class Character, whose objects represent characters, that can be used to create a String from a single char using an expression like:

// Given a char c
... new Character(c).toString() ...

The toString() method converts from a Character object to a String object. The two strings can then be concatenated using the + operator.

We now have all the pieces ready to write the program. However, a review of the problem statement to check we are still answering the right question highlights a problem: the sample palindrome is only a palindrome if the case of the letters is ignored, if the case of the letters is significant then the sample string is not a palindrome. If our current design was applied to this sample string, it would not be accepted as a palindrome. Given that the string was supplied as an example of a palindrome, it must be that case is not significant. We therefore need to address this in our design.

The answer to the problem is to convert all the characters in the input string to lower case before testing to see if it is a palindrome. A further search of the String class documentation provides the solution in the form of a method declared as String toLowerCase(), which returns a lower case version of the string it is called for. Calls to the method can be inserted appropriately to cause case to be non-significant.

// Written by A.Person, January 2006
// Answer to Ex1 example question 5
// Program to check for a palindrome.
public class Palindrome 
{
  // Return a String which is the reverse of the argument String
  private String reverse(final String s) 
  {
    String result = new String();
    int position = 0;
    while (position < s.length()) 
    {
      result = new Character(s.charAt(position)).toString() + result;
      position = position + 1;
    }
    return result;
  }

  // Check to see if the two argument Strings are the reverse of each
  // other. Return true if they are and false otherwise..
  private boolean check(final String s1, final String s2) 
   {
    String s = reverse(s2);
    if (s1.compareTo(s) == 0) {
      return true;
    } 
    else  
    {
      return false;
    }
  }

  // Get user to input a String.
  private String getInput()  
  {
    KeyboardInput in = new KeyboardInput ();
    System.out.print("Enter text to check: ");
    return in.readString();
  }

  // Do the palindrome test and display the result
  public void testForPalindrome(final String s) 
  {
    if (check(s.toLowerCase(), s.toLowerCase())) 
    {
      System.out.println("String is a palindrome.");
    } 
    else  
    {
      System.out.println("String is not a palindrome.");
    }
  }

  public static void main(final String[] args) 
  {
    Palindrome myObject = new Palindrome();
    myObject.testForPalindrome(myObject.getInput());
  }
}

Note that all the method parameters have been declared as final. This is an idiom that some programmers like to use. The value of a final parameter cannot be altered within the method body, so declaring a parameter final lets the programmer explicitly state that the parameter should not be changed and get the compiler to check that it doesn't. Note that when a variable of class type is declared final, it cannot be changed to refer to a different object. However, the object being referenced can still be altered by calling a method that changes its state (i.e., the value of an instance variable held by the object is changed).


Core questions

Note: Answers to all the questions involve writing programs consisting of two or more methods (not including the main method).

Q1.1 Write a one-class program that has the following methods:

  • a method that inputs and returns a double value,
  • a method that takes two double parameters, adds them together and returns the square root of the result,
  • a main method to create an object and call the other two methods, displaying the result of calling the second method.

Q1.2 Repeat Q1.1 but store the double values in two instance variables rather than using parameters.

Hint: you will need another instance method to call the input method to get values to store in each instance variable and to call the other methods. This instance method should be public, the others private. The input method should be unchanged. 


Q1.3 Write a method: public String toBase(int n, int b) that converts n to its String representation in number base b, i.e., toBase(3,2) will return "11". Put the method into a one-class program that allows you to input values and use the method.


Q1.4 Write methods to do the following:

  • Convert from millimetres to feet.
  • Convert from metres to inches.
  • Convert from kilometres to yards.

Include the methods in an interactive program that lets the user select which conversion to perform, and then inputs a value and displays the result.

An interactive program means one that displays a simple text menu, such as:

1. Convert millimetres to feet.
2. Convert metres to inches.
3. Convert kilometres to yards.
4. Quit

and then asks the user to type in a value from 1 to 4 to select which action to take. A loop will continually redisplay the menu until the user selects Quit (number 4) to terminate the program.

Notes: The conversion methods should take the value to be converted as a parameter and return a result. They should not do any input or display the result. Add additional methods if they will help structure the program.


Q1.5 Write a one-class program to determine if a long integer is a palindrome (i.e., represents the same value when reversed, for example 123454321).


Q1.6 Write a one-class program with suitable methods to read a text file character by character and count how frequently each character occurs.

Hints: You will need to use the FileInput class, which is used in a similar way to KeyboardInput. See the notes on using FileInput.
The FileInput .class file needs to be in the same directory as the program you are working on. Copy FileInput.class to all directories containing programs that need to use it.


Q1.7 Using the methods from Q1.6, write a drawing program to display a line or bar chart showing the frequency of each letter. The drawing of the chart should itself be done using a collection of methods — draw the x-axis, draw the y-axis, draw the lines/bars. Note, that the doDrawing method should effectively take the place of the main method of previous programs, so that all your methods are called directly or indirectly from doDrawing.

Method doDrawing can call other methods to do draw different parts of the graph. The Graphics object has to be passed as a parameter to the other methods.


Q1.8 Write a program using methods that copies and reverses a text file (i.e., so that the destination file contains a backwards copy of the original file contents).

This program will need both the FileInput and FileOutput classes (described in separate notes).


Harder Questions

Q1.9 Write a method to test if an integer of type long is a prime number. The method should return a boolean value. Test your method by writing a test one-class program that reads an integer typed at the keyboard and states whether the integer was prime.

Next, using your prime method, write a program that finds all the prime numbers that can be represented by an integer of type long.

Notes: This is not quite as easy as it might appear, especially if you want the program to produce results quickly. Search the web for information about prime numbers and algorithms for finding them - there are some excellent web sites.


Q1.10 Write a program that reads an integer between 0 and 999 and "verbalizes it". For example, if the program is given 123 it would display "one hundred and twenty three".

Hint: Write methods to deal with ranges of numbers, such as single digits between "zero" and "nine", numbers between 10 and 19 and so on.


Challenge

Q1.11 Redo some of the earlier questions using one or more different programming Object-Oriented Programming languages. In particular look at C++, Ruby and Python. You will find plenty of information about these languages on the web.

Last updated: January 25, 2007

Computer Science Department - University College London - Gower Street - London - WC1E 6BT - Telephone: +44 (0)20 7679 7214 - Copyright 1999-2006 UCL


 Search by Google