Sunday, December 9, 2007

Arrays.Classes and Inheritance

Declaring Arrays

Array declaration, like primitive declaration, associates a variable name with a data type. There are two ways to declare an array. The preferable format is

Component_type[] name; 

Note the square brackets after the component type. An example of this format is

float[] temperaturesCelsius;

This declaration says that temperaturesCelsius is the name of an array whose components are all of type float. The number of components will be specified later, when the array is created. Note the plural in the name, indicating that the variable relates to more than one temperature.

The alternative format for array declaration is

Component_type name[]; 

The only difference is that the empty square brackets now come after the name, rather than after the component type. This format is included as a holdover from older programming languages (C and C++). It is considered less readable than the first format, and we will not use it in this book.

Creating Arrays

At some point after you declare an array, you need to create it. This is new. With primitives, all you had to do was declare a variable's type, and the variable came into existence. Arrays, as well as all other kinds of objects, are different: You have to create them explicitly. This is done with the keyword new.

The format of an array creation statement is

name = new component_type[number_of_components]; 

you declared a variable named temperaturesCelsius to be an array whose components have float type. From this point on, we will say this more briefly: temperaturesCelsius is an array of floats. When you want to create the array, you first have to decide how many components it will have. If you want 10 components, for example, you would create the array like this:

temperaturesCelsius = new float[10];

The array size does not have to be a literal int. A variable is acceptable. Suppose you have a method called getNTempReadings, which returns the number of temperatures available to the program. Then you might do the following:

int nTemps = getNTempReadings();
temperaturesCelsius = new float[nTemps];

You could even get more terse:

temperaturesCelsius = new float[getNTempReadings()];

Arrays and Loops

Loops, and especially for loops, are ideal for processing arrays. No matter what you want to do to the array, you typically use a for loop with a loop counter that ranges from 0 through array-length-minus-1. Within the loop's body you perform whatever processing you want, using the loop counter as an array index.

For example, the following code computes the product of all the values in an array called measurements:

