A class is a blueprint or template from which objects are created. It defines the properties (attributes) and behaviors (methods) that an object of that class will have. An object is an instance of a class. For example, consider a Car
class. The class might have attributes like color
, model
, and year
, and methods like startEngine()
and stopEngine()
.
Inheritance is a mechanism in which one class inherits the properties and methods of another class. The class that inherits is called the subclass (or derived class), and the class being inherited from is called the superclass (or base class). Inheritance promotes code reuse and allows for the creation of hierarchical class structures.
Polymorphism means “many forms.” In Java, polymorphism allows objects of different classes to be treated as objects of a common superclass. This can be achieved through method overloading (having multiple methods with the same name but different parameters) and method overriding (providing a different implementation of a method in a subclass).
Encapsulation is the process of hiding the internal details of an object and providing a public interface to interact with it. This is done by making the class attributes private and providing getter and setter methods to access and modify the attributes. Encapsulation helps in maintaining data integrity and protecting the object from unauthorized access.
Here is an example of creating a simple Person
class and an object of that class:
// Define the Person class
class Person {
// Attributes
String name;
int age;
// Constructor
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Method
public void introduce() {
System.out.println("My name is " + name + " and I am " + age + " years old.");
}
}
public class Main {
public static void main(String[] args) {
// Create an object of the Person class
Person person = new Person("John", 25);
person.introduce();
}
}
Let’s create a Student
class that inherits from the Person
class:
// Define the Student class that inherits from Person
class Student extends Person {
String studentId;
public Student(String name, int age, String studentId) {
super(name, age);
this.studentId = studentId;
}
public void study() {
System.out.println(name + " is studying.");
}
}
public class Main {
public static void main(String[] args) {
Student student = new Student("Alice", 20, "S123");
student.introduce();
student.study();
}
}
Here is an example of method overloading and method overriding:
// Method overloading
class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
// Method overriding
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound.");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks.");
}
}
public class Main {
public static void main(String[] args) {
Calculator calculator = new Calculator();
System.out.println(calculator.add(2, 3));
System.out.println(calculator.add(2.5, 3.5));
Animal animal = new Dog();
animal.makeSound();
}
}
Here is an example of encapsulating the Person
class:
class Person {
// Private attributes
private String name;
private int age;
// Getter methods
public String getName() {
return name;
}
public int getAge() {
return age;
}
// Setter methods
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
if (age >= 0) {
this.age = age;
}
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person();
person.setName("Jane");
person.setAge(30);
System.out.println("Name: " + person.getName() + ", Age: " + person.getAge());
}
}
Inheritance and composition (using objects of one class as attributes in another class) are two common ways to achieve code reusability. By reusing code, we can save development time and reduce the chances of introducing bugs.
Modularity means dividing the code into smaller, independent modules. Each module should have a single, well-defined responsibility. This makes the code easier to understand, maintain, and test.
Encapsulation and proper naming conventions contribute to code maintainability. By hiding the internal details of an object and using meaningful names for classes, methods, and variables, it becomes easier to understand and modify the code in the future.
Choose names that clearly describe the purpose of the class or method. For example, instead of using a generic name like doSomething()
, use a more descriptive name like calculateTotalPrice()
.
Each class should have only one reason to change. This means that a class should have a single, well-defined responsibility. If a class has multiple responsibilities, it becomes harder to maintain and test.
Global variables can make the code harder to understand and maintain. They can also lead to naming conflicts and make it difficult to track down bugs. Instead, use local variables and pass data between methods and classes as needed.
Object-Oriented Programming in Java is a powerful paradigm that offers many benefits, including code reusability, modularity, and maintainability. By understanding the fundamental concepts of classes, objects, inheritance, polymorphism, and encapsulation, and following the common practices and best practices, beginners can write efficient and robust Java code. As you continue to learn and practice, you will discover more advanced OOP techniques and how they can be applied to real-world scenarios.