You are on page 1of 16

Chapter 9:

Encapsulation, Data Hiding & Inheritance

9.0 Introduction

Encapsulation literally means enclosing many smaller things within a larger outer
cover. Under the cover of a class definition, many data and method members remain
covered. So a class actually encapsulates a good number of variables and methods. The
internal details of a class can be kept hidden from a user by a technique called information
or data hiding. Such information or data hiding is necessary so that user cannot do any
damage or harm to the sensitive data or methods. Moreover, data hiding principle should
allow users to use classes without knowing their inner details.

To achieve such a goal, java provides visibility modifiers or access specifier like
private, public, default (i.e. when not specifier) or protected, etc. The protected specifier
can be applied only to inherited subclasses. A subclass can be derived from a base class
(called superclass) by inheriting the latter’s data and method definitions over which any
further addition of data or function members can be made to fulfil specialized requirements
of the subclass.

This chapter deals with the principles and practice of encapsulation, data hiding and
inheritance.

9.1 Use of private & public

When a member of a class (data or method) is specified as public, any other class or
external codes can get access to that member. The main () method is always made public so
that run-time classes or codes can have direct access to it. When a member of a class is
specified as private, only other members of its own class and none else can access that
member. By default, that is when no access specifier is explicitly mentioned, the member of
a class will be treated as public within its own package, but cannot be accessed by any
member outside of that package.

To protect your data from unwanted access, you can allow access only through
member methods and specify those members as private or public according to application
requirements. An access specifier is mentioned first in a member’s type specification. For
example --

public int x;
private int method( double a, char b) { ..... }

etc.
Example-9.1 will show how private data variable can be accessed using get () and set ()
methods only.

/* Example-9.1 Demo of Access Control */

class AccessVariables
{
int x; // default, so direct access possible
public int y; // public, so direct access possible
private int z; // private,
// so needs methods to access it
// for which methods are defined now
void setz (int j) {
z = j;
}

int getz () {
return z;
}
}

public class TestAccess {

public static void main() {


AccessVariables avs = new AccessVariables();

avs.x = 12; //x value can be directly set


avs.y = 65; //y value can be directly set

avs.setz(98); // value set through setz() method

System.out.println (" x is = " + avs.x); //direct access


System.out.println (" y is = " + avs.y); //direct access
System.out.println (" z is = " + avs.getz()); //access through a method
}
}

If you run this program, you will see the output –

x is = 12
y is = 65
z is = 98

You have noticed that to access the private variable z, the methods getz() and setz() have
been used by the class TestAccess as the variables (x, y, z) were defined in a separate
class AccessVariables. Thus proper access specifier can protect sensitive data and methods.
9.2 Scope & Visibility Rules

The scope rules are the rules by which it is decided how a particular data item
or method can be accessed in a program environment. Visibility is also a related term
used with respect to access control. Visibility is concerned with the question whether a
specified variable can be used from a given place in a program.

9.2.1 Scope & Visibility Rules of Java

* Data declared as instance variables in a class, called global data, can be used by all the
methods belonging to that class.
• Data declared within a method, called local data, can be used only in that method.
• If a local variable having the same name as that of a global variable, then local
variable will hide the global variable.
• Variables declared in a block are visible to every method in that block.
• Variables declared in an inner block are not visible outside that block.
• Variables declared in exterior blocks are always visible to interior blocks.
• A class has only two levels of access -- public or default.
• A subclass (i.e. inherited class) cannot access the private members of the superclass
(i.e. base class).

The scope and visibility rules can be summarized in a tabular form as shown in Table-9.1.

Scope Visibility
-------------------------------------------------------------------------------------------------------------
Public variables, methods &
Classes public visible to all classes

Default variables, methods


& Classes treated as public within visible to all classes
its package within the package

Private variables, methods private visible only within


The class
& Classes

Protected variables, methods protected visible to all classes


& Classes within the package &
inherited classes to
outside package

Table-9.1 Scope & Visibility Rules Table


9.3 Packages & Package level Access

Packages add another level of access control. Classes act as containers of data and
methods, whereas packages act as containers of classes, interfaces and sub-packages. Java’s
smallest unit of abstraction is class. Class members can be of four categories:--

• Subclasses (inherited from superclass) within the same package.


