Lecture 3: Constructors. Reference Types and Linked Lists.
Brief summary of things so far
So far we have looked at methods, classes, and objects. We now know that programs in Java are essentially a collection of class defintions, which are compiled and run. We have said that there are three things that you should expect to see in a class definition:
static) and
We have compared classes with factories that contain blueprints for manufacturing gadgets (objects). The collection of instance members that appear in the class definition represents the blueprint.
One can create new objects of a certain class with the new operator, which invokes a constructor. So far we have said we would not worry about that since if we don't specify any constructor for a class Java will provide a default one that has no arguments and that (for all we care right now) does nothing.
When a constructor is invoked the blueprint is checked against, and an object is produced right away. According to the blueprint (which contains the descriptions of all the instance members) the newly created object has its own set of instance members. When we rely on the default no-args constructor the instance variables are initialized to their default variables.
The initial state of an object can be customized by providing specialized constructors, and we will see a few examples of that today. The rule that we have to keep in mind is that if at least one constructor is provided with the class definition there is no default constructor available any longer.
So we need to remember that instance members are one per object,
while class (or static) members are one per class.
That means that class variables are global to all objects. The purpose
of the class variables is to keep state for the entire factory.
Among all static methods there is one that plays a special
role: it runs the show. It must be defined as public and
void and have the following signature
public static void main(String[] argv) {
}
and it can be used as a starting point of your program. Class methods cannot access instance members directly since there are as many instance members with the same name as created objects, so you can only access them through an object reference.
Class members can be accessed directly by instance methods. They can also be accessed through the class name or through a reference to an object of the class.
Since main runs the show you can put it separately in a
class of its own that does not have any other members declared other
than the main routine main. But it's perfectly OK to
place your main in one of the classes that you've
written for the program (see Point below) and in fact,
as we will show later, each class can have its own main,
which will enable you to run the show in more than one way (a technique
that is sometimes called unit testing).
Constructors
Let's consider the following class definition:
class Point {
double x;
double y;
}
We can draw a picture of that.
Let's say we want to create a few Point's in a
real program. We need a class method main in a
class somewhere. Instead of using another class we could use
class Point so we might as well develop it for
it's value as a Point as well as to run the show
(or the program).
So we have
class Point {
double x;
double y;
public static void main(String[] args) {
Point p, q;
p = new Point();
p.x = 3.0; p.y = -2.0;
q = new Point();
}
}
When this program is compiled and run the main method is invoked.
It declares two variables of type Point and it places in each
one of them a reference to two newly created Points. By default
the instance members (the coordinates) of each of the Points are
0.0 and 0.0. To change the coordinates of the first
one of the Points to (3.0, -2.0) we use the
reference to the object and the dot operator to access and change each one
of the instance variables to the desired values. We can provide a constructor that does exactly the same thing, and use it.
class Point {
double x;
double y;
Point(double coord1, double coord2) {
x = coord1;
y = coord2;
}
public static void main(String[] args) {
Point p, q;
p = new Point(3.0, -2.0);
q = new Point(); // this will no longer work
}
}
The first goal is achieved, but a problem arises. We can solve
it in one of two ways: class Point {
double x;
double y;
Point(double coord1, double coord2) {
x = coord1;
y = coord2;
}
public static void main(String[] args) {
Point p, q;
p = new Point(3.0, -2.0);
q = new Point(0.0, 0.0);
}
}
Point that initializes the
instance variables with the default values)
class Point {
double x;
double y;
Point(double coord1, double coord2) {
x = coord1;
y = coord2;
}
Point() {
x = 0.0;
y = 0.0;
}
public static void main(String[] args) {
Point p, q, r;
p = new Point(3.0, -2.0);
q = new Point();
}
}
which creates a classjavac Point.java
Point.class and then run it with
depending on the platrform you're running on (NT or Unix).java Point
Reference Types and Linked Lists
We start with the definition of a Pair.
class Pair {
int value;
Pair next;
}
We draw a picture of it.
It is not much different from Point.
The default value for reference types is null.
Let's write a customized constructor, one that creates Pairs
with a specific initial value.
class Pair {
int value;
Pair next;
Pair (int v, Pair n) {
value = v;
next = n;
}
}
Let's play with this constructor to see how it works.
We draw a picture and realize that this creates a linked listPair p, q; p = new Pair(1, null); q = new Pair(2, null); p.next = q;
1, 2.A shorter version would be
so we realize that we don't needPair p, q; p = new Pair(1, new Pair(2, null));
q any longer. Exercise
Consider the following code fragment:
Draw a picture for the structure thatPair p = new(1, new (6, new (3, new (2, null))))
p points to.
Now that we have Pair's we will create a List
class. List objects contain one instance variable, which is
a pointer to the first Pair in the list. They also must have
a constructor that takes one argument (an int) and produces
the desired number of Pairs the first one of which will be
pointed at by the instance variable p (a better name would
have been firstPair)
class List {
Pair p;
List (int size) {
p = new Pair(0, null);
Pair aux = p;
for (int i = 1; i < size; i++) {
aux.next = new Pair(1, null);
aux = aux.next;
}
}
}
The constructor assumes that the required size is
greater than 0. It creates a first cell that has
0 as the value and then it creates the rest of the
remaining cells and places in them consecutive integers. Thus,
at the end, the instance variable p will point to
a list of Pairs that will hold increasingly bigger
values from 0 to size - 1.
Our Lists differ only in size.
We will add an instance method length() that will enable
List objects to reply to the question 'What's your
length?' when asked about it.
int length() {
int result = 0;
if (p == null) return result;
else {
for (Pair aux = p; aux != null; aux = aux.next)
result += 1;
return result;
}
}
Essentially the function counts the number of cells by walking on them
with the help of auxiliary variable aux. Let's set things up for the method that runs the show.
We create a class called ListProcessor
class ListProcessor {
public static void main(String[] args) {
List l = new List(6);
System.out.println("The list has length " + l.length);
}
}
place all three classes in a file File.java in an empty
directory and compile it. Then run class ListProcessor,
which should print 6, the length of the created list.
Here is, for redundacy and summary, class List again, with
the just defined instance method that computes the length of the list:
class List {
Pair p;
List (int size) {
p = new Pair(0, null);
Pair aux = p;
for (int i = 1; i < size; i++) {
aux.next = new Pair(i, null);
aux = aux.next;
}
}
int length() {
int result = 0;
if (p == null) return result;
else {
for (Pair aux = p; aux != null; aux = aux.next)
result += 1;
return result;
}
}
}
Note that length() is implemented iteratively.
We then change class ListProcessor by adding a method
show() which given a first cell in a list walks through
it and returns the textual representation of the list as a sequence
of integer values separated by blanks.
class ListProcessor {
public static void main(String[] args) {
List l = new List(6);
System.out.println("New list: " + show(l.p));
System.out.println("The list has length " + l.length());
}
static String show(Pair p) {
if (p == null) { return " "; }
else { return p.value + " " + show(p.next); }
}
}
Note that show is static (why?) and is implemented
recursively.
So at this point we are able to create lists of any given length,
compute their length and display them to screen. The length is
computed through an instance method (of class List)
implemented iteratively and the display function is implemented
through a class method of class ListProcessor.
For your exercise we would like you to check your understanding of the program we have just developed by providing:
List that
iteratively displays the list (call it show())
ListProcessor
that recursively calculates the length of a list, given a
reference to the first cell in the list.
The desired behaviour is illustrated below:
The fragment below contains the code developed in the lecture, compiled and run.tucotuco.cs.indiana.edu% javac File.java tucotuco.cs.indiana.edu% java ListProcessor The list is: 1 The length: 1 The list is: 1 The length: 1 tucotuco.cs.indiana.edu% java ListProcessor The list is: 6 5 2 The length: 3 The list is: 6 5 2 The length: 3 tucotuco.cs.indiana.edu% java ListProcessor The list is: 3 2 1 The length: 3 The list is: 3 2 1 The length: 3 tucotuco.cs.indiana.edu% java ListProcessor The list is: 1 1 3 6 2 The length: 5 The list is: 1 1 3 6 2 The length: 5 tucotuco.cs.indiana.edu%
tucotuco.cs.indiana.edu% ls -l Assignment.*
-rw------- 1 dgerman students 1275 Jun 23 13:01 Assignment.java
tucotuco.cs.indiana.edu% cat Assignment.java
class ListProcessor {
public static void main(String[] args) {
List l = new List((int)(Math.random() * 6 + 1));
System.out.println("The list is: " + l.show());
System.out.println("The length: " + l.length());
System.out.println("The list is: " + show(l.p));
System.out.println("The lenght: " + length(l.p));
}
static int length(Pair p) {
return -1; // implement recursive length();
}
static String show(Pair p) {
if (p == null) { return " "; }
else { return p.value + " " + show(p.next); }
}
}
class List {
static final int SIZE = 6;
static final int VALUE = 10;
Pair p;
List (int size) {
p = new Pair((int)(Math.random() * VALUE + 1), null);
Pair aux = p;
for (int i = 1; i < size; i++) {
aux.next = new Pair((int)(Math.random() * VALUE + 1), null);
aux = aux.next;
}
}
int length() {
int result = 0;
if (p == null) return result;
else {
for (Pair aux = p; aux != null; aux = aux.next)
result += 1;
return result;
}
}
String show() {
return " "; // implement recursive show();
}
}
class Pair {
int value;
Pair next;
Pair(int val, Pair nextPair) {
value = val;
next = nextPair;
}
}
tucotuco.cs.indiana.edu% ls -l *.class
No match
tucotuco.cs.indiana.edu% javac Assignment.java
tucotuco.cs.indiana.edu% ls -l *.class
-rw-r--r-- 1 dgerman students 735 Jun 23 13:04 List.class
-rw-r--r-- 1 dgerman students 1285 Jun 23 13:04 ListProcessor.class
-rw-r--r-- 1 dgerman students 362 Jun 23 13:04 Pair.class
tucotuco.cs.indiana.edu% java ListProcessor
The list is:
The length: 4
The list is: 6 6 4 6
The lenght: -1
tucotuco.cs.indiana.edu% java ListProcessor
The list is:
The length: 5
The list is: 8 3 10 6 7
The lenght: -1
tucotuco.cs.indiana.edu%
Last modified: Jun 24, 1999