Welcome to Advanced Classes

In the last two lessons we've talked several times about inheritance, but we've never really fully explained how it works. We're going to do that now before we go any further. Also, we'll explore constructors, the finalize() method, static and constant variables, and we'll revisit our HelloWorldApplet

Let's pretend we have a class called Person. Person has methods such as walk(), talk(), breathe() (a private method), and eatAndDrink(), and variables such as height, weight, eye_color, and hair_color. Now, you'll notice that our Person class is very general. It has only the most basic elements. It does not even have gender.

O.K. We definitely need Male and Female classes. So, we'll inherit Male from Person. Males don't really do much of anything special, so we don't need to add any variables or methods. Now, to remain biblically correct, we'll inherit Female from Male. Now Female needs a new method, haveBaby(). The haveBaby() method will return a Baby object, so we need to define a Baby class. Baby will inherit from person since a baby isn't really a man or a woman. Baby will need a new method, cry(), and a new boolean variable, diapers_dirty. (A boolean variable is true or false.) The Baby class will also have to have new eatAndDrink() and walk() methods since babies don't do that quite the same as adults. What we'll have to do is override the eatAndDrink() and walk() methods from Person by declaring an eatAndDrink() method and a walk() method in Baby.

Alright, so what we have is a Male class with walk(), talk(), breathe(), eatAndDrink(), height, weight, eye_color, and hair_color. We also have a Female class with walk(), talk(), breathe(), eatAndDrink(), haveBaby(), height, weight, eye_color, and hair_color. And we have a Baby class with walk(), talk(), breathe(), eatAndDrink(), cry(), height, weight, eye_color, hair_color, and diapers_dirty. We also still have our original Person class. The pseudo-code follows. I have left the methods empty since the internals are not important right now.

import java.awt.Color; public class Person { protected float height; protected float wieght; protected Color eye_color; protected Color hair_color; public void walk() {} public void talk() {} public void breathe() {} public void eatAndDrink() {} } public class Male extends Person { } public class Female extends Person { private Baby haveBaby() {} } public class Baby extends Person { public boolean diapers_dirty; public void cry() {} public void eatAndDrink() {} public void walk() {} }

We now can declare a whole family! But, there's more to life than family. We can't forget work. So, let's define some working classes. Let's start with Actress. Actress should inherit from Female. Actress will need an act() method and a salary variable. Let's also define a Salesman. Salesman will inherit from Male, and will need a sell() method, a salary variable, and a stock variable. Let's also define a Mother class. Mother will inherit from Female. All mother needs is a Baby. Here's the code:

public class Actress { protected int salary; public void act(); } public class Salesman { protected int salary; private Vector stock; public void sell(); } public class Mother { protected Baby junior; }

Now that's more like it! The Vector object in Salesman is from the Java library. It provides an easy way to keep track of several of objects at once.
Our Actress class can act(), haveBaby(), eatAndDrink(), walk(), talk() and breathe(). It has a salary, height, wieght, eye_color, and hair_color. The Salesman and Mother class work similarly.

Now for the part that makes inheritance really powerful. Mother is a Female is a Person. If we need to, we can treat any object as one of it's parents. For example, if our Male class had a marry() method which took a Female as a parameter, we could pass it an Actress because Actress can act as a Female. To clarify, let's look at some pseudo-code.

public class Man extends Male { private Female wife; public void marry (Female woman) { wife = woman; } public void divorce () { wife = null; } } Man george = new Man (); Female gisela = new Female (); Actress zelda = new Actress (); george.marry (gisela); george.divorce (); george.marry (zelda);

Here you can see that the marry() method which expects a Female will accept either a Female or an Actress. It would also accept a Mother. The marry() method will accept any Female or any class inherited from Female. If we inherited a new class, AerobicsInstructor, from Actress, marry() would also accept that. The point I'm trying to make here is that marry() will accept any object that is somehow a decendant of Female.

