Primitive variables are those whose type is a primitive datatype, namely boolean
, char
, byte
, short
, int
, long
, float
, and double
.
int i = 0;
char c = 'a';
Reference variables are those whose type is a reference to an object.
String s = "John";
Person p = new Person();
Instance variables are those that are specific to each instance of a class, i.e., they are non-static fields.
public class Person {
String name; // instance variable
}
Class variables are those that belong to a class and are shared by its instance, i.e., static fields.
public class Fiat500 {
static String designer; // class variable
}
Local variables are declared within methods to store temporary state.
public double getHypotenuse(double adjacent, double opposite) {
double squaredAdjacent = Math.pow(adjacent, 2); // local variable
double squaredOpposite = Math.pow(opposite, 2); // local variable
return Math.sqrt(squaredAdjacent + squaredOpposite);
}
Parameters are values we pass to methods.
If not explicitly initialized, an instance variable is given a default value.
null
.0
'\u000'
, and0.0
A local variable is never given a default value and MUST be initialized by the developer.
Consider the code below. What do you expect to be the printed on the screen?
public class Tester {
public static void changeMe(int x) {
x = 5;
System.out.println("changeMe: " + x);
}
public static void main(String[] args) {
int x = 10;
changeMe(x);
System.out.println("main: " + x);
}
}
This?
changeMe: 5
main: 10
Or this?
changeMe: 5
main: 5
What about this program?
public class ModifyingObjectParameter {
public static void changeMe(Person x) {
x.name = "Morty";
System.out.println("changeMe: " + x.name);
}
public static void main(String[] args) {
Person x = new Person("Rick");
changeMe(x);
System.out.println("main: " + x.name);
}
static class Person {
public String name;
public Person(String name) {
this.name = name;
}
}
}
This?
changeMe: Morty
main: Rick
Or this?
changeMe: Morty
main: Morty
And this program?
public class ReplacingObjectParameter {
public static void main(String[] args) {
Person x = new Person("Rick");
changeMe(x);
System.out.println("main: " + x.name + " " + x.hashCode());
}
public static void changeMe(Person x) {
x = new Person("Morty");
System.out.println("changeMe: " + x.name + " " + x.hashCode());
}
static class Person {
public String name;
public Person(String name) {
this.name = name;
}
}
}
This?
changeMe: Morty 603742814
main: Rick 424058530
Or this?
changeMe: Morty 603742814
main: Morty 603742814
Java programs always pass parameters by value!
Primitive variable are passed by value.
Reference values are passed by value.
The key to understanding why the results were different in our previous experiment is realizing that in the code excerpt below.
Person me;
The variable me
is not a Person
object, but a pointer (or reference in Java's terminology) to a Person
object.
This is what actually happens when we initialize a variable:
Person x = new Person("Morty");
And when we pass a parameter we have
changeMe(x);
Primitive variables (int
, long
,
short
...) can be reassigned after being defined.
This can be prevented by using final:
int i = 15; // int is a primitive type
i = 20; // OK
final int j = 10;
j = 20; // Does not compile!
String
is not a primitive type but it’s also immutable.
Because of that, if we need to make a lot of modifications to strings of characters, we should use
StringBuffer
or StringBuilder
.
Unlike String
, StringBuffer
and
StringBuilder
objects can be modified without leaving behind a lot
of new unused objects.
Normal reference variables cannot be made immutable just by using the final
keyword, which only prevents reassignment to another object.
The object that the reference points to can still be changed, as shown in the code below.
static class Dog {
String name;
public Dog(String name) {
this.name = name;
}
}
public static void main(String[] args) {
final Dog dog = new Dog("Snuffles");
dog.name = "Snowball"; // OK
dog = new Dog("Snowball"); // Does not compile!
}