The test was marked out of 80, and the 20 marks for each question were divided equally among its parts (except 2e had 5 marks with 3 for the rest of question 2).
The reason for this is that if a language compiles directly to machine code there has to be a separate compiler for every type of machine, and the compiled version of the program for one type of machine won't run on another type of machine. In Java there has to be a separate Java byte code interpreter for each type of machine, but as Java byte code is always the same for any machine, the compiled version of Java can be run on any machine that has a Java byte code interpreter. This gives Java programs their "run anywhere" property.
int
variables, a
and
b
, then executing a=b
will cause the value
of a
to be copied to b
, but there is
no aliasing: after that there are no links of any sort between
b
and a
, so changing the value one of them
stores will not affect the other.
Aliasing is only going to matter with mutable objects (that is objects
whose field values can be changed either because they are not declared
as private
or they have methods that can chage them). In
that case, after executing a=b
the two variables a
and b
do refer to the same object. It will be the object which
b
referred to before the assignment was executed. Then, making
a change to the object by calling a method on either a
or
b
, or assigning a new value to a non-private field of
a
or b
will change the object both refer to as
it is literally the same object. Note, if either a
or
b
is later assigned to another object that does not change
what the other of them refers to. So if we later do b=c
,
then b
will refer to the object which c
referred to previously (and c
will still refer to it). But
a
will stay referring to the object which b
used to refer to.
Note it is possible for two separate objects to have data of equal values stored in their own fields. This is not aliasing. Aliasing refers to two or more names for the same object, not two or more identical but separate objects.
&&
and
||
, so it was strange that a lot of people wrote an
answer here which was too general, written as if there were large
numbers of different short circuit operators with different numbers of
operands. Actually, you could say there is a third short-circuit
operator, the conditional operator ?:
, since with
exp1?exp2:exp3
, the expression exp2
is
only evaluated if exp1
evaluates to true
and
exp3
is only evaluated if exp1
evaluates
to false
.
So what was expected was a precise description of how &&
works, that is that in an expression of the form exp1&&exp2
,
the subexpression exp2
is only evaluated if exp1
evaluates to
true
. This will only have an effect if exp2
has a side-effect (i.e. evaluating it can change the state of something) or
evaluating it can cause an exception. So exp2
cannot just
be a variable (some people wrongly referred to it as that).
However, the question did not ask for an expression which would
demonstrate that the operator has a short-circuit effect. Some people
chose to answer the question by giving such an expression but without
actually explaining what is meant by short-circuiting with respect to the
&&
operator. There were no marks for that, as it didn't
answer the question.
public
and private
. It
was about variables declared within a method, so they exist only as
long as the method is being executed in order to hold values temporarily
needed by that method. In Java you can declare a variable not just within
a method but within a block inside a loop, if
statement or
try-catch
statement. In that case the variable only exists for
as long as the block is executed. A block is the set of statements
enclosed by {
and }
.
/
has higher precedence than the subtraction operator
-
. So the expression a-b/c
should be
interpreted as a-(b/c)
and not as (a-b)/c
.
Some people who got that bit right forgot that division of two integers
in Java gives an integer. So b/c
where b
stores 10
and c
stores 3
results in 3
and not 3.3333333333333
. It
certainly does not result in 3 followed by a symbol which is a 1 on top
of a horizontal bar on top of a 3 (I'm not sure I can do that symbol in
HTML). Remember when you are asked to write what would be printed, you are
expected to write exactly what would be printed, and it ought to be
obvious that a one third symbol would not be.
b*=a+1
multiplies
b
by a+1
. It does not multiply b
by a
and then add 1 to the result.
++c
means "add 1 to the value
of c
then use it". So the value that is passed to
be printed out is one greater than the previous value of c
.
But remember (though it wasn't tested in this part of the question) that
c
keeps its new value, so if there were another line after
the System.out.println(++c);
line, c
would
have the value 13
in that line.
exp1?exp2:exp3
, evaluates to the value of
expression exp2
if exp1
evaluates to
true
and to the value of exp3
if
exp1
evaluates to false
. People who didn't
know this, presumably because they weren't at the lecture where it
was talked about, were, obviously, stuck on this question.
The ?:
is a little obscure - you don't see it that often,
you never really have to use it, and it's often mentioned as an
afterthought in textbooks. But if you're familiar with it, you won't be
confused if you come across some code where someone has decided to use it.
a--
means "use the value of
a
then subtract 1 from it". So when a
holds the value 9
, the statement b=a--
means "use
the value of a
(i.e. 9
) in the assignment, then
subtract 1 from it". So b
ends up as 9
and
a
ends up as 8
. The ++
and
--
operators have a permanent effect, so in this case
a
stays with the value of 8
until it is
assigned a new value in the statement a=b*2
.
Also in this part it should again be remembered that what was asked for
was exactly what would be printed. The println
call prints
the three number with a spaces in between them, not commas. A few
people still have not grasped that +
when a string is
involved means "join the string representations" and not "arithmetic
add", and of course " "
is a string, the string consisting
of one space character.
(double)a/b
is ((double)a)/b
and not
(double)(a/b)
. In other words, the double
equivalent of the value of a
is divided by the value of
b
. As a
has the value 8
, its
double equivalent is 8.0
and dividing by 3
(the
value of b
) gives 2.6666667
, or rather the
closest that can be got to two and two thirds represented in the way Java
represents fractional number. If (double)a/b
were interpreted
the other way round, you would have integer division of 8
by
3
, resulting in 2
and then the conversion to
a double
resulting in d
being set to 2.0
.
But note that in a similar way, (int)d*2
is interpreted
as ((int)d)*2
and not as (int)(d*2)
. In other
words, the integer conversion of d
is found first and
that is multiplied by 2
. As d
has value
2.6666667
or thereabouts, the integer conversion gives
the value 2
and that is multiplied by 2
in
c=(int)d*2
to give c
the value 4
.
It's not the case that d
is multiplied by 2
first, giving 5.3333333
and that is converted to an integer.
a<b<c
. That
is not valid Java, and there was no excuse for not knowing that as
it was explained why not in a lecture. Computer languages are not like
human languages where we can deduce a meaning if a sentence isn't quite
grammatical. If the grammar is wrong when you write something in a
programming language, it won't compile and that's it. It may be
obvious to us that a<b<c
is meant to mean
"a
is less than b
and b
is less
than c
" but it isn't to Java. Java doesn't have that form
so if you tried to write a program using it, you would get a compiler
error.
The reason it is not valid Java is that as <
is a binary
operator (i.e. takes two arguments, one to its left, the other to its
right), a<b<c
must be interpreted as either
(a<b)<c
or a<(b<c)
. But neither
of these makes sense because both would involve comparing a
boolean
(i.e. true
or false
,
the result of the <
within the brackets) with an
integer, which doesn't make sense.
A more subtle error in this question is that it didn't actually state
that a
was less than c
, though a lot of people
assumed it. As it wasn't stated, you had also to allow for the case
that c
was the lowest, b
the middle and
c
the highest, and print Yes
in that case.
By the way, when the question asked for code that printed Yes
it meant that, and not YES
. It is important to be precise
when writing programs, and if you are told to write a program to do
something make sure it does exactly that. You don't know what the code
you're writing is for, it may be used to provide input for another
program that tests for Yes
and not YES
.
The very earliest computers made no distinction between small and
capital letters but that was back in the
Stone Age when computer memory was so expensive that it made sense
to save space by minimising the set of characters usable, and anyway
computers just calculated and printed numbers. Most of you weren't
even born when that was the case, so there is no excuse for ignoring
case sensitivity now.
That also applies to writing Java reserved words
(if
, while
etc.) - they use small letters
and it's a mistake to use capital letters, Java won't interpret
If
as an if
and so on. Capital
letters are used in Java in words like String
, but that's
a predefined object-type, and charAt
but that's a
predefined method, neither are reserved words which are things that
can't be analysed in any other way than as symbols of Java. Of course,
it's equally wrong to use string
for String
and so on.
Another point on this question is that the question told you that you
have three int
variables a
, b
and c
, which have been given values. So it is assumed you
are writing a piece of code to put below an existing piece of code
which already has a line int a,b,c;
plus further code to
give values to a
, b
and c
(or some
other code which has the same effect). It is therefore wrong to
write as part of your answer int a,b,c;
. The effect of that
is to declare three new variables called a
,
b
and c
. Depending on circumstances (to do with
scope, as mentioned in question 1d), this would either be an error or would
mean the rest of your code refers to these three new variables which have
no connection to the a
, b
and c
of
the question.
A final point is that a lot of people seemed to be making up notation
that doesn't exist in Java. In some cases, from my knowledge of other
programming languages, they're using something from Pascal or Basic,
for example using the word and
as in Pascal, rather than
Java's &&
. This indicates the danger of thinking you know how
to program from previous experience. Since all you need for the test
was explained in the lectures, and can also be found in the web notes and
in your textbooks, using notation from a language you studied previously
suggests you haven't been working hard enough at studying Java.
Plenty of those making mistakes which suggest pre-university familiarity
with another language haven't got very far on other aspects of the test.
This indicates that pre-university programming experience may not be as
helpful as those without it sometimes imagine. Sometimes it's a case of
not realising how trivial the pre-university programming was, other times
the pre-university experience actually makes it harder to start thinking
about programming in the object-oriented style of Java.
A few people simply made up notation of their own, for example,
b!<a
for "b
not less than a
".
Maybe this seems as if it ought to work, since !=
is Java's
symbol for "not equal". However, it doesn't work - the symbol
b!<a
is not in any Java textbook or web site, and the
test was open book so you could check that. Also the mathematical
"less than or equal" and "greater than or equal" signs (I can just
about render them in HTML as <
and
>
) are not valid Java operators. That ought to
be obvious, since how would you be able to type them into a Java program
without a key on the computer keyboard for them? When you are asked to
write a Java program, do be accurate, and that involves little things like
using the correct <=
and >=
for "less than
or equal" and "greater than or equal".
Ship
examples.
When in ShipMain4.java
we had the statement
argo.setCourse(90)
we were calling method setCourse
on variable argo
with argument 90
.
When it's stated that a method has signature boolean hit(int force)
it means when the method is called it must be attached to an object.
If the method was declared as static
its call should be
attached to the name of its class, but hit
wasn't
declared as static
. Attaching a method to an object means
you have a reference to an object, followed by a dot, followed by the call.
Usually the reference is a variable which has been set to refer to an
object.
You were told that punchbag
was a variable on which that
method could be called. You didn't need to know anything else about the
method other than that fact and its signature. That fact meant you could
write punchbag.hit(arguments)
. As the return type
of hit
was given as boolean
the expression
punchbag.hit(arguments)
evaluates to a boolean
value, i.e. either true
or false
. So what happens
is that anywhere where punchbag.hit(arguments)
occurs
in your code, when the computer evalutes that code, it will go away,
execute the code in the hit
method, which will eventually
return true
or false
. You don't need to know,
and weren't told in the question how it does it. All you need to know is
what you are told by that return type, that where you wrote
punchbag.hit(arguments)
it's writing something that
will actually become true
or false
when the code
is evaluated, only you don't know which until that happens.
Now, in punchbag.hit(arguments)
, what
arguments
must be is given by what appears between
the opening and closing bracket in the signature. There is one
int
argument given there called force
.
It doesn't matter what the argument is called, because that's only
really relevant to the code for the method, which you weren't given.
What it means is that in your call to hit
, you must
make arguments
one thing which evaluates to
an integer value. The question tells you that you have one such thing,
a variable of type int
called b
, and it
tells you to use it as an argument. So your call to hit
on punchbag
with argument b
cannot be anything
except:
punchbag.hit(b)What actually happens when
punchbag.hit(b)
is executed is that
the code for the method hit
is executed with force
,
the name given to its int
parameter, replaced by b
,
the call argument.
The next issue is how to keep making that call until the number of times
in variable c
or until it returns false
. We can
do something a certain number of times by setting another variable to
0
and increasing it by 1
each time until it
reaches the value. The question stated that we could use the variable
a
to store temporary values. We can exit a loop before its
condition is met by using break
, and this is technique is
used in the following solution:
a=0; while(a<c) { if(!punchbag.hit(b)) break; a++; }When you test the value of
punchbag.hit(b)
in the
condition of the if
statement here it does actually call
the method, so it is called each time round the loop. The symbol
!
is Java's "not" operator, which must be applied to
a boolean expression. So if punchbag.hit(b)
returns
false
then !punchbag.hit(b)
is equivalent
to !false
which is true
, so break
is then obeyed which causes execution to leave the loop. Otherwise,
a
is increased by one and we go to the top of the loop and
test whether it is still less than c
, exiting the loop if
it isn't, obeying the loop again if it is.
Note the importance of good layout here. While the Java compiler
doesn't require it, it's essential to make your program easier for the
human reader to pick up. I prefer the closing }
to be
directly below the opening {
and both to be at the
beginning of the line, but many authors recommend a style where the
opening {
is on the same line as the while
and the closing }
is directly below the w
,
giving:
a=0; while(a<c) { if(!punchbag.hit(b)) break; a++; }I think that's makes it a little less clear what the structure is at a glance, but it's still perfectly acceptable. What is unacceptable is what many people wrote, something like:
a=0; while(a<c) { if(!punchbag.hit(b)) break; a++; }That's a sort of combination of the worse aspects of the previous layouts, and makes the structure of your code harder to see at a glance. I have never ever seen a textbook which recommends that code be laid out like that, so it's a mystery why so many students write code like that in tests and exams.
What's even worse than that is the complete lack of indentation:
a=0; while(a<c) { if(!punchbag.hit(b)) break; a++; }You can't tell from this which statement is inside the while loop and which is not, and which is inside the if statement inside the while loop. There is no excuse for this - the structured programming revolution in which programs were seen as nested structures rather than as lists of statements with control jumping about them took place in the 1960s. As part of thinking in a structured programming way, you should think of your while statement as taking the form:
while(test) { stuff }that is, before you even think about what's inside it you know the opening
{
has a matching }
, and the whole thing is one
block where you keep testing test
then doing
stuff
until test
evaluates to
false
. You should think of this as a single statement
which may have some statements inside it as part of it. What you should
NEVER EVER think of your programs as is as a list of statements into which
you insert {
s and }
s until the thing works, that
is failing to appreciate the way some statements form parts of other
compound statements.
Break statements are considered not really in accord with the principles of structured programming. When you have a loop like ours of the form
while(a<c) { stuff }you will expect that loop to exit when
a<c
is
false
, that is for a
to have a value which
is equal to or greater than c
. It might come as a surprise,
maybe because your program went wrong because it was based on that
expectation, to find sometimes the loop exits with a
less
than c
because hidden inside stuff
is a
break
statement. That wasn't too much problem in our small
example, but suppose stuff
was some fairly complex
code.
The break
wasn't necessary in our example. Here's an
alternative way of answering it:
a=0; while(a<c&&punchbag.hit(b)) { a++; }Since the body of the while statement has only one statement in it, you don't need the brackets, so this could be written:
a=0; while(a<c&&punchbag.hit(b)) a++;Note that whatever comes after this in the code is not within the while loop. A while loop officially takes the form
while(test) statement
, that is the
while(test)
part is followed by a single
statement. The {
and }
have the effect of
gathering together a list of statements and making them into a single
statement.
Our example quite nicely illustrates the short-circuit effect of
&&
. When it makes the test a<c&&punchbag.hit(b)
in the while loop, if a
is equal to c
then
a<c
is false
and we don't evaluate
punchbag.hit(b)
. That is just as well, because if we did, we
would be calling hit
with argument b
on
punchbag
one more time than we were asked to.
Since the test was set before we had discussed for
statements,
you wouldn't have been expected to have answered it using a for
statement. However, it could be done that way, and that wouldn't have been
wrong. A for
statement is just like a while
statement, except it has an extra part before the test and separated from
it by a ;
where you can initialise a variable to a value (or
even declare and initialise a new variable), and an extra part after the
test, again separated with a ;
, which is a statement executed
after executing all those in the
body. The following for
statement will answer the question:
for(a=0; a<c&&punchbag.hit(b); a++);That's it: there's no body to this
for
statement because
all that is needed is in the top part. That is indicated (rather subtly)
by the way it is followed by ;
. A for
statement takes the form
for(initialise;test;update) statement
.
Note there is no ;
after the )
. So if
a ;
is found after the )
, it is taken to
be that for
statement's statement
part. Anything else coming after it is not part of the for
statement. That is why it is an error to put a ;
after the
)
in a for
statement: it completely alters the
meaning of your code.
General problems with this question include failing to note that the question asked for a fragment of Java code. That means enough lines of Java code to do what the question asks and no more. It certainly does not mean writing:
class Something { public static void main(String[] args) { Some stuff } }That extra packaging is needed if you are writing a complete program in order for it to run under Java, but you are rarely going to be asked to write a complete program in a test or exam. You would more commonly be asked to write a complete method, which would include the method signature. But if you are asked to write a code fragment, it's just a few lines without any extra packaging.
Many of the same sort of problems as mentioned with question 3 occurred
again here: failure to appreciate that {
and }
always come as a pair, failure to use indentation properly,
inventing new notation which isn't part of Java or is part of another
language. Some people made use of the two methods which were in the
original example given as a class exercise, but not mentioned in the test.
If you are given a piece of code merely as an example in class, you should
not assume the marker of a test or exam is even aware of it. If a question
gives some example code, make use of that, any extra code you have written
for yourself as part of the answer, and anything which is built-in to
Java. But don't assume the existence of anything not given to you.
It was surprising to see how many people clearly did not understand how
to call a method on an object, despite the fact that we had been doing
that for weeks in the labs and lectures. So, let us go over it. The
three methods given for a Machine
object can only be called
attached to a Machine
object. The method getTemp
has signature:
int getTemp()which means it must be called with no arguments and returns an
int
.
So it can only be called in the form m.getTemp()
where
m
is any expression that refers to a
Machine
object. Most commonly m
will be
a variable of type Machine
but it could also be a call to
another method which returns a Machine
object. Actually there
is an exception to this, which wasn't relevant to this question:
getTemp
could be called within the code for a method of
type Machine
without being attached to a Machine
object, in that case it would be assumed to be attached to the object
to which the call the method whose code it is within is attached to.
As getTemp
returns an int
, the call
m.getTemp()
can occur anywhere where an
int
can occur, that is within an arithmetic expression or
as an argument to a method in a place where an int
is
asked for in the method's signature. The ()
in the signature
indicate that a call to getTemp
takes no arguments. However,
it is still necessary to put the ()
in the method call.
The method pushButton()
with signature:
Grommet pushButton()is very similar to
getTemp
except, as the signature indicates,
it returns a Grommet
rather than an int
,
so a call to it, which will take the form m.pushButton()
,
where m
is any expression that refers to a
Machine
object, and can be used in any place where a
reference to a Grommet
object can be used.
The method addFuel
is rather different. Its signature:
void addFuel(int amount)indicates it has no return type. The word
void
means
that. This means you cannot use a call to it in the place of any value,
but only as a statement on its own, taking the form
m.addFuel(n);
. Here while
m
is any reference to a Machine
object,
i
is any int
value, so it could be an
actual number, a variable of type int
, an arithmetic
expression which has an int
value, or a method call with
return type int
. You must give any call to
addFuel
exactly one int
argument, that
is what the (int amount)
in the signature means. However,
it does not mean you write the word int
in front of
the argument you give to a call to addFuel
.
The method getQuality
is a Grommet
method, which
means a call to it must be attached to a reference to a Grommet
object. That reference could be a variable of type Grommet
,
but it could also be a call to pushButton
(which must in turn
be attached to a reference to a Machine
object), as that
returns a Grommet
object. The signature for getQuality
is:
int getQuality()which means it takes no arguments and returns an
int
just
like getTemp
.
int
and
pass it as an argument to a method requiring an int
argument.
You need to be aware of the fact that to do any reading in Java you need
a BufferedReader
object. Actually there are other ways, but
Java input/output is complex, so assume that for simplicity. Some of the
notes refer to a class called Text
which was provided by
Judy Bishop to accompany her textbook. However, as that is not part of
official Java you should not assume it is available unless you are told
so in a question. This is how you have been told to create a
BufferedReader
object referred to by in
which
reads from the command window:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));There is nothing special about the name
in
, it could have been
called anything, but in
is usual. You cannot assume a
BufferedReader
variable called in
exists unless
you are told it does in the question, which you were not in this case.
You have been told that a BufferedReader
object has a
method called readLine
which reads a whole line of text.
It also has a method which reads a single character, but it does not have
a method which reads a single integer. So to read an integer we have to
read a line and then convert that line to an integer (if there was more than
the single integer on the line, we would need to use a StringTokenizer
to break it up, but we won't consider that here).
The static method parseInt
in built-in class Integer
takes a String
as its argument and returns the equivalent
int
value. Because it is static you must attach the call
to the name Integer
rather than to an Integer
object (note the type Integer
is an entirely separate thing,
which we have not covered yet, to the type int
). The
following declares a String
variable called str
and an int
variable called amount
then sets
the String
variable to the result of reading a line and the
int
variable to the result of converting that line to an
int
:
String str; int amount; str = in.readLine(); amount = Integer.parseInt(str);Java allows you to combine creating a variable with giving it a value, which enables us to write the following instead of the above:
String str = in.readLine(); int amount = Integer.parseInt(str);The variable
str
here is used only for this one purpose of
passing a String
value from the readLine
call to become the argument of the parseInt
call. So
really there is no need to have a separate named variable, we could
just put the readLine
call directly as the argument to the
parseInt
call, making:
int amount = Integer.parseInt(in.readLine());
Note that method arguments in Java are always fully evaluated before they
are passed to the code for the method. So in the above example,
in.readLine()
is evaluated first to a String
value, and that value is passed in to the code for parseInt
to take the place of the String
parameter to that code.
Starting a machine is simulated by creating a Machine
object.
You are told that a Machine
object can be created with a
zero-argument constructor, thus new Machine()
refers to a
newly created Machine
object. But there is no point in
creating a new object unless we either give it a name to allow us to
refer to it later, which we do so by assigning a value to refer to it,
or we pass it as an argument to something else. Below shows the
declaration of a variable called m
of type Machine
which is combined with setting it to refer to a new Machine
object:
Machine m = new Machine();
Now all we need do is pass the integer amount read as the argument to the
call of the method addFuel
on the new Machine
object. This is done by:
m.addFuel(amount);That is all that is needed, nothing in the question asked for any more calls to be made, and it's a mistake to add something to an answer which was neither asked for nor is needed to make something else work which was asked for. Quite a few people who answered this question made further method calls which might have lost them marks.
Note that although in the answer given here the argument to the method
addFuel
is a variable of the name amount
, which
is the same name as the parameter name in the signature given for
addCount
, that was not a necessity. An int
variable of any name could have been used as the argument, or a method
call or arithmetic expression which has an int
value.
In fact it was not necessary to have a separate int
variable at all, as all it was used for was this once-only passing of
an int
value. So we could have further combined statements
with the call to read an int
value as the argument to
addFuel
making:
m.addFuel(Integer.parseInt(in.readLine()));
if
rather
than a while
. The form if(test)stat
means that stat
is obeyed at most once, in the case
where test
evaluates to true
when
execution reaches it. But the form while(test)stat
means if test
still evaluates to true
after the execution of stat
then stat
is executed again, and repeatedly until after executing it
test
does not evaluate to true
.
We have already seen that Machine m = new Machine();
is
a statement that creates a new Machine
object and sets the
variable m
to refer to it. Then the temperature of that
Machine
object is obtained by m.getTemp()
.
Using this, a while loop which tests whether the temperature of the
machine is not less than 20 each time round the loop takes the form:
while(m.getTemp()>=20) { stuff }Note the Java symbol for "greater than or equals", which is the same as "not less than". It is
>=
. There should be no call
to getTemp
inside stuff
, the call is made
when the while loop's test is evaluated. A lot of people seemed to assume
that if you made a call m.getTemp()
somewhere inside
stuff
, then somehow a variable called temp
or
getTemp
is set to the result and may be used in the test.
This is, of course, nonsense.
A call of the method pushButton
on a Machine
object returns a Grommet
object but also changes the
internal state of the Machine
object so that next time
getTemp
is called on it, a different value will be
returned. That is why the following loop:
while(m.getTemp()>=20) { Grommet g = m.pushButton(); }will halt, because the internal state of the object referred to by
m
will change and eventually reach the point where
m.getTemp()>=20
will evalute to false
and the
loop will be exited. It is always necessary to have something in a loop
which changes something in the loop's test so that the test will at some
time evaluate to false
. I was surprised at how many people
failed to realise this elementary point and gave answers involving loops
that would quite obviously never terminate.
This loop could have been written:
while(m.getTemp()>=20) { m.pushButton(); }The
pushButton
method is unusual in that it both returns
a value and changes the state of the object that it's called on.
So you can call it on its own and not even acknowledge that it returns
a value, which is what is done when m.pushButton();
is used
as a statement on its own. However, it's good programming practice to
acknowledge when a method returns a value and use a call on its own only
when the call is to a method with return type void
. That is
why the recommended answer here assigns the result of calling
pushButton
to a Grommet
variable g
even though this variable is never used.
The loop given above doesn't do any counting, but that is easily corrected by adding a variable which is assigned initially to zero and increased by 1 each time round the loop. Note that although the answers given for the test did not use for loops, it would be natural to make the code a little more compact by using one, giving the answer:
Machine m = new Machine(); int count; Grommet g; for(count=0; m.getTemp()>=20; count++) g=m.pushButton(); System.out.println(count+" grommets produced");Note there are no
{
and }
because the loop has
only the statement g=m.pushButton();
within it. The next
statement which prints the final message is not within the loop. The
lack of {
and }
tells the Java compiler only to
include g=m.pushButton();
within the loop. The fact that we
also indent the line a few spaces means nothing to Java, but makes the
structure clear to human readers of the code. If there is only one statement
in a loop, it can't be a declaration, which is why the Grommet
variable g
is declared before the loop (I wouldn't expect you
to know a minor detail like that - I didn't know myself until this question
raised the possibility, and I checked to see for myself by attempting to
compile a version which had Grommet g = m.pushButton();
as the loop body, and got an error message).
Some people, aware that you can declare variables in the initialisation part of a for loop might have thought the for loop could have been written
for(int count=0; m.getTemp()>=20; count++) g=m.pushButton();and then you wouldn't have the earlier declaration statement
int count;
. This would have been wrong, however, as that
would mean that count
had scope only inside the for loop,
and so you wouldn't be able to refer to it after the loop as is needed
to print the final mesage.
I was surprised that quite a few people answering this question seemed
to think a call to the Grommet
method getQuality
would return the number of grommets produced. Since the question (and the
very name of the method!) suggests clearly that this method gives the
quality of a grommet, I am at a loss to understand why so many people made
a mistake that surely common sense rather than any familiarity with Java
would have dictated was stupid.
pushButton
is made, but in this case a loop with two counts kept, one of the number of
times the loop has been executed, and the other of the number of times
the grommet produced has a higher quality than that of g
.
Note that what is required is a code fragment which assumes that the
variable g
has already been declared and given a value.
You don't know how it was given a value, but the idea is that you are
writing some code that will be slotted in after the code that gives
g
a value. So it is a bad mistake to declare a new variable
g
and/or give g
a new value, as many did.
Many people did not seem to understand that the way to compare the
qualities of two grommet object is to call the getQuality
method on each of them, and compare the values returned. Also in this
case, unlike the previous two parts, you are told a Machine
variable called m
already exists and should be used so it's
equally wrong to declare and/or give a value to variable m
.
Actually, the answer previously circulated calls getQuality
on g
every time round the loop, even though g
and its quality aren't changed. So a slightly more efficient way of handling
it would be to make a call once to getQuality
on g
and store it in a variable. It takes less time for the Java execution
mechanism to look at the value of a variable than it does to call a method.
That would give the following answer:
int count=0, qcount=0; int gQual=g.getQuality(); while(count<100) { Grommet g1=m.pushButton(); count++; if(g1.getQuality()>gQual) qcount++; } System.out.println(qcount+" high quality grommets produced");While minor efficiency matters like this are not too important, and you will not lose marks if your code isn't quite as efficient as it could be, it's a good idea to be aware of such issues.
Note, again, the importance of good layout as shown here. The lines
Grommet g1=m.pushButton();
and count++;
are inside the while
loop and are indented (written after several spaces
to their left) so that is clearer to someone who looks at the program.
The next two lines are a single if
statement, again inside
the while
loop so indented, but the line qcount++;
is inside the if
statement which is inside the while
loop, so it is indented a bit more to make clear it is a statement inside
a statement inside a statement. It is important to think of a loop, such as
the while
loop here as a single statement, inside of which
are substatements, so the above example consists of four top-level statements:
the one on the first line which declares and initialises the two variables
count
and qcount
, the one on the second line
which declares and initialises (to the value obtained by calling method
getQuality
on variable m
) the variable
gQual
, the while
statement taking the next seven
lines, and the final statement being the call to the method println
on System.out
.
As with part b, a more compact piece of code could be obtained by using
a for
loop. In that case, the variable count
could be declared inside the for
part as it is used only
within the loop, but qcount
has to be declared before it as
it is used after the loop has finished. This would give us:
int qcount=0; int gQual=g.getQuality(); for(int count=0; count<100; count++) { Grommet g1=m.pushButton(); if(g1.getQuality()>gQual) qcount++; } System.out.println(qcount+" high quality grommets produced");For even more compact code, it could be noted that the variable
g1
is used only to convey a value from the call to
pushButton
on m
in order for getQuality
to be called on it in the if
statement test. Instead of
using a separate variable g1
the call to getQuality
could be attached directly to the call to pushButton
giving:
int qcount=0; int gQual=g.getQuality(); for(int count=0; count<100; count++) if(m.pushbutton().getQuality()>gQual) qcount++; System.out.println(qcount+" high quality grommets produced");This has exactly the same effect as the previous code fragment, but the
Grommet
object produced by the call to pushButton
is anonymous as it was never given a name. Note that the {
and }
are omitted. This is because the for
loop
now has only one statement - the if
statement inside it
(and inside the if
statement is the statement qcount++;
),
and {
and }
are not needed if they only enclose
a single statement. It wouldn't have been an error to leave them in,
and some authors (for example Barnes) recommend always using them even
when they do only enclose a single statement.
Matthew Huntbach 22nd December 2000