Understanding Java Object-Oriented Programming Concepts
Classes and Objects
Class Fundamentals
A class serves as a blueprint defining common attributes and behaviors for a group of related objects. A object is a concrete instance created from that blueprint.
In Java, the development workflow typically follows this pattern:
- Define the class structure first
- Instantiate objects from that class
Class Declaration Syntax
public class Product {
// instance variables (attributes)
private String productId;
private String productName;
private double price;
// instance methods (behaviors)
public void displayInfo() {
System.out.println("Product: " + productName + ", Price: " + price);
}
}
Object Creation
// Basic syntax
new ClassName();
// Common pattern: assign to variable for easier manipulation
Product prod = new Product();
Each class acts as a custom data type (reference type). When an object is instantiated, it gains access to all defined properties and methods.
Accessing Members
object.propertyName; // accessing instance variable (no parentheses)
object.methodName(); // invoking instance method (with parentheses)
prod.productId = "P001";
prod.displayInfo();
Object Initialization
Assign values to properties using the dot operator:
object.propertyName = value;
prod.price = 99.99;
Overriding toString()
Override this method in entity classes to control what information displays when printing objects.
Encapsulation
Constructor Methods
Constructors initialize object properties during instantiation.
public class Employee {
private int employeeId;
private String employeeName;
private String department;
private double salary;
// Default constructor (no parameters)
public Employee() {
}
// Parameterized constructor (initializes all fields)
public Employee(int employeeId, String employeeName, String department, double salary) {
this.employeeId = employeeId;
this.employeeName = employeeName;
this.department = department;
this.salary = salary;
}
}
Key characteristics:
- Constructors have no return type (not even void)
- Constructor name must match the class name exactly
- Multiple constructors with different parameter lists create overloaded constructors
- Constructors execute automatically when objects are created
- Pass arguments during object creation:
new Employee(1001, "Alice", "IT", 5000.0);
The this Keyword
When local variables share names with instance variables, this distinguishes between them:
public void setEmployeeId(int employeeId) {
this.employeeId = employeeId; // this.variableName refers to instance variable
}
Important note: If no constructor is explicitly defined, the JVM automatically provides a default no-argument constructor. Once any constructor is defined manually, the JVM stops generating the default one.
Data Encapsulation Pattern
Encapsulation protects object state by controlling external access to internal data.
Step 1: Make instance variables private
private String bookTitle;
private String author;
private int publishYear;
Step 2: Provide public getter and setter methods
public class Book {
private String bookTitle;
// Setter method (mutator)
public void setBookTitle(String bookTitle) {
this.bookTitle = bookTitle;
}
// Getter method (accessor)
public String getBookTitle() {
return this.bookTitle;
}
}
Best practice: Always encapsulate entity class properties.
Access Modifiers
Control visibility of class members:
| Modifier | Accessibility |
|---|---|
public |
Accessible from any class |
protected |
Accessible within same package and subclasses |
| default (no modifier) | Accessible within same package only |
private |
Accessible within declaring class only |
Practical Example: Library Management System
Member.java (Entity Class)
package library;
public class Member {
private String memberId;
private String registrationDate;
private int borrowedBooks;
public String getMemberId() {
return memberId;
}
public void setMemberId(String memberId) {
this.memberId = memberId;
}
public String getRegistrationDate() {
return registrationDate;
}
public void setRegistrationDate(String registrationDate) {
this.registrationDate = registrationDate;
}
public int getBorrowedBooks() {
return borrowedBooks;
}
public void setBorrowedBooks(int borrowedBooks) {
this.borrowedBooks = borrowedBooks;
}
@Override
public String toString() {
return "Member{id='" + memberId + "', registration='" + registrationDate + "', books=" + borrowedBooks + "}";
}
}
LibrarySystem.java (Main Application)
package library;
import java.util.Scanner;
public class LibrarySystem {
private static Scanner input = new Scanner(System.in);
private static Member[] members = new Member[200];
private static int memberCount = 0;
public static void displayMenu() {
System.out.println("=== Library Management System ===");
System.out.println("--------------------------------");
System.out.println("1. Register New Member");
System.out.println("2. Update Member Information");
System.out.println("3. Search Member");
System.out.println("4. List All Members");
System.out.println("5. Exit System");
System.out.println("--------------------------------");
}
public static void registerMember() {
System.out.println("Enter member ID:");
String id = input.next();
System.out.println("Enter registration date (MM/DD):");
String date = input.next();
System.out.println("Enter number of borrowed books:");
int count = input.nextInt();
Member newMember = new Member();
newMember.setMemberId(id);
newMember.setRegistrationDate(date);
newMember.setBorrowedBooks(count);
members[memberCount] = newMember;
memberCount++;
System.out.println("Member registration successful!");
}
public static void modifyMember() {
System.out.println("Enter member ID to modify:");
String id = input.next();
System.out.println("--------------------------------");
System.out.println("1. Update Registration Date");
System.out.println("2. Update Borrowed Books Count");
System.out.println("--------------------------------");
String selection = input.next();
switch (selection) {
case "1":
updateRegistrationDate(id);
break;
case "2":
updateBorrowedCount(id);
break;
default:
System.out.println("Invalid selection!");
}
}
private static void updateRegistrationDate(String id) {
for (int i = 0; i < memberCount; i++) {
if (members[i].getMemberId().equals(id)) {
System.out.println("Enter new registration date:");
String newDate = input.next();
members[i].setRegistrationDate(newDate);
System.out.println("Registration date updated!");
return;
}
}
System.out.println("Member not found!");
}
private static void updateBorrowedCount(String id) {
for (int i = 0; i < memberCount; i++) {
if (members[i].getMemberId().equals(id)) {
System.out.println("Enter new borrowed books count:");
int newCount = input.nextInt();
members[i].setBorrowedBooks(newCount);
System.out.println("Borrowed count updated!");
return;
}
}
System.out.println("Member not found!");
}
public static void searchMember() {
System.out.println("Enter member ID to search:");
String id = input.next();
boolean found = false;
for (int i = 0; i < memberCount; i++) {
if (members[i].getMemberId().equals(id)) {
System.out.println("ID\t\tDate\t\tBooks");
System.out.println(members[i].getMemberId() + "\t\t" +
members[i].getRegistrationDate() + "\t\t" +
members[i].getBorrowedBooks());
found = true;
break;
}
}
if (!found) {
System.out.println("No member found with specified ID!");
}
}
public static void listAllMembers() {
if (memberCount == 0) {
System.out.println("No members registered yet!");
} else {
System.out.println("ID\t\tDate\t\tBooks");
for (int i = 0; i < memberCount; i++) {
System.out.println(members[i].getMemberId() + "\t\t" +
members[i].getRegistrationDate() + "\t\t" +
members[i].getBorrowedBooks());
}
}
}
public static void main(String[] args) {
while (true) {
displayMenu();
System.out.println("Enter your choice (1-5):");
String choice = input.next();
switch (choice) {
case "1":
registerMember();
break;
case "2":
modifyMember();
break;
case "3":
searchMember();
break;
case "4":
listAllMembers();
break;
case "5":
System.out.println("Exiting system. Goodbye!");
System.exit(0);
break;
default:
System.out.println("Invalid option. Please try again.");
}
}
}
}
Inheritance
The static Keyword
Static Variables:
Shared across all instances. Only one copy exists in memory.
public class Configuration {
public static String APP_NAME = "InventorySystem";
public static int MAX_ITEMS = 1000;
}
// Access via class name (preferred)
String appName = Configuration.APP_NAME;
Static Methods:
Typically used in utility classes. Can be invoked using class name without creating objects.
public static int add(int a, int b) {
return a + b;
}
// Call using class name
int result = Calculator.add(5, 3);
Restriction: Static methods can only access static members directly.
Static Blocks:
static {
// Initialization code runs once when class loads
System.out.println("Configuration initialized");
}
Compare with instance blocks (run every time an object is created):
{
// Runs for each object instantiation
}
Inheritance Basics
Inheritance establishes a parent-child relationship between classes, enabling code reuse.
public class Vehicle {
protected String brand;
protected int speed;
public void displayInfo() {
System.out.println("Brand: " + brand + ", Speed: " + speed);
}
}
public class Car extends Vehicle {
private int doorCount;
public void showDetails() {
System.out.println("Brand: " + brand + ", Doors: " + doorCount);
}
}
Important rules:
- Java supports single inheritance (one direct parent per class)
- Child classes inherit all non-private members from parent
- Constructors are not inherited but can be invoked using
super() - Child class automatically calls parent no-argument constructor
The super Keyword
Represents the parent class instance.
Calling parent constructor:
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
}
public class Student extends Person {
private int grade;
public Student(String name, int grade) {
super(name); // invoke parent constructor first
this.grade = grade;
}
}
Accessing parent members:
super.brand = "Toyota"; // access inherited field
Method Overriding
Subclasses can provide specific implementations for inherited methods.
public class Animal {
public void makeSound() {
System.out.println("Some sound");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow");
}
}
Requirements:
- Method signature must match exactly
- Requires inheritance or implementation relationship
- Use
@Overrideannotation for compiler verification
Contrast with Overloading: Method overloading occurs within the same class with different parameter lists. No inheritance required.
The final Keyword
Prevents modifications:
| Usage | Effect |
|---|---|
final class |
Cannot be subclassed |
final method |
Cannot be overridden |
final variable |
Cannot be reassigned |
static final |
Compile-time constant |
public static final double PI = 3.14159265359;
public static final int MAX_CONNECTIONS = 50;
Convention: Constants use UPPERCASE names.
Package Organization
Packages organize related classes into namespaces.
package com.example.inventory; // declares package
import com.example.utils.StringHelper; // import specific class
import com.example.utils.*; // import all classes from package
Rules:
- Package names are lowercase by convention
- Without imports, classes in the same package can be accessed directly
- Cross-package access requires imports
The Object Class
Every class implicitly extends Object. Key methods:
equals() method:
By default, compares object references (memory addresses). Many classes override this behavior.
String s1 = "Hello";
String s2 = "Hello";
s1.equals(s2); // true because String overrides equals()
toString() method:
Returns string representation. Commonly overridden in entity classes.
hashCode() method:
Returns numeric identifier used in hash-based collections.
getClass() method:
Returns the runtime class of the object.
Custom classes should override equals() and hashCode() when needed for proper comparison in collections.
Abstract Classes and Interfaces
Abstract Classes
Define incomplete templates that must be completed by subclasses.
public abstract class Shape {
protected String color;
// Abstract method (no body)
public abstract double calculateArea();
// Concrete method
public void displayColor() {
System.out.println("Color: " + color);
}
}
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
this.color = "Red";
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
Rules:
- Abstract classes cannot be instantiated directly
- Classes containing abstract methods must be declared abstract
- Subclasses must implement all abstract methods (unless they are also abstract)
- Abstract classes may contain both abstract and concrete methods
staticandabstractcannot be combined- Abstract methods cannot have bodies
Interfaces
Interfaces define contracts with only abstract methods (prior to Java 8).
public interface Drawable {
void draw();
void resize(int width, int height);
}
public class Rectangle implements Drawable {
@Override
public void draw() {
System.out.println("Drawing rectangle");
}
@Override
public void resize(int width, int height) {
System.out.println("Resizing to " + width + "x" + height);
}
}
Key points:
- Use
implementskeyword for class-interface relationship - Interfaces cannot be instantiated
- Classes must implement all interface methods
- Interface methods are implicitly public (modifier can be omitted)
- A class can implement multiple interfaces
- Classes can extend a parent class and implement interfaces simultaneously:
public class Triangle extends Shape implements Drawable, Fillable {
// must implement methods from both interfaces
}
Interface inheritance: Interfaces can extend other interfaces (supporting multiple inheritance):
public interface Clickable extends Drawable {
void onClick();
}
Purpose: Interfaces enable loose coupling and define standards that different implementations can follow.
Polymorphism
Polymorphism allows a single reference to refer to objects of different types.
Definition: One entity, multiple forms
Animal cat = new Cat(); // parent type reference, child object
Animal dog = new Dog();
Reference Type Compatibility
Requirements: Must have inheritance or implementation relationship.
Behavior in polymorphic scenarios:
| Member Type | Access Behavior |
|---|---|
| Fields | Accessed from parent class |
| Non-static methods | Accessed from child class (override) |
| Static methods | Accessed from parent class |
class Parent {
int value = 10;
void display() { System.out.println("Parent display"); }
static void show() { System.out.println("Parent show"); }
}
class Child extends Parent {
int value = 20;
void display() { System.out.println("Child display"); }
static void show() { System.out.println("Child show"); }
}
Parent obj = new Child();
obj.display(); // prints "Child display"
obj.show(); // prints "Parent show"
obj.value; // returns 10 (parent field)
Type Checking with instanceof
Determine the actual object type at runtime:
public static void describeAnimal(Animal animal) {
if (animal instanceof Cat) {
Cat c = (Cat) animal;
System.out.println("Cat with " + c.getWhiskerCount() + " whiskers");
} else if (animal instanceof Dog) {
Dog d = (Dog) animal;
System.out.println("Dog with " + d.getTailWagSpeed() + " speed");
}
}
Type Conversion
Upcasting (automatic): Child reference converted to parent type
Animal pet = new Cat(); // implicit upcast
Downcasting (manual): Parent reference converted back to child type
Cat cat = (Cat) pet; // explicit downcast
Always check with instanceof before downcasting to avoid runtime errors.
Interface Polymorphism
// Without polymorphism
UserDAOImpl userDAO = new UserDAOImpl();
// With polymorphism (recommended)
UserDAO userDAO = new UserDAOImpl();
Benefits: Loose coupling enables easier maintenance and extensibility.
Layered Architecture
Organize projects using distinct layers:
| Layer | Package | Purpose |
|---|---|---|
| Presentation | controller |
Handle user interactions |
| Business | service |
Implement business logic |
| Data Access | dao |
Manage data persistence |
Inner Classes
Anonymous Inner Classes
Create one-time implementations without explicit class declaration.
Common uses:
- Implement interfaces on the fly
- Extend abstract classes quickly
- Create single-use objects
// Interface definition
interface ClickHandler {
void onClick();
}
// Using anonymous inner class
ClickHandler handler = new ClickHandler() {
@Override
public void onClick() {
System.out.println("Button clicked!");
}
};
handler.onClick();
// Common scenario: passing as method parameter
button.setOnClickListener(new ClickHandler() {
@Override
public void onClick() {
// handle click event
}
});
Characteristics:
- No class name (anonymous)
- Defined and instantiated in single expression
- Must extend a class or implement an interface
- Can access enclosing class members and final local variables