Saturday, 30 May 2015

Java Constructors

It is very common requirement to initialize an object immediately after creation. We can define instance methods for this purpose but they have to be invoked explicitly. Java has a solution for this requirement. Java allows objects to initialize themselves when they are created using constructors.

The syntax of the constructors is very similar to that of instance methods. They have the same name as the class and do not have any return type. This is because the implicit return type of a class’s constructor is the class itself. Constructors can be overloaded just like methods.

When operator new is used to create an instance/object of a class, JVM allocates memory for the object, then initializes the instance variables to their default initial values, and then calls the appropriate constructor to initialize the instance variables.

We have not explicitly used constructors so far. But every class has a default constructor that does not take any argument and its body does not have any statements. The compiler generates the default constructor automatically. The compilers stops generating default constructor as soon as you add your own constructor.

Note: The name constructor is a bit confusing. It appears as if the purpose of the constructor is to create an object/instance. The object is created and instance variables and static variables are initialized to their default initial values before constructor is called. So the purpose of the constructor is to initialize the instance variables after the object has been created.

1. Default Constructor

Example: The following program does not define any constructor so it uses the default constructor, which does not take any parameters.

1 class Box

2 { float width;

3 float height;

4 float depth;

5 public static void main(String args[])

6 { Box b = new Box();

7 float volume = b.width * b.height * b.depth;

8 System.out.println("Volume = " + volume);

9 }

10 }

The compiler would automatically generate the following default constructor while compiling the above program:

Box()

{

}

The output of the above program will be 0 (Zero) as constructor is not doing any initialization and default initial value of float type instance variables is 0 (Zero).

2. No Argument Constructor

You can replace the default constructor with your own no argument constructor. This will allow you to initialize the instance variables to any value.

Example: The following program defines a no argument constructor, which is called immediately after creation of object.

1 class Box

2 { private double width, height, depth; //data hiding

3 double volume()

4 { double vol = width * height * depth;

5 return vol;

6 }

7 Box() // No argument constructor

8 { System.out.println("Initializing Box");

9 width = 10;

10 height = 10;

11 depth = 10;

12 }

13 }

1 class BoxDemo7

2 { public static void main(String args[])

3  { Box b1 = new Box();

4 Box b2 = new Box();

5 double volume = b1.volume();

6 System.out.println(volume);

7 volume = b2.volume();

8 System.out.println(volume);

9 }

10 }

The user-defined no argument constructor would be called immediately after the object creation and would initialize the values of the instance variables width, height and depth to 10. Thus the output of the above program would be 1000 instead of 0 (Zero).

3. Parameterized Constructors

The no argument constructor defined in the previous example is not of much use as it always initializes with the same value. A constructor, which can take parameters, will be more useful.

this keyword:

Sometimes a method will need to refer to the object that invoked it. To allow this, Java defines this keyword. It can be used inside any method to refer to the current object. That is, this is always a reference to the object on which the method was invoked. You can use this anywhere a reference to an object of the current class’s type is permitted. To better understand what this refers to, consider the following example.

Instance Variable Hiding

As you know, it is illegal in Java to declare two local variables with the same name inside the same or enclosing scopes. Interesting, you can have local variables, including formal parameter to methods, which overlap with the name of the class’s instance variables. However, when a local variable has the same as an instance variable, the local variable hides the instance variable.

This is why width, height, and depth were not used as the names of the parameters to the Box() constructor inside the box class. If they had been, then width would have referred to the formal parameter, hiding the instance variable width.

While it is usually easier to simply use different names, there is another way around this situation. Because this lets you refer directly to the object, you can use it to resolve any name space collisions that might occur between instance variables and local variables.

Example: The following program defines a parameterized constructor, which is used for initializing the object.

1  class Box

2  { private double width, height, depth; //data hiding

3 double volume()

4 { double vol = width * height * depth;

5 return vol;

6 }

7 Box(double w, double h, double d) //Parameterized constructor

8 { System.out.println("Initializing Box");

9 width = w; height = h; depth = d;

10 }

11 /* Box(double width,  double height, double depth)  //Parameterized constructor

12 { System.out.println("Initializing Box");

13 this.width = width;

14 this.height = height;

15 this.depth = depth;

16 }*/

17  }

