In this lab you will learn two new dark corners of Java. The first is just for fun (it would take a very specialized application to make good use of it); the second reveals a little more information about how Java handles Strings.
Make a new directory for this lab and copy the the files from the course directory for this lab.
cp /cslab/class/cs245/lab9/* .
The general rule here is to prefer the standard. Finalize, for instance, is not standard; it is special, and it is a particularly fuzzy and silly word. Does it mean "terminate," or does it mean "put into final form"? One can't be sure, really, what it means, and one gets the impression that the person using it doesn't know, either, and doesn't want to know.
--- Strunk and White, The Elements of Style
Open the files FTest.java
and
FNumber.java
and do a quick look-over.
FNumber
is just a wrapper object for an integer
value (essentially a stripped down version of the standard class
Integer
).
FTest
is a driver program that allows users to
enter numbers to add to an ArrayList
of these
FNumber
s, and also to delete them.
Each time it displays the list as it currently stands.
Compile and run FTest
.
To add a number, type "a 14" (for example);
to delete that same number, type "d 14".
(I've tried to catch all the exceptions that could happen if you type
incorrect commands, but I may have missed some;
try not to type incorrect input anyway.)
What do you observe when you delete an element?
What could be triggering that message?
Take a look at FNumber
's superclass,
FObject
.
All it has is a method called finalize()
.
You have probably heard that Java is a garbage collected language. This means that when an object is no longer refered to by anything else in the system, the memory it takes up is marked as being available for another use. (When we do some programming in C later in this semester, we'll see what an inconvenience it is to have to free up memory we don't need anymore, since C is not garbage-collected.)
finalize()
is a method that is automatically (sort of)
called when an object is about to be garbage-collected.
The class Object
has a finalize()
method
which does nothing; other classes can override it.
finalize()
methods are often called finalizers.
Essentially they allow objects to make a last request of sorts,
to do one last thing before they are recycled.
There are several strange things about finalizers.
For one thing, you never know when the JVM is going to collect garbage---anyone
writing a Java virtual machine can pick a GC strategy they think
is going to be optimal (and there have been tons of research into that).
We've cheated a little in this example by making FTest
"force" a garbage collection by calling the method
System.gc()
Also notice that the finalizer seems to be running during the printing of the list (if you don't notice this, try making a really big list-- 10 or more elements-- and then deleting something). This is because the finalizer is run in a separate thread from the main program (a thread is just a control process that is part of a larger program).
One interesting consequence of finalizers is that they give an object that's about to be garbage collected an oppportunity to save itself. It won't be recycled as long as someone has a reference to it, so the object simply needs to get some other object to pay attention to it.
Write a finalizer for FNumber
(this will override
the one inherited from FObject
; in fact, FNumber
doesn't need to extend FObject
anymore)
that puts FNumber
back into the list.
Notice that list
is public in FTest
.
Effectively the object should refuse to be deleted from the list.
Test your revision. What do you observe?
If you don't get an exception, keep trying until you
do (again, you may have to do this on a large list).
The problem is that, just as the old finalizer ran during the printing of the
list, so is the new one.
That's bad because for an Iterator
to work right,
it assumes that there will be no changes to the list while it
is iterating--except possibly changes that it makes itself.
The best we can do is change the code in FTest
which
prints the list.
Instead do the following:
ConcurrentModifcationException
Test your code. Once it appears to be working (that is, you try to remove an object, but the object is still in the list), try to remove the same object a second time. What do you observe?
The reason why the object actually gets removed the second time you try to delete it is because finalizers get called only once for each object. Next time the object is about to be garbage collected, the garbage collector notices that it has already been finalized, and doesn't call the method again. It got a second chance, but no third chance.
The object can't put itself back in to the list a second time,
but there way to make it seem like it can.
Re-write the finalizer to that it will always look like an object
that is removed appears back in the list
(remember, the only way a user can identify a FNumber
object is by its value).
Test your new finalizer. What havoc can you wreak by having your finalizer add several objects to the list?
What possible use could a finalizer have? Mainly this feature is there in case an object is holding a system resource that needs to be cleaned up and released. For example, suppose an object has a file open, and it's writing to the file as it does its operations (maybe it's logging events, for debugging purposes). The object might need to write some final information to the file and close the file.
==
works better than it shouldEarly on in Java instruction, we tell students they must
always use the equals()
method to compare Strings
because ==
just compares the memory location
(all of which is true).
However, the instructor then hopes a students doesn't go off and
experiment and find results that don't seem to fit.
Look at the file StringSurprise.java
, predict what its
output should be, and then compile and run it.
What do you observe?
The String
class maintains a list of
"canonical representations" of all String
objects
that are created during the run of the program.
This means if there are more than one string with
identical content, one of those Strings is kept in this
private list in the String
class.
You can retreive a string's canonical feature with the method
intern()
.
To "intern" a string means to get a reference to its
canonical represenation to use instead.
Read
the description of the inter()
method
in the Java API and then
explain why StringSurprise
works the way
it does--- notice in particular what the API says about
"All literal strings...".
To make sure you caught all that, look at the
class StringGame
.
Predict what the output should be.
Then compile and run, and see how good your prediction is.
Turn in a hard copy of your FNumber.java
class.