Is a Java Reference a Pointer?
Students who come to Java from C or C++ often ask whether a Java reference is just a pointer. Students who come to Java without that background ask the opposite question: Java has no pointers, so what is a reference?
Part of what makes this confusing is that the word "reference" means different things in different languages. There are three separate ideas that get lumped together: the C pointer, the C++ reference, and the Java reference. Let's look at them one language at a time.
Pointers in C
In C, you work with memory addresses directly.
Start with an ordinary variable:
int x = 5;
x is a name for a block of memory that holds the value 5. That block has an address — a number that tells you where it is in memory. In C, you can get that address with the & ("address-of") operator:
int *p = &x;
p is a new variable, and its type is int *, which means "pointer to an int." p does not hold an integer; it holds an address: the location of x. If x lives at address 0x1234, then p holds the number 0x1234.
A pointer is a variable like any other. It has its own block of memory, its own address, and it holds a value. The only difference is that the value it holds is meant to be used as the location of something else.
To go from the pointer to the thing it points at, you dereference it with the * operator:
printf("%d", *p); // prints 5
*p = 10; // now x is 10
*p means "go to the address stored in p and use whatever is there." Writing through *p changes x, because p holds x's address.
Because a pointer is just a variable holding a number, you can do with it what you can do with any variable. You can point it somewhere else:
int y = 99;
p = &y; // p now holds the address of y instead
You can set it to point at nothing:
p = NULL;
You can also have a pointer to a pointer, since a pointer has its own address. C does not protect you from mistakes: if you dereference a pointer that holds an invalid address, the program has a bug, and C will not catch it for you.
To summarize C: a pointer is a variable that holds an address, you take addresses with &, and you follow them with *. Everything is explicit.
References in C++
C++ keeps C's pointers exactly as they are. It also adds a second, separate thing, and it calls that thing a reference.
Here is a C++ reference:
int x = 5;
int &r = x;
r is declared as int &, which means "reference to an int." It does not behave like a pointer. r is not a variable that holds an address. r is another name for x — an alias. After this line, r and x refer to the same block of memory.
You do not use & or * to work with it:
r = 10; // x is now 10
You use r as if you had written x, because it is x.
This is the part that confuses people. A reference has two rules that a pointer does not:
- It must be bound when it is created. You cannot write
int &r;with nothing on the right. A reference has to be an alias for something from the start. - It can never be re-bound. Once
ris an alias forx, it stays an alias forx. If you later writer = y;, that does not makerrefer toy. It copiesy's value intox, becauseris another name forx. There is no way to make an existing reference refer to a different object.
A pointer is different: you can re-seat it as often as you like, and it can be NULL. A reference cannot be re-seated, and in a correct program it cannot refer to nothing.
So C++ has two distinct tools:
- A pointer is a variable that holds an address. It can be re-seated, it can be null, it has its own address, and you work with it through
&and*. - A reference is an alias — a second name for an object that already exists. It is bound once, cannot be re-seated, is not nullable in correct C++ code, and needs no special syntax.
A C++ compiler usually implements a reference using a pointer internally, but the language treats them as different things with different rules.
Passing arguments: the swap example
The difference shows up when you pass something to a function.
Suppose you want a function that swaps two integers. In C, you use pointers, because the function needs the addresses of the caller's variables to change them:
void swap(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
swap(&x, &y); // pass the addresses
In C++, you can do the same thing with references. The parameters become aliases for the caller's variables:
void swap(int &a, int &b) {
int tmp = a;
a = b;
b = tmp;
}
swap(x, y); // no & needed; a and b are x and y
Inside this function, a and b are not copies. They are the caller's x and y under different names, so the swap takes effect in the caller.
Java cannot do this, as we will see next.
References in Java
Java has no C/C++-style pointers. There is no &, no *, no pointer arithmetic, and no way to get the numeric address of an object. So when Java says "reference," the natural assumption, especially coming from C++, is that it means the C++ reference: an alias, bound once, never null.
That assumption is wrong, and it is the main source of confusion. A Java reference is not a C++ reference. In its behavior it is closer to a C++ pointer, with some restrictions.
Here is what a Java reference can do:
Employee e1 = new Employee(1);
Employee e2 = new Employee(2);
e1 = e2; // re-seated: e1 now refers to the second object
e1 = null; // and it can refer to nothing
A Java reference can be re-seated and can be null. A C++ reference can do neither; those are pointer behaviors. What Java takes from the C++ reference is the syntax: there is no * to dereference. When you write something like e1.getId(), Java follows the reference for you before calling the method. The dereference is implicit, but it still happens.
So a Java reference is pointer-like: you can think of it as pointing to an object, but Java does not expose it as a raw memory address. You cannot do arithmetic on it, cannot read it as a number, and cannot dereference it explicitly. It has the behavior of a pointer, with the unsafe operations removed and the dereference made implicit. The name "reference" can make people think of C++ references, but the behavior is much closer to a restricted pointer.
The swap function shows this clearly. Here it is in Java:
static void swap(Employee a, Employee b) {
Employee tmp = a;
a = b;
b = tmp;
}
swap(e1, e2); // e1 and e2 are unchanged afterward
This does nothing to the caller's e1 and e2. Java passes the reference by value: a and b are copies of the references e1 and e2. Swapping the copies inside the function does not affect the originals. This is how a copied pointer behaves, and it is not how a C++ reference behaves; a C++ reference would have made the swap take effect.
There is one more point to be clear about, because it is a common source of confusion. Even though you cannot re-seat the caller's variable, you can change the object it points at:
static void giveRaise(Employee a) {
a.setSalary(a.getSalary() + 1000); // this does affect the caller's object
}
Here a is a copy of the reference, but the copy points at the same object as the caller's reference. Changing that object through a is visible outside the function. What you cannot do is make the caller's variable point at a different object.
This settles the common question of whether Java is pass-by-value or pass-by-reference. Java is always pass-by-value. For objects, the value being copied is the reference, not the object. You can follow the reference and change what is at the other end, but you cannot change the caller's reference itself.
The short version
- A C pointer is a variable that holds an address. It is explicit, can be re-seated, can be null, and uses
&and*. - A C++ reference is an alias — a second name for an object that already exists. It is bound once, cannot be re-seated, is not nullable in correct C++ code, and uses no special syntax.
- A Java reference uses the syntax of a C++ reference (no
*, implicit dereference) but has behavior closer to a C pointer (can be re-seated, can be null, passed by value). It is pointer-like, but Java does not expose it as a raw address.
The word "reference" can be misleading: a Java reference is not a C++ reference. In the ways that matter for this discussion, it behaves much more like a restricted pointer.