This property, called polymorphism, allows you to do some really neato stuff. You could, for instance, declare an array of Persons. In that array, you could put, Males, Females, Salesmans, and AerobicInstructors because they are all descendants of Person. We may see more of this in later lessons.

One other thing polymorphism allows you to do is to call a method from the parent, even if it has been overridden. For example:

public class Baby extends Person { public boolean diapers_dirty; public void cry() {} public void eatAndDrink() {} public void walk() {} public void adultEatAndDrink() { super.eatAndDrink(); } public void adultWalk() { super.walk(); } }

We just redefined our Baby class to add a little surprise for Mommy and Daddy. We added two methods, adultEatAndDrink() and adultWalk(). Both methods just call a method of the parent class by using the super keyword. Here's what this will do: When adultWalk() is called in a Baby, it calls the walk() method of Baby's parent, Person. The walk() method of Person is the way adults walk. So, what will happen is that Baby will get up off his hands and knees and start strutting like Travolta. Or something to that effect. adultEatAndDrink() will have a similar effect. It will call Parent's eatAndDrink() method, and Baby will be downing cheeseburgers and coffee.

In Java, a class can only inherit from one other class. There is no mulitple inheritance as C++ programmers are used to. Instead, Java offers something called interfaces. A class can implement any number of interfaces. An interface is like a skeletal class. It can only contain methods, no variables, and its methods must be abstract. Abstract methods contain no code. They simply state that the method exists. So, in effect, an interface is kind of like a set of parameters for a class to follow. It states what methods the class needs to implement. Again, polymorphism applies to interfaces. I know that didn't make much sense, so let's look at some more code. This time, we're going to come up with a more sensible way of defining working classes.

public interface Law { public abstract void doLawyerStuff (); public abstract void chargeHugeFee(); } public class MaleLawyer extends Male implements Law { public int number_of_cases_won; public void doLawyerStuff () {} public void chargeHugeFee() {} } public class FemaleLawyer extends Female implements Law { public void doLawyerStuff () {} public void chargeHugeFee() {} }

What we have now is an interface called Law. Any class that implements Law is required to have everything needed to be a lawyer, doLawyerStuff() and chargeHugeFee(). We also defined two kinds of lawyers, MaleLawyer and FemaleLawyer. Both define the above two methods. MaleLawyer also adds a variable to keep track of the number of wins so he can boast to his buddies.

The FemaleLawyer class is a Female and a Person, and quailifies at Law. So, FemaleLawyer can act as a Person, a Female, or a Law. I know that sounds a little odd, but it works. We can declare an array of type Law, and we can put FemaleLawyers and MaleLawyers in it. We cannot, however, put Laws in it since they don't really exist; Law is an interface, not a class. Again, we will see more of this in later examples. (Side note on Visibility.)

Let's now look at another part of the mechanics of a class, constructors. Constructors are used to create an instance of a class. For instance, when we declared george as a Man a little while back, we used a constructor for class Man. However, since we hadn't declared any constructors for Man, we ended up using the default constructor. Anytime there are no constructors defined, a default constructor is included by the compiler. Let's start with an example.

public class Man extends Male { private Female wife; public Man () { wife = null; } public void marry (Female woman) { wife = woman; } public void divorce () { wife = null; } }

Now we've extended our Man class to contain a constructor. Constructors are always named after the class, and never declare a return value, as you can see above. What this constructor does is set the wife variable to null when a Man is created, meaning that he has no wife.

What if we wanted to allow a Man to already be married when he is created? We can take advantage of another application of polymorphism. Methods can be polymorphic too. What that means is that two methods can have the same name, as long they have different parameters.

public class Man extends Male { private Female wife; public Man () { wife = null; } public Man (Female woman) { wife = woman; } public void marry (Female woman) { wife = woman; } public void divorce () { wife = null; } }

We can now create a Man with or without a wife. Notice that both constructors have the name Man, but on tkaes no parameters, and the other takes a Female as a parameter. Here's how we would use this class.

