Objects
Home - About Us
 

Objects and References

An object contains both data and instructions.  Objects are created according to the description supplied by a class. A Java class is a blueprint for one or more Java objects. An object is used to model some entity in the real world.  For example, a banking program might have a Bank object to represent the bank, many Customer objects to represent the customers, Account objects to represent the customers' bank accounts, and so on.  One object is used to keep track of the information for one of these entities -- for example, a Customer object would contain the data on one customer (name, address, telephone number, etc.) and also the instructions to manipulate that customer's data. To introduce some basic object and class concepts we'll start off with simple objects that only contain data.  Later we'll look at more complete objects that contain both data and the instructions to manipulate that data.

The following Bus class keeps track of the number of passengers currently on a single bus.  There are many things about a bus that could be modeled, such as the route it is following, the name of its driver, how much gas it has, its odometer, its current location, etc., but for now we'll just model the number of passengers on the bus:

	class Bus
	{
		int passengers;
	}

A Bus object is created from the Bus class via the new operator:

	new Bus()

When an object is created from a class with the new operator,  programmers will often say than they are "creating an instance" of the class (and therefore the creation process is often referred to as an "instantiation"). 

An important skill for a programmer is viusalization.  It is beneficial to think of an object as a physical entity, with physical boundaries and some content.  The Bus object has one data member, passengers, which can hold a single integer.  The default value of the passengers data member is zero.  The visualization I'll be using for a single Bus object is shown here

Again, the class is just a description of what you might want to build.  An object is a physical entity built from that description.  It is possible to build several objects from one class description, just like it is possible to build many houses from a single architectural blueprint.  If you were trying to model three different buses, each with a different number of passengers, then you would need to create three Bus objects:

	new Bus()
	new Bus()
	new Bus()

Here is a visualization of those three objects

Unfortunately, there is no way to for us to distinguish between the first, second, and third Bus objects. A convenient way to access an individual object is via a reference variable. A reference variable for a Bus object is created via a declaration statement, such as

	Bus trainStation;

This declaration statement does not create a Bus object.  It creates a small reference variable which can be made to reference a Bus object (a reference variable is something like a name tag for a Bus object).

The visualization for this reference variable is

Once the trainStation reference variable is created, a Bus object can be created and the reference
variable attached to it:

	trainStation = new Bus();

A picture of what is in memory after the previous two statements have run is shown below


Even though I draw the value of a reference variable as an arrow, in reality a reference variable holds the memory address of an object (i.e., the starting address of the bytes of memory that physically hold the object).

The declaration of the reference variable, the instantiation of the Bus object via the new operator, and the attachment of the reference variable to the Bus object, can be achieved in one statement instead of the two used above:

	Bus trainSation = new Bus();

An object's data members can be accessed via a reference to that object:

	trainStation.passengers = 33;

The "." after trainStation is a member access operator.  It allows access to the contents of the referenced object.  In this case we're starting with the trainStation reference , then use the "." member access operator to gain access to the passenger member of the referenced object (we then use the "=" assignment operator to change the passenger data member to 33).

Once the above assignment instruction has been performed by the CPU, the visualization of what is in memory (the "memory picture") changes to

Note that trainStation.passengers is an expression, with a value of 33.

Being able to create these simple memory pictures can greatly enhance your comprehension of object-oriented programming.  Take out a scrap of paper and draw a memory picture of the objects and reference variables that are in memory after the following instructions have been performed

	Bus airport = new Bus();
	Bus school = new Bus();
	Bus downtown = new Bus();
	airport.passengers = 5;
	school.passengers = 11;
	school.passengers++;
	downtown.passengers = airport.passengers + 2;
	airport.passengers -= 1;

After the above instructions have been performed, what are the values of the expressions airport.passengers, school.passengers, and downtown.passengers?  Click here to see the values.

Click here to see my representation of memory.

