This page provides a quick review of how Java classes, objects, primitive types, values, and variables work together in a Java program to create various “situations”, or combinations of objects and values in “Java land” or wherever you might imagine these abstract ideas playing out (we will draw pictures to illustrate these). But in reality, these values and events happen in the computer’s RAM, or random-access memory; we will also draw some pictures of what the RAM might look like, as this can be especially helpful in understanding how assignment statements affect primitively-typed variables and Object-typed variables differently. (This difference is one of the main themes of this review page.)
OK, imagine that we write up two different class definitions.
The first contains code for a Circle
class: each
Circle
has x
and y
coordinates and a radius r
, which are all Java
int
s. In addition, Circle
s have two
methods, one to return their circumference
and one to
scale
them by a given scale factor s
.
public class Circle { int x; int y; int r; public Circle(int x, int y, int r) { this.x = x; this.y = y; this.r = r; } public float circumference() { return 2 * Math.pi * r; } public void scale(int s) { r = s * r; } }
(Note in passing that the circumference
method implicitly
applies to the Circle
it is called on, returning a
float
scale method
implicitly affects the Circle
it is called on,
returning nothing (i.e., void
return) but instead
acting upon its object, and thus changing it.
The next class we define is just the main class for this “project”,
called Example
: it has a main
method which will be
the one that is run when the project is run.
public class Example { int a; int b; Circle c; Circle d; public static void main(String[] argh) { // the initial set-up: a = 7; b = 12; c = new Circle(10,5,3); d = new Circle(20,4,2); // ... see further code here } }
We are going to “run” this code, in the main
method,
up to the point labelled // ... see further code here and draw a
picture of what the situation looks like, both in abstract picture terms
but also in terms of the RAM (memory) of the computer (see below). Then we
will add sample lines of further code, one or two at a time, and see how the
situation changes.
(Note: if you’re reading this in a browser, it might help to open two copies of this window, one where you keep the code above visible, for reference, and another with the running examples below.)
Given the code above, what will the situation look like? We can visualize it as in the following picture:
Some things to note:
The values which are primitively-typed, like int
s,
are stored directly in the boxes we are using to show variables
(for example, a = 7
or b = 12
in the
picture).
Variables which have classes for their types, like c
and d
, do not store those objects directly in the
variables “box”. Instead, the object sits outside
somewhere, and what gets put in the
variable box is an arrow, which points to the object.
Notice the little blue “numbers” in the arrow boxes?
Those will be the addresses of the objects in the computer’s
memory as soon as we get to that part below :)
.
The actual Circle
objects also don’t really have
names
so much as they just are sitting somewhere in memory (see below).
You might imagine them just floating out there in “Java space”
(the abstract pictures land where we imagine all this is happening).
If you want to be able to refer to them, to make use of them,
access their data, call their methods, etc., then you need to be storing
an arrow that points at them (really: an address) in one of your own
boxes, the ones you have names for as variables (like c
and d
here).
What is stored inside an object is just the values of the variables
declared in its class (here x
, y
, and
r
for each Circle
). The code
that the object uses to update itself or return values, etc.,
i.e., the methods for the object’s class, is stored just once,
for all the objects of that class: they can each access it when needed,
but in some sense they all share it.
On the other hand, what is really going on inside the computer? Everything we are talking about (objects, primitive values, variables, even the method code!) is stored in the computer’s RAM or main memory. This is like a really big array, with weird indices (called addresses) that are written in base 16 or hexadecimal. This is just like our usual base 10, but it has 6 extra “digits” for the values 10 through 15, usually written as the letters ‘a’ through ‘f’. You might recognize numerals like these from error output in Java.
(Technically, the things stored in RAM are not so much in hexadecimal as in binary, or base 2 … but that’s pretty nasty to write out in full, so people usually use base 16, which abbreviates the base 2 more nicely than base 10 does. [Why? Because 16 is a whole power of 2, whereas 10 is not.] Even more technically, they are not “in” binary so much as there are two stable configurations of the corresponding circuits, one of which we call a ‘0’ and one that we call a a ‘1’.)
In any case, we can picture this situation (what numbers are where in RAM) as in the following picture:
Some things to note this time:
The little blue numbers (in hexadecimal) running down the left side of “RAM” (pictured here as an array of boxes running vertically) are the addresses, or roughly the array indices, of the storage boxes in RAM.
To the left of the addresses is a “key” showing the names of some of the relevant variables—these are not really in the computer’s RAM when the program runs (except sometime for debugging purposes), instead only the boxes of numbers corresponding to values in RAM are there. (Even the addresses are not “there” so much as implicit in the way the indexing hardware works.)
Note the difference between the address where the variable d
is stored (f36ba
) and the address where the object that
d
is pointing at at is currently sitting—namely, the object
we have labelled [Circle #1]
(f36c0
). When we put
the “back end” of a different arrow in a variable box, we are
putting an address into the box where that variable is benig stored.
Whenever we make an assignment to a variable, it over-writes the value in the corresponding box: for primitive types, this means the old value itself is now gone. But for objects, this means just that the back end of one arrow pointing at that object is gone: the object itself is still out there, and something else may also be pointing at it.
OK, now let’s try out a few things: we’ll write some code and then “run it” by changing the pictures—usually just the abstract pictures, but sometimes maybe the RAM as well, for emphasis.
In each of the illustrations below, we start with some situation pictured on the left. (Often this is either our original situation or one continuing from a previous example.) Next we “apply” the code given in the middle, and we end up with the situation on the right. (See the red arrows for the left-to-right flow of time.)
Look at the pictures and verify that you understand why and how the situation changes due to the effects of the code.
All changes in the final picture should be circled in red (unless I forget some … but I’ll try not to).
In this first example, we assign some variables of type int
, which
is of course primitive (in particular, it’s Java name starts with a lowercase letter).
The changes can be seen as the simple over-writing of the values in the boxes:
In this next example, we assign some variables of type Circle
, which
is a class type, and thus represented by references.
The changes in this case can be seen as an over-writing again, but now the
things over-written are references to actual objects in memory. This means that
changes in one variable’s value can be “seen” through references
via another variable. (Here c
and d
are independent of
each other in the first situation, but they both reference the same object in the
second one.)
Notice how the dot notation works: given a variable (or other Java expression)
of a class type, the dot following it means that we “run the arrow”
from the box holding the corresponding reference, then use the name to the right
of the dot to pick out a variable inside the scope of the corresponding
object-box. Once there, the “boxes” corresponding to variables behave
just as in the outer part of the picture: we can get the value, or assign to it
using the =
operator.