• Non-subclasses (i.e. different classes) existing in the same package.
• Subclasses from different packages.
• Classes that are neither in the same package nor existing as subclasses.

Anything declared public can be accessed from anywhere.


Anything declared private couldn’t be seen outside that class.
Absence of specifier, i.e. default, ensures visibility to classes and subclasses within
the same package.

Protected specifier can be used when you want to allow an element to be seen outside
your current package, but only for the classes inherited from the base class or superclass.

The package level access rules can be displayed in the form of a table as shown in Table-9.2.

Private Default Protected Public

Same Package subclass No Yes Yes Yes

Same Package non-subclass No Yes Yes Yes

Different Package subclass No No Yes Yes

Different Package non-subclass No No No Yes

Table – 9.2 Package Level Class Member Access

9.4 Importing Packages

Normally Java classes remain stored in some named packages. Java includes the import
statement to bring any particular class/es or entire package into visibility of a particular
program. Once imported, a class can be referred directly. Import statement(s) should occur
immediately following the package statement, if any, and before any class defined by users.
The general form of the import statement looks like:

import pkg1[.pkg2].class_name | * ( indicating all);


where pkg1 is the top level package, pkg2 is the subordinate package inside the outer
package separated by a dot (.). Java’s all standard library classes remain stored in a package
called java. All library classes essentially required to execute any java program remain
stored in a sub-package called java.lang, which is automatically imported by java compiler
with any java program while compiling.

When a package is imported, only those items specified as public in that package
will be made available for users’ programs. Let us now examine one example program
which makes use the java’s IO library package along with one user defined class
Wrap1Demo (example-9.2) that is capable of displaying a given decimal number with its
equivalent binary, octal and hexadecimal representations.

Example- 9.2(a) User Defined Wrap1Demo class

public class Wrap1Demo


{
int number;

public void display (int number){

System.out.println (" binary equivalent of " + number + " is = " +


Integer.toBinaryString (number));

System.out.println (" octal equivalent of "+ number + " is = " +


Integer.toOctalString (number));

System.out.println (" hexadecimal equivalent of " + number + " is = " +


Integer.toHexString (number));
}
}

Example-9.2 (b) ConvertDemo class imports both java’s Library IO Package


and the Wrap1Demo (stored in the BlueJ’s Default Package area)

import java.io.*; //one library package


import Wrap1Demo; //one user defined class

public class ConvertDemo {

public static void main() throws IOException {

BufferedReader invalue = new BufferedReader (new InputStreamReader


(System.in));
String str;
int number;
System.out.println (" Enter a decimal integer value.");
str = invalue.readLine ();
try {
number = Integer.parseInt (str);
System.out.println ("Number you have entered is = "+ number);

Wrap1Demo value = new Wrap1Demo()


value.display(number);
}
catch (NumberFormatException e) {
System.out.println (" Invalid Entry.");
}
}
}

Compile and run this program to see how by entering a decimal integer value, you can have
its equivalent representation (Picture 9.1) in binary, octal and hexadecimal.

Picture 9.1

(Verify the results by hand calculations)

9.5 Inheritance

Inheritance is one of the most important characteristics of Object Oriented Programming.


From a common base class ( or superclass) many specialized sub-classes called derived
classes can be created for use in a java program. For example, from a common base class
Employee --- many special sub-classes like TechEmployee, AdminEmployee,
OfficeEmployee, etc can be created by inheriting the common data and method members of
the base class Employee and by adding other suitable member definitions (both data and
method) specially required for specialized sub-classes. Therefore, a sub-class can be
regarded as a specialized or extended version of a superclass or base class.
9.5.1 Inheritance Basics

To inherit the properties of a base class we have to make use of the keyword extends. We
will now examine how from a superclass (i.e. base class) we can derive a sub-class and can
use both together for problem solving. Study example-9.3 carefully.

Example – 9.3 Demo of a sub-class inheriting properties of a superclass

/* Write a description of class SuperA first */

public class SuperA


{
int i,j;

void show ()
{
System.out.println (" values of i and j :" + i + " " + j);
}
}

// create a sub-class SubB from a SuperA

public class SubB extends SuperA // using inheritance


{
int k;
void showk()
{
System.out.println ("value of k:" + k);
}
void sum()
{
System.out.println ("sum of i, j, k =" + (i + j + k));
}
}

//use of both Base & Derived classes in a class with main() method

public class Inheritance


{
public static void main()
{
SuperA supobj = new SuperA();

SubB subobj = new SubB();


supobj.i = 20;
supobj.j = 30;
System.out.println (" i & j of super class:");
supobj.show();
System.out.println ("*************************************");
subobj.i = 15;
subobj.j = 25;
subobj.k = 40;
System.out.println (" sub-object data values:");
subobj.show();
subobj.showk();
System.out.println ("Sum of sub-object's values:" );
subobj.sum();
}
}

If you run this program, the following output will appear on the terminal window ---

i & j of super class:


values of i and j :20 30
*************************************
sub-object data values :
values of i and j :15 25
value of k:40
Sum of sub-object's values:
sum of i,j,k = 80

Try to understand the usefulness of the inheritance property of Object Oriented


Programming. Now we will examine how multi-level inheritance can be used in java
programming (Example-9.4)

Example-9.4 Demo of Multi-Level Inheritance

/**
Definition of a Base Class
*/

public class Student {


String name;
String branch;
String department;
private String hno_road;
private String town;
private int pincode;
/**
* Constructor for objects of class Student
*/
public Student(String fullname, String discipline, String dept)
{

name = fullname;
branch = discipline;
department = dept;
}

/**
* An example of a method
*/
public void setAddress (String road, String city, int pin)
{

hno_road = road;
town = city;
pincode = pin;
}
public void displayDetails() {

System.out.print (name + " "+ branch + " "+ department);


System.out.println ();
System.out.println (hno_road);
System.out.println (town);

System.out.println (pincode);
}
}

// First level inheritance

public class TechStud extends Student


{
String category;
String specialization;
/**
* Constructor for objects of class TechStud
*/

TechStud(String sname, String sdisp, String sdept, String quota )


{
super ( sname, sdisp, sdept);
category = quota;

/**
* An example of a method
*/
public void setMajor(String major)
{

specialization = major;
}
public void getFullDetails(){

System.out.println (category + " "+ specialization);


}
}

/**
Second level inheritance
*/
public class PGTechStud extends TechStud
{
String thesisName;
String guide;

/**
* Constructor for objects of class PGTechStud
*/
public PGTechStud(String pgsname, String pgsdisp,String pgdept, String
special, String sguide)
{
super ( pgsname, pgsdisp, pgdept, special);
guide = sguide;
}

/**
* An example of a method
*/
public void setThesisName(String thesis)
{
thesisName = thesis;
}
public void allDetails() {

System.out.println (guide + " "+ specialization);


System.out.println ("Thesis on : " + thesisName);
}
}

/* Demo of Multi-Level Inheritance


*/
public class DemoMLinherit
{
public static void main(){
// different objects created
PGTechStud pgEngg_1 = new PGTechStud("Amit Roy", " Engg", " Mechanical","Heat
Power", "Prof A.Pal");
Student stud_1 = new Student(" Amita Adak", "Science", "Physics" );
TechStud ugstud_2 = new TechStud ("Sudip Das", "Engg", "Electronics",
"general");

stud_1.setAddress("230 B T Road,", "24 Pgs(s),", 700127);


stud_1.displayDetails();
System.out.println ();

ugstud_2.setAddress(" 23 C R Avenue,", "Kolkata-", 700001);


ugstud_2.displayDetails();
ugstud_2.setMajor("Microwave");
ugstud_2.getFullDetails();
System.out.println ();

pgEngg_1.setAddress(" 235 A J C Bose Road,", "Kolkata -", 700017);


pgEngg_1.displayDetails();
pgEngg_1.setMajor("Heat Power");
pgEngg_1.setThesisName(" Improvement of Thermal Efficiency");
pgEngg_1.allDetails();

}
}
.
If you run this program, the output will appear on the terminal window as shown in
Picture 9.2 - --
Picture 9.2

Study example-9.4 very carefully to see how from a base class Student, TechStud subclass
is derived as a first level inheritance and then the TechStud is further extended to
PGTechStud as a second level inheritance. The DemoMLinhirit class containing main ()
method controls the members of all the classes as per application requirements.

Please note that library classes present in the java packages can be used as base classes to
derive subclasses as per software requirements. Thus software development becomes easier
and simpler by reuse of library classes either directly or by deriving classes from them
utilizing the inheritance property.

To know more about java’s inheritance properties, you have to consult the Complete
Reference: Java 2.

9.6 Java’s Interfaces

Interface literally means a connection mechanism between two dissimilar entities. In


java, it is used as a tag to differentiate between a fully defined class and a skeletal or an
abstract definition of a class. The keyword interface allows a class definition in an abstract
form without showing any implementation details. Using interface, one can specify what
a class is supposed to do, but not telling how it can do that.
Interfaces syntactically look like classes without having any body specified. That
means, variables and methods are not defined within an interface keeping scope of
implementation details in various ways.

Interfaces can be used to support dynamic method binding at run time. Java
does not allow Multiple inheritance (i.e. inheriting properties from different base or super
classes instead of from a single base or super class as used in C++). An interface can help
implementing multiple inheritance like features in java, if required at all. Just remember
that java allows only multi-level single inheritance but not multiple inheritance.

Interface can also disconnect the definition of a method or a set of methods from the
inheritance hierarchy and can allow “ one interface, multiple methods” aspect of
polymorphism.

An interface is defined just like a class having a general structural form ---

[public | not used] interface name {


<ret-type> method_1 ( parameter-list);
< ret-type> method_2 (parameter-list);
<type> final-varname1 = value;
|
|
<ret-type> method_M (parameter-list);
<type> final-varnameM;
}

Once an interface is defined, one or more classes can implement that interface by including
the implement clause in a class definition. The general form of a class which includes the
implement clause looks like ----

[public | not used ] class classname [ extends superclass]


[ implements interface [, interface ...]]
{
// class body
}

Now an example will be show that will implement the stack operations – push & pop – by
taking help of an interface. (See Example-9.5)

Example-9.5 Stack Implementation using interface

/** An integer Stack interface


is defined here */

interface IntegerStack {
void push(int data);
int pop();
}

/**
* a description of class FixedLenthStack */

public class FixedLenthStack


{
// instance variables
private int flstack[];
private int topostk;

/**
* stack initialization
*/
public FixedLenthStack(int size)
{

flstack = new int[size];


topostk = -1;
}

public void push ( int data)


{if (topostk = = flstack.length-1)
System.out.println (" Stack overflow.");
else
flstack [++topostk] = data;
}

public int pop() {


if (topostk <0) {
System.out.println (" Stack Underflow.");
return 0;
}
else
return flstack[topostk--];
}
}

/**
* Write a description of class DemoInterface here. */
public class DemoInterface
{
public static void main()
{FixedLenthStack stk_1 = new FixedLenthStack(6);
FixedLenthStack stk_2 = new FixedLenthStack(12);

for ( int i=0; i < 6; i++){


int j = 2*i;
stk_1.push(j);
}
for (int i=0; i<12; i++) {
int j = i+2;
stk_2.push(j);
}

System.out.println (" stk_1 stored following 6 data.");


for (int i =0; i <6; i++)
System.out.print (stk_1.pop()+ " ");
System.out.println ();

System.out.println (" stk_2 stored following 12 data.");


for ( int i =0; i <12; i++)
System.out.print (stk_2.pop() + " ");
System.out.println ();
}

If you run this program, the output will appear as shown below: ----

stk_1 stored following 6 data.


10 8 6 4 2 0

stk_2 stored following 12 data.


13 12 11 10 9 8 7 6 5 4 3 2

The output reveals the fact that a stack is having a Last in First out (LIFO) data structure.

9.6 Conclusions

Encapsulation & Data hiding are the two important aspects of the Object Oriented
Programming paradigm. Instance variables and methods, encapsulated within a class, can be
further protected from misuse by using access specifiers like private, public, etc. The topics
have been discussed in details using examples. The scope and visibility rules have been
explained and presented in a tabulated form. The package level access control rules are also
shown in the form of a table.

With appropriate examples, it has been shown how java’s library and predefined user
classes can be imported and used together to construct a new class to fulfil various
application requirements.

The inheritance property of java has been explained with suitable examples. Java
supports only single inheritance but not multiple inheritance like C++. However multi-level
inheritance is very much possible and allowed by java. The importance of java’s interface
has just been touched upon.