[ Team LiB ] Previous Section Next Section

2.6 Computing Statistics

So far, the classes we've defined have modeled mathematical abstractions like rectangles and complex numbers. It is easy to imagine other objects that model things like a mailing address or a record in a database. This is not a requirement, however: classes do not have to model "things." They merely have to hold some state (i.e., define some fields) and optionally define methods to manipulate that state. Example 2-6 is just this kind of class: it computes simple statistics about a series of numbers. As numbers are passed to the addDatum( ) method, the Averager class updates its internal state so that its other methods can easily return the average and standard deviation of the numbers that have been passed to it so far. Although this Averager class does not model any "thing," we've followed the Java naming convention of giving classes names that are nouns (although, in this case, we had to use a noun that does not appear in any dictionary).

Example 2-6. Averager.java
package je3.classes;
 * A class to compute the running average of numbers passed to it
public class Averager {
    // Private fields to hold the current state.
    private int n = 0;
    private double sum = 0.0, sumOfSquares = 0.0;

     * This method adds a new datum into the average.
    public void addDatum(double x) {
        sum += x;
        sumOfSquares += x * x;
    /** This method returns the average of all numbers passed to addDatum( ) */
    public double getAverage( ) { return sum / n; }

    /** This method returns the standard deviation of the data */
    public double getStandardDeviation( ) {
          return Math.sqrt(((sumOfSquares - sum*sum/n)/n));

    /** This method returns the number of numbers passed to addDatum( ) */
    public double getNum( ) { return n; }

    /** This method returns the sum of all numbers passed to addDatum( ) */
    public double getSum( ) { return sum; }

    /** This method returns the sum of the squares of all numbers. */
    public double getSumOfSquares( ) { return sumOfSquares; }

    /** This method resets the Averager object to begin from scratch */
    public void reset( ) { n = 0; sum = 0.0; sumOfSquares = 0.0; }

     * This nested class is a simple test program we can use to check that 
     * our code works okay.
    public static class Test {
        public static void main(String args[  ]) {
            Averager a = new Averager( );
            for(int i = 1; i <= 100; i++) a.addDatum(i);
            System.out.println("Average: " + a.getAverage( ));
            System.out.println("Standard Deviation: " +
                               a.getStandardDeviation( ));
            System.out.println("N: " + a.getNum( ));
            System.out.println("Sum: " + a.getSum( ));
            System.out.println("Sum of squares: " + a.getSumOfSquares( ));

Example 2-6 introduces an important new feature. The Averager class defines a static inner class named Test. This class, Averager.Test, contains a main( ) method and is thus a standalone program suitable for testing the Averager class. When you compile the Averager.java file, you get two class files, Averager.class and Averager$Test.class. Running this nested Averager.Test class is a little tricky. You ought to be able to do so like this:

% java je3.classes.Averager.Test

However, current versions of the Java SDK don't correctly map from the class name Averager.Test to the class file Averager$Test.class. So, to run the test program, you must invoke the Java interpreter using a $ character instead of a . character in the class name:

% java je3.classes.Averager$Test

On a Unix system, however, you should be aware that the $ character has special significance and must be escaped. Therefore, on such a system, you have to type:

% java je3.classes.Averager\$Test


% java 'je3.classes.Averager$Test'

You must use this technique whenever you need to run a Java program that is defined as an inner class.

    [ Team LiB ] Previous Section Next Section