Note that a single object can be referenced by several different reference variables simultaneously.  The following code causes all three reference variables to reference the same object -- the object with the value 35 (originally the object held the value 50).  The object with the value 17 is left unreferenced.  When you assign one reference variable to another, such as in the statement airport = school, shown below, the reference (i.e., the "arrow", which is in fact a memory location of the referenced object) gets copied from one reference variable to the other.

	Bus airport = new Bus();
	Bus school = new Bus();
	Bus downtown;
	airport.passengers = 17;
	school.passengers = 50;
	airport = school;   // change the reference airport, not what it is referencing
	downtown = school;
	airport.passengers = 35;

Click here to see my representation of memory.  Click here to see the values of the expressions airport.passengers, school.passengers, and downtown.passengers.

If you are not following the manipulations of the reference variables in the two examples shown above, I have a couple of discussions that you can listen to.  The first discussion makes sure that you are familiar with the simple fundamentals of the assignment statement, followed by a discussion of assignments statements and object references (since these are audio/visual files that are fairly large -- three or four megabytes in size).

Defining Methods Inside the Objects

In addition to holding data values, objects usually also contain instructions to manipulate the data values.  These instructions are grouped together in methods.  A method is a group of instructions with a name.  Here is an updated version of the Bus class -- I've added a method whose instructions add one to the passenger data member.  The name of the method is oneMoreAboard. I've also added a method named everyoneOff.  The everyoneOff method empties the bus of all passengers.

	class Bus
	{
		int passengers;
		
		void oneMoreAboard()
		{
			passengers++;
		}
		
		void everyoneOff()
		{
			passengers = 0;
		}
	}

The instructions of the method can be run by "calling" the method's name, as shown below:

 	Bus school = new Bus();
	school.oneMoreAboard();

The oneMorePassenger method is accessed via "school.oneMorePassenger()"

This "calls" the method, which causes the instructions of the method to be performed.  In this case, there is one instruction, which adds one to the passengers variable:

Here is another example, in which the oneMorePassenger method is called three times:

 	Bus school = new Bus();
	school.oneMoreAboard();
	school.oneMoreAboard();
	school.oneMoreAboard();   // school bus now has three passengers

Changing the values via a method, such as with school.oneMorePassenger(), is preferable to directly accessing the variables, such as with school.passengers++.  The latter method seems more direct, but on larger programs it is much safer to have the variables accessed and changed only by well-written methods.  In fact, most Java programmer will put the keywork "private" in front of the variable declarations, making it impossible for outside references to directly access the variables.  And the keyword "public" is put in front of the method definitions, making the methods open to all outside references.  Here is the Bus class again, this time with the appropriate "private" and "public" designations:

	class Bus
	{
		private int passengers;
		
		public void oneMoreAboard()
		{
			passengers++;
		}
		
		public void everyoneOff()
		{
			passengers = 0;
		}
	}
	

Constructors

Most professionally written classes contain constructors. A constructor initializes a newly created object's instance variables, and can also allow the initial values to be specified by the creator of the object. The Bus class, above, could be improved by adding a constructor that allows the starting number of passengers to be specified when the object is created.  For the moment I'm going to remove the two methods oneMoreAboard and everyOff so that we can focus on the constructors. A constructor looks similar to an ordinary method -- but its name is always exactly the same at the name of the class.  Even though a constructor looks like a method, it cannot be called like the oneMoreAboard method was called several times in examples, above.  The instructions in a constructor happen automatically when an object is created with the new operator.  The example below shows a Bus class with a constructor that allows the starting number of passengers to be sent into the object when the object is created (the requested number of passengers is sent in as the startPassengers variable):

	class Bus
	{
		int passengers;
	
		public Bus (int startPassengers)
		{
			passengers = startPassengers;
		}
	}

Now that we have a constructor that can recieve a value, we can create Bus objects that specify their initial number of passengers:

	public class Driver
	{
		public static void main(String [] args)
		{
			Bus route1 = new Bus(3);
			Bus route2 = new Bus(10);
		}
	}

