CSC212 May22

slide version

single file version

Contents

  1. Abstract Classes
  2. Abstract Methods
  3. Non-Abstract Methods in an Abstract Class
  4. Advantages of Abstract Classes
  5. Java's AbstractList Class
  6. MyList Example
  7. Using Exceptions in Classes
  8. Applications and Exceptions
  9. Example 1
  10. Example 2
  11. Example Application
  12. Writing to Files
  13. PrintWriter and Buffered Output
  14. Example
  15. Simple Debugging
  16. Simpler Debugging
  17. Command Line Arguments
  18. TextPad and Command Line Arguments
  19. A Debug Class
  20. Example

Abstract Classes[1] [top]

An abstract class conceptually lies between an interface, no methods are implemented, and a concrete class, all methods must be implemented.

By concrete class I just mean an ordinary non-abstract class.

A class is abstract if one or methods in the class is abstract.

An abstract method in a class X is one that is not given an implementation in X.

The syntax is:

public abstract class X
{
  ...
}

Abstract Methods[2] [top]

The syntax of an abstract method includes the qualifier abstract and as in an interface, the body of the method is replaced by a semicolon.

For example, we could make the Person class be an abstract class like this:

public abstract class Person
{
  public abstract String getTitle();
  public abstract boolean superiorTo(Person p);
}

Non-Abstract Methods in an Abstract Class[3] [top]

An abstract class with all the methods abstract is essentially an interface. But in that case you should make it be an interface as a class can implement multiple interfaces but can only extend one class (abstract or not).

So the question is what do non-abstract methods look like in an abstract class.

We might add an implementation of the toString() method to the Person class:

public abstract class Person implements
{
  public abstract String getTitle();
  public abstract boolean superiorTo(Person p);
  
  public String toString()
  {
    return getTitle();
  }

}  

Which superiorTo method is called from this toString() method?

Advantages of Abstract Classes[4] [top]

In the previous example, we could have overriden the toString() method in each one of the concrete classes:

But if we override it in the abstract class Person, and all these classes extend the abstract class Person, then we only have to write one new implementation rather than 5.

Note 1: If Person is changed from an interface to an abstract class, the concrete classes must use extend Person, instead of implement Person.

Note 2: An abstract class inherits from Object just like a non-abstract class. So an abstract class already inherits toString, equals, hashCode, ...

Java's AbstractList Class[5] [top]

The List interface includes all these methods:

The abstract class AbstractList implements all of these methods so you don't have to. But it implements them using some methods you do have to implement.

These are the abstract methods in AbstractList you must implement:

When defining a new type, should you prefer to define it as an interface, an abstract class, or a concrete class?

Java often does all three. It defines

MyList Example[6] [top]

Here is an example class that is implemented by extending the AbstractList class.

Using Exceptions in Classes[7] [top]

Here is the MyVector class, written from scratch.

It has many of the properties of the Vector and ArrayList classes.

It grows automatically and it provides an iterator() method that returns an Iterator for its elements.

How are Exceptions useful in writing this class.

Consider adding two more methods:


  public Object set(int index, Object elem);

  public Object get(int index);

Each of these has the precondition:

   0 <= index && index < size()

Should these methods check that the precondition is met?

If so, what should the methods do if the precondition is NOT met?

Applications and Exceptions[8] [top]

Classes that define types allow you to write application programs using instances of those classes.

The application code that uses the classes should generally handle any exceptions that methods of those classes might throw.

Example 1[9] [top]

Often programs are written that expect a user to input a certain type - e.g. an integer. But the program should be able to recover if the user instead enters a string or non-integer.

A typical example is a program that

Methods for converting a string to a number include the static methods:

  int Integer.parseInteger(String s)
  double Double.parseDouble(String s)

Each of these methods converts the argument string s to the corresponding basic type (int or double) if possible.

If the conversion fails, each of these throws an exception:

NumberFormatException

Example 2[10] [top]

The MyVector class method

public Object get(int index)

throws the exception

IndexOutOfBoundsException 

if the index is < 0 or >= size().

Example Application[11] [top]

Lets write an application using the MyVector class that inserts Person instances into a MyVector and lists them.

Then we will prompt the user for an integer index and list the Person at that index.

Two things can go wrong:

The application should handle these two problems; that is, it should recover and continue, not terminate because of either exception.

Writing to Files[12] [top]

The methods for writing to standard output:

are in the PrintWriter class.

You can create an instance of this class associated with an output file rather simply.

Suppose you want to write output to a file named foo.txt. Here's how:

     PrintWriter outFile = new PrintWriter("foo.txt");

     outFile.println("Some output");
     outFile.println("More output");

     outFile.close();

This PrintWriter constructor will throw the FileNotFoundException if the file can't be opened for writing.

However, there are some efficiency considerations.

Java converts between sixteen bit unicode and byte sequences. (A byte is 8 bits).

If you are writing to a file, a standard efficiency is to buffer the output until a large enough amount of data has been collected and then write the contents of the buffer to the file all at once and clear the buffer.

The code above doesn't include this buffering, but you can add "buffering" property.