Man george = new Man (); Female gisela = new Female (); Man frank = new Man (gisela); Actress zelda = new Actress (); frank.divorce(); frank.marry(zelda); george.marry(gisela);

Love is so complicated! Notice that Frank was created with a wife, Gisela, whom he later divorces, and George was created without a wife but later marries the recently divorced Gisela. To be perfectly PC, and include alternative lifestyles, we could redefine Man as follows, using the polymorphics property:

public class NewMan extends Male { private Person partner; public Man () { partner = null; } public void marry (Female woman) { partner = woman; } public void marry (Male man) { partner = man; } public void divorce () { partner = null; } } NewMan fred = new NewMan (); Actress zelda = new Actress (); MaleLawyer bob = new MaleLawyer (); fred.marry (zelda); fred.divorce; fred.marry (bob);

There are many different levels of polymorphism at work here. First, wife is now a person, so it can accept anything inherited from Person, including Actress and MaleLawyer. There are now two marry () methods. One accepts a Female, and the other a Male. There is no difference in the way they are called. The difference is in what kind of object you pass it. If you pass a Female, it knows to use the first marry() method. If you pass a Male, it knows to use the second marry() method. If you try to pass a Baby, you will get an error, because there is no marry() method which expects a Baby or just a Person. Besides, that's illegal!

Now, one more thing about constructors. A constructor in a child class can call the constructor of its parent class. In fact, it does this automatically, whether you want it to or not. But, it is always better to explicitly declare, just so that nothing is going on behind the scenes. To call the parent's constructor, use the super() method. The call to super() has to be the first statement in the constructor. Let's see another example.

public class Father extends Man { protected Baby junior; public Father () { super (); } public Father (Female woman) { super (woman); } public Father (Female woman, Baby kid) { super (woman); junior = kid; } }

What we have created is a father class. We can create our Father with no wife, with a wife, or with a wife and a child. Notice that we use the wife variable from Man, so we don't have to declare one in Father. We also take advantage of the constructors in Man. The first one in Father, the empty constructor, would be supplied by the compiler anyway, but it's always a good idea to declare it explicitly.

On the other side of constructors is the finalize() method. The finalize() method is used to clean things up before an object is destroyed. Proper use of this method can result in faster clean up time and better performance. However, for most basic application, it's not necessary.

public class Man extends Male { private Female wife; public Man () { wife = null; } public void finalize () { wife = null; } public void marry (Female woman) { wife = woman; } public void divorce () { wife = null; } }

Here we've added a finalize() method. The finalize() method always has a return value of void, and takes no parameters. This finalize() method just assigns null to the wife variable so that the automatic garbage clean up will get to it faster. You can only have one finalize method per class. We will be seeing much more of constructors and a little more of the finalize() method in the following lessons.

Alright, let's talk a little about variables. There are many modifiers we can add to a variable declaration to make subtle changes to the way the variable works. Right now we're going to look at two of them, static and final.

Final makes the variable a constant. After it is declared, the variable can no longer be modified. It's very similar to the const statement in C++.

Static does something a little odd with the variable it modifies. It makes it into a "class variable." Class variables exists whether there are any instances of the class or not. Only variables declared outside of the classes methods can be declared static.

Let's look at an example.

public class Test { public static int var1 = 0; public final int var2 = 2; public static final int var3 = 3; public Test () { var1++; } } In this example, var1 keeps track of the number of Test objects that have been instantiated. Since it is static, var1 is a class variable. There is only one var1, no matter how many Test objects get instantiated. And every Test object will have the same var1. In this example, the constructor for the Test class increments var1. So, every time a Test object is created, var1 is incremented. This means that var1 always holds the number of Test objects that have been created. Also, var1 can be accessed by any object as Test.var1.
var2 is a constant equal to 2. It will always equal 2, and it cannot be changed. Because it is public, it can be accessed by other objects, but a Test object has to be instantiated, and var2 must be referenced through it. For instance, it we had a Test object called test_object, we could access it's var2 with test_object.var2. Note, however, that it is accessible by other objects only because it is public. If var2 were private, only methods within Test could access it.
var3 is a constant equal to 3. It can be accessed by any object as Test.var3, but it cannot be changed by any object. It is rather a cross between var1 and var2.

