IntObj Example: Part 1

Primitive Types

Java has a small number of primitive types. You have already seen int which is used to declare variables which store integers. There is also char which is used to declare variables which store characters, double which is used to declare variables which store numbers with fractional parts, and boolean used to declare variables which can only be set to either true or false.

There are a few other primitive types which come abvout because Java uses a fixed amount of computer store for each primitive type. For example, an int is always stored in 32 binary digits (bits). That means the maximum value an int variable can store is 2,147,483,647. There is another type called long which you can use if for any reason you need to handle bigger values than that. So, for example

long val;
declares a variable called val which can hold bigger integers than the above value (it uses 64 bits to store integers). If you were sure your integer variable never needed to store a value greater than 32,767, you could use the type short which takes up only 16 bits to store an integer. This would only be important if you had a program which stored lots of integer data and so was in danger of taking up all the store available. As we are not going to deal with programs of that sort in this course, or programs involving very large integers, we shall not deal further with long and short.

All other Java types, however, including Strings are object types. Variables which are object types behave in a slightly different way than variables which are of primitive types. Below we discuss the behaviour of variables of primitive types. In the next section we show the different behaviour of assignment of object type variables.

Suppose we have two int variables, a and b, so declared:

int a, b;
If we assign b to a with:
a=b;
the effect is that the value which is in variable b is copied into variable a. Whatever was in a before is overwritten and lost. But a and b are two separate store locations. If b is changed to something else, a will continue to hold whatever value was copied in it from b. Here is an example:
 1 class Ints0
 2 {
 3
 4  public static void main(String[] args)
 5  {
 6   int a=5, b=7;
 7   System.out.println("Starting with: ");
 8   System.out.println("a = "+a);
 9   System.out.println("b = "+b);
10   System.out.println();
11   System.out.println("Now executing a=b");
12   a=b;
13   System.out.println("a = "+a);
14   System.out.println("b = "+b);
15   System.out.println("Now executing b=3");
16   b=3;
17   System.out.println("a = "+a);
18   System.out.println("b = "+b);
19  }
20 }
You will find similar with double variables and the other primitive types. If this is compiled and run, you will see how a first stores 5 and b stores 7, then executing line 12 causes a to store 7 as well, and a still stores 7 when b is assigned the new value 3 on line 16.

A Simple Object Type

Java has many object types provided as part of the language. However, we can also define our own object type. An object type is defined by a class. We have already used the Java keyword class because it is necessary in every Java program. However, most Java programs consist of several classes, and most Java classes are "object descriptors".

A Java class consists of the word class, the name of the class, an open bracket, some contents and a close bracket. Some of the content looks like variable declarations. But these are actually declaring fields. The rest of the class consists of methods. Here's a simple example:

 1 class IntObj
 2 {
 3  private int val;
 4  
 5  public IntObj(int n)
 6  {
 7   val=n;
 8  }
 9  
10  public void add(int m)
11  {
12   val=val+m;
13  }
14
15  public int value()
16  {
17   return val;
18  }
19
20 }
Usually a class is written in a separate file with the same name (plus the suffix java) as the class, so the above should be put in a file called IntObj.java.

Once this is in place, we have a type IntObj. We can declare variables of type IntObj, using IntObj just like we used int:

IntObj a,b;
However, we cannot assign an int to an IntObj. Suppose we had
int k;
alongside the above declaration of a and b, then it would be a compiler error to have a=k, or k=a, or a=3, and so on.

An IntObj can be thought of as a "thing" which has its own int which it calls val. This is what line 3 above says. The word private means that nothing else may change val except the IntObj itself.

Lines 5-8 give a constructor method. A constructor method has the same name as the class, and is used to create new objects of the type of that name. Line 5 says that a new IntObj object is created by

new IntObj(expr)
where expr is any expression that evaluates to an integer value. The name n means this expression is temporarily called n when the new IntObj is created. The integer value called n is assigned to the new IntObj object's val field in line 7.

So, for example

a = new IntObj(5);
will create a new IntObj object whose val field is 5, and give it the name a, while
b = new IntObj(k+3);
will create a new IntObj object whose val field is 3 greater than whatever is in the int variable k. The word public on line 5 states that any method in any other class may make use of the constructor to create a new IntObj object.

Lines 10-13 are how an IntObj object changes the value of its val field. These lines are a method called add which is attached to IntObj objects. As it is public, any other method which has an IntObj variable may cause the IntObj object that variable names to change its val field by adding the value it gives as an argument. For example,

a.add(7);
causes 7 to be added to the val field of the object named a. The argument m on line 10 can be considered a temporary variable which the 7 is copied into, and which is then used just once, on line 12, when a new value is constructed by adding it and the value in the val field of a, and copying the result of the addition into the val field (obviously, overwriting what was already in there). The void on line 10 means that this method does not return a value. A call to it is used as a statement on its own.

