![]() | Monkeys at Keyboards: APE Antics © Michael James Heron and Pauline Belford | ||||
| Topic: Java Programming Level: 1 Version: alpha | |||||
14 - An Object Lesson 1 - Objects and Classes (Advanced) | |||||
| Previous | Table of Contents | Next |
| Forum |
| Chapter Objectives |
By the end of this chapter, the reader will be able to: |
So, you may or may not have noticed, but we haven't spent an awful lot of time talking about objects in the previous chapters. This may seem strange since Java is an object oriented language and objects form the bedrock of its power. In this chapter, we'll start looking at objects and what they mean to us. Objects and classes serve as the fundamental building blocks of modern OO programming languages - hardly surprising when we consider that OO stands for Object Orientation. Object Orientation provides a particular means of breaking a problem down, and organising a program. Some people find Object Orientation easy to grasp, and a natural way too structure things. Others do not find it a natural way to analyse problems. Don't worry if you fall into the latter group. Object Orientation is the next big step up in terms of complexity, and it takes a lot of time to get your head around it. It is also the last big step up. (We also need to look at File IO, but that is just a small topic in comparison.). Despite the complexity, object orientation is a powerful programming paradigm and the tools it provides can lead to the development of richer and more elegant programming solutions than is possible with other, earlier development frameworks. We won't concern ourselves with a lot about this for now instead we'll focus only on those aspects of object orientation that are relevant to our own smaller tasks. We need to simplify the problem somewhat for our own tiny brains.
Oo, and now we get to the meat of the issue and end up with naught but gristle. There are many books and articles on the subject of object orientation and none of them come up with much in the way of a usable definition for an object... or at least, a definition that helps mere mortals like ourselves to understand what these objects actually are. So we're going to use a useless definition too: an object is a thing. What the heck does that mean? Well, we'll see as we go on through this chapter. But before we do, let's firm up our definition a little with some obtuse jargon:
These three things here distinguish an object. An attribute is something that describes some information pertaining to the object - if you were an object, your attributes would include your hair colour, the colour of your eyes, your name, and so forth. Behaviours are things that an object can do - you can run, jump, skip and dance. All of these are behaviours of the object that would be you. And finally there is a state - your attributes say what information belongs to you as an object. It is the state of an object that determines what values these attributes have. So we know that as a person you have an attribute that contains your name. Your state is the value that your name actually has. Then we have the distinction between and object and a class. We're going to use a loose distinction here - classes tell the object what its attributes are and what its behaviours are, but it's the object that deals with the state. All objects need a class to tell it what it should contain, but that doesn't mean anything until a class is used to create an object. Essentially a class is a blueprint that we use to create fresh objects. We start by writing the class and then later create objects from this class (in a process called instantiation - we've already done this part quite a lot in the course of the book). Complicated stuff? Perhaps - but it should make a little more sense as we progress through the chapter. And if not, don't stress it - it may be some time before this stuff 'clicks' in your mind, and that's normal.
Any College needs to have a system to store student details. Students are all unique individuals, but all students have certain attributes, such as:
The class Student is a template of the attributes which a Student has. (Of course, each student also has many other attributes, but we only need to include the attributes which are important for this particular program. No college needs to know the colour of a student's hair, for example, so we leave that out of the class we design). The class may also have some behaviours that go with it. These behaviours let us perform what functionality is needed for the object.:
An object is an instantiation of a class. This is a grand way of saying 'it's a particular instance of a class', or 'It's a new one of those'. So we have the class Student that defines what attributes a particular student has, and we can have an object (for example, student1) that determines the state of those particular attributes.
Likewise we may also have a second student:
Both of these objects share a degree of similarity in that they have the same attributes and behaviours. The object is what determines what the state of these attributes is going to be.
The box below shows our Student class with the attributes declared:
The first line should be familiar to us. The public keyword tells Java that the class has the most open level of access, but we're not going to worry our pretty little heads about that at all. The class keyword tells Java that we are writing a class, and not a structure of some other kind (this may seem obvious, but Java lets you write different kinds of program as you go along. This is something you don't need to worry about at all for now). As with all our previous programs, the class name must be exactly the same as the filename, otherwise the file will not compile. At the moment, we have attributes but no behaviours. People using the student records system will want to be able to access student's names, addresses, course details, etc, and be able to update these records. To do this, we will need to provide methods. We can provide methods for our classes that allow us to update and query the state of an object. The state of an object is the current values of the data contained in that object. This gives us our first solid look at a behaviour - it's just another way of talking about a method. Strictly speaking, a behaviour is a method that is located within a class. We will use these terms interchangeably throughout the course of this book.
These methods act on the attributes of a class, but as has been indicated above, it is only when we have an instance of a class that these attributes have an actual state. We have provided some methods that can be used on our attributes, but for these particular methods to work we require an actual object to be instantiated. In other words, we need to create an object from our class. If we compile this class, we should receive a message "Process completed". But try to run it and you will get the following error:
The reason for this is that our Student class doesn't actually do anything. It is simply a template, and cannot exist by itself. We need a class with a main method which instantiates the class. Remember, all programs in java (at least, all the ones we are writing) must have a main method and this method contains the code Java will start working with. All that is required to turn a class into a program is the addition of a main method, but we're going to do something slightly different with this one.
We have been creating PacmanGame objects since the beginning of this book. The PacmanGame class is a template for a Pacman game, but we need to create our own object to be able to make use of it. We have been creating our own PacmanGame object like so:
This line of code tells us that we want to create a new instance (an object) of the Pacman game, which will be referred to by the name main. Similarly the following line of code creates a new object of type Student, called student1:
We can then set the state of the object, and change its state, using the methods which belong to that class. So for example, once we had created our instance of the PacmanGame, which we called main, we were able to use the move() method to change the location of pacman on the map. So we use the class, PacmanGame, to instantiate an object called main. We can then make use of the behaviours of our object main to manipulate the attributes contained within. Similarly, for the Student program, once we have created our instance of student, we can use the methods to change the state of the attributes:
So the class Example is now making use of the class we have written, Student. This interaction between classes is very important and powerful, as we will come to see.
It is important to remember that the Student class (and indeed the PacmanGame class) does not have a main method. These are classes which are designed as templates, or blueprints. They are compiled but not run. It is the instances of these classes, the objects, that are manipulated when the program is run. The class template provides the attributes that can be set for each Object, and also the methods by which to alter the state of the attributes. In short, our class definition tells Java what our class should have, but it's only when it becomes an object that it can say what the object is. It's all very Zen. The classes we have written up to this chapter have all had main methods. Every Java project needs one, and only one, main method. This is located in a special class, often referred to as the Engine class. The Engine class is the class which contains the main method - the code to make something happen, and is the class which we run. Every program you write from now on will therefore need to be a new Project. Each Project will contain at least two classes:
To make use of our Student class we need an Engine class containing a main method. In this main method, we put the code to create our student object, and to set the state of our student object. So, in one class file we have the following:
And in our Engine class, we have the code to create a student object:
If we compile this program containing these two classes, it will compile successfully. However, when we run it, it does very little.
In order for the program to actually do something, we first need to set the values of the object. This can be done through the engine class, using the methods provided for setting attribute values. This can also be done when the object is created, using something called a constructor method.
A constructor method is simply a method which a class can have in order to initialise the values of the attributes when the class is created. The simplest constructor method will simply initialise the starting values of the attributes to 0 for integers, and to "" (an empty string) for String attributes, and so on. The constructor method for the PacmanGame sets Pacman's location to the bottom left hand corner of the map, for example. If it were not for this constructor method, you would need to tell the program where Pacman was to be placed. In comparison, a default map is not set in the constructor method, which is why we need to use the setMap(String mapName) method to choose the map we wish to use in this particular instance of the game. A constructor method has a particular format. The name of the method must be the same as the name of the class. In addition, it does not have a return type. So if we wanted a constructor method for the Student class which set the default values of the attributes to null, we would do so like this:
Remember that the constructor method does not need to be called from the main method in order to run. It automatically runs when the class is instantiated. This is why we have the brackets after the class name when we declare an object:
Constructor methods do not need to set values to null. The Student class is not a good example for this, as there are not really any sensible initial values to set. We shall therefore consider a Bank account class. A Bank account has a balance attribute, and there are several behaviours, such as withdrawing and depositing funds. Generally, when you open an account there will be 0 pounds until you make a deposit.
Some Banks offer incentives to encourage people to open an account, such as crediting your account with 50 pounds. To set up an account object so that the starting balance was 50 pounds, we would do the following:
It is possible to have several constructor methods for a class. You could have a default constructor method, which does not take in any input from the main method, then other constructor methods which do take in data. As long as the method signature - what type of thing or things the method takes in, in what order, is different for each constructor method, this will work. The two possible constructor methods above could not exist in the same class as they both have the same method signature. But we could have one constructor method which takes in no variables, and another which takes in the value to set the starting balance to. This would be more flexible, as we can then set it to different values for different accounts.
We can then choose which constructor method to use when we create our Account object.
We have previously allowed the user to enter in several instructions at once for pacman, which we have stored in an array and then executed. This made things faster for the user than having to choose a new move after each step: they could just put in the instructions and then sit back and watch pacman move. But it was still quite a lot of effort for our poor, belaboured user. The had to type in each instruction, even if they wanted some of the instructions repeated. No matter that they wanted pacman to move 10 times before turning: rather than allowing them to enter one instruction "move pacman 10 times", we made them enter "move", "move", "move", "move", "move", "move", "move", "move", "move", "move". That wasn't very nice of us, was it? But then, they are only users and to be feared and loathed. Why didn't we just allow the user to enter in an instruction, along with the number of times they wanted it executed? Well, we could have done this using a 2D array, or using two 1D arrays. But it would all get horribly complicated. But now we know about classes, we can do this much more simply. What we need in an Instruction class. This class has two attributes: the instruction (move, turnLeft, turnRight, stop), and the number of times that instruction is to be repeated. It is good practice to make all variables in a class private. This prevents them from being overwritten directly from the Engine class. Let us say we forgot to do this, and just had the following in our class.
In our Engine class, we could then set the value of times to 4 in the following way:
When we compile and run this program, it will tell us the following:
This is bad practice and should be avoided. If you just let people overwrite your variables then you could accidentally lose important information. Think about the Bank class we looked at earlier. For a real life bank account, the balance should not be able to be set directly, i.e. by someone setting it to 1000000.00. The value of the balance should only change through the methods of withdrawing funds and depositing funds, and of interest being added. By making variables private and the methods through which to manipulate them public, the programmer has more control over how the state of the object can be changed. So it is advisable to make our attributes private, and allow access to them though accessor methods. Accessor methods are get and set methods. Get methods are used to find our the current value of a variable. Set methods are used to set the value of a variable. With private variables and public accessor methods, our class looks like this:
And now, we are prevented from setting the value of times the way we did before. We now get properly told off by the compiler. So we need to use the methods provided by the class to set the values of the variables, like so. We use the set methods to set the variables to appropriate values. We then use the get methods to check that the current value of the variables.
It may seem like a lot of work for no real benefit, but it has huge knock-on effects when writing real code for real people. It's not something you'll need to worry about now, but it's a habit you should get into... well... the habit of. We'll talk about this more in the Javanomicon, should your journey into Java continue that far. It's all about control, security and ensuring consistency of data within objects - complex and important topics that we should not concern ourselves with for now. What happens when we want to extend this now as an array? Turns out, it's as easy as pie:
The exact same syntax we use to set up a regular array is the syntax we use to set up an array of objects. However, whereas Java will give a default value to things like ints and doubles when you create them as an array, it won't do the same for objects. Every element in this array is empty - technically, any attempt to make use of one of these elements will result in a null pointer exception. Let's talk about why that is. We won't return to this particular example, because we're going to leave adding this functionality as an exercise for you, the gentle reader. Aren't you lucky?
Java actually has two kinds of variables. The simplest of these are called primitive variables, and they are the basic numerical and alphanumerical types:
We create and manipulate these with a slightly different syntax than how we deal with, for example, an object of the PacmanGame class. Everything else in Java is a complex variable. All objects are complex variables, as are Strings and Arrays. Complex variables are dealt with at a fundamentally different level in Java. When you pass a primitive data type into a method for example, Java makes a copy of the variable and works with that. Any changes you make in the method won't affect the first variable:
The change made to the variable in the printVariable method doesn't change the value of the variable in the main method. This is true for all primitive data types, and is known formally as passing by value. Objects and other complex data types work in a different way, a way which is known as passing by reference. If you pass a complex data type to a method, it only passes the memory location rather than a copy of the complex data type itself. In short, with objects you are always working with live ammunition, as it were:
Running this program gives us the following output:
This is an important point - Java treats primitives and objects differently. They differ in how they are treated when passed between methods, but they also differ in terms of what happens when they are created. Primitive variables get a starting value of 0. Objects get no starting value, and if you attempt to work with them without creating an instance using the new keyword, you get a null pointer exception:
Before we can use them, we must initialise them:
Once we've done this, we have objects in place for all the elements and we can work with them in the normal Array Way.
Hoo-Boy, objects and classes are tricky. We haven't even scratched the surface here either - we've only seen a little of what they can do and why we need to use them. In the next chapter we'll start building on this simple foundation to develop more complicated (and thus, more powerful) classes. I'm sure you can't wait!
Remember how we left the Instruction class in a single variable form and then talked about arrays of objects? Well, that's where you come in. You're going to implement an array of instruction objects within a pacman game and have him respond to these when issued an appropriate command. You should prompt the user repeatedly for information:
Your program should request this information in a loop until the user provides the instruction 'GO', at which point the loop should end and pacman should start executing the instructions. When pacman has finished the program, the user should be prompted once again and asked if they would like to modify the program. If they do, then they should again be prompted, in a loop, to provide the following information:
For example, if Pacman is currently moving forward five times at instruction five, I should be able to change instruction five so that he instead moves forward four times. This project requires exercise of a number of different skills:
It should prove a sufficiently interesting example to ensure that you are getting to grips with the book content. Further ReadingThe following table details further reading on the topic in this chapter, and also any external resources that you may find useful.
|
| Previous | Table of Contents | Next |
© 2004-2006 Michael James Heron and Pauline Belford