When you create a Bus object you can specify the number of passengers that start out on the bus. Note that a constructor's instructions are automatically performed when a new object is created. You do not (and cannot) directly call a constructor -- it is part of the creation process of an object.  A constructor has exactly the same name as the class, and does not have a return type (e.g., does not have the keyword "void" that preceded the names of the methods we wrote, above). Above I've created a Driver class, whose only purpose is to create and test a couple of Bus objects.  The first Bus object is created with the expression "new Bus(3)".  The value is an integer that is going to be passed to the startPassengers variable in the Bus class' constructor.  The value three is can be refered to by many different names or expressions.  Some common expression are "3 is the value passed to the constructor", or "3 is a parameter passed to the constructor", or "3 is an argument passed to the constructor".

Every class has to have a constructor. If the programmer does not specify a constructor, then a default constructor is added automatically. So our original Bus class, which we wrote as

	class Bus
	{
		int passengers;
	}

actually looked like this

	class Bus
	{
		int passengers;
	
		public Bus ()
		{
			super();  // ignore this line for now
		}
	}

after it was compiled.  Ignore the "super()" line for the moment -- we'll cover that later on. It is the default constructor that allowed our earlier lines of code to work properly -- that allowed the creation of a new Bus object with expressions like "new Bus()":

	Bus trainStation;
	trainStation = new Bus();

Once the programmer writes a constructor, the default constructor is no longer automatically added.  If we write a constructor that takes a value then the default "no argument" constructor is no longer written for us (we will have to write that ourselves).  The following code won't compile because the main method in the Driver class is creating objects where a value is passed to the constructor (the values 3 and 10) and also tries to use a constructor where no value is passed (i.e., "new Bus()"):

	class Bus
	{
		int passengers;
	
		public Bus (int startPassengers)
		{
			passengers = startPassengers;
		}
	}
	
	public class Driver
	{
		public static void main(String [] args)
		{
			Bus route1 = new Bus(3);
			Bus route2 = new Bus(10);
			Bus route3 = new Bus();   // this line won't compile
		}
	}

If we want to be able to create Bus objects both by specifying the initial number of passengers, and also by just letting the number of passengers default to zero, then we need to have two constructors:

	class Bus
	{
		int passengers;
	
		public Bus ()
		{
			passengers = 0;
		}
	
		public Bus (int startPassengers)
		{
			passengers = startPassengers;
		}
	}
	
	public class Driver
	{
		public static void main(String [] args)
		{
			Bus route1 = new Bus(3);
			Bus route2 = new Bus(10);
			Bus route3 = new Bus(); 
		}
	}
	

The above code will work properly -- the first two instantiations, "new Bus(3)" and "new Bus(10)", use the constructor that receives an input value as startPassengers, and the last instantiation, "new Bus()", uses the "no argument" constructor.

Pulling It All Together

Here is a short program in which the Sample class creates several Bus objects.  The visualization shown below the code illustrates the action of the constructors and the methods.  This example has a Bus class with the constructors talked about above, plus the oneMoreAboard and everyOff methods, and new method report that prints a short message to the console.

	class Bus
	{
		private int passengers;
	
		public Bus ()
		{
			passengers = 0;
		}
	
		public Bus (int startPassengers)
		{
			passengers = startPassengers;
		}
		
		public void oneMoreAboard()
		{
			passengers++;
		}
		
		public void everyoneOff()
		{
			passengers = 0;
		}
		
		public void report()
		{
			System.out.println("Passengers on this bus: " + passengers);
		}
	}
	
	public class Sample
	{
		public static void main(String [] args)
		{
			Bus route1 = new Bus(22);
			Bus route2 = new Bus(4);
			Bus route3 = new Bus(); 
			
			route1.oneMoreAboard();
			route1.report();
			
			route2.oneMoreAboard();
			route2.oneMoreAboard();
			route2.oneMoreAboard();
			
			route3.oneMoreAboard();
			
			route1.everyoneOff();
			route1.oneMoreAboard();
			route1.report();
		}
	}

When the code runs the following message shows up on the console:

	Passengers on this bus: 23
	Passengers on this bus: 1

