This site is retired and is no longer updated since November 2021. Please visit our new website at https://www.teamscode.org for up-to-date information!
Objects

Vocab Bar
Mutable An object whose fields can be changed
Immutable An object whose fields cannot be changed
Pass by value A copy of the data is passed
Pass by reference A reference to the object is passed
Primitive variables A data type representing a single value only

In the beginning of this course, we learned the basic primitive variables that are used in programming, such as ints and doubles. More recently, we covered creating classes and instances of classes, otherwise known as objects.

There are several differences between objects and primitives. First off, primitive variables cannot be null and instead all have a default value if they aren’t instantiated. Objects, on the other hand, can be null. For example, there is a class called Integer that’s included by Java that is the same as a primitive int but the difference is that it is an object rather than a primitive:

    int num1; // Defaults to 0

    Integer num2; // Variable declared, but must be initialized before using

    Integer num3 = null; // Points to nothing, has no value

An object can have variables, methods, and constructors, but when an object is set to null, any attempt to call those variables and methods will result in an error (for example: str.substring(0, 1); will produce an error if str is null).

In addition to having methods specified in their classes, all objects inherit certain methods, such as toString(), which returns a string representation of the object. By default, the toString() method returns strings looking something like this: ClassName@53814r (which you might have seen before if you ever tried to directly print an array to the console*). This result is obviously not very useful. Luckily, the process of overriding methods (taught in later lessons) allows developers to change the code behind toString to produce more legible output and is a fundamental part of what is known as object oriented programming.

*The proper way to call toString() on an array is to use the following code: Arrays.toString(arr);

Other important concepts in object oriented programming are mutable vs. immutable objects and pass-by-value vs. pass-by-reference, ideas that will become clear as you read through this lesson. For this lesson, we’ll be working with a classed called Num shown below:

    public class Num {

        private int someNum;

        public Num(int val) {

            someNum = val;

        }

        public int getNum() {

            return someNum;

        }

        public void setNum(int val) {

            someNum = val;

        }

    }

To start off, let’s go back into the basics of creating an object.

    Num n = new Num(5);

The above line of code consists of three parts. The first part is Num n. What this does is create a variable named n that is of type Num (Declaration). With this code alone, the variable n has not been assigned any value yet. All we know is that it will be of type Num.

The second part, with the = sign, is assigning the initial value given on the right of the equals sign to the variable n (Initialization).

The final part is the new keyword (Instantiation). This is the part of the code that creates a new object that the variable n will point to, or rather, the variable n contains a reference to.

Essentially, the new Num(5) part creates a new object, and the Num n is a variable that points to, or references, that object. Note that instantiation does not always follow the initialization of a variable; sometimes, a variable is initialized to reference an existing object, and thus no object is being instantiated. In the following example, there are two variables that contain references to the same object (only one object is instantiated, but two variables are initialized):

    Num n = new Num(5); 

    Num n2 = n;

Now, n and n2 both reference that same object that was created by doing new Num(). It’s important to know that n and n2 by themselves are not objects; rather, they reference a certain location in memory where the object (created by the code new Num(5)) actually is located. If you run the following code:

    n.setNum(10); // changes the someNum variable from 5 to 10

You’ll notice that n.getNum() and n2.getNum() now both return 10, despite never calling the setNum method for n2. This is because since both n and n2 are now referencing the same object, any changes to that object made through either n or n2 will be reflected from the other variable.

This fact can be very useful when working with methods that modify objects. For example, you might have a method that takes in an array (which is an object) and modifies it by adding 1 to every index:

    public static void main(String[] args) {

        int[] actualArray = {5, 6, 7};

        addOne(actualArray); // actualArray now contains {6, 7, 8}

    }

    public static void addOne(int[] arr) {

        for (int i = 0; i < arr.length; i++) {

            arr[i]++;

        }

    }

Calling this method will modify actualArray because when modifying arr inside the method, arr contains a copy of the reference to actualArray, and therefore the changes made inside the method are reflected on the outside as well.

If you are still confused, the official Java Documentation on creating objects provides excellent detail into this idea.