Methods can also be declared final and static. A final method cannot be overriden in a subclass. A static method acts rather like a static variable. It is a class method, and can be called even if no objects have been instantiated from the class.

A good example of a static final variable is Math.PI. It is declared in java.lang.Math, but it's usable from anywhere without instantiating a Math object.
A good exmaple of a static method is Math.sqrt(). It is also declared in java.lang.Math. You can always call it, even if no Math objects exist.

Now, on a completely different note, let's go back and look at our HelloWorldApplet again.

The thing everyone wants to do with Java is to add pictures, sound, and animation. In the remainder of this lesson, we'll cover sound and pictures. Animation will come later.

First I'll show you the code to add an image to an applet, then I'll explain it.

import java.applet.*; import java.awt.*; public class HelloWorldApplet6 extends Applet { private Image coffee; public void start () { coffee = this.getImage (this.getDocumentBase (),"/crash/java/javaback.gif"); } public void paint (Graphics g) { g.fillRect (0,0,57,62); g.drawImage (coffee,2,2,this); g.drawString ("Hello", 15, 25); g.drawString ("World", 12, 45); } }

First, this HelloWorldApplet draws a filled rectangle that is slightly larger than the image we're going to use. The effect is that the rectangle becomes a frame for the image. I did that just to make the image stand out against the background.
Next, it loads a copy of the image and stores it in a variable called coffee. It does so by using a method of the Applet class, getImage(). It is called as this.getImage() to specify that the applet is calling the method. getImage() takes two parameters, a URL where the image is located, and the name of the image. Since the image is in the same directory as the applet, we can use the URL of the applet. We get the URL of the applet by using getDocumentBase(), again called with the this keyowrd. getDocumentBase returns the URL of the applet it's called from.
Next, we draw the image on the screen. drawImage is a method of Graphics we haven't seen before. It simply draws the indicated image on the screen with the upper left corner at the specified coordinates, (2,2) in this case. The last parameter that we passed this is one of the really advanced topics, and we probably won't cover that in this tutorial. Just take it as given that you have to pass this as the forth parameter of drawImage().
Finally, we print "Hello World" on the screen. We had to break it up because the image was too small.

We put the image load in the start() because it only needs to be done once every time you view the page. The paint() method gets called several times while you look at a page. In general, you only want things necessary for redrawing the applet to be in the paint() method. That's all there is to it. You can draw any number of images in this way. Just be sure to import java.awt.*, because that's where the Image class is located.

Sound works in a very similar way.

import java.applet.*; import java.awt.*; public class HelloWorldApplet7 extends Applet { private Image coffee; public void start () { this.play (this.getDocumentBase (), "c_welcome.au"); coffee = this.getImage (this.getDocumentBase (),"/crash/java/javaback.gif"); } public void paint (Graphics g) { g.fillRect (0,0,57,62); g.drawImage (coffee,2,2,this); g.drawString ("Hello", 15, 25); g.drawString ("World", 12, 45); } }

The only real difference between loading an image and loading a sound is that the sound plays immediately; you don't have to display it or tell it to play.

Again we've put the sound in the start() method. This means that the soud will only be played when the page is reloaded, which is a much better alternantive than putting it in paint() and having called several times in rapid succession.

We'll look at alternative way to play sounds when we get to threads.

Well, that about does it for this lesson. The pace of this tutorial is a bit fast. If you're having trouble understanding, try reading back through it and try writing some basic programs based on the examples. The best way to learn Java is to play with it. When you're ready to go on, the next lesson is GUIs.

Next Lesson: GUIs

Return to the Main Menu.