Object-Oriented Programming (OOP) Concepts in Java
Java is an object-oriented programming language that follows key OOP principles: Encapsulation, Inheritance, Polymorphism, and Abstraction.
1. Encapsulation
Encapsulation is the process of bundling data (variables) and methods that operate on the data into a single unit, i.e., a class. It helps achieve data hiding and keeps the internal workings of an object hidden from the outside world.
Data Hiding
Data hiding is achieved by declaring variables as private, making them inaccessible from outside the class. Access to these variables is provided through public setter and getter methods.
public class Employee {
private String name; // Private variable
// Setter
public void setName(String name) {
this.name = name;
}
// Getter
public String getName() {
return this.name;
}
}
Constructors
Constructors are special methods used to initialize objects. A constructor is called when an object of a class is created.
public class Employee {
private String name;
// Constructor
public Employee(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Employee emp = new Employee("John");
System.out.println(emp.getName()); // Output: John
The 'this' Keyword in Java
The this keyword is a reference variable in Java that refers to the current object instance. It can be used in various contexts to refer to the instance of the current class.
1. Referring to Current Class Instance Variables
When instance variables and method parameters have the same name, the this keyword is used to distinguish the instance variables from the parameters.
class Employee {
private String name;
// Constructor
public Employee(String name) {
this.name = name; // 'this.name' refers to the instance variable
}
public String getName() {
return this.name; // 'this.name' is referring to the instance variable
}
public static void main(String[] args) {
Employee emp = new Employee("John");
System.out.println(emp.getName()); // Output: John
}
}
In the example above, the this keyword helps to differentiate the instance variable name from the constructor parameter name.
2. Calling Current Class Methods
The this keyword can also be used to call other methods in the current class. It's not mandatory to use this to call methods, but it can be useful for clarity, especially when method names are the same as variables or when calling from a constructor.
class Employee {
private String name;
// Constructor
public Employee(String name) {
this.name = name; // Initialize the name using the constructor parameter
this.display(); // Call another method from the same class
}
// Method to display the name
public void display() {
System.out.println("Employee Name: " + this.name);
}
public static void main(String[] args) {
Employee emp = new Employee("Alice");
}
}
In the example above, the this.display() call within the constructor invokes the display() method of the current class, which outputs the name of the employee.
3. Calling Constructors from One Another
In Java, you can call one constructor from another constructor within the same class using this. This is known as constructor chaining.
class Employee {
private String name;
private int age;
// Constructor with two parameters
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
// Constructor with one parameter, calling the two-parameter constructor
public Employee(String name) {
this(name, 30); // Calling the two-parameter constructor with a default age value
}
public void display() {
System.out.println("Name: " + this.name + ", Age: " + this.age);
}
public static void main(String[] args) {
Employee emp1 = new Employee("John", 25);
emp1.display(); // Output: Name: John, Age: 25
Employee emp2 = new Employee("Alice");
emp2.display(); // Output: Name: Alice, Age: 30
}
}
In the example above, the constructor with one parameter calls the constructor with two parameters using this(name, 30), which demonstrates constructor chaining. This allows for the reuse of code within constructors.
2. Inheritance
Inheritance is a mechanism where one class acquires the properties and behaviors of another class. This helps in code reuse and hierarchical classification.
Types of Inheritance
- Single Inheritance: A class inherits from only one superclass.
- Multilevel Inheritance: A class inherits from another class, and that class inherits from another, forming a chain.
- Hierarchical Inheritance: A single class is inherited by multiple subclasses.
Why Java Does Not Support Multiple Inheritance
Java does not support multiple inheritance (a class inheriting from more than one class) to avoid ambiguity. If two superclasses have the same method, the subclass won't know which one to inherit. This is resolved through interfaces.
Uses of the 'super' Keyword in Java
The super keyword in Java is used to refer to the immediate parent class of the current object. It can be used in various ways to interact with parent class members, including instance variables, methods, and constructors.
1. Referencing Parent Class Instance Variables
The super keyword can be used to access instance variables in the parent class, especially when there is a name conflict with the subclass instance variables.
class Animal {
protected String name = "Animal";
public void display() {
System.out.println("Animal: " + name);
}
}
class Dog extends Animal {
private String name = "Dog";
public void display() {
System.out.println("Dog: " + name);
System.out.println("Parent class name: " + super.name); // Access parent class variable
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.display();
}
}
Output:
Dog: Dog
Parent class name: Animal
In this example, the super.name refers to the parent class's name variable, while the name in the Dog class refers to the subclass's own instance variable.
2. Calling Parent Class Methods
The super keyword is used to call methods from the parent class, especially when the method is overridden in the subclass.
class Animal {
public void sound() {
System.out.println("Animal makes sound");
}
}
class Dog extends Animal {
@Override
public void sound() {
System.out.println("Dog barks");
super.sound(); // Call parent class method
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.sound();
}
}
Output:
Dog barks
Animal makes sound
In this example, the super.sound() calls the sound() method from the parent class Animal after executing the overridden method in the subclass Dog.
3. Calling Parent Class Constructor
The super keyword is used to call the constructor of the parent class. This is useful when the parent class constructor needs to be executed before the subclass constructor.
class Animal {
public Animal(String name) {
System.out.println("Animal's name: " + name);
}
}
class Dog extends Animal {
public Dog(String name) {
super(name); // Calling parent class constructor
System.out.println("Dog's name: " + name);
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog("Buddy");
}
}
Output:
Animal's name: Buddy
Dog's name: Buddy
In this example, the super(name) call invokes the constructor of the Animal class before the subclass constructor executes.
Difference Between this and super
this and super both refer to objects in Java, but they serve different purposes:
| this | super |
|---|---|
| Refers to the current object instance of the class in which it is used. | Refers to the immediate parent class of the object. |
| Used to access the current class's variables, methods, and constructors. | Used to access the parent class's methods and variables, and to call parent class constructors. |
| Can be used to differentiate between method parameters and instance variables when they have the same name. | Used to invoke overridden methods or constructors from the parent class. |
3. Polymorphism
Polymorphism allows one object to take on many forms. It enables one interface to be used for a general class of actions.
Method Overloading
Method overloading is when multiple methods have the same name but different parameter lists. It is determined at compile time.
class MathOperation {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
MathOperation operation = new MathOperation();
System.out.println(operation.add(2, 3)); // Output: 5
System.out.println(operation.add(2.5, 3.5)); // Output: 6.0
Method Overriding
Method overriding occurs when a subclass provides a specific implementation of a method already defined in its superclass. It is determined at runtime.
class Animal {
public void sound() {
System.out.println("Animal makes sound");
}
}
class Dog extends Animal {
@Override
public void sound() {
System.out.println("Dog barks");
}
}
Animal obj = new Dog();
obj.sound(); // Output: Dog barks
4. Abstraction
Abstraction is the process of hiding the implementation details and showing only the functionality to the user. Java provides abstraction through abstract classes and interfaces.
Abstract Class
An abstract class is a class that cannot be instantiated and may contain abstract methods (without implementation) as well as concrete methods (with implementation).
abstract class Animal {
abstract void sound(); // Abstract method
public void eat() { // Concrete method
System.out.println("Eating...");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Barking...");
}
}
Animal animal = new Dog();
animal.sound(); // Output: Barking...
Interface
An interface is a contract that defines a set of methods without providing implementations. A class that implements an interface must provide implementations for all of its methods.
interface Animal {
void sound();
}
class Dog implements Animal {
@Override
public void sound() {
System.out.println("Barking...");
}
}
Animal animal = new Dog();
animal.sound(); // Output: Barking...
Difference Between Abstract Class and Interface
| Abstract Class | Interface |
|---|---|
| Can have both abstract and concrete methods. | Can only have abstract methods (prior to Java 8, but can have default methods in Java 8+). |
| Can have constructors. | Cannot have constructors. |
| Used for "is-a" relationship. | Used for "can-do" relationship. |
Marker Interface
A marker interface is an interface with no methods or fields. It is used to mark a class for a specific behavior.
interface Serializable {}
class Person implements Serializable {
private String name;
public Person(String name) {
this.name = name;
}
}
Access Modifiers in Java
In Java, access modifiers are used to control the visibility and accessibility of classes, methods, and variables. The four main access modifiers in Java are:
- private
- default (no modifier)
- protected
- public
1. private Access Modifier
The private access modifier restricts access to members (variables and methods) within the same class only. It cannot be accessed from outside the class.
class MyClass {
private int number = 10;
private void display() {
System.out.println("Number: " + number);
}
public static void main(String[] args) {
MyClass obj = new MyClass();
// System.out.println(obj.number); // Error: number has private access
// obj.display(); // Error: display() has private access
}
}
In this example, the variable number and method display() are private and cannot be accessed outside the class.
2. default (No Modifier)
If no access modifier is specified, it is considered the default access modifier. Members with default access can only be accessed within classes in the same package.
class MyClass {
int number = 10; // default access modifier
void display() { // default access modifier
System.out.println("Number: " + number);
}
}
class AnotherClass {
public static void main(String[] args) {
MyClass obj = new MyClass();
System.out.println(obj.number); // Accessible because it's in the same package
obj.display(); // Accessible because it's in the same package
}
}
In this example, the variable number and method display() have default access and can be accessed by other classes in the same package.
3. protected Access Modifier
The protected access modifier allows access to members from the same package and subclasses (even if they are in a different package).
class Animal {
protected String sound = "Roar"; // protected variable
protected void makeSound() { // protected method
System.out.println("Sound: " + sound);
}
}
class Dog extends Animal {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println(dog.sound); // Accessible because Dog is a subclass of Animal
dog.makeSound(); // Accessible because Dog is a subclass of Animal
}
}
In this example, the sound variable and makeSound() method are protected and can be accessed within the subclass Dog, even though it is in a different class.
4. public Access Modifier
The public access modifier allows access to members from anywhere, regardless of package or class.
class MyClass {
public int number = 10; // public variable
public void display() { // public method
System.out.println("Number: " + number);
}
}
class AnotherClass {
public static void main(String[] args) {
MyClass obj = new MyClass();
System.out.println(obj.number); // Accessible because it's public
obj.display(); // Accessible because it's public
}
}
In this example, the variable number and method display() are public and can be accessed from any class, even if it's in a different package.
Summary of Access Modifiers
| Access Modifier | Access Level | Can Be Accessed By |
|---|---|---|
private |
Members are accessible only within the same class. | Only the class itself. |
default |
Members are accessible within the same package. | Classes in the same package. |
protected |
Members are accessible within the same package and subclasses. | Classes in the same package and subclasses (even if they are in different packages). |
public |
Members are accessible from anywhere. | Any class, regardless of package. |