In the past, when working with primitive variables, you might have noticed that int and double values changed inside a method don’t reflect outside in the way described above. If a method accepted a primitive variable, changing that value inside the method would not affect anything outside of the method. Similarly, assigning one primitive variable to another and then changing one of the variable’s values did not cause the other to change.

To understand why this is the case, consider what primitives are. There are no methods, constructors, or fields within primitive variables; they simply hold basic values and nothing else. If you want to change a primitive variable’s value, you give it a new bit sequence to represent a new value by doing myInt = 12 or myInt = 16.

This means that when you assign one primitive variable to be the same as another, you are passing a copy of the bits representing that value. Because you passed a copy of those bits, changing one of the variable’s value later will give that variable a new bit sequence but it won’t affect the other variable.

    int i = 5;

    int j = i; 

    j = 10; // Assigned j a new value, but i remains unchanged

With objects however, assigning a variable to another passes a copy of the reference to that object. Both variables will therefore reference the same object, and changes to that object can be seen from both variables.

Of course, this means that while primitive variables can be thought of as immutable, meaning their value cannot be changed without assigning it a completely new identity (a primitive’s value essentially is its identity), most objects are mutable, meaning that the data inside an object can be changed; you don’t need to create a whole new object if you want to change one of its instance variables, for example. If you take a look at the Num class above, each Num object contains the integer someNum, an instance variable. Changes to someNum in an object will be reflected through the variables referencing that object (the references will not be changed).

There are a few classes that are immutable. In immutable objects, none of the variables can be changed once they are assigned an initial value. This can be accomplished by assigning all field variables to be final, meaning that if you ever want to alter that object’s data, you’d have to create a new object with the updated values. Two common examples of immutable objects are the String class and the Integer class. This means that both these classes behave similarly to primitives. For example, every time you change the value of a String, you’re actually creating a new object with the updated characters and changing your reference, which is why editing one String variable does not affect any other String variables that were previously referencing the same object.

One common mistake when dealing with references is when people try to modify the object a variable references, usually when dealing with methods, but they end up creating a new object and not changing the original variable at all. Take a look at the following example:

    Num n1 = new Num(5);

    modify(n1);

    public void modify(Num n2) {

        n2 = new Num(10);

    }

You might expect n1 to now have a value of 10 because n1 and n2 shared the same reference, but this is not the case. When you do n2 = new Num(10), you are creating a new object and then assigning n2 to reference that object. Remember that when you assign one variable to another, a copy of the reference is passed. In this case, the variable n2 inside the method is assigned a new reference, but since it originally only had a copy of n1’s reference, n1’s reference remains unchanged and doesn’t point to the new object. There are two solutions to this problem:

    // Solution 1: changing the original object

    Num n1 = new Num(5);

    modify(n1);

    public void modify(Num n2) {

        n2.setNum(10);

    }

    // Solution 2: changing the original variable’s reference

    Num n1 = new Num(5);

    n1 = modify();

    public Num modify() {

        return new Num(10);

    }

In some languages, however, that original code example n2 = new Num(10) would actually work in changing n1. In those languages, this is known as pass-by-reference, meaning that, in our example, n2 isn’t given a copy of n1’s reference but rather n2 is given the same reference as n1, which means changing n2’s reference ends up also changing n1’s reference. The other way of doing things is pass-by-value, which is basically how primitive variables work in Java: a copy of the value is passed over but none of the modifications are reflected on the original variable.

Many languages actually allow you to choose whether you pass by value or by reference. For example, in C++, using the & symbol allows you to tell the compiler to pass by reference. Java passes objects by value, but not in the same way as primitive values are passed. Java passes a copy of the reference to an object, making it slightly in-between pass-by-value and pass-by-reference.

Lesson Quiz

1. What is the output to the console?

    String s1 = "Foo";

    String s2 = s1;

    s1 += "Bar";

    System.out.println(s2);
a. Foo
b. FooBar
c. Bar

2. Which of the following statements is incorrect about primitives and objects?

a. Objects can store multiple types of data whereas primitives only store one item of data.
b. Primitive data is immutable, whereas objects are most often mutable.
c. Primitives pass by value whereas objects pass by reference.

Written by Alan Bi

Notice any mistakes? Please email us at learn@teamscode.com so that we can fix any inaccuracies.