Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Java - Pass By Value
#1
This was written quite awhile ago on another site, which is why it may look familiar to some. Note that I am in fact the original author.



I feel like this is a common issue among people who are just learning the language, and took me awhile to fully grasp the underlying concept. If you want to even begin understanding this you're going to need to know about two areas of memory, the stack and the heap. To start off, let's have a quick look at what happens in memory when we define variables in Java.

When we start our Java program, the system automatically allocates a chunk of RAM (if you don't know what this is just think of it as memory) for the JVM to use. Two categories contained inside of that memory are called the stack and the heap. You should be familiar with them if you're an experienced Java developer, but if not here's a quick definition of both (and a nice little picture to go along with it):
  • Stack - Used for storing variables/commands
  • Heap - Area of memory which holds data about reference type objects

[Image: ccc8a541aff7fe0363b2f78a0e3d8780.png]

Now were going to create a basic Java class with a main method, followed by a simple primitive variable declaration:

Code:
public class Learning {

   public static void main(String... args) {
       int exampleInt = 5;
   }

}

What happened to the Stack/Heap? As the stack holds the variable values, we are going to push (I will be using common stack terminology in this tutorial so if you're unfamiliar with it a quick Google search will do you wonders) the initialized variable exampleInt onto the stack (the JVM will allocate the appropriate memory to hold the variable value) with the value of 5:

[Image: 92079dfec17d53fb9dc7187a0f651c13.png]

Easy enough right? Now to give an example of what happens when I create an object, let's first make a simple class called Person with a field called name:

Code:
public class Person {

   private String name;

   public Person(String name) {
       this.name = name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public String getName() {
       return name;
   }

}

Create a simple Person object with the name of Bob, and the JVM will create the necessary memory location for our new Person object:

Code:
public class Learning {

   public static void main(String... args) {
       int exampleInt = 5;
       Person bob = new Person("Bob");
   }

}

[Image: 5cd55edeb8d799ce8780737a20e08f56.png]

Wait, didn't you just say variables were initialized and placed on the stack? Yes they are, however when it comes to reference types the only thing created on the stack is a variable with a pointer to the object that is created on the heap (in this case if you can read it it's 709F) with a bunch of meta data among other things were not going to worry about right now. Now that we finished covering the absolute basics of what's going on under the hood when we make variables, let's take a look at how this applies to passing variables to methods.

Let's create a method which takes our exampleInt and attempts to change the value:

Code:
public class Learning {

   public static void main(String... args) {
       int exampleInt = 5;
       attemptToChange(exampleInt);
       System.out.println(exampleInt);
   }

   public static void attemptToChange(int testInt) {
       testInt = 10;
       System.out.println(testInt);
   }

}

Now before you look at my commented code try to think about what's going to happen. Remember, Java is pass by value, so what do you expect is going to happen to this int?

Code:
public class Learning {

   public static void main(String... args) {
       int exampleInt = 5; //variable with the name of exampleInt is pushed on the stack
       attemptToChange(exampleInt); //invoking the method on the int
       System.out.println(exampleInt); //prints 5
   }

   public static void attemptToChange(int testInt) {
       testInt = 10;
       System.out.println(testInt); //prints 10
   }

}

When we passed our exampleInt to the #attemptToChange(int testInt) method, we passed the actual value of the variable and created a whole new variable called testInt which now holds the exact same value exampleInt did (essentially a complete copy)! That's why when we printed our the value of exampleInt after we invoked the method on it, it still contained the same value. Just to reiterate that one more time:
  1. We created an int variable called exampleInt
  2. We passed it to a method which took the value and placed a copy in another int called testInt
  3. The value of testInt was printed inside of the method which showed 10
  4. exampleInt was printed right after the method call inside of the main method and printed 5
Great, now we understand what happens to primitives when their passed to methods. Regarding reference types, let's look at the same general code we've been working with and think about what's going to happen to the Person object bob:

Code:
public class Learning {

   public static void main(String... args) {
       Person bob = new Person("Bob");
       attemptToChange(bob);
       System.out.println(bob);
   }

   public static void attemptToChange(Person person) {
       person.setName("Jake");
       System.out.println(person);
   }

}

Think carefully! When you think you know the outcome look below at my comments:

Code:
public class Learning {

   public static void main(String... args) {
       Person bob = new Person("Bob"); //creates a new instance on the stack with a reference to the newly created object on the heap
       attemptToChange(bob); //pass the person object to our method
       System.out.println(bob); //prints Jake
   }

   public static void attemptToChange(Person person) {
       person.setName("Jake"); //attempt to set the name to Jake
       System.out.println(person); //prints Jake
   }

}

Let's see what happened here if you don't understand:
  1. Created a new Person object called bob with the name field set to Bob
  2. Passed bob to our method
  3. Our new object person now contains the value passed to it (a pointer to the object on the heap)
  4. We call the #setName(String name) on person
  5. This call changes the value which is points to, AKA the original bob object we set
  6. We print out the name for person
  7. The name for bob is printed, which we can see has changed!

Sorry for the abrupt ending, but hopefully this was a semi decent tutorial for people learning Java and possibly even advanced programmers alike.
Reply
#2
Stickied this, extremely informative about memory handling, good job on this.
Reply
 


Forum Jump:


Users browsing this thread: 1 Guest(s)

About The Bytecode Club

We're a community focused on Reverse Engineering, we try to target Java/Android but we also include other langauges/platforms. We pride ourselves in supporting and free and open sourced applications.

Website