• No results found

Variables and values

In document Java (Page 87-94)

Primitive and Reference Types

Primitive types are for example ints and doubles and chars. These are not objects. They have single values.

Reference types are objects.

So for example

int x = 3; // x is a primitive type

String text = new String("Click me"); // text is a reference type

In fact there are significant differences in the way these are stored. If we have three int variables x y and z, with values 1 2 and 3, we might think of them as being held in memory like this:

It is more effective to think of this as a symbol table:

But if we have say

String a = new String("One");

String b = new String("Two");

we can think of memory like this:

Here object 'a' is held in memory starting at address 21. Object 'b' starts at address 25. In the symbol table, next to 'a' it says 21, the start address of the object. Next to 'b' it says 25, the start address of the object. In the symbol table therefore we have pointers to where the object is in memory. A pointer to something is the address of it. This is what a reference is.

In a Java program we do not know, and cannot find out, the actual numeric address of these things.

But we do not need to know. It is more useful to think of this situation like this:

Here we have pointers as arrows. In reality they are numeric addresses, but they can be thought of as actual pointers to different memory locations.

References and taking out the garbage Suppose we have this code:

String a = new String("One");

String b = new String("Two");

b = a;

What happens? After the first two statements the situation is as in the previous diagram. After the third statement, we have

b=a means that b now points to the same object as 'a' points to. Both a and b reference the same object.

It does NOT mean this:

it does not make b point to a - it makes b point to what a points to.

But what now of object b? Nothing refers to it. This means that our Java program can no longer use that object, since we have effectively lost track of where it is.

It has become garbage - data in memory which is no longer needed. What will happen is that the garbage collector , part of the runtime software, will return this memory block to free memory, from where it can be re-used.

More precisely, the garbage collector might do that. It takes time to do a garbage collection, and if there is plenty of free memory already, it is a waste of time. So the garbage collector has an algorithm which means it will only do it if it is worthwhile.

null

The special value null is a 'pointer to nowhere'. We will see some uses for it later.

A common problem relates to null. This code:

String s="cat";

System.out.println(s);

executes as expected. But

String s="cat";

s=null;

System.out.println(s);

results in a run-time exception:

Exception in thread "main" java.lang.NullPointerException at Main.main(Main.java:12)

Line 12 is the println statement. This error usually arises when we try to invoke a method on an object reference which is null - so in effect the object does not exist. Typically the program is trying to access uninitialised data. Sometimes the compiler can detect this - but not always.

The println method invokes the toString() method of its argument. toString is a method which all objects have, which converts the object to an equivalent string. So this println statement is in effect

System.out.println(s.toString());

so we are invoking a method on s.

The line s=null; means that s points to nowhere - it does not point to an object, so we cannot invoke a method call on it. If we do, we get a NullPointerException.

Swapping reference type variables

We saw that to exchange the values of primitive types, we needed to use a third temporary location, like:

int x=1;

int y=2;

int temp;

temp=x;

x=y;

y=temp;

Will the same pattern work for reference types? Strings are objects - how about

String s1 =new String("One");

String s2 = new String("Two");

String temp;

temp=s1;

s1=s2;

s2=temp;

After the first three statements, we have:

After temp=s1; this changes to

After s1=s2;

and after s2=temp;

so this has done what we wanted.

Exercise

For Strings, a statement like:

s2=s2+"ne";

constructs a new string object, containing the text in s2 joined at the front to 'ne'. It then makes s2 refer to this new object.

What do you think the following code will do?

String s1="One";

System.out.println("Equal");

Try it.

Explain what happens.

Look at the String API and look at the equals() method.

Parameter passing

Suppose we have a method which finds the average of two values:

double average(double x, double y) {

double result;

result=(x+y)/2.0;

return result;

}

and we invoke this method by:

double a; double b;

a=2.0;

b=3.0;

double c = average(a,b);

How exactly does this work?

The items x and y, or a and b, are called

parameters or arguments. The x and y in the

definition of the method are sometimes called formal parameters, while the ones used in the invocation of the method (in this example, a and b) are actual

parameters. Actual parameters can be constants, variables or expressions.

The way this works varies between different languages. In Java, the values of

actual parameters are copied, and these are passed to the formal parameters. This is sometimes called "pass by value".

Which parameter is copied to which depends on the order - that is, the first (a) is copied to the first (x), the second (b) to the second (y) and so on. The process involves a stack - the actual parameter copies are pushed onto a stack, then in the start of the method the values are popped back off the stack and assigned to the formal parameters.

double average(double x, double y)

Does changing x affect a?

The answer is no, because x is a copy of a. Changing the copy will not affect the original.

Reference types as parameters

Does it make any difference if the parameter is a reference type - an object? In one sense no, since the parameter is still copied. But for a reference type, this is a pointer to the object. The pointer is copied, not the object.

For example, suppose we want a method to find which day of the week it is:

int getDay(GregorianCalendar formalDate) {

int result = formalDate.get(GregorianCalendar.DAY_OF_WEEK);

return result;

}

This uses the class GregorianCalendar, which does what we want. We could use this by:

GregorianCalendar date=new GregorianCalendar();

System.out.println("Day (sunday =1) "+getDay(date));

But suppose the method changes the formal parameter:

int getDay(GregorianCalendar formalDate) {

int result = formalDate.get(GregorianCalendar.DAY_OF_WEEK);

// make it Sunday

formalDate.set(GregorianCalendar.DAY_OF_WEEK, 1);

return result;

}

and see what happens:

GregorianCalendar date=new GregorianCalendar();

System.out.println("Day (sunday =1) "+getDay(date));

System.out.println(date.getDisplayName(GregorianCalendar.DAY_OF_WEEK, GregorianCalendar.LONG, Locale.ENGLISH));

The output of this (run on a Thursday) is:

Day (sunday =1) 5 Sunday

So it correctly tells us it is Thursday - but then changing the formal parameter has made it Sunday.

How come a reference type can be changed?

The rule is the same - parameters are copied then the copy is passed into the method. But a reference type parameter is a pointer to an object. Changing the pointer, such as by

formalDate=null;

would not affect the actual parameter, since it changes a copy. But if the method alters the object being pointed at, that change will be seen in the invoking code.

Another way of looking at this is to count the objects. In the code above, the keyword new is only used once. There is only one GregorianCalendar object. A pointer to it is copied and passed into the method - and the method uses the pointer to change the object pointed to.

Arrays are objects, so this is applicable for arrays as parameters.

Exercise

What do you think this code would do?

static void swap(int[] one, int[] two) { int temp;

for (int i = 0; i < one.length; i++) { temp = one[i];

one[i] = two[i];

two[i] = temp;

} }

public static void main(String[] args) { int[] a = {1, 2, 3};

int[] b = {4, 5, 6};

swap(a, b);

for (int i = 0; i < a.length; i++) { System.out.println(a[i]);

}

for (int i = 0; i < b.length; i++) { System.out.println(b[i]);

} }

Write a method called zero, which takes an array of ints as parameters, and makes every element zero. Test it.

In document Java (Page 87-94)