References
- We’re going to take some time to think very carefully about how variables are stored in memory.
- Most of the time, we don’t have to worry about what is happening under the hood; but, occasionally, strange things happen, and the only way to debug your code is to have a solid metal model of what’s really going on.
- Imagine Memory (RAM) as a really big array.
- If you have 8GB of RAM, its as if your array has a billion slots (each slot is 8 bytes on a 64 bit machine).
- When you declare a variable (e.g.,
int x
), Java labels one of those slots asx
. - Once a variable is declared, referencing that variable again gets the data from the memory slot.
x = 17
put the data into RAM.z = x
read the data from the memory slot.
- Notice that an assignment is just copying data from one box to another.
- When we add expressions, we just manipulate the data before we put it in its new box (
z = x + 15
) -
Notice that
int x = 17
actually does two separate steps: Declares the variable (i.e., allocate a memory spot), and initializes the variable (i.e., gives it its initial value). - When you call a method, data is “passed by value”
-
Consider
int add(int a, int b) { return a + b }
- When you call
add(5, 7)
- new variables
a
andb
are declared (i.e., new spots in memory are labeleda
andb
) - then, they are initialized by position (5 goes to
a
and 7 goes tob
)
- new variables
- Similarly, when you call
add(x, y)
- new variables
a
andb
are declared (i.e., new spots in memory are labeleda
andb
) - the values in the boxes for
x
andy
are copied into the boxes fora
andb
- new variables
- Check out
Primitive.java
in https://github.com/kurmasz-SampleCode/CIS163-SampleCode - Point out that referencing uninitialized values is a syntax error.
- Look at
subtract
and remember that parameters are always passed by position.- Step through the first couple sections of
main
- Highlight the use of the debugger
- Setting breakpoints
- Examining local variables
- Examining variables in other scopes
- Step in vs. step over
- Step through the first couple sections of
- Because the values are copied, changes to the variable inside the method don’t affect the
variable outside the method.
- Step through
method1
inPrimitive.java
- Step through
- This is true, even if the formal parameter and actual parameter have the same name.
- : .to-me} Step through
method2
inPrimitive.java
- : .to-me} Step through
- Like humans, variables have “first names” and “full names”.
y
is like a first name. Just as each family can have a “John”, each method can have it’s owny
.- Inside the method,
y
refers to the local variabley
, not whichevery
may exist elsewhere.
- Remember: Changes to a local variable remain local!
Primitive data vs. objects
- There are two types of data in Java: primitive data and Objects
- Primitive data includes
int
,long
,char
,float
,double
,boolean
. - They are called “primitive” because they consist of just one datum, and you can’t call methods on them.
- Objects are defined by a class, instantiated with
new
and have methods. - They are not primitive, because they (usually) consist of several components (i.e., instance variables)
- Primitive data includes
- Primitive data is stored directly in the slot of RAM identified by its variable.
- Objects variables are references: They refer to the place in memory that actually contains the object.
- Think of an object’s variable as a “phone number” that tells you how to reach the object.
- Objects don’t exist until you specifically create them using
new
. -
Draw a diagram showing memory at each step of
Date d; d = new Date(3, 4, 1945)
- Notice that when we say
Date d = new Date(1, 2, 1934)
we are actually combining four steps- Declaring the variable (which creates the reference)
- Instantiating the object (that’s what
new
does) - Running the constructor (giving initial values to the instance variables)
- Assigning a value to the variable.
- This difference between primitive data and objects is important to understand because it can cause subtle, unexpected differences in behavior.
- Step through
ReferenceExamples1#nullPointer
- Object references also must be initialized to something.
- Uninitialized is not the same thing as
null
.
- Uninitialized is not the same thing as
- A null variable does have a value:
null
- If you call a method on a
null
variable, you get aNullPointerException
: There is no object to call the instance method on.
- Object references also must be initialized to something.
- Step through
ReferenceExamples1#assignRefs
- The assignment statement (
=
) copies the value in one box to another. - Since the data in the box is not the object, but just a reference, it is only the reference that is copied.
- As a result, the object now effectively has two names (
point1
andpoint3
) - Think of it like this: When you write down a repair man’s phone number and give it to a friend, you aren’t copying the repair man.
- Remember, changes seen through one reference are also visible through the other.
- If your friend calls the repair man and books an appointment at 9:00 a.m., then you call the next day, you will find that the 9:00 slot is unavailable. This makes sense because you are calling the same repair man.
- The assignment statement (
Passing refs as parameters
- Think about what happens when you pass a reference as a parameter to a method.
- Just as with primitive data,
- A variable (i.e., “box in RAM”) is created for each parameter.
- The value for the actual parameter is copied into the formal parameter.
This copying is just like setting
point3 = point
in the previous example: You now have two different variables that refer to the same object.
- As a result, an object passed to a method can be modified by the method.
- Think carefully about this distinction: The variables are being treated the same way; but, the indirection causes a different behavior to be observed for objects.
- We say that primitive data is passed “by value” while objects are passed “by reference”.
- Look at
ReferenceExamples1#refsAsParams
- Note however: Only the object at the end of the reference can be changed. The reference itself cannot (for the same reason you can’t modify primitive data)
- Look at
ReferenceExamples1#refsAsParams2
Switch to lab.
== vs .equals
- Understanding refs allows us to better understand the difference between
==
and.equals
==
compares the value of the two variables. In other words, just compares the data in the “box” in RAM.- Thus,
==
asks “Are these the same object?” or “Are these two names for the same object?”
- Thus,
.equals
looks at the instance data and asks whether the two objects represent the same item (same Date, same Car, sameStudent, etc.)- Step through
ReferenceExamples1#compareRefs
- Beware of
String
s.- String literals (e.g.,
"Foo"
) all refer to the same object “under the hood”. - Therefore, you can sometimes get away with using
==
when you should be using.equals
. - However, your code will beak if the code ever changes such that one of the objects is no longer a literal (e.g., comes from a file or the keyboard.)
- String literals (e.g.,
- Step through
ReferenceExamples1#compareStringRefs
Copying
- What should you do if you want two separate copies of an object:
- Option 1: Write a copy constructor
- This is just a constructor that takes an object of the same type as a parameter.
- Option 2: Implement a
clone
method.- This comes with a lot of “baggage”. Avoid it unless you really need it.