The test was marked out of 80, and the 20 marks for each question were divided equally among its parts (with 7 marks for each part of questions 2 and 3, but a maximum of 20 overall). However, since the test was done very badly by most people, and there were complaints that it was too much for the time available, I have decided to grade it by counting complete and correct answers to three questions as enough to get full marks. In other words, a mark of 60 is equivalent to 100%, and the pass mark is 40% of 60, which is 24. The three people who got over 60 marks will be recorded as getting 100%.
Even after this scaling, over half those taking the test failed. I do not believe this is an acceptable performance. Anyone who had paid any attention to what was going on in the classes and done a significant amount of labwork ought to have had no trouble getting 24 marks.
Object
" is not. Note
this font
(which has the name courier
) is
consistently used in the notes, in many textbooks, in the tests, and will
be in the final exam, for wording that comes directly from a Java program,
and not for anything else. So that means Object
must be a
word that can be used in a Java program. There is no excuse for not knowing
what is means and how it is used: we covered it in week 18, it is covered
prominently in the lecture summary
for that week, and it was used in some of the bank examples which were
talked about in the lectures and you were expected to study until you
understood them.
char
is the word used for the ordinary character type in
Java, so Character
is something different. It is one of the
primitive wrapper types, which were clearly covered in the lectures and
are mentioned in the week 18 lecture summary.
interface
",
some people still answered it as if it was "what is an interface?". It
is unfortunate that we have the general term "interface" in computing,
which we use in phrases like "graphical user interface", but in Java also
the much more specific concept of an interface defined by using the
keyword interface
. However, the wording and Courier
font
used for the question made it very clear it was the latter
meaning that was intended, and this was covered in week 11, you can
find it mentioned in the lecture summary
for that week, and extensive use of it has been made in the course since
then.
Note that it was not certainly enough to say "we use the word to declare a Java interface", since that really does not indicate an understanding of what is meant by the term. "We use it to define a type in terms of what objects of that type can do" is better, but a little imprecise, so a completely correct answer would need to mention that an interface lists the methods that all objects of that type are expected to be able to have called on them.
static
as indicating that an entity isn't
associated with a particular object created from a class. Some people
mentioned static initialiser blocks, which we haven't covered at all
in the course. It would be acceptable to mention static initialiser blocks
if you just wanted to be complete in your answer and demonstrate reading
beyond what was in the course, but mentioning just this use and not the
main use of the keyword static
suggests just a random
copying without understanding of a phrase from a textbook.
Ship
class, and in the Grommet
exercises in week 6. Those who did
badly when they were tested on being able to do this in
test 1 ought to have learnt from experience
and made sure they had corrected their misunderstandings. That is one
of the main reasons for having term-time tests. If you do badly in a
test you need to go over it, work out what you didn't understand, and
make sure you do understand it in future.
One big difference here from what we did before, however, is that
questions 2 and 3 deal with arrays of objects. That ought not to have been
a problem since we dealt extensively with arrays of objects in the
banks examples in class. Question 3 also deals with a class
(Councillor
) which has a subclass (Alderman
).
Again, we dealt with something similar in the banks example
(AccountB
with subclass DepositAccountB
).
Many people still don't seem to understand the basic idea
of methods, parameter passing and value returning. When you are
asked, for example to "write a method which takes a Thing
and a Doodah
and returns a Grommet
", you
ought immediately to start by writing a header something like:
Grommet myMethod(Thing t, Doodah d)Here,
myMethod
is just a name you've chosen to call the
method, you might be asked to call a method a given name, otherwise
you should choose a name which fits in with the intended use of the
method. You have to give names to your arguments, here they are just
t
and d
. If there are more meaningful variable
names you can think of, then you should use those. For example, in this
question all the methods take an array of Councillor
objects
as an argument. It is much better to name this argument council
than something meaningless like a
.
Note that if we have a class called MyType
, then the type
MyType[]
is the type for an array of MyType
objects. This is true for any type, so if an argument is to be
an array of Grommet
objects, then its type is
Grommet[]
, for example.
Suppose the method we started above called myMethod
is
static, so its full signature is:
public static Grommet myMethod(Thing t, Doodah d)and suppose it's in a class called
MyClass
. Then if
t1
is a Thing
value and d1
is
a Doodah
value (they could be variables of type
Thing
and Doodah
, but they could also be
method calls that return values of that type), then
MyClass.myMethod(t1,d1)
is a Grommet
value. The
exact value is given by executing the code which follows the
{
after the signature given above,
but initially setting t
in the code to t1
and
d
in the code to d1
. Since object assignment
is aliasing, that means that t
and d
are local
names for t1
and d1
. The value which the
call evaluates to is the value given in the return
statement
that is executed in the method's code.
If the method is not static, then its full signature will be:
public Grommet myMethod(Thing t, Doodah d)Now, as it is in class
MyClass
when it is called it must
be attached to a MyClass
object. If m
is
a myClass
value, then m.myMethod(t1,d1)
is
a Grommet
value. The value is given by executing the code
of the method as before, but the word this
in the code
should be considered replaced by m
(remember this
means "the object this method is attached to). Also if there are any calls
to non-static methods or mentions of non-static fields belonging to
class MyClass
in the code for myMethod
, then
in this call they should be considered as attached to m
because the call they are in is attached to m
.
isPresent
method
on the Councillor
object indexed by the index variable
i
), and if so adding the councillor's vote (obtained by calling
the agrees
method) to a total counting the yes and no votes.
One surprisingly common error, which is obviously wrong if you think about it, was to write something like this:
for(int i=0; i<council.length; i++) if(council[i].isPresent()) if(council[i].agrees(m)) return true; else return false;Why is this wrong? Because whenever a
return
statement is
executed, execution of the method is halted and whatever value is given
with the return
statement becomes the value of the call as
a whole. In this case it means that as soon as a councillor is found who is
present, the councillor's vote on the motion is found and just that one
vote is returned as the value of the whole method, no other councillors'
votes will be counted. It's common enough in a loop to find a
statement of the form if(test) return true;
or
if(test) return false;
where test
is some test. This just means that we have found something that means
we know the method should return true
or false
without having to go through the rest of the loop (for example if we
were checking for the presence of a particular item in an array - once
we have found it we don't need to cary on checking the rest of the
array). But if you find yourself writing something like
if(test) return true; else return false;
you've almost certainly made a mistake because either one way or the
other it will halt the loop and the method it is in prematurely.
For some reason, some people thought you would have to use the method
getName
somewhere in this answer, for example
council[i].getName().agrees(m)
. This would attach the
call agrees(m)
to the string returned by getName()
and not to the Councillor
object. Obviously strings can't
agree with motions, so that doesn't make sense.
instanceof
test to
test whether a Councillor
object in the array was
also an Alderman
object. Remember that as Java is an
object-oriented object, an object may have several types. If we
have class Doodah extends Thing
, for
example, then every object of type Doodah
is also
of type Thing
(but not every object of type
Thing
is of type Doodah
). If t
is of type Thing
, the test to see if it is also of the more
specialist type Doodah
is t instanceof Doodah
.
I was surprised that a number of people wrote insteadof
rather than instanceof
, though I was willing to excuse it
as just one of those mistakes made when you mean to write one thing but
find yourself writing something else.
Note something similar was done with the banks example where we went
though an array of AccountRecord
objects looking for those
which had DepositAccountB
objects in them so we could call
the method update
on them. This shows the importance of
looking through the programs that were given as examples in class and
making sure you understand them and how they work. Ask
me if you don't. All the examples
were given for a reason: to illustrate principles which you need to know.
Again, some answers to this question showed some strange uses of
getName
. The name of the councillor is just an extra
feature, and the question does not suggest it has anything to do with
whether the councillor is a member of the executive.
void
returning method. This means a method which when you call it doesn't
return a value. Instead it does something, in some case that might be
to print or display something, in others it might be to change
the value of the object the method is attached to, or an object given
to it as an argument. It is very important that you understand the
difference between a method which returns something and a method
which prints something. If a value is printed, you can see it
on the screen but it can't be used any further than that. If a value is
returned it means it can be passed on to be used somewhere else in the
program. Obviously it is far more useful to have a method which returns
something you can use elsewhere (including passing to System.out.print
to print it), than one which just prints something, so on the whole
never write a method which prints a value rather than returns
a value unless you really are asked to write a method to print something.
Since the method you are asked to write in part c is static, it won't
be called attached to an object. So it will change the value of the array
passed to it as an argument (by setting one of the array cells that
held a Councillor
object to hold an Alderman
object).
Note that the test for whether two strings, str1
and
str2
, are equal is str1.equals(str2)
. It is
not str1==str2
because strings are objects and the
==
test tests whether two object references are to the
same object. It evaluates to false
if it is applied to
two object references which aren't to the same object, even if they
are to diffeent objects with the same content. Another way to test
two strings are equal is str1.compareTo(str2)==0
, since
the compareTo
method returns a negative number if
str1
is less than str2
in alphabetic order,
a positive number if it is greater than str2
alphabetically,
and 0
if they are equal in content.
this
to mean "the object the method is attached to". As the question here
specifically says that the method is to be called on a Motion
object, it can't be static. The Motion
object it is called
on is referred to as this
inside the code for the method,
and has to be referred to in this way in order that it can be the
argument to the call to the method agrees
attached to
Councillor
objects.
In this question, the phrase "assuming all councillors were present"
was given, indicating that unlike parts a and b of question 3, there
should not be a check using the isPresent
method on the
Councillor
objects.
true
if the
first motion is more popular than the second, false
otherwise),
or a reference to the more popular motion. Also, it could be static,
in which case when it's called it's not attached to any Motion
object, so the Motion
objects being compared are both given as
arguments to it, or it could be non-static, in which case one of the
Motion
objects it is comparing is the one it is attached to,
the other is an argument to the method. The council must also be given
as an argument in all cases (with the type Councillor[]
i.e. array of Councilor
objects). In this question, like
parts a and b if question 2, it didn't really matter how you dealt with
motions getting an equal number of votes, as the question didn't tell
you how to deal with them.
The question gave the hint that you could use the method from part a, and this made it much simpler to answer. Despite this, some people gave answers in which code very similar to that of their answer to part a was repeated in order to count the votes first for one motion then for the other. It is always permissible in a programming exam to save time by making use in one part of a method you've defined in another, should that be appropriate.
public static Motion morePopular(Motion m1, Motion m2, Councillor[] council) { if(m1.countVotes(council)>m2.countVotes(council) return m1; else return m2; } public static Motion bestMotion(Motion[] motions, Councillor[] council) { Motion best = motions[0]; for(int i=1; i<motions.length; i++) best = Motion.morePopular(best,motions[i],council); return best; }You can see the idea here is to have a variable referring to the best motion found so far. Initially it's set to the first motion, but it is set to other motions if more popular ones are found as a check is made across the array of motions. Note that in the solution given in the model answers, the
best
variable held
the location in the array of motions of the best motion found so far,
rather than a direct reference to it.
A number of people thought as the question asked for a reference to
the most popular motion, the name of the method should be something
like reference
. That is to misunderstand why when we
are being precise we use the term "reference" in Java. We could say
the method just returns the most popular motion from the array, but
actually it returns a reference because all Java variables
except those of the primitive types hold references to objects rather
than the objects themselves. That is why you can have two object
variables referring to the same object, because they don't actually
hold the object itself, they hold a reference to it. We sometimes
show this in diagrams by showing the actual object as one thing, and
the reference to it as an arrow pointing to it.
Some people drew diagrams with two arrows coming from a box. It's not possible for a single Java object field or variable to refer to two objects at once, although of course an object can be referred to by several different references. I was not sure if the people who gave answers like this actually believed that a field could refer to two objects at the same time, or it was meant to be working out.
Some people drew cells which were initially part of the structure, but which nothing mentioned in the question pointed to after the execution of the code fragment in the question. This should not be done. If nothing points to a cell, it should be considered to have disappeared. To be technical, it is automatically garbage collected. "Garbage collection" refers to letting cells be reused. In Java (but not in some other language, such as C and Pascal) garbage collection is done automatically, so once a cell is not referred to by anything, the computer memory that was used for that store could be reused any time more memory is required when another cell is created.
Another common problem was not showing where cells were shared. In some cases, from their answers it looked as if people knew that sharing occurred, but they just didn't show it in their diagrams, in other cases they perhaps mistakenly believed that assigning one object variable or field to another meant the object was copied rather than shared.
It is important, for example, to distinguish:
from
Although both represent cases where n1
represents the
list [x,a,b]
and n2
represents the list
[y,a,b]
, they represent different situations, and this
needs to be made clear in the diagrams. Consider what would happen
if following this, the statement n1.next.first='z'
were executed. In the first case, n1
would then
represent [x,z,b]
and because of the sharing n2
would then represent [y,z,b]
. In the second case, while
n1
still comes to represent [x,z,b]
,
n2
stays representing [y,a,b]
because its
cell holding 'a'
is a different cell from the one
which holds the 'a'
for n2
.
For those who haven't understood how sharing happens, suppose our initial situation is:
and then we execute n2.next=n1.next
. The situation will then be
as in the first diagram above, and not as in the second. This is because
the next
fields of the list cell objects are of object
types (the same type as the list cell), and when an object field or
variable is assigned an object value, it causes aliasing to
occur, as we saw in
week 4. So here n2.next
becomes an alias for
n1.next
, in other words it points to the same cell that
n1.next
pointed to (and n1.next
still points
to that cell).
n2
points to as well as n1
. Although what n2
points
to wouldn't be changed by the code fragment being executed, it was necessary
to show it, in order to indicate that part of it becomes shared by
n1
's list.
first
field
in cells is of type int
, which is a primitive type. When
you assign the value of one primitive type field of variable to another,
the value is copied into the other, no aliasing is set up. So,
executing n2.next.first=n1.next.first
simply copies the
3 from the second box of n1
's list into the second box of
n2
's list.
Following this, the +=
is just the normal +=
of Java, where a+=b
is shorthand for a=a+b
.
So n1.next.first += n1.first
is shorthand for
n1.next.first = n1.next.first+n1.first
, in
other words, the contents of the first cell in n1
's list
is added to the contents of the second cell of the list.
ptr
down the list doubling the values of each cell.
However the semi-colon at the end of the first line means that the
statement ptr.first *= 2;
is not
inside the loop. Another hint to this was the fact that this line
was not indented (i.e. extra spaces put at the left-hand side).
Code given in an exam will always be properly indented (unless
there is a deliberate reason not to). Also the code you write as
answers should always be properly indented. This is because although
the Java compiler doesn't take any account of how the code is laid out,
so the indentation doesn't make any difference to the meaning of the
code, it makes it much easier for the human reader to be able to tell
the structure of the code at a glance.
So this code moves ptr
down the list represented by
n1
, until it points to a cell whose first
field is not less than 8. After that, the statement
ptr.first *= 2;
is executed, doubling the
value of that cell whose number is not less than 8 (i.e. changing
9 to 18). The next statement sets the next
field of
that same cell to point to the cell that n2
points to,
so causing sharing of cells. The final statement triples the
value of the number in the second cell of n2
's list,
and due to cell-sharing that tripling is shared because that cell is
now also the fifth cell of n1
's list.
next
field of the cell ptr
is
pointing to to point to. But that new cell's next
field is the old next
field of the cell ptr
was pointing to, so the result is to insert a 0 into the list.
Note that the update part of the for-loop header moves ptr
two places down the list, this means each time round the list
it is moved past the new 0 that was inserted.
The final line of the code in this example is not in the loop. We
can tell this because the loop body does not begin with {
,
so only the first statement following the loop header is in the loop
body. It is also indicated (to us, not to the Java compiler) by the
fact that the third line is not indented. The final line causes the
next
field of the cell n1
points to to
point to the same cell that the next
field of the cell
n2
points to points to. Or (to use less convoluted grammar)
it makes n1
share all but the first cell of the list
n2
refers to.
k
which holds int
values. There are two statements in
the body of the for-loop, and we can easily tell this by the way they
are bracketed together by opening and closing curly brackets. Note
this is a slightly confusing for-loop, because although the initialisation
part initialises ptr
and the update part changes the value
of ptr
, the test part tests not ptr
but
n2
, whose value is changed in the loop body. This was
not a mistake. We could alternatively have written the loop:
for(ptr=n1; n2!=null; n2=n2.next) { k += n2.first; ptr=ptr.next; }or we could have written it using the equivalent while loop:
ptr=n1; while(n2!=null) { k += n2.first; n2 = n2.next; ptr=ptr.next; }The effect is that both
n2
and ptr
are
moved down their lists. As nothing else points to the list
n2
initially pointed to, that list is lost, but
k
holds the sum of its elements. Since ptr
was initially set to the list n1
refers to, and that
list was longer than the list n2
initially referred to,
when the loop is exited with n2
having the value
null
, ptr
still points to a cell, which
happens to be the last cell of the list n1
refers to.
So the last line sets the number of that cell to the sum of the elements
that were in the list which n2
initially referred to,
i.e. 17.
Matthew Huntbach 6th April 2001