PrintWriter and Buffered Output[13] [top]

Here is the same example, but with the added efficiency of buffering the output:

     PrintWriter outFile = new PrintWriter(
                               new BufferedWriter(
                                   new FileWriter("foo.txt")));

     outFile.println("Some output");
     outFile.println("More output");

     outFile.close();

Unfortunately, the BufferedWriter class that adds the buffering property dos not have a constructor that takes only a file name.

The BufferedWriter constructor expects a Writer instance and does not throw an exception.

The FileWriter constructor will throw an IOException if the file can't be opened for writing. This is a checked exception and so must be handled or announced.

Note that this PrintWriter constructor is different from the first example. This constructor expects a Writer and does not throw an exception.

Example[14] [top]

Write the titles of the Person instances to a file named "foo.txt".

Simple Debugging[15] [top]

If you are using a simple text editor (e.g. Notepad, Textpad, or equivalent), there are still some fancy debugging environments you can use. One such example is JSwat at

http://jswat.sourceforge.net/

JSwat is close to netbeans, but is slightly less complicated than using a full integrated development environment (IDE's) such as netbeans or eclipse.

However, you can do some much simpler things for trying to find bugs in a program that compiles but produces incorrect results or fails in some way when it is executed.

Simpler Debugging[16] [top]

The simplest thing you can do usually is to insert print statements.

One big problem with this is that you usually want to remove the debugging print statements after you have found all the errors.

This is a bit tedious and you may find that all the bugs aren't gone and wish you hadn't removed the print statements quite so soon.

Commenting the print statements rather than actually removing them, makes it fairly easy to get them back - just uncomment them.

But this can mean a fair amount of searching and editing and you also have to recompile each time you comment or uncomment such executable print statements.

It would be nice to be able to insert print statements that can be selectively activated - some, all, none. And without having to edit or recompile.

Command Line Arguments[17] [top]

First a diversion is necessary, namely a discussion of command line arguments.

The main method takes an argument. It is an array of Strings.

  public static void main(String[] args)

What is in this args array?

If you execute a java application named App from a command line prompt, the examples below describe what will be in the args array:

                    
                                   args.length      args contents
 $ java App                            0              

 $ java App hello                      1            args[0] = "hello"

 $ java App hello  5                   2            args[0] = "hello"
                                                    args[1] = "5"

 $ java app a b cd                     3            args[0] = "a"
                                                    args[1] = "b"
                                                    args[2] = "c"

So the args array just contains the strings on the command line - the command line arguments that are passed to main.

TextPad and Command Line Arguments[18] [top]

TextPad provides a simple integrated enviroment for Java (and can be adapted for other programming languages). It lets you compile and execute java programs without resorting to a console window.

How can you enter command line arguments.

The menu bar has a Configure item that you can use to have TextPad prompt you for the command line arguments before executing a Java program.

  Configure > Preferences

  In the dialog box on the left click on 
      Tools and then on 
          Run Java Application

  You should then see on the right some textboxes and some 
  checkboxes, one of which is

  Prompt for parameters

If you check the "Prompt for parameters" checkbox, then each time you run a Java Application, it will first display a small prompt with the name of your application. You just add any command line arguments that are appropriate (or none).

These arguments are then passed to the main method just as if you had run the program from a command line prompt.

A Debug Class[19] [top]

Here is a class that provides the promised printing functionality. That is, selective printing without recompiling.

public class Debug
{
  private static String flags;
  public static void setFlags(String str) {
    flags = str;
  }
  
  public static void print(String flgs, String s)   {
    if ( enabled(flgs) ) {
      System.out.print(s);
    }
  }
  
  public static void println(String flgs , String s)   {
    if ( enabled(flgs) ) {
      System.out.println(s);
    }
  }

  private static boolean enabled(String f)   {
    for(int i = 0; i < f.length(); i++ ) {
      if ( flags.indexOf(f.charAt(i)) != -1 ) {
	return true;
      }
    }
    return false;
  }
}

The print and println methods in the Debug class print to standard output just like System.out.print and System.out.println.

The Debug versions only print if one of the characters in the first string is enabled.

The characters which are enabled can be passed in a command line string and this does not require recompiling the program.

Example[20] [top]

If you want to use the Debug printing, in an application, here's how:

  1. In the main method,

      public static void main(String[] args)
      {
    
        String flags = "";
        if ( args.length > 0 ) {
          flags = args[0];
        }
        
        Debug.setFlags(flags);
        ...
      }
    
    
  2. Then in any method where you want to insert a print statement for debugging, choose a flag character to control the printing and use the static Debug.print or Debug.println method:

        1	      Debug.println("1", "At 1: x = " + x + " and y = " + y);
        2	      if ( x < y ) {
        3	          a[x]++;
        4	      } else {
        5	          a[y]++;
        6	      }
    

    Suppose the main class is called App, then the print statement at line 1 will print or not print when the application is run like this:

      $ java App 0345     // Line 1 doesn't print the flag "1" is not in
                          // command line argument "0345"
    
      $ java App 012      // Line 1 does print since the flag "1" is in 
                          // the command line argument "012"