3.1 Class Fundamentals: The General Form of A Class
3.1 Class Fundamentals: The General Form of A Class
3.1 Class Fundamentals: The General Form of A Class
MODULE 3
Syllabus:
Introducing Classes: Class Fundamentals, Declaring Objects, Assigning Object Reference Variables, Introducing
Methods, Constructors, The this Keyword, Garbage Collection, The finalize( ) Method, A Stack Class.
A Closer Look at Methods and Classes: Overloading Methods, Using Objects as Parameters, A Closer Look at
Argument Passing, Returning Objects, Recursion, Introducing Access Control, Understanding static, Introducing
final, Arrays Revisited.
Inheritance: Inheritance, Using super, Creating a Multilevel Hierarchy, When Constructors Are Called, Method
Overriding, Dynamic Method Dispatch, Using Abstract Classes, Using final with Inheritance, The Object Class.
Class is a basis of OOP languages. It is a logical construct which defines shape and nature of an object.
Entire Java is built upon classes.
class classname
{
type var1;
type var2;
…….
type method1(para_list)
{
//body of method1
}
type method2(para_list)
{
//body of method2
}
………..
}
Here, classname is any valid name given to the class. Variables declared within a class are called as
instance variables because every instance (or object) of a class contains its own copy of these
variables. The code is contained within methods. Methods and instance variables collectively called as
members of the class.
A Simple Class
Here we will consider a simple example for creation of class, creating objects and using members of the
class. One can store the following program in a single file called BoxDemo.java. (Or, two classes can be
saved in two different files with the names Box.java and BoxDemo.java.)
2
class Box
{
double w, h, d;
}
class BoxDemo
{
public static void main(String args[])
{
Box b1=new Box();
Box b2=new Box();
double vol;
b1.w=2;
b1.h=4;
b1.d=3;
b2.w=5;
b2.h=6;
b2.d=2;
vol=b1.w*b1.h*b1.d;
System.out.println("Volume of Box1 is " + vol);
vol=b2.w*b2.h*b2.d;
System.out.println("Volume of Box2 is " + vol);
}
}
When you compile above program, two class files will be created viz. Box.class and BoxDemo.class.
Since main() method is contained in BoxDemo.class, you need to execute the same.
In the above example, we have created a class Box which contains 3 instance variables w, h, d.
Box b1=new Box();
The above statement creates a physical memory for one object of Box class. Every object is an instance
of a class, and so, b1 and b2 will have their own copies of instance variables w, h and d. The memory
layout for one object allocation can be shown as –
Box Class
BoxDemo Class
Heap
JVM
3.2 Declaring Objects
Creating a class means having a user-defined data type. To have a variable of this new data type, we
should create an object. Consider the following declaration:
Box b1;
This statement will not actually create any physical object, but the object name b1 can just refer to the
actual object on the heap after memory allocation as follows –
b1 = new Box ();
We can even declare an object and allocate memory using a single statement –
Box b1=new Box();
Without the usage of new, the object contains null. Once memory is allocated dynamically, the object b1
contains the address of real object created on the heap. The memory map is as shown in the following
diagram –
Statement Effect
b1 = new Box(); w
b1 h
d
Closer look at new
The general form for object creation is –
obj_name = new class_name();
Here, class_name() is actually a constructor call. A constructor is a special type of member function
invoked automatically when the object gets created. The constructor usually contains the code needed
for object initialization. If we do not provide any constructor, then Java supplies a default constructor.
Java treats primitive types like byte, short, int, long, char, float, double and boolean as ordinary variables
but not as an object of any class. This is to avoid extra overhead on the heap memory and also to
increase the efficiency of the program. Java also provides the class-version of these primitive types that
can be used only if necessary. We will study those types later in detail.
With the term dynamic memory allocation, we can understand that the keyword new allocates memory for
the object during runtime. So, depending on the user’s requirement memory will be utilized. This will
avoid the problems with static memory allocation (either shortage or wastage of memory during runtime).
If there is no enough memory in the heap when we use new for memory allocation, it will throw a run-time
exception.
b1 w
h
d
b2
Thus, any change made for the instance variables of one object affects the other object also. Although b1
and b2 both refer to the same object, they are not linked in any other way. For example, a subsequent
assignment to b1 will simply unhook b1 from the original object without affecting the object or affecting
b2. For example:
Here, b1 has been set to null, but b2 still points to the original object.
NOTE that 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.
ret_type method_name(para_list)
{
//body of the
method return
value;
}
Here, ret_type specifies the data type of the variable returned by the method. It may be any
primitive type or any other derived type including name of the same class. If the
method does not return any value, the ret_type should be specified as void.
method_name is any valid name given to the method
para_list is the list of parameters (along with their respective types) taken the method. It may
be even empty also.
body of method is a code segment written to carryout some process for which the method is
meant for.
return is a keyword used to send value to the calling method. This line will be absent if
the ret_type is void.
class Box
{
double w, h, d;
void volume()
{
System.out.println("The volume is " + w*h*d);
}
}
class BoxDemo
{
public static void main(String args[])
{
Box b1=new Box();
Box b2=new Box();
b1.w=2;
b1.h=4;
b1.d=3;
b2.w=5;
b2.h=6;
b2.d=2;
b1.volume();
b2.volume();
}
}
In the above program, the Box objects b1 and b2 are invoking the member method volume() of the Box
class to display the volume. To attach an object name and a method name, we use dot (.) operator. Once
the program control enters the method volume(), we need not refer to object name to use the instance
variables w, h and d.
Returning a value
In the previous example, we have seen a method which does not return anything. Now we will modify the
above program so as to return the value of volume to main() method.
class Box
{
double w, h, d;
double volume()
{
return w*h*d;
}
}
class BoxDemo
{
public static void main(String args[])
{
Box b1=new Box();
Box b2=new Box();
double vol;
b1.w=2;
b1.h=4;
b1.d=3;
b2.w=5;
b2.h=6;
b2.d=2;
vol = b1.volume();
System.out.println("The volume is " + vol);
System.out.println("The volume is " + b2.volume());
}
}
As one can observe from above example, we need to use a variable at the left-hand side of the
assignment operator to receive the value returned by a method. On the other hand, we can directly make
a method call within print statement as shown in the last line of above program.
class Box
{
double w, h, d;
double volume()
{
return w*h*d;
}
class BoxDemo
{
public static void main(String args[])
{
Box b1=new Box();
Box b2=new Box();
b1.set(2,4,3);
b2.set(5,6,2);
In the above program, the Box class contains a method set() which take 3 parameters. Note that, the
variables wd, ht and dp are termed as formal parameters or just parameters for a method. The values
passed like 2, 4, 3 etc. are called as actual arguments or just arguments passed to the method.
3.5 Constructors
Constructor is a special type of member method which is invoked automatically when the object gets
created. Constructors are used for object initialization. They have same name as that of the class. Since
they are called automatically, there is no return type for them. Constructors may or may not take
parameters.
class Box
{
double w, h, d;
double volume()
{
return w*h*d;
}
class BoxDemo
{
public static void main(String args[])
{
Box b1=new Box();
Box b2=new Box();
Box b3=new Box(2,4,3);
When we create two objects b1 and b2, the constructor with no arguments will be called and the all the
instance variables w, h and d are set to 5. Hence volume of b1 and b2 will be same (that is 125 in this
example). But, when we create the object b3, the parameterized constructor will be called and hence
volume will be 24.
Concept of Stack: A stack is a Last in First Out (LIFO) data structure. Following figure depicts the basic
operations on stack:
Inserting an element into a stack is known as push operation, whereas deleting an element from the
stack is pop operation. An attempt made to push an element into a full stack is stack overflow and an
attempt to delete from empty stack is stack underflow.
class Stack
{
int st[] = new int[5];
int top;
Stack()
{
top = -1;
}
int pop()
{
if(top==-1)
{
System.out.println("Stack underflow.");
return 0;
}
else
return st[top--];
}
}
class StackDemo
{
public static void main(String args[])
{
Stack mystack1 = new Stack();
Stack mystack2 = new Stack();
System.out.println("Contents of mystack1:");
for(int i=0; i<5; i++)
System.out.println(mystack1.pop());
System.out.println("Contents of mystack2:");
for(int i=0; i<5; i++)
System.out.println(mystack2.pop());
}
}
NOTE that, only the return type of the method is not sufficient for overloading.
class Overload
{
void test() //method without any arguments
{
System.out.println("No parameters");
}
class OverloadDemo
{
public static void main(String args[])
{
Overload ob = new Overload();
ob.test();
ob.test(10);
ob.test(10, 20);
ob.test(123.25);
}
}
3.11 Overloading Constructors
One can have more than one constructor for a single class if the number and/or type of arguments are
different. Consider the following code:
class OverloadConstruct
{
int a, b;
OverloadConstruct()
{
System.out.println("Constructor without arguments");
}
OverloadConstruct(int x)
{
a=x;
System.out.println("Constructor with one argument:"+a);
}
OverloadConstruct(int x, int y)
{
a=x;
b=y;
System.out.println("Constructor with two arguments:"+ a +"\t"+ b);
}
}
class OverloadConstructDemo
{
public static void main(String args[])
{
OverloadConstruct ob1= new OverloadConstruct();
OverloadConstruct ob2= new OverloadConstruct(10);
OverloadConstruct ob3= new OverloadConstruct(5,12);
}
}
Output:
Constructor without arguments
Constructor with one argument: 10
Constructor with two arguments: 5 12
class Test
{
int a, b;
Test(int i, int j)
{
a = i;
b = j;
}
class PassOb
{
public static void main(String args[])
{
Test ob1 = new Test(100, 22);
Test ob2 = new Test(100, 22);
Test ob3 = new Test(-1, -1);
System.out.println("ob1 == ob2: " + ob1.equals(ob2));
System.out.println("ob1 == ob3: " + ob1.equals(ob3));
}
}
Output:
ob1 == ob2: true
ob1 == ob3: false
In the above case, both b1 and b2 will be referring to same object, but not two different objects. So, we
can write a constructor having a parameter of same class type to clone an object.
class Box
{
double h, w, d;
In Java, when you pass a primitive type to a method, it is passed by value. When you pass an
object to a method, they are passed by reference. Keep in mind that when you create a variable of a
class type, you are only creating a reference to an object. Thus, when you pass this reference to a
method, the parameter that receives it will refer to the same object as that referred to by the argument.
This effectively means that objects are passed to methods by use of call-by-reference. Changes to the
object inside the method do affect the object used as an argument.
class Test
{ int a, b;
Test(int i, int j)
{
a = i;
b = j;
}
void meth(Test o)
{
o.a *= 2;
o.b /= 2;
}
}
class CallByRef
{
public static void main(String args[])
{
Test ob = new Test(15, 20);
System.out.println("before call: " + ob.a + " " + ob.b);
ob.meth(ob);
System.out.println("after call: " + ob.a + " " + ob.b);
}
}
Output:
before call: 15 20
after call: 30 10
Test incrByTen()
{
Test temp = new Test(a+10);
return temp;
}
}
class RetOb
{
public static void main(String args[])
{
Test ob1 = new Test(2);
Test ob2;
ob2 = ob1.incrByTen();
System.out.println("ob1.a: " + ob1.a);
System.out.println("ob2.a: " + ob2.a);
ob2 = ob2.incrByTen();
System.out.println("ob2.a after second increase: " + ob2.a);
}
}
Output:
ob1.a: 2
ob2.a: 12
ob2.a after second increase: 22
3.15 Recursion
A method which invokes itself either directly or indirectly is called as recursive method. Every recursive
method should satisfy following constraints:
It should have at least one non-recursive terminating condition.
In every step, it should be nearer to the solution (that is, problem size must be decreasing)
class Factorial
{
int fact(int n)
{
if (n==0)
return 1;
return n*fact(n-1);
}
}
class FactDemo
{
public static void main(String args[])
{
Factorial f= new Factorial();
}
}
Output:
Factorial of 3 is 6
Factorial of 8 is 40320
Some aspects of access control are related to inheritance and package (a collection of related classes).
The protected specifier is applied only when inheritance is involved. So, we will now discuss about only
private and public.
When a member of a class is modified by the public specifier, then that member can be accessed by any
other code. When a member of a class is specified as private, then that member can only be accessed by
other members of its class. When no access specifier is used, then by default the member of a class is
public within its own package, but cannot be accessed outside of its package. Usually, you will want to
restrict access to the data members of a class—allowing access only through methods. Also, there will
be times when you will want to define methods that are private to a class. An access specifier precedes
the rest of a member’s type specification. For example,
public int x;
private char ch;
class Test
{ int a;
public int b;
private int c;
void setc(int i)
{
c = i;
}
int getc()
{
return c;
}
}
class AccessTest
{
public static void main(String args[])
{
Test ob = new Test();
ob.a = 10;
ob.b = 20;
// ob.c = 100; // inclusion of this line is Error!
ob.setc(100);
System.out.println("a, b, and c: " + ob.a + " " + ob.b + " "
+ ob.getc());
}
}
class UseStatic
{
static int a = 3;
static int b;
Output:
Static block initialized.
x = 42
a=3
b = 12
Outside of the class in which they are defined, static methods and variables can be used independently
of any object. To do so, you need only specify the name of their class followed by the dot operator. The
general form is –
classname.method();
class StaticDemo
{
static int a = 42;
static int b = 99;
Output:
Inside static method, a = 42
Inside main, b = 99
class Test
{
public static void main(String args[])
{
int a1[]=new int[10];
int a2[]={1, 2, 3, 4, 5};
int a3[]={3, 8, -2, 45, 9, 0, 23};
Output:
Length of a1 is 10
Length of a2 is 5
Length of a3 is 7
3.19 Inheritance
Inheritance is one of the building blocks of object oriented programming languages. It allows creation of
classes with hierarchical relationship among them. Using inheritance, one can create a general class that
defines traits common to a set of related items. This class can then be inherited by other, more specific
classes, each adding those things that are unique to it. In the terminology of Java, a class that is inherited
is called a superclass. The class that does the inheriting is called a subclass. Therefore, a subclass is a
specialized version of a superclass. It inherits all of the instance variables and methods defined by the
superclass and add its own, unique elements. Through inheritance, one can achieve re-usability of the
code.
In Java, inheritance is achieved using the keyword extends. The syntax is given below:
class A //super class
{
//members of class A
}
class A
{
int i, j;
void showij()
{
System.out.println("i and j: " + i + " " + j);
}
}
class B extends A
{
int k;
void showk()
{
System.out.println("k: " + k);
}
void sum()
{
System.out.println("i+j+k: " + (i+j+k));
}
}
class SimpleInheritance
{
public static void main(String args[])
{
A superOb = new A();
B subOb = new B();
superOb.i = 10;
superOb.j = 20;
System.out.println("Contents of superOb: ");
superOb.showij();
subOb.i = 7;
subOb.j = 8;
subOb.k = 9;
System.out.println("Contents of subOb: ");
subOb.showij();
subOb.showk();
Note that, private members of the super class can not be accessed by the sub class. The subclass
contains all non-private members of the super class and also it contains its own set of members to
achieve specialization.
superclass
subclass
Multilevel Inheritance: If several classes are inherited one after the other in a hierarchical
manner, it is known as multilevel inheritance, as shown below –
D
3.19.2 A Superclass variable can reference a subclass object
A reference variable of a superclass can be assigned a reference to any subclass derived from that
superclass. Consider the following for illustration:
class Base
{
void dispB()
{
System.out.println("Super class " );
}
}
class Derived extends Base
{
void dispD()
{
System.out.println("Sub class ");
}
}
class Demo
{
public static void main(String args[])
{
Base b = new Base();
Derived d=new Derived();
Note that, the type of reference variable decides the members that can be accessed, but not the type
of the actual object. That is, when a reference to a subclass object is assigned to a superclass
reference variable, you will have access only to those parts of the object defined by the superclass.
class Box
{
double w, h, b;
Box(double wd, double ht, double br)
{
w=wd; h=ht; b=br;
}
}
class ColourBox extends Box
{
int colour;
ColourBox(double wd, double ht, double br, int c)
{
w=wd; h=ht; b=br; //code redundancy
colour=c;
}
}
Also, if the data members of super class are private, then we can’t even write such a code in subclass
constructor. If we use super() to call superclass constructor, then it must be the first statement
executed inside a subclass constructor as shown below –
class Box
{
double w, h, b;
Box(double wd, double ht, double br)
{
w=wd; h=ht; b=br;
}
}
class Demo
{
public static void main(String args[])
{
ColourBox b=new ColourBox(2,3,4, 5);
}
}
Here, we are creating the object b of the subclass ColourBox . So, the constructor of this class is
invoked. As the first statement within it is super(wd, ht, br), the constructor of superclass Box is
invoked, and then the rest of the statements in subclass constructor ColourBox are executed.
To access superclass member variable when there is a duplicate variable name in the
subclass: This form of super is most applicable to situations in which member names of a
subclass hide members by the same name in the superclass.
class A
{
int a;
}
class B extends A
{
int a; //duplicate variable a
B(int x, int y)
{
super.a=x; //accessing superclass a
a=y; //accessing own member a
}
void disp()
{
System.out.println("super class a: "+ super.a);
System.out.println("sub class a: "+ a);
}
}
class SuperDemo
{
public static void main(String args[])
{
B ob=new B(2,3);
ob.disp();
}
}
class A
{ int a;
}
class B extends A
{ int b;
}
class C extends B
{ int c;
C(int x, int y, int z)
{
a=x; b=y; c=z;
}
void disp()
{
System.out.println("a= "+a+ " b= "+b+" c="+c);
}
}
class MultiLevel
{
public static void main(String args[])
{
C ob=new C(2,3,4);
ob.disp();
}
}
class A
{
A()
{
System.out.println("A's constructor.");
}
}
class B extends A
{
B()
{
System.out.println("B's constructor.");
}
}
class C extends B
{
C()
{
System.out.println("C's constructor.");
}
}
class CallingCons
{
public static void main(String args[])
{
C c = new C();
}
}
Output:
A's constructor
B's constructor
C's constructor
class A
{
int i, j;
A(int a, int b)
{
i = a;
j = b;
}
void show() //suppressed
{
System.out.println("i and j: " + i + " " + j);
}
}
class B extends A
{
int k;
B(int a, int b, int c)
{
super(a, b);
k = c;
}
void show() //Overridden method
{
System.out.println("k: " + k);
}
}
class Override
{
public static void main(String args[])
{
B subOb = new B(1, 2, 3);
subOb.show();
}
}
Output:
k: 3
Note that, above program, only subclass method show() got called and hence only k got displayed. That
is, the show() method of super class is suppressed. If we want superclass method also to be called, we
can re-write the show() method in subclass as –
void show()
{
super.show(); // this calls A's show()
System.out.println("k: " + k);
}
Method overriding occurs only when the names and the type signatures of the two methods (one in
superclass and the other in subclass) are identical. If two methods (one in superclass and the other in
subclass) have same name, but different signature, then the two methods are simply overloaded.
class A
{
void callme()
{
System.out.println("Inside A");
}
}
class B extends A
{
void callme()
{
System.out.println("Inside B");
}
}
class C extends A
{
void callme()
{
System.out.println("Inside C");
}
}
class Dispatch
{
public static void main(String args[])
{
A a = new A();
B b = new B();
C c = new C();
A r; //Superclass reference
r = a; //holding subclass object
r.callme();
r = b;
r.callme();
r = c;
r.callme();
}
}
A class containing at least one abstract method is called as abstract class. Abstract classes can not be
instantiated, that is one cannot create an object of abstract class. Whereas, a reference can be created
for an abstract class.
abstract class A
{
abstract void callme();
void callmetoo()
{
System.out.println("This is a concrete method.");
}
}
class B extends A
{
void callme() //overriding abstract method
{
System.out.println("B's implementation of callme.");
}
}
class AbstractDemo
{
public static void main(String args[])
{
B b = new B(); //subclass object
b.callme(); //calling abstract method
b.callmetoo(); //calling concrete method
}
}
Example: Write an abstract class shape, which has an abstract method area(). Derive three classes
Triangle, Rectangle and Circle from the shape class and to override area(). Implement run-time
polymorphism by creating array of references to supeclass. Compute area of different shapes and display
the same.
Solution:
class AbstractDemo
{
public static void main(String args[])
{
Shape r[]={new Triangle(3,4), new Rectangle(5,6),new Circle(2)};
for(int i=0;i<3;i++)
System.out.println(r[i].area());
}
}
Output:
Area of Triangle is:6.0
Area of Rectangle is:30.0
Area of Circle is:12.5664
Note that, here we have created array r, which is reference to Shape class. But, every element in r is
holding objects of different subclasses. That is, r[0] holds Triangle class object, r[1] holds Rectangle class
object and so on. With the help of array initialization, we are achieving this, and also, we are calling
respective constructors. Later, we use a for-loop to invoke the method area() defined in each of these
classes.
3.26 Using final
The keyword final can be used in three situations in Java:
To create the equivalent of a named constant.
To prevent method overriding
To prevent Inheritance
To create the equivalent of a named constant: A variable can be declared as final. Doing so prevents
its contents from being modified. This means that you must initialize a final variable when it is declared.
For example:
final int FILE_NEW = 1;
final int FILE_OPEN = 2;
final int FILE_SAVE = 3;
final int FILE_SAVEAS = 4;
final int FILE_QUIT = 5;
It is a common coding convention to choose all uppercase identifiers for final variables. Variables
declared as final do not occupy memory on a per-instance basis. Thus, a final variable is essentially a
constant.
To prevent Inheritance: As we have discussed earlier, the subclass is treated as a specialized class
and superclass is most generalized class. During multi-level inheritance, the bottom most class will be
with all the features of real-time and hence it should not be inherited further. In such situations, we can
prevent a particular class from inheriting further, using the keyword final. For example –
final class A
{
// ...
}
class B extends A // ERROR! Can't subclass A
{
// ...
}
Note:
Declaring a class as final implicitly declares all of its methods as final, too.
It is illegal to declare a class as both abstract and final since an abstract class is incomplete by
itself and relies upon its subclasses to provide complete implementations
Method Purpose
Object clone( ) Creates a new object that is the same as the object being cloned.
void notifyAll( ) Resumes execution of all threads waiting on the invoking object.
The methods getClass( ), notify( ), notifyAll( ), and wait( ) are declared as final. You may override
the others. The equals( ) method compares the contents of two objects. It returns true if the objects
are equivalent, and false otherwise. The precise definition of equality can vary, depending on the
type of objects being compared. The toString( ) method returns a string that contains a description of
the object on which it is called. Also, this method is automatically called when an object is output
using println( ). Many classes override this method.
Question Bank:
1. Define class. Give syntax and example.
2. Briefly explain static members of the class with suitable examples.
3. Discuss method overloading. Write a program to overload a method area() to compute area of a
triangle and a circle.
4. Define a constructor. What are the salient features of Constructor? Write a Java program to show
these features.
5. How do you overload a constructor? Explain with a program.
6. Define recursion. Write a recursive program to find nth Fibonacci number.
7. Write a program to implement stack operations.
8. What are different parameter passing techniques in Java? Discuss the salient features of the
same.
9. What are various access specifiers in Java? List out the behaviour of each of them.
10. Create a Java class called Student with the following details as variables (USN, Name, Branch,
Phone Number). Write a Java program to create n student objects and print USN, Name, Branch,
and Phone number with suitable heading.
11. What is inheritance? Discuss different types of inheritance with suitable example.
12. Discuss the behavior of constructors when there is a multilevel inheritance. Give appropriate code
to illustrate the process.
13. Mention and explain the uses of super keyword in Java.
14. How do you pass arguments to superclass constructor through the subclass constructor? Explain
with a code snippet.
15. Discuss usage of final keyword in Java. Give suitable examples.
16. What do you mean by method overriding? Discuss with a programming example.
17. Explain abstract class and abstract method with suitable code snippet.
18. Write a note on:
a. Use of this keyword
b. Garbage Collection in Java
c. Finalize() method
d. Object Class
e. Dynamic Method Dispatch
19. Create an abstract class called Employee. Include the members: Name, EmpID and an abstract
method cal_sal(). Create two inherited classes SoftwareEng (with the members basic and DA)
and HardwareEng (with members basic and TA). Implement runtime polymorphism (dynamic
method dispatch) to display salary of different employees by creating array of references to
superclass.
20. Differentiate method overloading and method overriding.