1   class BoxDemo8

2  { public static void main(String args[])

3   { Box b1 = new Box(10,20,15);

4 Box b2 = new Box(3,6,9);

5 double vol;

6 double volume = b1.volume(); System.out.println(volume);

7 volume = b2.volume(); System.out.println(volume);

8 }

9  }

The parameterized constructor allows you to initialize the box with any dimensions.

4. Overloading Constructors

It is possible to overload the constructor just like methods. For example, the constructor of class Box can be overloaded to initialize different types of boxes. If we have a cube then we need to pass just one argument as all the sides of the cube would be of same dimension.

Example: The following program makes use of overloaded constructor to create and initialize the different type of boxes.

1  class Box

2  { private double width, height, depth; //data hiding

3 double volume()

4 { double vol = width * height * depth;

5 return vol;

6 }

7 Box(double w, double h, double d)

8 { width = w; height = h; depth = d;

9 }

10 Box()

11 { width = -1; height = -1; depth = -1;

12 }

13 Box(double len)

14 { width = height = depth = len;

15 }

16 Box(Box ob)  // Copy Constructor

17 { width = ob.width; height = ob.height; depth = ob.depth;

18 }

19  }

1 class BoxDemo9

2 { public static void main(String args[])

3  { Box b1 = new Box(10,20,15);

4 Box b2 = new Box();

5 Box b3 = new Box(5);

6 Box b4 = new Box(b1);

7 double vol;

8 vol = b1.volume();

9 System.out.println(vol);

10 vol = b2.volume();

11 System.out.println(vol);

12 vol = b3.volume();

13 System.out.println(vol);

14 vol = b4.volume();

15 System.out.println(vol);

16 }

17 }

Java Method Overloading

We can have more than one method with the same name as long as they differ either in number of parameters or type of parameters. This is called method overloading as discussed earlier.

While calling an overloaded method it is possible that type of the actual parameters passed may not match exactly with the formal parameters of any of the overloaded methods. In that case parameters are promoted to next higher type till a match is found. If no match is found even after promoting the parameters then a compilation error occurs.

Example: The following program overloads the method area() to find the area of a circle, square, rectangle and  a triangle.

1  class Area

2  { static final double PI = 3.1415;

3  static double area(double radius) //area of circle

4 { return PI * radius * radius;

5 }

6 static float area(float size) // area of square

7 { return size * size;

8 }

9 static float area(float length, float width) // area of rectangle

10 { return length * width;

11 }

12 static float area(float a, float b, float c) // area of triangle

13 { float s = (a+b+c)/2;

14 float  area = (float) Math.sqrt(s * (s-a) * (s-b) * (s-c));

15 return area;

16 }

17 public static void main(String a[])

18 { double carea = area(3.0); //Circle’s area() method is invoked

19 System.out.println(carea);

20 float sarea = area(3.0f); //Square’s area() method is invoked

21 System.out.println(sarea);

22 float rarea = area(3.0f,4.0f); //Rectangle’s area() method is invoked

23 System.out.println(rarea);

24 float tarea = area(3.0f,4.0f,5.0f); //Triangle’s area() method is invoked

25 System.out.println(tarea);

26 // next higher type matching with the parameter is float

27 float area = area(3);  //Squares’s area() method is invoked

28 System.out.println(area);

29 area = area(3,4);  //Rectangle’s area() method is invoked

30 System.out.println(area);

31 area = area(3,4,5);  //Triangles’s area() method is invoked

32 System.out.println(area);

33 }

34 }

Java Methods

Java methods are equivalent to C++ functions. Objects can communicate with each other using methods. Java’s methods can be classified in the two categories:


  • Instance Methods
  • Static Methods 


Just like static variables and instance variables, there are static methods and instance methods.

1. Instance Methods

The general form of an instance method is:

<return-type> <method-name> (parameter-list)

{

<method-body>

}

Method definition has four parts:


  • Name
  • Return Type
  • List of Parameters
  • Method Body

The return type can be a primitive data type or reference data type. In case, the method returns a 
reference, the return type will be the name of the class to which the object referred to by the returned reference belongs. It is must to declare the return type even if the method does not return any value. In this case, the return type should be declared as void.