The method on lines 15-18, however, does return a value. In fact this is all it does. It has no arguments, but the brackets are still needed when it is declared and when it is used. So to use it, for example, we have a.value(). However, it makes no sense to have a.value(); on a line on its own, as that returns a value but does nothing with it. It only makes sense if it used for something. As it has return type int, a call to the value method of an IntObj object can be used wherever an int can be used. For example, it can be assigned to an int variable:

k = a.value();
it can be used in an arithmetic expression:
k = 4*a.value()+5;
and it can be used as an argument in a method call which requires an int argument:
b.add(a.value());
In all of these a.value() stands for the value returned by the value method of a at the point when it is called. A line starting return in a non-void method gives the value that the method returns, in this case, as shown on line 17, it's just the value in the val field of the object.

Here's a program which demonstrates IntObj as defined so far:

 1 class Ints1
 2 {
 3
 4  public static void main(String[] args)
 5  {
 6   IntObj a,b;
 7   int k=5;
 8   a = new IntObj(k);
 9   b = new IntObj(a.value());
10   System.out.println("Starting with: ");
11   System.out.println(" a's value: "+a.value());
12   System.out.println(" b's value: "+b.value());
13   System.out.println(" k's value: "+k);
13   System.out.println();
14   System.out.println("Now executing a.add(3)");
15   a.add(3);
16   System.out.println(" a's value: "+a.value());
17   System.out.println(" b's value: "+b.value());
18   System.out.println(" k's value: "+k);
19   System.out.println("Now executing b.add(k*2)");
20   b.add(k*2);
21   System.out.println(" a's value: "+a.value());
22   System.out.println(" b's value: "+b.value());
23   System.out.println(" k's value: "+k)
24  }
25 }
It's common to refer to the arguments of a method as its "input" and its return value as its "output". However, it's very important not to confuse this usage of the terms "input" and "output" with the usage that refers to reading values from the screen or file and writing values to a file or the screen. These are two completely different things. We may say that the method add above takes an int as "input", but it does not read the int from an input device. Similarly, though we may say the method value "outputs" and int, it does not write anything to an output device.

Object assignment is aliasing

As mentioned above, assignment of primitive types involves copying the values into variables. If we have:
int a,b;
and later
a=b;
at the point a=b is executed, the value in the variable a is copied into the variable b, but there is no further connection between a and b. However, assignment of object variables is completely different, as it means aliasing.

Aliasing means giving two or more separate names to one thing, so that one name name is an "alias" for the other. If we have:

IntObj a,b;
and later
a=b;
the effect is that a becomes an alias for b. Or, in other words, a and b are two names for the same object. The object they name is whatever b referred to before the execution of a=b, and if a referred to some different object before that it no longer does. This sort of behaviour applies to all object types.

Here is some code which demonstrates this:

 1 class Ints2
 2 {
 3 
 4  public static void main(String[] args)
 5  {
 6   IntObj a,b;
 7   a = new IntObj(3);
 8   b = new IntObj(7);
 9   System.out.println("Starting with: ");
10   System.out.println(" a's value: "+a.value());
11   System.out.println(" b's value: "+b.value());
12   System.out.println();
13   System.out.println("Executing a=b");
14   a=b;
15   System.out.println(" a's value: "+a.value());
16   System.out.println(" b's value: "+b.value());
17   System.out.println();
18   System.out.println("Executing b.add(5)");
19   b.add(5);
20   System.out.println(" a's value: "+a.value());
21   System.out.println(" b's value: "+b.value());
22   System.out.println();
23   System.out.println("Executing b = new IntObj(4)");
24   b = new IntObj(4);
25   System.out.println(" a's value: "+a.value());
26   System.out.println(" b's value: "+b.value());
27   System.out.println();
28   System.out.println("Executing b.add(2)");
29   b.add(2);
30   System.out.println(" a's value: "+a.value());
31   System.out.println(" b's value: "+b.value());
32  }
33 }
Note that when on line 19, the add method with argument 5 is called for IntObj variable b the effect is that the value returned by the value method on a changes from 7 to 12 as well. This is because a and b refer to the same object, which happened after line 14 was executed.

If two object variables refer to the same object, this changes when one is assigned to another. For example, just before line 24 above is executed, a and b refer to the same object, but after it while a still refers to that object (which is the one created on line 8 which b originally referred to), b refers to a new object which returns 4 when the value method is called on it.

It is possible to use initialisation with object variables, so long as the initialisation does not refer to any variables before they are decared. So lines 6-8 above coule have been written more concisely:

IntObj a = new IntObj(3), b = new IntObj(7);
Note that when an object variable is declared, it is not the case that an object is created for it to name. Thus, for example, after the execution of line 7 above but before the execution of line 8, the variable b is declared but does not name any object. If a variable is used without being initialised, the program will not compile.
Matthew Huntbach

These notes were produced as part of the course Introduction to Programming as it was given in the Department of Computer Science at Queen Mary, University of London during the academic years 1998-2001.

Last modified: 28 Sep 1999