I will try to copy the text from the book below so you can see the
example I am reading. Taken from page 298:
import java.util.Scanner;
class ProcessPurchasesss {
public static voic main(String args[]) {
Scanner myScanner = new Scanner (System.in);
Purchase aPurchase;
for (int count =0; count < 3; count++) {
aPurchase = new Purchase();
System.out.print("Amount: ");
aPurchase.amount = myScanner.nextDouble();
System.out.print ("Taxable? (true/false) ");
aPurchase.taxable = myScanner.nextBoolean();
if (aPurchase.taxable) {
aPurchase.total = aPurchase.amount * 1.05;
} else {
aPurchase.total = aPurchase.amount;
}
System.out.print("Total: ");
System.out.println(aPurchase.total);
System.out.println();
}
}
}
I have not run this code, but it doesn't make sense that it
initializes aPurchase 3 times and in the following paragraph in the
book it discusses the fact that there are now 3 objects generated from
this code. How does this work? I don't understand. How do I then
call each object individually?
Thanks for the help.
This declaration should appear inside the loop. Outside, its scope is too wide.
> for (int count =0; count < 3; count++) {
> aPurchase = new Purchase();
>
> System.out.print("Amount: ");
> aPurchase.amount = myScanner.nextDouble();
> System.out.print ("Taxable? (true/false) ");
> aPurchase.taxable = myScanner.nextBoolean();
>
> if (aPurchase.taxable) {
> aPurchase.total = aPurchase.amount * 1.05;
> } else {
> aPurchase.total = aPurchase.amount;
> }
>
> System.out.print("Total: ");
> System.out.println(aPurchase.total);
> System.out.println();
>
> }
> }
> }
>
> I have not run this code, but it doesn't make sense that it
> initializes aPurchase 3 times and in the following paragraph in the
> book it discusses the fact that there are now 3 objects generated from
> this code. How does this work? I don't understand.
The book is right, and the book is wrong.
There is a difference between the object ('new Purchase()'), and the pointer
to it ('aPurchase'). A pointer can point to any number of objects in turn,
which is what the book describes. Each invocation of 'new Purchase()' is
supposed to create a new 'Purchase' object; each time the pointer 'aPurchase'
is pointed to the newly created object. Can you not point to three different
objects with the same finger?
Saying that there are "now" three objects depends on when "now" is. It is
quite possible that one, two or all three objects will have been
garbage-collected and no longer exist.
It is also possible that the optimizer will notice that each older object is
never again used, and re-use the same storage for each "new" instance. This
is a run-time detail that did not concern the book's author and need not
concern you.
Conceptually, three separate objects were created with each invocation of
'new', and the same pointer was used for each in turn.
> How do I then call each object individually?
You don't "call" objects, you refer to objects and call methods.
You refer to each object through a pointer ('aPurchase'), correctly called a
"reference", that points to the object at the time of reference.
--
Lew
> the name aPurchase. I don't understand how this is possible. Is it
> an error in the book? How can you initialize an object or a variable
> multiple times within a loop, with unique different names.
You can access an object in more ways than just through a static name.
First, somewhat unrelated, you can access the object in the loop, use it
somehow, then discard it at the end of the loop. This does make a new
object each loop, it just doesn't keep them around.
I can't think of a great example right now, but if the object is fairly
complicated, then it makes sense that it might have some method you
can't duplicate easily just by calling a static method.
The OTHER way to do this is just to put the object somewhere where it
will be saved, but not need a name. Array initialization is a perfect
example of this. This code fragment here makes 10 objects, and keeps
them accessible, though they don't have individual names.
Integer[] intArr = new Integer[10];
for( int i = 0; i < intArr.length; i++ ) {
intArr[i] = new Integer( i );
}
Almost any class that implements Collection or Map is also a great
candidate for saving an object for later access. Here's your example,
slightly reworked.
> import java.util.Scanner;
>
> class ProcessPurchasesss {
>
> public static voic main(String args[]) {
> Scanner myScanner = new Scanner (System.in);
List<Purchase> cart = new ArrayList<Purchase>();
> Purchase aPurchase;
>
> for (int count =0; count < 3; count++) {
> aPurchase = new Purchase();
>
> System.out.print("Amount: ");
> aPurchase.amount = myScanner.nextDouble();
> System.out.print ("Taxable? (true/false) ");
> aPurchase.taxable = myScanner.nextBoolean();
>
> if (aPurchase.taxable) {
> aPurchase.total = aPurchase.amount * 1.05;
> } else {
> aPurchase.total = aPurchase.amount;
> }
>
cart.add( aPurchase );
> //System.out.print("Total: ");
> //System.out.println(aPurchase.total);
> //System.out.println();
>
> }
double sum = 0.0;
for( Purchase p : cart ) {
System.out.println( "Item: "+p.total );
sum += p.total;
}
System.out.println( "Your GRAND TOTAL: "+ sum );
> }
> }
>
So here I can access each object, without a name. You should be able to
do the same thing with an array yourself. Collections (like List and
ArrayList) probably haven't been covered by your book yet, but they are
coming. Which is (likely) why your book is trying to impress on you the
idea now that three objects have been created. It'll be very important
later on.
It's a good rework, except that it makes me deucedly uncomfortable to see that
'aPurchase' is still declared outside the loop.
>> import java.util.Scanner;
>>
>> class ProcessPurchasesss {
>>
>> public static voic main(String args[]) {
It's more idiomatic to put the '[]' after the 'String'.
>> Scanner myScanner = new Scanner (System.in);
> List<Purchase> cart = new ArrayList<Purchase>();
>> Purchase aPurchase;
>>
>> for (int count =0; count < 3; count++) {
>> aPurchase = new Purchase();
>>
>> System.out.print("Amount: ");
>> aPurchase.amount = myScanner.nextDouble();
>> System.out.print ("Taxable? (true/false) ");
>> aPurchase.taxable = myScanner.nextBoolean();
>>
>> if (aPurchase.taxable) {
>> aPurchase.total = aPurchase.amount * 1.05;
>> } else {
>> aPurchase.total = aPurchase.amount;
>> }
>>
> cart.add( aPurchase );
>> }
> double sum = 0.0;
> for( Purchase p : cart ) {
> System.out.println( "Item: "+p.total );
> sum += p.total;
> }
> System.out.println( "Your GRAND TOTAL: "+ sum );
>> }
>> }
That final 'System.out.println' fixes one of the things that bothered me most
about the original example, the failure to total all the 'amount' values. It
is such an obvious and pedagogically useful thing to illustrate.
The other thing that bothers me most from the original example is the use of
'total' as an instance variable. It's redundant in this example, although one
can conceive of use cases where it might be needed. A better example would
not use an instance 'total' field, and instead put the totaling in the second
loop.
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;
public class ProcessPurchases
{
public static void main( String [] args )
{
Scanner myScanner = new Scanner( System.in );
List <Purchase> cart = new ArrayList <Purchase> ();
for ( int count = 0; count < NR_ITEMS; ++count )
{ // really the loop should run until end of input
Purchase aPurchase = new Purchase();
System.out.print("Amount: ");
aPurchase.amount = myScanner.nextDouble();
System.out.print ("Taxable? (true/false) ");
aPurchase.taxable = myScanner.nextBoolean();
cart.add( aPurchase );
}
double sum = 0.0;
for( Purchase p : cart )
{
sum += (p.taxable? 1.05 * p.amount : p.amount);
}
System.out.println( "Your GRAND TOTAL: "+ sum );
}
}
Another issue is that 'double' is the wrong type for 'Purchase#amount' and 'sum'.
--
Lew
This is great. Since I am a newbie, some of the nuances that are
being identified, I think will take me a bit to digest. I really
appreciate it. Mark, I have a rudimentary understanding of Arrays,
but you are right, ArrayLists, have still not been covered.
The reason for this question was it seemed to be useful to be able to
dynamically create objects would be useful and then be able to refer
to said objects (via their pointer, did I get that right?) in a future
part of the program. I may be jumping ahead and maybe it is important
for me to be patient and just keep learning and practicing. Maybe the
correct solution is with this ArrayList function (no need to explain
it to me here, I will read about it and then ask questions if I have
them.)
Thank you very much for your help. I will re-read your comments again
to try to make sure I am understanding all of the suggestions.
While it is a pointer, the Java name for that is "reference". I used
the term "pointer" before because it is more evocative of the actual
purpose, but it risks confusion with C++ pointers, which are
different.
A Java "reference" is a thing that refers to an object. It is valid
to say that it points to the object, but more Javaesque to say
"refers".
Unlike C++, you cannot do pointer arithmetic, thus it's illegal, say,
to add 3 to an object reference as you can in some languages.
Another way to look at it is that a (non-primitive) variable is a
reference - the value that it holds is a reference to an object
instance. In your code,
Purchase aPurchase = new Purchase();
the 'new' expression creates an object instance and returns a
reference to it, which is stored in the reference variable
'aPurchase'. A reference can refer (point) to different instances at
different times.
It's extremely useful to remember the difference between references
and the object instances to which they refer.
--
Lew
Lew, Thanks! I am not sure I understand how one reference can
reference multiple things at the same time (and know which one is the
correct one), but I will read more about it and try to understand
before I ask more questions. I have a feeling that I am jumping ahead
and that the answer is infront of me. I appreciate your help.
Thanks.
Ryan
At different times.
Purchase aPurchase = new Purchase("1");
aPurchase = new Purchase("2");
When the new Purchase("2") is created, the first reference is lost.
aPurchase now refers to the second Purchase. Since there are no
references to the first Purchase, it becomes available for garbage
collection, and the memory it occupies available for other use.
Doing this in C/C++ would leave Purchase("1") behind and occupying
memory, also known as a memory leak.
It is actually quite common to reasign references this way, especially
within loops, where you need a new instance for each iteration of the
loop.
You can also create a new instance within the loop, then "save" the
reference by placing it into a collection:
ArrayList<Purchase> purchases = new ArrayList<Purchase>(max);
for ( int c = 0; c < max; c++ )
{
aPurchase = new Purchase(c);
...
purchases.add(aPurchase);
}
Since the collection "purchases" now holds a reference, each new
Purchase does not get garbage collected. If all references to the
collection are gone, then the collection is garbage collected, along
with each Purchase.
--
Wojtek :-)
> I assumed that each object must have its own unique
>name,
No. It is just that objects without references pointing to them will
be garage collected. Often you have an Array that points to hundreds
of objects.
A field in an object can point to another object. There is only one
field name but you can have hundreds of objects pointing to hundreds
of other objects by that field name.
--
Roedy Green Canadian Mind Products
http://mindprod.com
We are almost certainly going to miss our [global warming] deadline.
We cannot get the 10 lost years back, and by the time a new global agreement to
replace the Kyoto accord is negotiated and put into effect, there will probably
not be enough time left to stop the warming short of the point where we must not
go. ~ Gwynne Dyer