The method’s formal parameters/arguments are specified within the parenthesis after the method name. The syntax is same as in C/C++.

The method body may contain any valid Java statement. If the method has a return type, then a value must be returned through return statement.

Invoking/Calling Instance Methods

The dot (.) operator is used to invoke/call the instance methods. The general form for calling the 

instance methods using the dot operator is: 

<object-reference>.<method-name>(paremeter-list)

Where <object-reference> is some reference variable pointing to an object and <method-  name> is the name of an instance method. For example, after creating an object of type Box and storing its reference in the reference variable b1, we can call the instance method  volume() using dot operator as follows: 

Box b1 = new Box();

b1.volume();

Instance methods can access the instance variables as well as static variables.

Example: The following program demonstrates how to call a instance method.

1 class Box 

2 { double width; //instance variables

3 double height;

4 double depth;

5 void volume() //instance method

6 { System.out.println("volume is:" + width * height * depth); 

7 }

8 }

1 class BoxDemo_4

2 { public static void main(String args[])

3   { Box b = new Box();

4 b.width = 10; 

5 b.height = 20;

6 b.depth = 15;

7 b.volume();

8 }

9 }

In the main() method an instance of the class Box is created, its instance variables are initialized and then the instanced method is called. The instance method volume() calculates and displays the volume of the Box.

While accessing an instance variable inside main() method, we have used object reference. It is required, as we can not access an instance variable directly inside a static method. However, when an instance variable is accessed by a method that is defined in the same class as the instance variable, the instance variable can be referred directly. 

In fact we cannot specify the object reference in the method volume(), in the above example. This is due to the fact that unlike instance variables, there is only one copy of the instance methods. The instance methods use the copy of the instance variables of the object through which they are called. So the same method called through different object references will use different copy of the instance variables and hence the name instance method.

Implicitly an instance variable referred directly in a method uses this reference. For example, in the above program the direct reference to variables width, height and length is equivalent to this.width, this.height and this.length. The reference this inside a method refers to the current object (i.e. the object on which the method is called). The reference this is replaced by the reference of the invoking object at run-time. Hence the same instance method invoked through different objects will use different set of instance variables and hence will normally give different results.

Example: The following program demonstrates that calling non-static/instance method on different objects results in different output. 

1 class Box 

2 { double width; //instance variables

3 double height;

4 double depth;

5 void volume() //instance method

6 { System.out.println("volume is "+width*height*depth); 

7 }

8 }

1 class BoxDemo4

2 { public static void main(String args[])

3   { Box b1 = new Box();

4 Box b2 = new Box();

5 b1.width = 10; b1.height = 20; b1.depth = 15;

6 b2.width = 3; b2.height = 6;  b2.depth = 9;
7 b1.volume();

8 b2.volume();

9   }

10 }

The first call to method volume() displays the volume of the box b1 correctly as 6000 and the 
second call to method volume() displays the volume of the box b2 as 162.

Method Returning a value

Example: The following program demonstrates use of method, which returns a value i.e. its 
return type, is not void.

1 class Box 

2 { double width;

3 double height;

4 double depth;

5 double volume()

6 { return width * height * depth; 

7 }

8 }

1 class BoxDemo5

2 { public static void main(String args[])

3   { Box b1 = new Box();

4 Box b2 = new Box();

5 double vol;

6 b1.width = 10;

7 b1.height = 20;

8 b1.depth = 15;

9

10 b2.width = 3;

11 b2.height = 6;

12 b2.depth = 9;

13 vol = b1.volume();

14 System.out.println("Volume is : " + vol);

15 vol = b2.volume();

16 System.out.println("Volume is : " + vol);

17 }

18 }

Passing Arguments to Methods.

In the previous examples using class BoxDemo5, we initialized the instance variables directly to 
keep the things simple. This should not be done as it defeats the purpose of data encapsulation.

Moreover the instance variables can be made private so that they cannot be accessed from other 
classes. In that case it would not be possible to access the instance variables directly. The only 
alternative in that case is to define a method that takes width, height, and length as arguments 
and then initializes the instance variables.

Example: The following program defines two classes: Box and BoxDemo6. The class Box has a 
method that takes arguments. The classes may be defined in same source file or in two different 
source files. But in both the cases you will get two classes after compilation: Box.class and 