double product = 1; for (int i=0; iNote that this code works on arrays of all sizes, because it reads the array size from measurements.length.

Multi-Dimensional Arrays

The arrays we have looked at so far have been one-dimensional. This means that each component is specified by a single unique index. Java also supports multi-dimensional arrays, in which each component is specified by a unique sequence of indices. The number of indices in the sequence is the array's dimension. In a two-dimensional array, for example, each component has two indices. some one-dimensional arrays as columns of boxes. We can portray a two-dimensional array as a lattice or matrix, where each component is identified by its row and column

for (int i=0; i<3; i++)

for (int j=0; j<2; j++)

twoDimInts[i][j] = 39;

for (int hour=0; hour<24; hour++)
{
float tempTotal = 0;
for (int stn=0; stn<50; stn++)
tempTotal += temps[hour][stn];
float tempAvg = tempTotal / 50;
System.out.println("Average temp at time " + hour +
" = " + tempAvg);
}

On the other hand, you might want the average temperature over the entire day for each station. For that, you would use the following code:

for (int stn=0; stn<50; stn++)
{
float tempTotal = 0;
for (int hour=0; hour <24; hour++)
tempTotal += temps[hour][stn];
float tempAvg = tempTotal / 24;
System.out.println("Average temp at station " + stn +
" = " + tempAvg);
}

Classes

Plato would have approved of the concept of classes.

The easiest way to learn about classes is to step outside the domain of object-oriented programming for a moment and look at the real world. (Plato might not have approved of calling it "real.") We experience things in the world, and we create categories in our minds so that we can think about those things collectively.

Objects and Their Data

You have already learned that objects, like arrays, contain clusters of data. You have also learned that the data in an object, unlike the data in an array, can be of differing types. The number, types, and names of an object's data elements are all defined in the object's class definition.

Let's look at a very simple example. Here is a very simple class definition:

public class Person { int age; short weight; }

This is our first example of a class that does not contain a method called main. In fact, this class definition has no methods at all. The class just defines a bundle of data. The bundle contains an int called age and a short called weight.

To create an individual instance of this class, you would use the following code:

Person keara; keara = new Person();

Multiple Objects

You have learned that a class is a kind of stencil or cookie cutter for creating objects. Cookie cutters are especially useful if you're going to make lots of cookies. Similarly, classes are really useful because you can use a class to make multiple instances of that class. Different cookies made from the same cutter have the same outline, but they can be frosted or decorated differently. Similarly, different instances of the same class can have different data values.

Objects and Their Methods

In addition to containing data, objects can also contain methods. You might have suspected as much, because all the application classes presented in this book have contained at least one method (public static void main(String[] args), and sometimes more than one. All those methods have had the keyword static in their declarations. Later in this chapter you will see that static means, in a sense, "not object-oriented." The methods had to be static because we had not yet introduced objects. Now it is time to present genuinely object- oriented methods.

Let's add a method to the Person class:

public class Person
{
int age;
short weight;

int ageInNYears(int n)
{
return age + n;
}
}

The method computes how old the person will be in n years. It has a lot in common with the methods you have already seen. It has a declaration that specifies the method's return type, name, and argument list. It has a body enclosed in curly brackets, and it returns a value.

There are two major differences between this method and the ones you have looked at in previous chapters:

  • There is no static in the declaration.

  • The method refers to a field (age) of the class where the method is defined.

To call a method of an object, you again use the "reference-dot" notation. The following code shows how this is done:

1. Person ed = new Person();
2. ed.age = 62;
3. ed.weight = 220;
4. int n = ed.ageInNYears(3);

5. System.out.println("Ed will be " + n +" in 3 years.");

Construction and Constructors

When you invoke new to construct a new instance of a class, automatically a call is made to a piece of code in that class, called its constructor. This may come as a surprise, because for the past two chapters you have constructed objects without ever writing constructors, or even knowing about them. In a moment you will see how this works out.

A constructor looks like a method. In fact, there are only two differences between a constructor and a method:

  • A constructor has no return type.

  • The constructor's name is the same as the class's name.

Like a method, a constructor has a body, enclosed in curly brackets. The code in the body can initialize the newborn object. In fact, this initialization is the basic job of constructors. A constructor can always assume that the object's variables and methods (whether declared in the class or inherited) exist and are accessible for reading, writing, and calling.

Let's add a constructor to the Worker class. Assume that all workers in the company have the same salary: $34,567.89. The Worker class (with a different main() method) becomes the following:

1. public class Worker extends Employee 2. { 3. boolean getsOvertime; 4. 5. Worker() 6. { 7. salary = 34567.89f; 8. } 9. 10. void dumpSalary () 11. { 12. System.out.println("Salary = " + salary); 13. } 14. 15. public static void main(String[] args) 16. { 17. Worker dagwood = new Worker(); 18. dagwood.dumpSalary(); 19. } 20. }

The constructor is lines 5-8. Constructors, variables, and methods can appear in any order in the body of a class definition. However, it is common practice to have variables come first, followed by constructors, followed by methods. Usually, the main() method comes at the end. When this application is run, line 17 constructs a new instance of Worker. First, space for all variables is allocated. Then the constructor code is called. Line 7 of the constructor initializes salary to 34567.89, so the call to printSalarydumpSalary() prints out Salary = 34567.89.

Overloading Constructors

It is not especially realistic to expect every worker in a company to have the same salary. Fortunately, you can pass arguments into a constructor the same way you pass them into a method. As with a method, you can put an argument list inside the parentheses that follow the constructor name. Those arguments are accessible within the method. The next version of Worker has a constructor that accepts an argument.

 1. public class Worker extends Employee
2. {
3. boolean getsOvertime;
4.
5. Worker(float sal)
6. {
7. salary = sal;
8. }
9.
10. void dumpSalary ()
11. {
12. System.out.println("Salary = " + salary);
13. }
14.
15. public static void main(String[] args)
16. {
17. Worker dagwood = new Worker(55555.55f);
18. dagwood.dumpSalary();
19. }
20. }

The constructor now takes a float argument, and the invocation on line 17 passes a float. The output is Salary = 34567.89.

In Chapter 4, "Methods," you learned that methods are polymorphic. That is, different methods within a class may share a common name, as long as their argument lists are different. The practice of reusing a method name in a class is called overloading. (The term has a negative connotation in real life. When people or bridges are overloaded, that's bad. But in programming, there is nothing bad about overloading.) You can also overload a class's constructor so that the class has multiple constructors, as shown here:

public class Manager extends Employee
{
Worker[] workers;

Manager(int nWorkers)
{
workers = new Worker[nWorkers];
}

Manager(float sal, int nWorkers)
{
salary = sal;
workers = new Worker[nWorkers];
}
}

This class has two constructors. In both versions, you specify the number of workers. In the second version, you also specify the manager's salary.

Default Constructors

This section answers an important question: In the previous chapter and the first part of this one, how was it possible to construct objects in classes that didn't have any constructors? To understand the answer, you have to know what a no-args constructor is. It's just a constructor with an empty argument list.

When you create a class with no constructors, the compiler creates a no-args constructor automatically. A no-args constructor that is created automatically is called a default constructor. You only get a default constructor if your class does not explicitly have any constructors. If your class has constructors, no matter how many, no-args or otherwise, no default constructor is created for you.

This mechanism assures that every class has at least one constructor, even if the class was written by someone who has never heard of constructors!

Overriding

You have already seen method overloading, where a method name is reused in a class definition. Java also supports method overriding, which looks like it ought to be similar to overloading because the spellings are so similar. In fact, overriding is indeed a kind of method name reuse. In this case, the reuse is within an inheritance hierarchy.






No comments: