CS 124, Fall 2009
Solutions to Test #2

1. Explain the terms subroutine and parameter.

Answer: A subroutine (or "method") is a set of instructions for performing some task that have been grouped together and given a name. It is possible to "call" the subroutine by name any time that task has to be performed, so you just have to write one line instead of repeating all the code that is inside the subroutine. Parameters are used to send information to the subroutine that the subroutine needs in order to perform its task. For example, if the task of a subroutine is to process some string, then that string could be a parameter. There are two types of parameter: actual parameters, which are values that are given to the subroutine when it is called, and dummy parameters, which are placeholders that are used in the definition of the subroutine to stand in for the actual parameters. When a subroutine is called, the actual parameter values in the subroutine call statement are assigned to the dummy parameters in the subroutine definition so that the subroutine can use those values.

2. Explain what is meant by a pointer (or reference), and what this has to do with objects and with the special value null.

Answer: In Java, a variable cannot hold an object. Instead, the object is stored in a part of memory known as the heap, and the variable merely holds a pointer to the object. The pointer is actually the address in memory where the object is located. The computer can use the pointer to locate the actual object. The special value null can be stored in a variable to mean that the variable does not point to any object. If a variable contains null, and if you try to use the variable to get at an object, the result will be a NullPointerException.

3. Discuss the meaning of the "access modifiers" public and private and why they might be used.

Answer: Access modifiers can be applied to methods and member variables to control to control the scope of the methods and variables. If a method or member variable is public, then it can be used from anywhere in the program, including other classes from the one where it is defined. On the other hand, a method or member variable that is declared to be private can only be accessed in the same class where it is declared. By declaring a member variable private, you have total control over that variable. Only the code that you write in the same class can manipulate the variable, so you know everything that can be done with it. You can, for example, guarantee that only certain values can be assigned to the variable. This makes the behavior of the class more predictable and easier to understand. Making methods and variables private can also make the class easier to understand from the outside, since someone who uses the class doesn't need to understand or even know about the private part of the class. On the other hand, the public part of the class is important because it specifies the public interface of the class -- it represents the things that can be done with the class by other parts of the program.

4. Show the contents of the array A after the following code has been executed:

int[] A;
A = new int[7];
A[0] = 1;
A[1] = 1;
for (int i = 2; i < 7; i++) {
   A[i] = A[i-1] + A[i-2];
}

Answer: There are 7 elements in the array, A[0] through A[6]. Here is how the values in these elements are computed and what they are:

           A[0] = 1
           A[1] = 1
           A[2] = A[2-1] + A[2-2] = A[1] + A[0] = 1 + 1 = 2
           A[3] = A[3-1] + A[3-2] = A[2] + A[1] = 2 + 1 = 3
           A[4] = A[4-1] + A[4-2] = A[3] + A[2] = 3 + 2 = 5
           A[5] = A[5-1] + A[5-2] = A[4] + A[3] = 5 + 3 = 8
           A[6] = A[6-1] + A[6-2] = A[5] + A[4] = 8 + 5 = 13

If you want a picture, it might look like this:

                   +------+
                   |  (7) | (length)
                   +------+
                 0 |    1 |
                   +------+
                 1 |    1 |
                   +------+
                 2 |    2 |
                   +------+
                 3 |    3 |
                   +------+
                 4 |    5 |
                   +------+
                 5 |    8 |
                   +------+
                 6 |   13 |
                   +------+

5. Write a static subroutine that computes and returns the average of all the values that are stored in an array of type double[]. The array should be a parameter to the subroutine.

Answer: The subroutine has a parameter of type double[] and a return type of double. The name of the subroutine is immaterial. Here is a possible definition:

public static double arrayAverage( double[] numbers ) {
   double total;
   double average;
   total = 0;
   for ( int i = 0; i < numbers.length; i++ ) {
      total = total + numbers[i];
   }
   average = total / numbers.length;
   return average;
}

6. Let's say that I don't like the letter 'E'. Write a static subroutine named stripE that will remove all the E's (both upper and lower case) from a given string. The original string is a parameter of the subroutine, and the modified string is the return value. For example, stripE("Everyone") would return the string "vryon".

Answer: The subroutine has a parameter of type String, and it returns a String. The name of the subroutine is specified as stripE. One way to write it is:

public static String stripE( String str ) {
   String stripped;
   stripped = "";
   for ( int i = 0; i < str.length(); i++ ) {
       char ch;
       ch = str.charAt(i);
       if ( ch != 'e' && ch != 'E' )
          stripped = stripped + ch;
   }
   return stripped;
}