Only the data values are shown inside the objects this visualization of the above code.

Note that if you put two Java classes in one file, then one of the classes should be labeled "public" and the file's name should exactly match the name of that public class. In the above example there are two classes, Bus and Sample, and the Sample class is labeled as "public" -- therefore, these classes should be kept in a file named "Sample.java".  You could also put each class in its own file (i.e., public class Bus in Bus.java and public class Sample in Sample.java).

Adding a Value Returning Method

In the Bus shown below has a new method, getPassengers. The getPassengers method returns an integer that represents that number of people currently on the bus:

	class Bus
	{
		private int passengers;
	
		public Bus ()
		{
			passengers = 0;
		}
	
		public Bus (int startPassengers)
		{
			passengers = startPassengers;
		}
		
		public void oneMoreAboard()
		{
			passengers++;
		}
		
		public void everyoneOff()
		{
			passengers = 0;
		}
		
		public void report()
		{
			System.out.println("Passengers on this bus: " + passengers);
		}
        
		public int getPassengers()  // note the "int" return type, instead of "void"
		{
			return passengers;
		}
	}
	
	public class Sample
	{
		public static void main(String [] args)
		{
			Bus route1 = new Bus(22);
			Bus route2 = new Bus(4);
			Bus route3 = new Bus(); 
			
			route1.oneMoreAboard();
			route1.report();
			
			route2.oneMoreAboard();
			route2.oneMoreAboard();
			route2.oneMoreAboard();
			
			route3.oneMoreAboard();
			
			route1.everyoneOff();
			route1.oneMoreAboard();
			route1.report();
            
			int num = route1.getPassengers();
			System.out.println("Route One has " + num + " passengers");
            
			int amount = route2.getPassengers();
			System.out.println("And route two has: " + amount);
		}
	}
        

When the code runs the following message shows up on the console:

	Passengers on this bus: 23
	Passengers on this bus: 1
	Route one has 1 passengers
	And route two has: 7
        

Get functions are often just one line of code -- they just return the requested value.

Passing Parameters to a Method

The code above forces you to board passengers one-at-a-time. If twenty people board a bus, you need to call oneMoreAboard twenty times! In the code below we've added a method board that lets you specify how many people are getting on at one time:

	class Bus
	{
		private int passengers;
	
		public Bus ()
		{
			passengers = 0;
		}
	
		public Bus (int startPassengers)
		{
			passengers = startPassengers;
		}
		
		public void oneMoreAboard()
		{
			passengers++;
		}
		
		public void everyoneOff()
		{
			passengers = 0;
		}
		
		public void report()
		{
			System.out.println("Passengers on this bus: " + passengers);
		}
        
		public int getPassengers()  // note the "int" return type, instead of "void"
		{
			return passengers;
		}

		public void board(int num)
		{
			passengers += num;
		}
	}
	
	public class Sample
	{
		public static void main(String [] args)
		{
			Bus route1 = new Bus(22);
			Bus route2 = new Bus(4);
			Bus route3 = new Bus(); 
			
			route1.oneMoreAboard();
			route1.report();
			
			route2.oneMoreAboard();
			route2.oneMoreAboard();
			route2.oneMoreAboard();
			
			route3.oneMoreAboard();
			
			route1.everyoneOff();
			route1.oneMoreAboard();
			route1.report();
            
			int num = route1.getPassengers();
			System.out.println("Route One has " + num + " passengers");
            
			int amount = route2.getPassengers();
			System.out.println("And route two has: " + amount);
            
			route1.board(20);  // there was one passenger on board - after this method call there will be 21
			route1.board(3);   // now the number of passengers will go from 21 to 24
			route1.report();
		}
	}
        

When the code runs the following message shows up on the console:

	Passengers on this bus: 23
	Passengers on this bus: 1
	Route one has 1 passengers
	And route two has: 7
	Passengers on this bus: 24
        

 

Home - About Us
Copyright © 2006 by Kiowok, Ann Arbor, Michigan, USA