Java Notes Unit II Java
Java Notes Unit II Java
Unit-II
Java, renowned for its simplicity, reliability, and portability, has been a cornerstone in the
programming landscape for decades, largely due to its effective use of OOP (Object-Oriented
Programming) concepts. The inception of Java, with its oops concepts in java, reshaped how
developers approached programming, offering a platform-independent language that emphasizes
security and simplicity. Through the implementation of what is oops in java, including its
fundamental OOPs concepts like encapsulation, inheritance, and polymorphism, Java’s enduring
popularity in various domains, from web applications to enterprise solutions, is a testament to its
robustness and versatility. The concept of OOPs in java underpins its design philosophy, making
it an ideal language for both beginners learning like the basics of OOPs in java and seasoned
professionals delving into more advanced concepts of oops in java.
In the world of Java programming, it’s essential to grasp the fundamental object-oriented
concepts in Java. Object-Oriented Programming, or OOPs in Java, is not just a set of rules; it’s
a powerful paradigm that drives Java’s design and development principles.
Exploring OOPs in Java: So, what exactly is OOPs in Java? Let’s break it down and understand
the concept of OOPs through Java’s core principles.
Objects:
In Java, an object is more than just a runtime entity; it’s the embodiment of a class. These objects
mirror real-world entities, complete with both state and behavior. Think of a ‘Car’ object based on
a ‘Car’ class. It possesses attributes like color, brand, and speed, along with behaviors like
‘accelerate’ and ‘brake’. This is where Java OOP concepts come to life.
Classes:
A class in Java serves as a blueprint for creating objects. It bundles data (attributes) and behavior
(methods) that define the object. Consider a ‘Car’ class; it defines attributes like color and methods
like ‘accelerate()’. Essentially, a class is a template that characterizes what can be instantiated as
an object, exemplifying OOPs concepts.
Abstraction:
Abstraction in Java involves concealing complexity while exposing only the essential
aspects. It’s realized through abstract classes and interfaces. For instance, a ‘Vehicle’ interface
declares a ‘move()’ method, but the actual implementation is left to classes like ‘Car’ or ‘Bike’.
This focus on “what an object does” rather than “how it does it” is a core OOPs concept in Java.
Inheritance:
Inheritance is a mechanism where a new class (subclass) derives attributes and methods
from an existing class (superclass). This fosters code reusability and establishes a hierarchical
relationship between classes. For example, an ‘ElectricCar’ class can inherit traits from the ‘Car’
class, showcasing the practicality of Java OOPs concepts.
Polymorphism:
Polymorphism allows Java methods and objects to assume multiple forms. It finds common
use in method overloading (same method name, different parameters) and method overriding
(same method name and parameters in subclass as in parent class). Consider a ‘draw()’ method
implemented differently in ‘Circle’, ‘Square’, and ‘Triangle’ classes as a prime example
of polymorphism in Java.
Encapsulation:
Encapsulation is a pivotal OOPs principle in Java. It entails bundling data (attributes) and
code (methods) into a cohesive unit. Moreover, it limits direct access to an object’s components,
preventing inadvertent interference. Techniques like using private variables and offering public
getter and setter methods exemplify Java OOPs concepts put into practice.
Java’s strength lies in its adherence to the OOP principles. Understanding the characteristics of
OOPs in Java is pivotal for crafting efficient and modular code. These Java OOPs concepts form
the foundation of modern software development, enabling developers to create elegant and
scalable solutions.
OOPs Concepts in Java with Examples: OOPs Concepts in Java with Examples:
Class
Class is a blueprint or template for its objects that describes characteristics and behaviour.
Each object of the class possesses the characteristics and behaviour (data and functions) described
within the class. A number of objects of a particular class contain different characteristics but they
share common behaviour.
The class and objects can be considered to be the two sides of the same coin, which cannot be
disintegrated. It is insignificant to discuss an object without referring to its 'Class'.
Thus, we can say that Class is simply a representation of similar types of Objects. It is the
blueprint/plan/template that describes the details of an Object.
Class is a blueprint of an object. When a class is defined, it doesn't acquire any space in memory,
it is only the attributes which must be common to all the objects of that class• Moreover, when an
object of a class is created, it includes instance variables described within the class. This is the
reason why an object is called an instance of a class.
Let us take an example of a class ‘Text_Book’. The different subjects (objects) can be created from
the class ‘Text_Book’ which are as shown below:
Here, each object of the class “Text_Book” will possess different characteristics like name and
publisher but will have common behaviour like reading, preparing for exam and making project.
Syntax
class ClassName {
// class body
}
The name of the class should be followed by the keyword class. The body of the class is enclosed
within curly braces {}.All the data members and member functions of the class are declared inside
the body of the class.
Code example: Let's look at the example of the Java class below:
This Java class has the name Person.The keyword public is an access modifier, which determines
who can access this class. In this case, the class is accessible by any other class. Inside the body
of the class, we can declare fields and methods.
Fields
Fields are variables that contain data. In the example above, we have a field named name.Fields
are usually declared at the top of the class before any methods.
Methods
Methods are functions that perform actions. In the example above, we have a method
named printName(), which prints the value of the name field to the screen.Methods are usually
declared after the fields in a class. To use a class, we need to create an instance of the class. To do
this, we use the keyword new.
Object is also called the instance of the class. When an object of a class is created, then it is said
to be instantiation. All the instances share the state (attribute) and the behaviour described within
the class. But the attributes (i.e., the states) are unique for each object. A single class may have
any number of instances.
For example, if we want to create an instance of the Person class, we would use the following
code:
Note: new operator- The keyword 'new' is used to allocate space in the dynamic memory for the
storage of data and functions belonging to an object. This creates an object called myObj, which
is an instance of the Person class. We can then access the fields and methods of this object using
the dot notation, like this:
myObj.printName();
This set the name field to "SathyabamaUniversity" and then call the printName() method, which
print the value of the name field to the screen.
class Person {
// fields
String name;
// methods
void printName() {
System.out.println(name);
}
class Main {
myObj.printName();
Let us design a program which includes the object 'Student l' of class 'Performance', describing the
above data members and member methods:
Similarly, other objects of the classs ‘Performance’ can be created bu using the following
statements:
Each object of class ‘Performance’ created above will contain different data values as Name, marks
in Physics, Chemistry and Biology along with the common functions(methods) as Accept(),
Calculate() and Display().
Constructor
A constructor in Java Programming is a block of code that initializes (constructs) the
state and value during object creation. It is called every time an object with the help of a new ()
keyword is created. Even if you haven’t specified any constructor in the code, the Java compiler
calls a default constructor. The default constructor is used to assign default states and values, such
as 0, null, etc., to the object. The general syntax of a constructor is:
Invoking a Constructor
A constructor can be invoked, at the time of creating an object of a class. The syntax of invoking a
constructor, is as shown below:
For example,
Here, the keyword ‘ob’ creates object of the class Item and invokes constructor to initialize the
current object.
Features of a Constructor
The constructor is defined with the same name as class name.
With reference to the above example, the method Item( ) has the same name name Item.
Hence. it is a constructor.
The constructor is used only to initialize the data members/instance variables and will not
to be used for any other task.
The constructor is automatically called while creating an object. When an object created,
the constructor gets called implicitly. You need not call a constructor through the object
like other member methods.
For example,
The constructor needs no return type. It is only used to initialize the data members. No
arithmetical or logical operation is performed in a constructor. Hence, return from a
constructor is not at all required.
The constructor is always public. It is always called from outside the class while creating
an object. Hence, access specifier of a constructor is always public because a private
member method can never be accessed outside the class premises.
The constructor is overloaded automatically. It means when a number of constructors
created for a class are automatically overloaded as they will possess the same name as the
class name and will contain different type of parameters.
Types of Constructor
There are four different types of constructors which you will learn throughout the discussion.
They are:
Default Constructor:
A constructor that is used to initialize the instance variables with default initial values, is known
as Default Constructor. When no constructor is defined in a program, the compiler creates a
constructor on its own. As soon as an object of a class is created, it uses this constructor to
initialize the instance variables with system approved initial values.
//A program to illustrate Default Constructor
class Dconst
{
char c;int a;long I;float f;double d;String s;
void display()
{
System.out.println("lnitial value of c="+c);
System.out.println("lnitial value of a="+a);
System.out.println("lnitial value of l="+l);
System.out.println("lnitial value of f="+f);
System.out.println("lnitial value of d="+d);
System.out.println("lnitial value of s="+s);
}
Non-parametrized constructor
// Instance variables
String name;
int age;
// Non-parametrised constructor
public Person()
this.name = "Unknown";
this.age = 0;
person.displayInfo();
}
Explanation:
1. Class Definition: We define a class named Person with two instance variables, name and
age.
Name: Unknown
Age: 0
This demonstrates how a non-parametrized constructor is used to set default values for an
object's properties in Java.
Parameterized constructor
A parameterized constructor in Java is a constructor that accepts one or more
parameters, allowing you to initialize an object with specific values. Here’s a simple example
to illustrate a parameterized constructor:
public class Person {
// Instance variables
String name;
int age;
// Parameterised constructor
public Person(String name, int age)
{
this.name = name;
this.age = age;
}
// Method to display the person's information
public void displayInfo()
{
System.out.println("Name: " + name);
System.out.println("Age: " + age);
}
public static void main(String[] args)
{
// Creating an object using the parameterised constructor
Person person = new Person("John Doe", 30);
// Displaying the provided values
person.displayInfo();
}
}
Explanation:
1. Class Definition: We define a class named Person with two instance variables, name and
age.
This demonstrates how a parameterized constructor is used to set specific values for an object's
properties in Java.
Copy Constructor
A copy constructor in Java is a special type of constructor used to create a new object as a
copy of an existing object. Although Java does not provide a built-in copy constructor like C++,
you can define one yourself by writing a constructor that takes an object of the same class as a
parameter.
// Parameterised constructor
public Person(String name, int age)
{
this.name = name;
this.age = age;
}
// Copy constructor
public Person(Person another)
{
this.name = another.name;
this.age = another.age;
}
// Method to display the person's information
public void displayInfo()
{
System.out.println("Name: " + name);
System.out.println("Age: " + age);
}
public static void main(String[] args)
{
// Creating an object using the parameterised constructor
Person person1 = new Person("John Doe", 30);
// Creating a new object using the copy constructor
Person person2 = new Person(person1);
// Displaying the values of the original object
System.out.println("Original Person:");
person1.displayInfo();
// Displaying the values of the copied object
System.out.println("\nCopied Person:");
person2.displayInfo();
}
}
Explanation:
1. Class Definition: We define a class named Person with two instance variables, name and
age.
This demonstrates how a copy constructor is used to create a new object with the same values as an
existing object in Java.
this keyword
In Java, the this keyword is used to refer to the current object within a method or constructor.
It can be used for various purposes such as distinguishing instance variables from local
variables, invoking other constructors within the same class, or passing the current object as
a parameter.
// Instance variables
String name;
int age;
// Parameterized constructor
this.name = name;
this.age = age;
// Non-parameterized constructor
public Person()
this("Unknown", 0);
person1.displayInfo();
System.out.println();
person2.displayInfo();
Explanation:
1. Class Definition: We define a class named Person with two instance variables, name and
age.
Name: Unknown
Age: 0
This demonstrates how the this keyword is used in various contexts within a class in Java.
Method Overloading
Method overloading in Java is a feature that allows a class to have more than one
method with the same name, but with different parameter lists. It provides flexibility and
readability in your code by allowing you to call a method in different ways based on the
arguments passed to it.
Explanation:
This demonstrates how method overloading allows a class to have multiple methods with
the same name but different parameter lists, enabling methods to handle different types and
numbers of inputs.
Let’s consider a more complex example with a Shape class where method overloading is
used to calculate the area of different shapes:
if (isTriangle) {
}
return 0.0; // default case, not a triangle
Explanation:
3. main Method: In the main method, we create an instance of the Shape class and call
the overloaded area methods with different sets of parameters.
This example demonstrates the flexibility of method overloading, allowing the same
method name to be used for different calculations based on the parameters provided.
Inheritance in Java
It is a term derived from biology which means the transfer of characteristics. Similarly in
Java, some properties of a class is also transferred to another class in order to share data
members or member methods. Hence, a process by which some properties of a class are shared
by another class or classes is known as inheritance.
Let us take a real life example. A child inherits some of the features of his parents. Along
with the inherited traits, he also possesses his own unique characeristics. In this case, parents
can be referred to as Base class and the child inheriting the properties of base class can be
referred to as Derived class. Hence, a base class is a class that is inherited by another class,
but a Derived class or Target or Sub class is one which inherits from a base class. The
following points must be kept in mind when a class inherits from another class:
(i) Private members of the base class cannot be accessed in the derived class.
(ii) Public members of the base class can be used in the derived class.
(iii) Protected members of the base class can be used in the derived class.
The functions geta() and input() will be accessed as the public members of derived class Xyz.
The private data of class Abc, i.e., (data member a) can not be used in the class Xyz (as it is
not accessible outside the class's visibility). Hence, the value of 'a' is returned through a
function input() of the Base class to the add() function of derived class in order to be added
with x.
Java does not support multiple inheritance directly, which means a class cannot extend more than
one class. However, Java provides a way to achieve similar functionality through interfaces.
Hierarchical inheritance in Java occurs when multiple classes inherit from a single superclass. This
type of inheritance represents a tree-like structure, where the parent class can have multiple child
classes, each of which inherits the properties and methods of the parent class.
Explanation:
1. Superclass (Animal):
o Contains a name property.
o Defines two methods: eat and sleep.
2. Subclass (Dog):
o Inherits from Animal.
o Adds a bark method.
3. Another Subclass (Cat):
o Inherits from Animal.
o Adds a meow method.
4. Another Subclass (Bird):
o Inherits from Animal.
o Adds a fly method.
5. Main Method:
o Creates instances of Dog, Cat, and Bird.
o Sets the name property for each instance.
o Calls inherited methods (eat and sleep) and subclass-specific methods (bark, meow, fly).
// Superclass
class Employee {
String name;
int id;
public void work() {
System.out.println(name + " is working.");
}
public void attendMeeting() {
System.out.println(name + " is attending a meeting.");
}
}
// Subclass
class Manager extends Employee {
public void manageTeam() {
System.out.println(name + " is managing the team.");
}
}
// Another subclass
class Engineer extends Employee {
public void developSoftware() {
System.out.println(name + " is developing software.");
}
}
// Another subclass
class Intern extends Employee {
public void learn() {
System.out.println(name + " is learning.");
}
}
public class Company {
public static void main(String[] args) {
// Manager instance
Manager manager = new Manager();
manager.name = "Alice";
manager.id = 101;
manager.work(); // Inherited from Employee
manager.attendMeeting(); // Inherited from Employee
manager.manageTeam(); // Defined in Manager
// Engineer instance
Engineer engineer = new Engineer();
engineer.name = "Bob";
engineer.id = 102;
engineer.work(); // Inherited from Employee
engineer.attendMeeting(); // Inherited from Employee
engineer.developSoftware(); // Defined in Engineer
// Intern instance
Intern intern = new Intern();
intern.name = "Charlie";
intern.id = 103;
intern.work(); // Inherited from Employee
intern.attendMeeting(); // Inherited from Employee
intern.learn(); // Defined in Intern
}
}
Explanation:
1. Superclass (Employee):
o Contains properties name and id.
o Defines two methods: work and attendMeeting.
2. Subclass (Manager):
o Inherits from Employee.
o Adds a manageTeam method.
3. Another Subclass (Engineer):
o Inherits from Employee.
o Adds a developSoftware method.
4. Another Subclass (Intern):
o Inherits from Employee.
o Adds a learn method.
5. Main Method:
o Creates instances of Manager, Engineer, and Intern.
o Sets the name and id properties for each instance.
o Calls inherited methods (work and attendMeeting) and subclass-specific methods
(manageTeam, developSoftware, learn).
An interface in Java is a reference type, similar to a class, that can contain only constants, method
signatures, default methods, static methods, and nested types. Interfaces cannot contain instance
fields or constructors.
// First interface
interface CanFly {
void fly();
}
// Second interface
interface CanSwim {
void swim();
}
// A class implementing both interfaces
class Duck implements CanFly, CanSwim {
@Override
public void fly() {
System.out.println("Duck is flying.");
}
@Override
public void swim() {
System.out.println("Duck is swimming.");
}
}
public class Main {
public static void main(String[] args) {
Duck duck = new Duck();
duck.fly();
duck.swim();
}
}
Explanation:
1. Interface (CanFly): Contains a method signature for fly.
2. Interface (CanSwim): Contains a method signature for swim.
3. Class (Duck): Implements both CanFly and CanSwim interfaces, providing concrete
implementations for the fly and swim methods.
4. Main Method: Creates an instance of Duck and calls its fly and swim methods.
When you run this program, it will output:
Duck is flying.
Duck is swimming.
Java 8 introduced default methods in interfaces, which allow methods in interfaces to have a body.
This provides more flexibility when designing interfaces and allows for a form of multiple
inheritance.
If a class implements multiple interfaces that contain default methods with the same signature,
it must override the conflicting method to resolve the conflict.
Explanation:
4. Main Method: Creates an instance of Duck and calls its move method.
Example:
Class Person
{
Person()
{
System.out.println("Person constructor");
}
void nationality()
{
System.out.println("Indian");
}
void place()
{
System.out.println("Mumbai");
}
}
class Emp extends Person
{
Emp()
{
System.out.println("Emp constructor");
}
void organization()
{
System.out.println("IBM");
}
void place()
{
System.out.println("New York");
}
}
class Manager extends Emp
{
Manager()
{
System.out.println("Manager constructor");
}
void subordinates()
{
System.out.println(12);
}
void place()
{
System.out.println("London");
}
}
class Check
{
public static void main(String arg[])
{
Manager m=new Manager();
m.nationality();
m.organization();
m.subordinates();
m.place();
}
}
Output:
Manager constructor
Indian
IBM
12
London
In the above example, as discussed above, the constructor of grandparent class (Person) is
executed first, then parent class (Emp) and then that of child class (Manager).
Then the method nationality() of Person and the method organization() of Emp are accessed
through Manager object as if they are own methods of Manager. Then the
method subordinates() of Manager (its own) is called.
We can see the corresponding outputs here. At last, we have invoked the method place() which
exists in all the three classes. Here, the method of Person is overridden in Emp and the same
method is overridden again in Manager.
So the version in Manager got executed. Had that method been not overridden in Manager, then
the invocation could have executed the Emp version.
If we don’t want a parent class feature (method or variable) to be available outside the
class then we can make it private.
In that case, such feature will not be available to child class also.
If we want to control the accessibility so that they a feature can be accessed in the current
package but not from outside then we make the feature as default (normal).
In the above example, all the methods are normal (default). So they cannot be accessed
from other packages. If we make a feature a protected, then they can be accessed anywhere
in the current package and from child classes of other packages.
If we make a feature as public, then there is no restriction, such elements can be accessed
from anywhere.
In the above example, all the methods are normal (default). So they cannot be accessed from
other packages. If we make a feature a protected, then they can be accessed anywhere in the
current package and from child classes of other packages.
If we make a feature as public, then there is no restriction, such elements can be accessed from
anywhere.
Example:
class X
{
int i;
X()
{
i=5;
}
}
class Y extends X
{
int j;
Y()
{
j=6;
}
}
class Z extends Y
{
int k;
Z()
{
k=7;
}
}
class MultipleInheritanceExample
{
public static void main(String args[])
{
X a=new X(); // class X consist one variable i
Y b=new Y(); // class Y consist variables j, i(it came from parent class X)
Z c=new Z(); //class Z consist variables k,j(class y), i(class X)
System.out.println(a.i); // a-->i=5
System.out.println(b.i+b.j);// b--->i=5,j=6
System.out.println(c.i+c.j+c.k);// c-->i=5,j=6,k=7
}
}
Output:
5
11
18
Hybrid Inheritance:
Let's consider an example where we use both single inheritance and multiple inheritance
through interfaces to achieve hybrid inheritance.
Example Scenario
// Superclass
class Animal {
String name;
public void eat() {
System.out.println(name + " is eating.");
}
}
// Interface CanFly
interface CanFly {
void fly();
}
// Interface CanSwim
interface CanSwim {
void swim();
}
// Subclass Bird that inherits from Animal and implements CanFly
class Bird extends Animal implements CanFly {
@Override
public void fly() {
System.out.println(name + " is flying.");
}
}
// Subclass Duck that inherits from Bird and implements CanSwim
class Duck extends Bird implements CanSwim {
@Override
public void swim() {
System.out.println(name + " is swimming.");
}}
1. Superclass (Animal):
o Contains a name property and an eat method.
2. Interface (CanFly):
o Contains a method signature for fly.
3. Interface (CanSwim):
o Contains a method signature for swim.
4. Subclass (Bird):
o Inherits from Animal.
o Implements the CanFly interface, providing an implementation for the fly method.
5. Subclass (Duck):
o Inherits from Bird.
o Implements the CanSwim interface, providing an implementation for the swim
method.
6. Main Method:
o Creates instances of Bird and Duck.
o Sets the name property for each instance.
o Calls inherited methods (eat and fly) and subclass-specific methods (swim).
Using Super
In Java, the super keyword is used to refer to the immediate parent class object. It can be used
for accessing parent class members (methods and variables) and constructors. This is
particularly useful when working with inheritance, as it allows a subclass to invoke methods
and constructors from its superclass.
Using super to Call Parent Class Methods
When a subclass needs to call a method from its superclass, the super keyword can be used.
class Animal {
String name;
public void eat() {
System.out.println(name + " is eating.");
}
}
// Subclass
class Dog extends Animal {
public void eat() {
super.eat(); // Call the superclass method
System.out.println(name + " is eating dog food.");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "Buddy";
dog.eat(); // Calls the overridden method in Dog
}
}
Explanation:
1. Superclass (Animal):
o Contains a name property and an eat method.
2. Subclass (Dog):
o Overrides the eat method.
o Calls the superclass eat method using super.eat() and then adds its own implementation.
3. Main Method:
o Creates an instance of Dog.
o Sets the name property.
o Calls the overridden eat method, which first calls the superclass method and then prints
its own message.
Explanation:
1. Superclass (Animal):
o Contains a name property.
o Has a parameterized constructor that initializes the name.
o Contains a display method.
2. Subclass (Dog):
o Contains a breed property.
o Has a parameterized constructor that calls the superclass constructor using
super(name).
o Overrides the display method, calling super.display() to display the name from the
superclass, and then displays the breed.
3. Main Method:
o Creates an instance of Dog using the parameterized constructor.
o Calls the display method.
The super keyword in Java is a powerful feature when dealing with inheritance. It allows
subclasses to:
Method Overriding
Method overriding in Java occurs when a subclass provides a specific implementation for
a method that is already defined in its superclass. The method in the subclass should have
the same name, return type, and parameters as the method in the superclass.
1. Same Method Signature: The overridden method must have the same name, return
type, and parameter list as the method in the superclass.
2. Access Modifier: The access level of the overridden method in the subclass cannot be
more restrictive than the method in the superclass.
3. @Override Annotation: It is a good practice to use the @Override annotation, which
helps to ensure that a method is correctly overridden.
4. Dynamic Method Dispatch: Method calls are resolved at runtime based on the
object's actual type, which enables polymorphism.
Let's create a superclass Animal and a subclass Dog to demonstrate method overriding.
// Superclass
class Animal {
public void sound() {
System.out.println("Animal makes a sound");
}
}
// Subclass
class Dog extends Animal {
@Override
public void sound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Animal(); // Animal reference and object
Animal myDog = new Dog(); // Animal reference but Dog object
myAnimal.sound(); // Calls the method from Animal class
myDog.sound(); // Calls the overridden method from Dog class
}
}
Explanation:
1. Superclass (Animal):
o Contains a method sound that prints a generic message.
2. Subclass (Dog):
o Overrides the sound method from Animal with a specific implementation that
prints "Dog barks".
o Uses the @Override annotation to indicate that the method is overriding a
superclass method.
3. Main Method:
o Creates an instance of Animal and calls its sound method.
o Creates an instance of Dog but refers to it using an Animal reference,
demonstrating polymorphism. The overridden sound method in the Dog class is
called.
Dog barks
Summary
The overridden method must have the same name, return type, and parameter list.
The access level of the overridden method cannot be more restrictive.
Using the @Override annotation helps ensure correct overriding.
Covariant return types allow a subclass to override a method and return a subtype of
the declared return type.
By understanding and using method overriding effectively, you can create flexible and
reusable code with clear hierarchical relationships between classes.
Abstract Classes
Abstract classes in Java are classes that cannot be instantiated on their own and are meant to be
subclassed. They can contain abstract methods (methods without an implementation) as well as
concrete methods (methods with an implementation). Abstract classes are used to provide a common
template for subclasses, ensuring that certain methods are implemented in the subclasses while
allowing some shared functionality to be defined in the abstract class itself.
Let's create an abstract class Animal with both abstract and concrete methods, and two
subclasses Dog and Cat that provide implementations for the abstract methods.
// Abstract superclass
Subclass (Dog):
Inherits from Animal.
Provides a constructor that calls the superclass constructor using super(name).
Implements the abstract method sound.
Another Subclass (Cat):
Inherits from Animal.
Provides a constructor that calls the superclass constructor using super(name).
Implements the abstract method sound.
Main Method:
Creates instances of Dog and Cat.
Calls the eat method (inherited from Animal).
Calls the sound method (implemented in the subclasses).
Summary
Abstract classes in Java provide a way to define a common template for related classes,
ensuring that certain methods are implemented in the subclasses while allowing for
shared functionality. By understanding and using abstract classes effectively, you can
create a clear and organized class hierarchy, promote code reuse, and ensure that
subclasses adhere to a defined contract.
In Java, the final keyword can be used in various contexts, and it has different meanings
depending on where it is applied. When used with inheritance, final can be applied to
classes, methods, and variables to enforce specific restrictions.
When a class is declared as final, it cannot be subclassed. This means no other class can
extend a final class. This is useful when you want to prevent inheritance for security
reasons or to ensure the integrity of the class's behavior.
// Final class
final class Animal {
public void sound() {
System.out.println("Animal makes a sound");
}
}
// The following class declaration will cause a compile-time error
// class Dog extends Animal {
// public void sound() {
// System.out.println("Dog barks");
// }
// }
public class Main {
public static void main(String[] args) {
Animal animal = new Animal();
animal.sound();
}
}
When a method is declared as final, it cannot be overridden by subclasses. This is useful when
you want to ensure that the implementation of the method remains unchanged across all
subclasses.
class Animal {
public final void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
// The following method declaration will cause a compile-time error
// public void sound() {
// System.out.println("Dog barks");
// }
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.sound(); // Calls the final method from Animal class
}
}
Using final with Variables
When a variable is declared as final, it can be assigned only once. This is useful for defining
constants or ensuring that the value of the variable cannot be changed after it is initialized.
class Animal {
// Final variable
public final String name;
// Constructor to initialize the final variable
public Animal(String name) {
this.name = name;
}
public void display() {
System.out.println("Animal's name: " + name);
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Animal("Buddy");
animal.display();
// The following assignment will cause a compile-time error
// animal.name = "Max";
}
}
Summary of final Keyword Usage in Inheritance
1. Final Classes:
Prevents the class from being subclassed.
Ensures that no one can extend the class and alter its behavior.
2. Final Methods:
Prevents the method from being overridden by subclasses.
Ensures that the method's implementation remains consistent across all subclasses.
3. Final Variables:
Prevents the variable from being reassigned after it has been initialized.
Useful for defining constants or immutable data.
By using the final keyword appropriately, you can enforce specific constraints in your Java
programs, ensuring that classes, methods, and variables are used as intended and maintaining
the integrity of your code.
Garbage collection
Garbage collection in Java is the process of automatically identifying and discarding objects
that are no longer needed by the program, freeing up memory resources. Java's garbage
collection mechanism helps manage memory efficiently and reduces the risk of memory leaks.
Output:
When you run the program, you might see the following output:
MyObject is being garbage collected
End of main method
Important Notes:
Summary:
Garbage collection in Java is a crucial feature that automates memory management, allowing
developers to focus on writing code without worrying about memory leaks. By understanding
how garbage collection works and using best practices for resource management, you can
write efficient and robust Java applications.