9 minutes read

In this topic, we will talk about an important feature of programming languages that is a must for every programmer: you will learn how JVM passes arguments to a method. The topic will be based on practical examples, and we strongly recommend that you write and execute all the code yourself.

Brief overview

When working with Java, you should be aware of two mechanisms for passing an argument to a method. They are:

  • Pass-by-value. This approach is used when passing primitive data types. In this case, we pass a copy of the variable value and whatever happens to the copy does not affect the original variable value.
  • Pass-by-reference. This one is a bit more complicated. In fact, Java does not implement this approach in its pure form. Instead of passing a reference to an object, we pass a copy of the reference. That is, we pass a reference by value. It is used for reference types. In this approach, a method gets access to the object and can change its state.

Don't worry if the idea behind these approaches still seems a bit vague to you. We will explore both of them in the following sections.

Passing primitive types

As we mentioned in the previous section, primitive types in Java are passed by value. Now it is time to see how it works in practice:

public class PassArgumentsDemo {
    public static void main(String[] args) {
        int javaReleaseYear = 1995;

        // num = copy of javaReleaseYear value
        increment(javaReleaseYear);

        System.out.println(javaReleaseYear); // 1995
    }

    public static void increment(int num) {
        num++;
    }
}

In this simple example, you can understand how the pass-by-value mechanism works. A copy of the javaReleaseYear variable value was made and assigned to the num variable. Therefore, no matter how the num value changes inside the increment(int num) method, it will not affect the javaReleaseYear value.

Passing reference types

To understand this approach, we have to dig deeper. First, let's understand what a reference is. When we create an object, it is stored in the heap and has its physical address in memory. In some languages, for example in C++, it is possible to store this address in a variable. Let's imagine such an object: Object obj = new Object(). In this case, the obj variable would store the physical address of the created object. In C++ such variables are called pointers.

In Java, we do not store the physical address of an object in a variable — the reference does it for us. Variables are assigned with references that store object addresses. So, reference type variables store the reference instead of the value, which is copied and passed to the method. That's why when passing a reference type we have access to the object and can change its state: we have a copy of the reference storing the object's physical address.
Let's move on to practice. First, we will create a class with a single field and the necessary class members.

Further in the theory section and in practical tasks, we will use the ProgrammingLanguage class from the code below.

class ProgrammingLanguage {
    private int releaseYear;

    public ProgrammingLanguage(int releaseYear) {
        this.releaseYear= releaseYear;
    }

    public int getReleaseYear() {
        return releaseYear;
    }

    public void setReleaseYear(int releaseYear) {
        this.releaseYear = releaseYear;
    }
}

We will set the initial value for the releaseYear field and change it later:

public class PassArgumentsDemo {
    public static void main(String[] args) {
        ProgrammingLanguage java = new ProgrammingLanguage(1995);
        System.out.println(java.getReleaseYear()); // 1995

        // language = copy of java reference to the object
        updateReleaseYear(java, 2011);

        System.out.println(java.getReleaseYear()); // 2011
    }

    public static void updateReleaseYear(ProgrammingLanguage language, int year) {
        language.setReleaseYear(year);
    }
}

In this example, we can see the behavior described above: we passed a ProgrammingLanguage type variable to the method, the language variable received a copy of the java variable object reference and was able to change the instance variable releaseYear value.

String and all wrapper classes are reference types but passing them as an argument will not change the original value since they are all immutable classes.

In such examples, it is important to take one detail into account. When we passed a ProgrammingLanguage type object, we were able to change the value of its field. But what if we passed this field to the method separately?

public class PassArgumentsDemo {
    public static void main(String[] args) {
        ProgrammingLanguage java = new ProgrammingLanguage(1995);
        System.out.println(java.getReleaseYear()); // 1995

        updateReleaseYear(java.getReleaseYear(), 2011);

        System.out.println(java.getReleaseYear()); // 1995
    }

    public static void updateReleaseYear(int actualReleaseYear, int year) {
        System.out.println(actualReleaseYear); // 1995

        actualReleaseYear = year;

        System.out.println(actualReleaseYear); // 2011
    }
}

Of course, releaseYear is an instance variable, but we do not pass it through the object inside which it is located. Instead, we pass it directly as a primitive type variable, so it is passed using the pass-by-value mechanism like a primitive type.
Keep in mind that you can not change or remove the physical address stored by the reference. Look at this code sample:

public class PassArgumentsDemo {
    public static void main(String[] args) {
        ProgrammingLanguage java = new ProgrammingLanguage(1995);
        System.out.println(java.getReleaseYear()); // 1995

        referenceDemo(java, 2011);

        System.out.println(java.getReleaseYear()); // 1995
    }

    public static void referenceDemo(ProgrammingLanguage language, int year) {
        System.out.println(language.getReleaseYear()); // 1995

        language = new ProgrammingLanguage(year);

        System.out.println(language.getReleaseYear()); // 2011
    }
}

Inside the method main, we created an object whose physical address is stored in a reference (let's call it ref#1), and assigned it to the java variable. Inside the method referenceDemo, we created a new object whose physical address is stored by another reference accordingly named ref#2. We did not change the address ref#1 stores: it is impossible. So, after creating the new object, inside that method, the language variable has nothing in common with the java variable.

Conclusion

In this topic, we discussed a very important mechanism each developer should know. Note that in Java we only use pass-by-value, though we have a mechanism providing us with the pass-by-reference effect for reference types. Practice using the samples we provided, make some changes and run your code — it will help you master the topic.
If you are curious about pointers, we encourage you to do some research. We recommend you search for info on the difference between pointers and references in C++ and about the pass-by-pointer approach.

22 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo