Unit 1_Chapter 2_3
Unit 1_Chapter 2_3
Unit 1_Chapter 2_3
Classes are the core of Java. A class defines a new data type. Once defined, this new type can
be used to create objects of that type. Thus, a class is a template for an object, and an object is
an instance of a class.
type methodname1(parameter-list) {
// body of method
}
type methodname2(parameter-list) {
// body of method
}
// ...
type methodnameN(parameter-list) {
// body of method
}
}
The data, or variables, defined within a class are called instance variables. The code is
contained within methods. Collectively, the methods and variables defined within a class are
called members of the class. Variables defined within a class are called instance variables.
A simple class
class Box {
double width;
double height;
double depth;
void volume() {
System.out.print("Volume is ");
System.out.println(width * height * depth);
}
}
class BoxDemo {
public static void main(String args[]) {
Box mybox1 = new Box();
mybox1.width = 10;
mybox1.height = 20;
mybox1.depth = 15;
mybox1.volume();
}
}
Creating instances of a class
When a class is created, a new data type is created. This new data type can be used to declare
objects of that type. Obtaining objects of a class is a 2 step process.
1. Declare a variable of the class type : This variable does not define an object but is a
variable that can refer to an object.
2. Acquire an actual, physical copy of the object and assign it to that variable : This can
be done using the new operator. The new operator dynamically allocates memory for
an object and returns a reference to it.
The above statement combines the two steps. It can be rewritten like below to show each step
more clearly:
Constructors
A constructor initializes an object immediately upon creation. It has the same name as the
class in which it resides and is syntactically similar to a method. Once defined, the
constructor is automatically called immediately after the object is created, before the new
operator completes. Constructors do not have a return type, not even void.
class Box {
double width;
double height;
double depth;
// This is the constructor for Box.
Box() {
System.out.println("Constructing Box");
width = 10;
height = 10;
depth = 10;
}
// compute and return volume
double volume() {
return width * height * depth;
}
}
class BoxDemo{
public static void main(String args[]) {
double vol;
vol = mybox1.volume();
System.out.println("Volume is " + vol);
}
}
Output
Constructing Box
Volume is 1000.0
The constructor in the above program contains no parameters. However a constructor can
also have parameters and those are termed as parameterized constructors.
class Box {
double width;
double height;
double depth;
// This is a parameterized constructor for Box.
Box(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
double volume() {
return width * height * depth;
}
}
class BoxDemo {
public static void main(String args[]) {
vol = mybox1.volume();
System.out.println("Volume is " + vol);
}
}
Output
Volume is 3000.0
“this” keyword
“this” can be used inside any method to refer to the current object. It is illegal in Java to
declare two local variables with the same name inside the same or enclosing scopes. One can
have local variables, including formal parameters to methods, which overlap with the names
of the class’ instance variables. Because this lets user to refer directly to the object, one can
use it to resolve any namespace collisions that might occur between instance variables and
local variables.
Garbage Collection
Objects are dynamically allocated by using the new operator. Objects should be destroyed
and their memory must be released for later reallocation. In C++, dynamically allocated
objects must be manually released by use of a delete operator. Java handles deallocation
automatically. The technique that accomplishes this is called garbage collection. When no
references to an object exist, that object is assumed to be no longer needed, and the memory
occupied by the object can be reclaimed. Different Java run-time implementations will take
varying approaches to garbage collection.
The finalize() method
An object will need to perform some action before it is destroyed. If an object is holding
some non-Java resource such as a file handle, it has to be freed before an object is destroyed.
To handle such situations, Java provides a mechanism called finalization. By using
finalization, one can define specific actions that will occur when an object is just about to be
reclaimed by the garbage collector.
The finalize( ) method has this general form:
// Initialize top-of-stack
Stack() {
tos = -1;
}
The Stack class defines two data items and three methods. The stack of integers is held by the
array stck. This array is indexed by the variable tos, which always contains the index of the
top of the stack. The Stack( ) constructor initializes tos to –1, which indicates an empty stack.
The method push( ) puts an item on the stack. To retrieve an item, pop( ) is called.
The class TestStack, demonstrates the Stack class. It creates two integer stacks, pushes some
values onto each, and then pops them off.
class TestStack {
public static void main(String args[]) {
Stack mystack1 = new Stack();
Stack mystack2 = new Stack();
System.out.println("Stack in mystack2:");
for(int i=0; i<10; i++)
System.out.println(mystack2.pop());
}
}
This program generates the following output:
Stack in mystack1:
9
8
7
6
5
4
3
2
1
0
Stack in mystack2:
19
18
17
16
15
14
13
12
11
10
A Closer Look at Methods and Classes:
Overloading methods
In Java it is possible to define two or more methods within the same class that share the same
name, as long as their parameter declarations are different. When this is the case, the methods
are said to be overloaded, and the process is referred to as method overloading. Method
overloading is one of the ways that Java supports polymorphism. When an overloaded
method is invoked, Java uses the type and/or number of arguments as its guide to determine
which version of the overloaded method to actually call. Thus, overloaded methods must
differ in the type and/or number of their parameters. While overloaded methods may have
different return types, the return type alone is insufficient to distinguish two versions of a
method. When Java encounters a call to an overloaded method, it simply executes the version
of the method whose parameters match the arguments used in the call.
Example Program
class OverloadDemo {
void test() {
System.out.println("No parameters");
}
// Overload test for one integer parameter.
void test(int a) {
System.out.println("a: " + a);
}
Output
No parameters
a: 10
a and b: 10 20
double a: 123.25
Result of ob.test(123.25): 15190.5625
Method test( ) is overloaded four times. The first version takes no parameters, the second
takes one integer parameter, the third takes two integer parameters, and the fourth takes one
double parameter. The fourth version of test( ) also returns a value is of no consequence
relative to overloading, since return types do not play a role in overload resolution.
Java looks for a match between the arguments used to call the method and the method’s
parameters. However, this match need not always be exact. In some cases, Java’s automatic
type conversions can play a role in overload resolution.
// Automatic type conversions apply to overloading.
class OverloadDemo {
void test() {
System.out.println("No parameters");
}
Output
No parameters
a and b: 10 20
Inside test(double) a: 88
Inside test(double) a: 123.2
This version of OverloadDemo does not define test(int). Therefore, when test( ) is called
with an integer argument inside Overload, no matching method is found. However, Java can
automatically convert an integer into a double, and this conversion can be used to resolve the
call. Therefore, after test(int) is not found, Java elevates i to double and then calls
test(double). Of course, if test(int) had been defined, it would have been called instead. Java
will employ its automatic type conversions only if no exact match is found.
Overloading Constructors
In addition to overloading normal methods, constructor methods can also be overloaded. For
most real-world classes that are created, overloaded constructors will be the norm.
class Box {
double width;
double height;
double depth;
double volume() {
return width * height * depth;
}
}
Box( ) constructor requires three parameters. All declarations of Box objects must pass three
arguments to the Box( ) constructor. The following statement is currently invalid:
Box ob = new Box();
Since Box( ) requires three arguments, it’s an error to call it without them. As a solution to
this the Box constructor can be overloaded so that it handles the above situation.
class Box {
double width;
double height;
double depth;
// constructor used when all dimensions specified
Box(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
// constructor used when no dimensions specified
Box() {
width = -1; // use -1 to indicate
height = -1; // an uninitialized
depth = -1; // box
}
// constructor used when cube is created
Box(double len) {
width = height = depth = len;
}
// compute and return volume
double volume() {
return width * height * depth;
}
}
class OverloadCons {
public static void main(String args[]) {
// create boxes using the various constructors
Box mybox1 = new Box(10, 20, 15);
Box mybox2 = new Box();
Box mycube = new Box(7);
double vol;
Output
Example Program.
class Test {
int a, b;
Test(int i, int j) {
a = i;
b = j;
}
// return true if o is equal to the invoking object
boolean equals(Test o) {
if(o.a == a && o.b == b) return true;
else return false;
}
}
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));
Output
The equals( ) method inside Test compares two objects for equality and returns the result. It
compares the invoking object with the one that it is passed. If they contain the same values,
then the method returns true. Otherwise, it returns false. The parameter o in equals( )
specifies Test as its type.
In order to construct a new object that it is initially the same as some existing object, a
constructor that takes an object of its class as a parameter has to be defined. The example
below shows how an object can be passed as a parameter to a constructor.
class Box {
double width;
double height;
double depth;
// Notice this constructor. It takes an object of type Box.
Box(Box ob) { // pass object to constructor
width = ob.width;
height = ob.height;
depth = ob.depth;
}
// constructor used when all dimensions specified
Box(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
// constructor used when no dimensions specified
Box() {
width = -1; // use -1 to indicate
height = -1; // an uninitialized
depth = -1; // box
}
// constructor used when cube is created
Box(double len) {
width = height = depth = len;
}
double volume() {
return width * height * depth;
}
}
class OverloadCons2 {
public static void main(String args[]) {
// create boxes using the various constructors
Box mybox1 = new Box(10, 20, 15);
Box mybox2 = new Box();
Box mycube = new Box(7);
Box myclone = new Box(mybox1); // create copy of mybox1
double vol;
// get volume of first box
vol = mybox1.volume();
System.out.println("Volume of mybox1 is " + vol);
// get volume of second box
vol = mybox2.volume();
System.out.println("Volume of mybox2 is " + vol);
// get volume of cube
vol = mycube.volume();
System.out.println("Volume of cube is " + vol);
// get volume of clone
vol = myclone.volume();
System.out.println("Volume of clone is " + vol);
}
}
Argument Passing
There are two ways in which an argument can be passed to a subroutine.
1. Call by value
2. Call by reference
The Call by value approach, copies the value of an argument into the formal parameter of the
subroutine. Therefore, changes made to the parameter of the subroutine have no effect on the
argument. In the second approach (call by reference), a reference to an argument is passed to
the parameter. Inside the subroutine, this reference is used to access the actual argument
specified in the call. The changes made to the parameter will affect the argument used to call
the subroutine.
The operations that occur inside meth( ) have no effect on the values of a and b used in the
call; their values here did not change to 30 and 10.
Output
ob.a and ob.b before call: 15 20
ob.a and ob.b after call: 30 10
The actions inside meth( ) have affected the object used as an argument.
Returning objects
A method can return any type of data, including the class types that are created.
class Test {
int a;
Test(int i) {
a = i;
}
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
Each time incrByTen( ) is invoked, a new object is created, and a reference to it is returned
to the calling routine.
Access Control
Encapsulation links data with the code that manipulates it. Encapsulation provides another
important attribute: access control. Java’s access specifiers are public, private, and
protected. Java also defines a default access level. protected applies only when inheritance
is involved. 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. main( ) is always preceded
by the public specifier. It is called by code that is outside the program—that is, by the Java
run-time system. 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. An access
specifier precedes the rest of a member’s type specification. Here is an example:
public int i;
private double j;
private int myMethod(int a, char b)
class Test {
int a; // default access
public int b; // public access
private int c; // private access
// methods to access c
void setc(int i) { // set c's value
c = i;
}
int getc() { // get c's value
return c;
}
}
class AccessTest {
public static void main(String args[]) {
Test ob = new Test();
// These are OK, a and b may be accessed directly
ob.a = 10;
ob.b = 20;
// This is not OK and will cause an error
// ob.c = 100; // Error!
// You must access c through its methods
ob.setc(100); // OK
System.out.println("a, b, and c: " + ob.a + " " +
ob.b + " " + ob.getc());
}
}
Inside the Test class, a uses default access, which for this example is the same as specifying
public. b is explicitly specified as public. Member c is given private access. It cannot be
accessed by code outside of its class. inside the AccessTest class, c cannot be used directly. It
must be accessed through its public methods: setc( ) and getc( ).
When there is a need to define a class member that will be used independently of any object
of that class static keyword should be used. It is possible to create a member that can be used
by itself, without reference to a specific instance. To create such a member, precede the
member’s declaration with the keyword static. When a member is declared static, it can be
accessed before any objects of its class are created, and without reference to any object. Both
methods and variables can be defined to be static.
Instance variables declared as static are, essentially, global variables. When objects of its
class are declared, no copy of a static variable is made. Instead, all instances of the class
share the same static variable.
If any computation needs to be done in order to initialize static variables, a static block can be
declared. This block will get executed only once, when the class is first loaded.
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, one needs to only specify the name of the class
followed by the dot operator.
Final keyword
A variable can be declared as final. Doing so prevents its contents from being modified.
A final variable must be initialized when it is declared.
It is a common coding convention to choose all uppercase identifiers for final variables.
class CommandLine {
public static void main(String args[]) {
for(int i=0; i<args.length; i++)
System.out.println("args[" + i + "]: " +
args[i]);
}
}