BoxDemo6.class. You can execute only class BoxDemo6.class as only this class has main() 
method. It also makes use of class Box for creating an instance/object and calling methods.

1. class Box

2. { private double width, height, depth; //data hiding

3. double volume()

4. { double vol = width * height * depth;

5. return vol;

6. }

7. void setDimensions(double w, double h, double d)

8. { width = w;

9. height = h;

10. depth = d; 

11. }

12. /* Instance Variable Hiding 

13. void setDimensions(double width,double height,double depth)

14. { width = width;

15. height = height;

16. depth = depth; 

17. }

18. */

19. /* Instance Variable Hiding 

20. void setDimensions(double width,double height,double depth)

21. { this.width = width;

22. this.height = height;

23. this.depth = depth; 

24. }

25. */

26. }

1 class BoxDemo6

2 { public static void main(String args[])

3    { Box b1 = new Box();

4 Box b2 = new Box();

5 b1.setDimensions(10, 20, 30);

6 double volume = b1.volume();

7 System.out.println(volume);

8 b2.setDimensions(5, 10, 15);

9 volume = b2.volume();

10 System.out.println(volume);

11 }

12 }

You cannot access the instance variables of class Box though object reference in the class 
BoxDemo6 as instance variables are declared private. The private instance variables can be 
accessed only in the class in which they are declared.

Nesting of Instance Methods

One instance method can invoke the other instance method of the same class without qualifying 
i.e. by just specifying the method name. For example, in the following program, calCost() 
method, which is an instance method, can call the instance method area() of the same class 
simply by its name.

1 class Circle

2 { static final float  PI = 3.1415f;

3 float puCost; // say cost of painting unit area

4 float radius;

5 float area()

6 { return PI * radius * radius;

7 }

8 float calCost()

9 {

10 float cost = puCost * area();

11 return cost;

12 }

13 public static void main(String args[])

14 { Circle circle = new Circle();

15 circle.puCost = 100;

16 circle.radius = 3;

17 float area = circle.calCost();

18 System.out.println(area); 

19 }

20 }

2. static Methods 

The static methods can be accessed like static variables through the class name without creating 
any instance/object. The static methods can also be called using the object reference. The static 
methods can directly access only static variables irrespective of the fact that they are invoked 
through class name or object reference.

The general form of a static method is:

static <return-type> <method-name> (parameter-list)


<method-body>


The general form is same as that of a instance method with the only difference that the modifier 
static is used before the return type.

Invoking/Calling static Methods

Example: The following program demonstrates the use of static method. The method area() 
takes one argument representing the radius of the circle and calculates its area. The method area() does not use any instance variable so it is declared as static and can be invoked through class name without creating any instance/object.

1 class Circle

2 { static final float  PI = 3.1415f;

3 static float area(float radius)

4 { return PI * radius * radius;

5 }

6 public static void main(String args[])

7 { int r = 4;

8 float area = Circle.area(r); 

9 //float area = area(r); //Nesting of static methods 

10 System.out.println("Area: "+area); 

11 }

12 }

Here the static variable PI is declared to be constant by putting modifier before the type. This is 
equivalent to const of C/C++.

Nesting of Class Methods

One class method can invoke the other class method of the same class without qualifying i.e. by 
just specifying the method name. For example, in the above program main() method, which is 
static can call the static method area() of the same class simply by its name: 

float area = area(r);

This nesting can go to any depth.

Java Variables

Java has three kinds of variables:


  • Local variables
  • Instance variables
  • Static Variables


We have already discussed about local variables. Local variables are used inside blocks or methods. The other two types of variables (also referred to as data members) are defined within the class but outside any method/block.

1. Instance Variables

A class provides the data encapsulation by declaring the variables within the class definition. For example, in the class Box defined above, the variables width, height and length are instance variables. Instance variables are declared in the same way as local variables except that they are declared outside the methods.

An instance variable occupies the memory on the per-object basis. For example, if you create two objects of type Box using operator new then both the objects will have different set of variables corresponding to instance variables width, height and length.

Initialization

The instance variables are initialized as soon as the object is created i.e. as soon as the memory is allocated with the new operator. The variables are initialized according to their types as shown in
the following tables.

For example, as soon as an object of type Box is created, the instance variables width, height and

length are initialized to 0 (Zero).

Accessing Instance Variables

The dot (.) operator is used to access the instance variables. The general form for accessing the
instance variables using the dot operator is:

<Object Reference>.<Variable Name>

Where <Object Reference> is some reference variable pointing to an object and <Variable
Name> is the name of an instance variable. For example, after creating an object of type Box and
storing its reference in the reference variable box, we can access the instance variables using dot
operator as follows:

box.width box.height box.length

Thus the way of accessing instance variables is very similar to the way in which we access the
members of structures in C or C++. But there is one significant difference. In C/C++, we can
access structure members directly as well as through pointers. But in Java program, we never
have access to an object directly. We always access an object through its reference. Thus the dot
(.) operator of Java is like -> operator of C/C++.

We can assign values to instance variables just like local variables as shown below:

box.width = 10; box.height = 20; box.length = 30;

We can use instance variables in expressions as shown below:

double vol =  box.width * box.height * box.length;

We can display the values of the instance variables, just like local variables as shown below:

System.out.println(box.height);

Example: The following program demonstrates the concepts discussed till now.

1 class Box

2 { double width;

3 double height;

4 double depth;

5 public static void main(String args[])

6   { Box b = new Box();

7 double vol;

8 b.width = 10;

9 b.height = 20;

10 b.depth = 15;

11 vol = b.width * b.height * b.depth;

12 System.out.println("volume is " + vol);

13 }

14 }

Example: The following program provides the same functionality as the previous one but it uses
two classes: Box and BoxDemo. Here it is assumed that both the classes are defined in different
files: Box.java and BoxDemo.java. It is also possible to define both the Java classes in one file.

Box.java

1 class Box

2 { double width;

3 double height;

4 double depth;

5 }

BoxDemo.java

1 class BoxDemo

2 { public static void main(String args[])

3    { Box b = new Box();

4 double vol;

5 b.width = 10;

6 b.height = 20;

7 b.depth = 15;

8 vol = b.width * b.height * b.depth;

9 System.out.println("volume is " + vol);

10  }

11 }

Compiling Box.java will generate Box.class file and compiling BoxDemo.java will generate
BoxDemo.class file. After compiling these two files you should run the BoxDemo class, because
it contains the main() method.
It is not necessary for both the Box and BoxDemo classes to be in the different source file. For
example, both the classes might be defined in the source file Box.java but compiling this file will
also generate two different classes: Box.class and BoxDemo.class.

Example: The pervious example is modified so that the classes Box.java and BoxDemo.java
belong to the package p1.

Box.java

1 package p1;

2 class Box

3 { double width;

4 double height;

5 double depth;

6 }

BoxDemo.java

1 package p1;

2 class BoxDemo

3 { public static void main(String args[])

4 { Box b = new Box();

5 double vol;

6 b.width = 10;

7 b.height = 20;

8 b.depth = 15;

9 vol = b.width * b.height * b.depth;

10 System.out.println("volume is " + vol);

11 }

12 }

To run the above program you should put both the classes in folder p1 in the working folder and
then give the following command from the working folder (parent of p1):

java p1.BoxDemo

The folder p1 will be created automatically (if it does not exist), in the working folder, if you
compile the above java files using the command:

javac  -d . Box.java

javac  -d . BoxDemo.java

Example: The pervious example is modified so that the classes Box.java and BoxDemo.java
belong to package p1 and p2 respectively.

Box.java

1 package p1;

2 public class Box

3 { public double width; //instance variables

4 public double height;

5 public double depth;

6 }

BoxDemo.java

1 package p2;

2 import p1.*;

3 class BoxDemo

4 { public static void main(String args[])

5    { Box b = new Box();

6 double vol;

7 b.width = 10; b.height = 20; b.depth = 15;

8 vol = b.width * b.height * b.depth;

9 System.out.println("volume is " + vol);

10   }

11 }

The folder p1 and p2 will be created automatically (if do not exist), in the working folder, if you
compile the above java files using the commands:

javac  -d . Box.java

javac  -d . BoxDemo.java

To run the above program you should give the following command from the working folder

(parent of p1 and p2):

java  p2.BoxDemo

Note that the class Box is declared public as it used in class BoxDemo that belongs to a different
package.  Similarly all the data members of the class Box are also declared public as they are
being accessed in class BoxDemo.

Also note that the class BoxDemo imports class Box so that it can use the class without
qualifying with the package name.

Example: Creating two instances of Box class
If you have two Box objects, each has its own copy of depth, width, and height. It is important to
understand that changes to the instance variables of one object have no effect on the instance
variables of another.

Box.java

1 class Box

2 { double width; //instance variables

3 double height;

4 double depth;

5 }

BoxDemo2.java

1 class BoxDemo2

2 { public static void main(String args[])

3  { Box b1 = new Box();

4 Box b2 = new Box();

5 double vol;

6 b1.width = 10;

7 b1.height = 20;

8 b1.depth = 15;

9 b2.width = 3;

10 b2.height = 6;

11 b2.depth = 9;

12 vol = b1.width * b1.height * b1.depth;

13 System.out.println("volume is " + vol);

14 vol = b2.width * b2.height * b2.depth;

15 System.out.println("volume is " + vol);

16    }

17 }

2. Static Variables

Static variables are also declared outside methods/blocks like instance variables. But the static
variables make use of the modifier static before the data type. For example, the variable width in
the previous example can be made static variable if declared as:

static double width;

The static variables are global to a class and all of its instances (objects). They are useful for
keeping track of global states. For example, a static variable count in a class can store the
number of instances/objects of the class created in the program at any instance of time. The
objects can communicate using static variables just like C functions communicate through global
variables.

For static variables there is only one copy of the variable irrespective of number of instances/objects created in the program, which is shared across all the instances.

The dot (.) operator is used to access the static variables also. The general form for accessing the static variables using the dot operator is:

<Class Name>.<Variable Name>

Where <Class Name> refers to the name of the class in which the static variable is declared and
<Variable Name> is the name of a static variable. For example, if we declare the variables width,
height and depth as static then we can access them using dot operator as follows:

Box.width

Box.height

Box.length

We can also access the static variables through object reference and dot operator as follows but any reference will point to the same copy:

box.width

box.height

box.length

Example: The following example illustrates that for static variables there is only one copy of the
variable irrespective of number of instances/objects created in the program, which is shared across all the instances.

Initialization

The static variables are initialized as soon as the class is loaded/used. The variables are initialized with default values according to their types. The default values are same as those for instance variables.

Box.java

1 class Box

2 { static double width;

3 static double height;

4 static double depth;

5 }

BoxDemo.java

1 class BoxDemo3

2 { public static void main(String args[])

3    { Box b1 = new Box();

4 Box b2 = new Box();

5 double vol;

6 b1.width = 10;

7 b1.height = 20;

8 b1.depth = 15;

9

10 b2.width = 3;

11 b2.height = 6;

12 b2.depth = 9;

13 vol = b1.width * b1.height * b1.depth;

14 System.out.println("volume is " + vol);

15 vol = b2.width * b2.height * b2.depth;

16 System.out.println("volume is " + vol);

17  }

18 }

Java assigning object reference variables

The following example explains what happens when we assign one object reference to another object reference.

Example: 

Box b1 = new Box();

Box b2 = b1;

You might think that b2 is being assigned a reference to a copy of the object referred to by b1. That is, you might think that b1 and b2 refer to separate and distinct objects. However, this would be wrong.

Instead, after this fragment executes, b1 and b2 will both refer to the same object. The assignment of b1 to b2 did not allocate any memory or copy any part of the original object. It simply makes b2 refer to the same object, as does b1. Thus, any changes made to the object through b2 will effect the object to which b1 is referring, since they are the same object.

Although b1 and b2 both refer to the same object, they are not linked in any other way. For example, a subsequent assignment of null to b1 will simply unhook b1 from the original object without affecting the object or b2:

Box b1 = new Box();

Box b2 = b1;

.

b1 =null;

Here, b1 has been set to null, but b2 still points to the original object.

Note: When you assign one object reference variable to another object reference variable, you are not creating a copy of the object, you are only making a copy of the reference.