7. Suppose that Foo is the name of a class. It is possible that both of the following are legal:

                      new Foo[10];
               and
                      new Foo(10);

Explain the difference.

Answer: The expression "new Foo[10]" creates an array. It calls an array constructor to create an array of 10 elements. The base type of the array is Foo; that is, each element of the array is a variable of type Foo. (Note that no objects of type Foo have been created -- the values in the array are all null. The only object that has been created is the array itself, which is of type Foo[].)

On the other hand, new Foo(10) calls a constructor in the class Foo to create an object of type Foo. The number 10 is an actual parameter that is passed to the constructor. This is legal as long as the class includes a definition for a constructor that has one parameter of type int (or, possibly, of other numeric type such as double or long.)

8. (a) Write a complete definition for a simple class named Player (representing a player in some game). The class must have instance variables to represent the name and the score of the player. It must have a constructor that specifies the name of the player. It must have an instance method that adds some points to the player's score. And it must have instance methods that can be used to get the name and the current score.

(b) Using your class from part (a), write a few lines of code that will create an object of type Player and give it 42 points.

Answer:

       (a)  public class Player {

               private String name;  // the Player's name
               private int score;    // the Player's score
               
               public Player( String playersName ) {  // constructor
                  name = playersName;  
               }
            
               public void addPoints( int numberOfPoints ) {
                   score = score + numberOfPoints;  // add points to score
               }
            
               public String getName() {  // retrieve the name
                  return name;
               }
            
               public int getScore() {  // retrieve the score
                   return score;
               }

            }

       (b)  Player p;  // declare the variable
            p = new Player("Fred");  // create the object
            p.addPoints( 42 ); // add 42 to the player's score

9. Suppose that A and B are arrays of type String[]. (Assume that A and B have already been declared, created, and filled with data.) Write a code segment that creates a new array that contains all the values from A and all the values from B. (Hint: Create an array whose length is equal to the length of A plus the length of B. Copy the elements of A into the new array, then copy the elements of B.)

Answer: The length of the new array must be A.length + B.length, since that is the total number of elements in A plus in B. The elements of A can simply be copied into the corresponding positions in the new array. The tricky part is getting the elements of B into the correct positions in the new array. Positions 0 through A.length-1 are occupied with values from A. So, B[0] should be placed into position number A.length, B[1] into position number A.length+1, and so on. In general, B[i] is placed into position number A.length + i

    int[] C;  // create a variable to refer to the new array
    C = new int[ A.length + B.length ];  // make the array
    for (int i = 0; i < A.length; i++) {
       C[i] = A[i];   // copy an element of A into the same position in C
    }
    for (int i = 0; i < B.length; i++) {
       C[ A.length + i ] = B[i];  // copy B[i] into correct position in C
    }

10. Complex systems have to be carefully designed. Subroutines can help in the design process. One reason for their usefulness is that they can be used as black boxes. Write an essay that explains what this means and what it is about black boxes that make them useful in design. (A good essay will include a discussion of how they are useful both in top-down and in bottom-up design.)

Answer: A black box is something that can be used without understanding what goes on inside the box. A black box has two aspects, the implementation, which is the inside of the box that makes it work, and the interface, which is how the box is used. To use a block box, you only need to understand the interface, not the implementation. Conversely, when designing, building, and testing the box itself, you are working with the implementation. In that case, you only have to worry about getting the implementation correct; you don't have to know anything about the larger systems in which the black box will be used as a component. Black boxes can be used to build "modular" systems, which are made up of relatively independent components that can be developed separately and which interact only in relatively simple, easy-to-understand ways.

In programming, subroutines act as black boxes. The implementation is the code inside the subroutine. Once that code has been written, you can forget about how it works, and you can simply call the subroutine by name. (Classes are another type of black box.)

In top-down design, a big problem is broken down into smaller problems (which can then themselves be further broken down if necessary). In this case, a subroutine can be written to solve each (non-trivial) subproblem. The design process tells you what subroutines to write and what they have to do. Then, designing the subroutine is another exercise, which can be done separately because subroutines are modular black boxes.

In bottom-up design, you begin by developing (or locating) components or tools that are relevant to the problem that you are trying to solve. These components can be combined to produce more complex components, until eventually you have a component that will solve the whole problem. Subroutines are one type of component. It is important that subroutines are black boxes, since it means that when you want to use the subroutine as a component in a larger system, you don't have to worry about how the subroutine works, and that makes the design of the larger system less conceptually complex.