Sunteți pe pagina 1din 18

In order to have a deep understanding of the Object Oriented Programming in Java or any

other OOP language (like C#) you must know how things are managed internally by the Java
process and by the JVM. Of course Java syntax and Java implementations of OOP principles
are important but you will have a more clear image about the application resources,
memory, performance, argument passing, threads and garbage collection if you put
questions beyond How I do that ? or How I write that ?. The real questions should be How or
Why it is happening like this ? (of course, at some point you should stop and move forward).
In this post I will describe how the application variables are managed, regarding where they
are stored (Stack or Heap) and for how long.
Other topics that are part of this Java tutorial are accessible through Java 6 Tutorial
Contents

What is the Stack and the Heap


Keeping things simpler (if you know some assembler background you will see that this is a
superficial approach) your application is processing data by storing it into memory areas in
Random Access Memory. These areas are called:
Stack:

a memory space reserved for your process by the OS;

the stack size is fixed and it is determined in the compiler phase based on variables
declaration and other compiler options;

it is important to establish that the stack is limited and its size is fixed (one the
process has started, it cant change the stack size);

most of the time, the stack it is used to store functions/methods variables (input
arguments and local variables).

each method has its own stack (a zone in the process stack), including main, which
is also a function.

a method stack exists only during the lifetime of that method: from the calling
moment until the return moment;
Heap:

a memory space managed by the OS and used by processes to get additional space
at run-time;

this area it is a global, meaning that any process can use it (of course, processes
cant read or write in another process Heap reserved area);

the role of this memory is to provide additional memory resources to processes that
need that supplementary space at run-time (for example, you have a simple Java
application that is constructing an array with values from console);

the space needed at run-time by a process is determined by functions like new


(remember, it the same function used to create objects in Java) which are used to get
additional space in Heap.

What is stored in Stack and in Heap


Based on previous rules, lets analyze this simple Java application:
class Student{
int age;

//instance variable

String name;

//instance variable

public Student()
{
this.age = 0;
name = "Anonymous";
}
public Student(int Age, String Name)
{
this.age = Age;
setName(Name);
}
public void setName(String Name)
{
this.name = Name;
}
}
public class Main{
public static void main(String[] args) {
Student s;
//local variable - reference
s = new Student(23,"Jonh");
int noStudents = 1;
//local variable
}
}

In order to determine the minimum space (because we focus only on the important elements
and we keep things as simple as possible) lets analyze the Stack requirements. In the next
paragraph I describe each method stack separately but in the end all these are placed on
the same stack, the application stack.
We start with the main method because for a Java process everything begins and ends
with main. The main local variables, or variables stored on its Stack are:

the args reference (it is an array of Strings);

the Student reference, named s;

the integer (4 bytes) value, named noStudents;


The default constructor local variables are:

the current created object reference, named this;


thats all (remember: the object and its values are stored in Heap).

The second constructor, the one with arguments, local variables:

the reference of the current created object, named this;

the input argument, Age, an integer value

the input argument, Name, a String reference


The setName method local variables are:

the calling object reference, named this; (remember: each non-static class method
it is called by an object and that object reference is passed to the method as this)

the input argument, Name, a String reference


As I said earlier, all these methods stacks are in fact, parts of a single applications stack.
Each part exists during the function lifetime. If you analyze the order in which these
methods are called you can define a bigger picture the process call stack.

For the previous example, when the constructor with arguments it is executed, the call stack
looks like this:

Call Stack Example


As you can see, from the previous image, the call stack is generated by all the active
methods. Because the main method is the process entry point, it is the first method on the
call stack. After this moment, each time a method is called, it will be placed on the call
stack.
For the previous example, the call stack has the maximum size when it is called
the setNamemethod from the Student constructor.
In Heap there are stored all the values that are created using new operator, meaning mostly
object values. So, for the previous example, the references are stored on the method stack
and the objects are stored in Heap:

Stack & Heap values for the Call Stack example


In the previous image, there are described the methods local variables, which are stored on
their stack. Also, you can see that objects values (the Student and the String) are stored in
Heap.
The Heap values are created by:

the Student memory area in Heap is created by calling the new operator and the
class constructor;
the String value is created during the object initialization inside the class constructor.

What is the lifetime of variables from Stack and Heap


The general rule regarding the lifetime of variables is that they exist at least for the time you
need them.
For stack variables, because they are on the method stack, they exist as long as the method
is executed. Because main method is a special method (the process starts and ends with
main) its local variables exist for the entire execution of the process. For other methods,
their stack exists only from the moment we call the method until it ends (with a return or
because of an exception).
The values in Heap (object values) exist as long as we have a reference that has the address
of that memory area in Heap. If the Heap memory area cant be reached through a reference
(the reference doest exist or it has another value/address), the Garbage Collector will
release that space. (more on Garbage Collector in next tutorial).
Other types of variables, like the class static attributes, are managed in the same way,with
the difference that they are stored on the process stack, before using that stack for methods
stacks.

How to define a class in Java


The syntax used in Java 6 to define a class is:
[access_modifier] class class_name [extends base_class]
[implements interface1, interface2, ]
{//class code block start
//attributes
//methods
//initialization code blocks
//other classes
}//end of class code block
where:
access modifier describes the access rights for the class from a Java program; this
attribute is optional, with a default value equal to private; although there is another post
dedicated just to understand the access modifiers, now we keep in mind the possible values:

public in terms of security, the class can be used anywhere in Java;

protected
private

class the keyword that defines this structure in Java;


class name class name defined by the programmer; class names must follow the same
naming conventions as well as any variable (see the post about Java variables and naming
conventions)
extends allows derivation from a base class (more in the tutorial about the
derivation/inheritance)
implements allows the derivation from one or more interfaces (more in the tutorial about
interfaces)
The simplest syntax required to define a class is:
class nume_clasa
{//class code block start
//attributes
//methods
//initialization code blocks
//other classes
}//end of class code block
Regarding attributes we can define in a class:

instance variables or attributes of objects;


static variable a type of global variables.

Regarding methods (functions) we can define in a class:

constructor methods;
access functions(get and set);
processing methods;

Regarding the declarations of attributes and methods there are no rules on how they should
be defined, but code management is easier if the code and attributes are defined grouped.

There are some rules regarding how to define a class:


1.
2.

in a Java file, .java , can be defined several classes;


in a Java file, can be defined only one public class;

3.

the Java source file containing the public class has the same name as the public class
(case sensitive); Book.java contains public class Book ;
4.
the code block of a class is defined by { and };
5.
if in a source file, .java, are defined more classes, then by compiling the source file
there are obtained bytecode files, .class, for each class.
Based on the simple syntax, we define a simple Java class that describes a book. The book is
described by its attributes price, title and author , and the behavior is defined by its
methodsgetPret() and display().
class Book{
//define attributes - instance variables
float price;
String title;
String author;
//define methods
public float getPrice(){
return price;
}
public String display(){
return "The book "+titlu+" has as author "+author;
}
}

How to construct objects


If the class represents something abstract, then objects represent something real. Objects
are instances of the class because the story behind the construction of an object class
becomes something real: a space memory in Heap, in which the instance attributes have
values.
Objects are constructed by the new operator that will call the class constructor (with or
without parameters).
To test the class Book defined earlier, we use a new public class, TestBook (Java file is also
called TestBook.java):
public class TestBook {
public static void main(String[] args) {
//define references
Book book1;
//are valoarea null
Book book2 = null;
//create objects
book1 = new Book();
book2 = new Book();
}
}

The object variable that represents a reference (pointer) manages an address from the
Heap.Through this address we have access to the memory area reserved for the object. In
this zone we find its attributes values.

By defining a simple class variable, you get a reference with the default value equal to null.
To give a value to this reference you must built (instantiates) the object using new.

How to access object methods and attributes


The object has access to its attributes and methods (not static ones) through the . (point)
operator.
public class TestBook {
public static void main(String[] args) {
Book book1 = new Carte();
book1.price = 23;
book1.title = "Dune";
book1.author = "Frank Herbert";
System.out.println(book1.display());
Book book2 = new Carte();
book2.price = 35;
book2.title = "Harry Potter";
book2.author = "J.K. Rowling";
System.out.println(book2.display());
}
}

Access to object attributes and methods outside the class, as in the previous example in
which the object is used in TestBook class, can be controlled by access modifiers (public,
private, protected, that will be analyzed in detail in another post).

How are looking objects in memory


To understand the principles of OOP in Java it is important to note that in Java, all objects
(eg Booktype variables) are references and the objects values (given by its attribute values)
are in the Heap memory area.
The two objects of type Book, constructed in the above example, look in memory (notice this
is a simplified representation because String is also an object, which means it is also a
reference variable and its value it is in the Heap) like this:

Simplified representation of two objects in memory


Other topics that are part of this Java

How we define arrays in Java


The syntax for defining an array in Java:
base_type[] array_name;
base_type array_name[]; // style similar to C/C++
Important! By definition, array_name is an uninitialized variable of type reference (pointer)
with a default value equal to null. null is a Java keyword which indicates a nonexistent
address with value equal to 0.
//defining an array with the recommended syntax for Java
int[] intValues ;
//define array with vector-like sintax in C/C++
long longValues[];

How to initialize arrays in Java


Because they are objects, the array values are stored in HEAP (an area of RAM memory with
dynamic allocation in which processes can reserve space at run-time), an array is initialized
in 3 steps:
1.
2.
3.

Define the array;


Reserve space for it;
Initialize items value (optional, because during the memory allocation, the
elements get default values associated with the base type of the array for primitive
types check and for references the default value is null)

In this tutorial arrays are analyzed with primitive values, and after we see classes and
objects related concepts we will analyze arrays of objects.
//array definition with Java recommended syntax
int[] intValues;
//define array with vector-like sintax in C/C++
long longValues[];
//allocating space = array initialization
intValues = new int [10]; // 10 elements with value 0
longValues = new long [5];
//5 elements with value 0

Initialization of separate elements is done using operator []. ( WARNING! index used to
access the elements has values in [0, length-1])
intValues [0]
intValues [1]
intValues [2]

=
=
=

10;
20;
30;

//initialize first element


//initialization of the 2nd element
//initialization of the 3rd element

If we consider that the array variables (pointers or references) are allocated on main
function stack, and the array elements space is reserved in HEAP, then the data memory
image is:

Memory image of the array and its values

How to generate unreachable objects or memory leaks


In order to generate unreachable objects or memory leaks and to enjoy the benefits of
having a Garbage Collector you must loose or remove all the references for that object.
1. The simplest way is to null a reference:
public class Main{
public static void main(String[] args)
{
int[] intArray = null;
//request space in Heap
intArray = new int[10000];
//process your data
//...
//make the reference null

//the array values are becoming eligible for GC


intArray = null;
}
}

In this way the object becomes eligible for garbage collector because we dont have any
other reference used to reach the object.
2. Reassigning the reference we make the previous object unreachable:
public class Main{
public static void main(String[] args)
{
int[] intArray = null;
//request space in Heap
intArray = new int[10000];
//process your data
//...
//create a new object and use the same reference
//the previous array values are becoming eligible for GC
intArray = new int[99];
}
}

In this way the first object becomes eligible for garbage collector because we dont have any
other reference used to reach the object.
3. Isolating a reference it is a more hidden scenario because at a first glance you may say
that your object is still alive. If we consider the next class:
class Student{
int age;
int[] marks;

//instance variable
//instance variable

public Student()
{
this.age = 0;
marks = new int[10];
}
}

you can see that every Student object contains an array reference as an instance variable. If
we test the class like this
public class Main{
public static void main(String[] args)
{
Student s = null;
//request space in Heap
s = new Student();
//process your data
//...
//remove the reference
s = null;
}
}

it is clear that the Student object is going to be collected by the GC. But, what is going to
happen with the integers array inside the object. If we analyze the Heap, we can see that
the array object is still reachable through the marks reference.

Example of Isolated Reference


But what about reaching the marks reference ? Because, it is part of an unreachable object,
the reference is considered isolated and, in this case, the referenced object is marked for
garbage collecting.

How to define methods


The general syntax used to define methods is
[access_modifier] [non-access modifier] return_type method_name (type
parameter1, type parameter2, , type parameterN)
where (access modifiers and non-access ones will be described better in a separate topic):
access_modifier define the visibility of the method; possible values are:

default - when you dont write anything;


public - methods is visible in other classes;
private methods are not visible from other classes;
protected methods are visible only in subclasses.

non-access modifier define other attributed for the method like: static (the method is
defined at class level its not an object method), final (the method cant be overridden in
subclasses),abstract (the method cant be instantiated because contains abstract methods
virtual pure methods)

return_type any primitive or reference type; if the method doesnt return a value then the
return type is void; if you define a return type, then the method must end with a return
value/variablestatement, where the value/variable type is the defined return type:
public class Test {
public static void main(String[] args) {
int vb1 = 10;
int vb2 = 20;
//calling the doSum method
int sum = doSum(vb1, vb2);
System.out.println("The sum is "+sum);
}
public static int doSum(int a, int b)
{
int result = a + b;
return result;
}
}

method_name must respect the same naming convention used for variables; Java
recommends using camelCase naming convention (the first word starts with a small letter);
also, the names should be verb-noun pairs, like getName, doSum;
parameters represent the input values (primitive or references) for the method; regarding
parameters you must know this:

the method header variables are called parameters; when you call a method, the
input values are called arguments;
methods input parameters are local variables for the method;
you can define a variable number of input parameters var-arg methods;

One important rule regarding parameters is:

method arguments are always passed by their value; methods parameters


are always copies of the arguments (maybe the most important rule);
a primitive parameter represent a value copy of the calling argument;
a reference parameter represent a value copy (the reference value IS NOT
AN OBJECT, but the OBJECT ADDRESS) of the calling reference argument;

If you remember also rules regarding


Heap, then the next example

Tutorial Java #8 Understand Stack and

class MyValue
{
public int value;
}
public class Main {
public static void main(String[] args) {
int vb1 = 10;
int vb2 = 20;
//calling the doSum method
int sum1 = doSum(vb1, vb2);
System.out.println("The sum is "+sum1);

MyValue ref1
MyValue ref2
ref1.value =
ref2.value =

= new MyValue();
= new MyValue();
100;
200;

//calling the doObjectSum method


int sum2 = doObjectSum(ref1, ref2);
System.out.println("The objects sum is "+sum2);
}
//the method parameters are 2 integer values
public static int doSum(int a, int b)
{
int result = a + b;
return result;
}
//the method parameters are 2 references
public static int doObjectSum(MyValue v1, MyValue v2)
{
int result = v1.value + v2.value;
return result;
}
}

generates in memory a image like this:

Methods arguments are passed by their value


From the previous image, you can see that the methods arguments are values of the calling
variables.

How to pass parameters and return multiple values


Before we describe this concept, keep in mind one important Java rule: Method arguments
are always passed by their value; methods parameters are always copies of the
arguments.
There are 2 types of parameters: value type and object references. The difference between
them is a logical one because references are like numerical variables with an important
characteristic: their values represent addresses of objects stored in Heap.
In C++ things are different because we have arguments passed by their value and also by
their address (when you use pointers). Also, in C# you have arguments passed by their
values or by their pointers (unsafe) or using ref as input parameters attribute.
Because , in Java, method arguments are always passed by their values, it means that
method local variables have the same value as the calling arguments. This may represent a
problem when we want to write methods that return more than one value (for one value, you
can use the method return type).
For example, lets write a method used to interchange the values of 2 primitive variables. If
we write like this:
public class Main {
public static void main(String[] args) {
int vb1 = 10;
int vb2 = 20;
doInterchange(vb1, vb2);
System.out.println("vb1 = "+vb1+", vb2 = "+vb2);
}
public static void doInterchange(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
}

then the output is


vb1 = 10, vb2 = 20

Despite the doInterchange() method is implementing the correct algorithm, the output is
not the expected one because, based on the previous rule, the method is interchanging the
values of its own local variables a and b.

So we must write a better method, that will interchange the values of 2 main variables. Lets
try with references by boxing the 2 integers into their corresponding objects, like this:
public class Main {
public static void main(String[] args) {
int vb1 = 10;
int vb2 = 20;
doInterchange2(vb1, vb2); //the values are boxed by default
System.out.println("vb1 = "+vb1+", vb2 = "+vb2);
}
public static void doInterchange2(Integer ra, Integer rb)
{
int temp = ra;
//default unboxing
ra = rb;
rb = temp;
//boxing
}
}

but, surprise, the output is the same


vb1 = 10, vb2 = 20

Interchange with Integer references - doInterchange2


In this case, the reason is more subtle because the default boxing mechanism is creating a
new object with the value of the primitive variable. Also keep in mind that Integer objects
are immutable (see Tutorial Java SCJP #12 Immutable, String and Integer).
The correct solution for the interchange method is to use references of your own classes, like
this:
class MyValue
{
public int value;
public MyValue(int input)
{
value = input;
}
}
public class Main
public static
int vb1 =
int vb2 =

{
void main(String[] args) {
10;
20;

MyValue value1 = new MyValue(vb1);


MyValue value2 = new MyValue(vb2);
doInterchange3(value1, value2);
vb1 = value1.value;
vb2 = value2.value;
System.out.println("vb1 = "+vb1+", vb2 = "+vb2);
}

public static void doInterchange3(MyValue aValue, MyValue bValue)


{
int temp = aValue.value;
aValue.value = bValue.value;
bValue.value = temp;
}
}

The previous example generates the desired output


vb1 = 20, vb2 = 10

because the references from main stack and from doInterchange3 stack reference the same
objects in Heap:

Interchange with objects references - Interchange3


Because, in Java, method arguments are always passed by their values (also references) DO
NOTmake the mistake to think this will work:
public static void doInterchange4(MyValue aValue, MyValue bValue)
{
//interchange references VALUES and NOT objects values
MyValue temp = aValue;

aValue = bValue;
bValue = temp;
}

Remember, aValue reference value is the copy of the value1 reference value.
Another solution, is to use an array as a wrapper for the primitive values, like this:
public class Main
public static
int vb1 =
int vb2 =

{
void main(String[] args) {
10;
20;

int[] anArray = {vb1,vb2};


doInterchange5(anArray);
vb1 = anArray[0];
vb2 = anArray[1];
System.out.println("vb1 = "+vb1+", vb2 = "+vb2);
}
public static void doInterchange5(int[] values)
{
int temp = values[0];
values[0] = values[1];
values[1] = temp;
}
}

Regarding how to pass variables into methods in Java you must also read:

How to define methods with variable number of input parameters, var-arg


methods
What is shadowing a variable;

S-ar putea să vă placă și