1. java
  2. /oop
  3. /classes-objects

Master Java Classes and Objects Fundamentals

Classes and Objects in Java

Classes and objects are the fundamental building blocks of Java programming. A class serves as a blueprint or template for creating objects, while objects are instances of classes that contain actual data and can perform actions.

What is a Class?

A class is a user-defined blueprint or prototype from which objects are created. It represents the set of properties or methods that are common to all objects of one type.

Class Structure

public class ClassName {
    // Fields (instance variables)
    private dataType fieldName;
    
    // Constructor
    public ClassName(parameters) {
        // Initialization code
    }
    
    // Methods
    public returnType methodName(parameters) {
        // Method body
        return value;
    }
}

What is an Object?

An object is an instance of a class. When a class is defined, no memory is allocated until objects of that class are created. Objects have:

  • State: Represented by attributes/fields
  • Behavior: Represented by methods
  • Identity: Unique identification

Class Definition Example

public class Student {
    // Instance variables (fields)
    private String name;
    private int age;
    private String studentId;
    private double gpa;
    
    // Default constructor
    public Student() {
        this.name = "Unknown";
        this.age = 0;
        this.studentId = "000000";
        this.gpa = 0.0;
    }
    
    // Parameterized constructor
    public Student(String name, int age, String studentId) {
        this.name = name;
        this.age = age;
        this.studentId = studentId;
        this.gpa = 0.0;
    }
    
    // Full constructor
    public Student(String name, int age, String studentId, double gpa) {
        this.name = name;
        this.age = age;
        this.studentId = studentId;
        this.gpa = gpa;
    }
    
    // Getter methods
    public String getName() {
        return name;
    }
    
    public int getAge() {
        return age;
    }
    
    public String getStudentId() {
        return studentId;
    }
    
    public double getGpa() {
        return gpa;
    }
    
    // Setter methods
    public void setName(String name) {
        if (name != null && !name.trim().isEmpty()) {
            this.name = name;
        }
    }
    
    public void setAge(int age) {
        if (age >= 0 && age <= 120) {
            this.age = age;
        }
    }
    
    public void setGpa(double gpa) {
        if (gpa >= 0.0 && gpa <= 4.0) {
            this.gpa = gpa;
        }
    }
    
    // Business methods
    public void study(String subject) {
        System.out.println(name + " is studying " + subject);
    }
    
    public boolean isHonorStudent() {
        return gpa >= 3.5;
    }
    
    public String getGradeLevel() {
        if (gpa >= 3.7) return "A";
        else if (gpa >= 3.3) return "B+";
        else if (gpa >= 3.0) return "B";
        else if (gpa >= 2.7) return "C+";
        else if (gpa >= 2.0) return "C";
        else return "Below C";
    }
    
    // toString method for string representation
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", studentId='" + studentId + '\'' +
                ", gpa=" + gpa +
                '}';
    }
}

Object Creation and Usage

public class StudentDemo {
    public static void main(String[] args) {
        // Creating objects using different constructors
        
        // Using default constructor
        Student student1 = new Student();
        System.out.println("Default student: " + student1);
        
        // Using parameterized constructor
        Student student2 = new Student("Alice Johnson", 20, "STU001");
        System.out.println("Student 2: " + student2);
        
        // Using full constructor
        Student student3 = new Student("Bob Smith", 19, "STU002", 3.8);
        System.out.println("Student 3: " + student3);
        
        // Using setter methods
        student1.setName("Charlie Brown");
        student1.setAge(21);
        student1.setGpa(3.2);
        
        // Using getter methods
        System.out.println("Student 1 name: " + student1.getName());
        System.out.println("Student 1 GPA: " + student1.getGpa());
        
        // Calling business methods
        student2.study("Mathematics");
        student3.study("Computer Science");
        
        // Checking honor status
        System.out.println(student2.getName() + " is honor student: " + 
                         student2.isHonorStudent());
        System.out.println(student3.getName() + " grade level: " + 
                         student3.getGradeLevel());
    }
}

Key Concepts

Instance Variables (Fields)

public class Car {
    // Instance variables - each object has its own copy
    private String brand;
    private String model;
    private int year;
    private double price;
    private boolean isRunning;
    
    // These variables store the state of each car object
}

The this Keyword

The this keyword refers to the current object instance:

public class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;  // this.name refers to instance variable
        this.age = age;    // age parameter shadows instance variable
    }
    
    public void setName(String name) {
        this.name = name;  // Distinguish between parameter and field
    }
    
    public Person getOlderPerson(Person other) {
        if (this.age > other.age) {
            return this;   // Return current object
        } else {
            return other;
        }
    }
}

Method Overloading

Multiple methods with the same name but different parameters:

public class Calculator {
    // Method overloading - same name, different parameters
    public int add(int a, int b) {
        return a + b;
    }
    
    public double add(double a, double b) {
        return a + b;
    }
    
    public int add(int a, int b, int c) {
        return a + b + c;
    }
    
    public String add(String a, String b) {
        return a + b;
    }
}

Memory Management

Stack vs Heap

public class MemoryExample {
    public static void main(String[] args) {
        // Primitive variables stored in stack
        int number = 10;
        boolean flag = true;
        
        // Object reference stored in stack, object data in heap
        Student student = new Student("John", 20, "STU001");
        
        // Multiple references can point to same object
        Student anotherRef = student;  // Both point to same object in heap
        
        // Creating new object
        Student student2 = new Student("Jane", 19, "STU002");
    }
}

Object Lifecycle

  1. Creation: Memory allocated in heap
  2. Usage: Object methods called, state modified
  3. Garbage Collection: When no references exist, object is eligible for GC

Advanced Concepts

Object Initialization Block

public class Example {
    private int value;
    
    // Instance initialization block - runs before constructor
    {
        System.out.println("Instance initialization block");
        value = 10;
    }
    
    // Static initialization block - runs once when class is loaded
    static {
        System.out.println("Static initialization block");
    }
    
    public Example() {
        System.out.println("Constructor");
    }
}

Nested Classes

public class OuterClass {
    private int outerField = 10;
    
    // Non-static nested class (inner class)
    public class InnerClass {
        public void display() {
            System.out.println("Outer field: " + outerField);
        }
    }
    
    // Static nested class
    public static class StaticNestedClass {
        public void display() {
            System.out.println("Static nested class");
        }
    }
}

// Usage
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
OuterClass.StaticNestedClass nested = new OuterClass.StaticNestedClass();

Real-World Example: Library Management System

public class Book {
    private String isbn;
    private String title;
    private String author;
    private boolean isAvailable;
    private String borrowerName;
    private Date borrowDate;
    
    public Book(String isbn, String title, String author) {
        this.isbn = isbn;
        this.title = title;
        this.author = author;
        this.isAvailable = true;
        this.borrowerName = null;
        this.borrowDate = null;
    }
    
    public boolean borrowBook(String borrowerName) {
        if (isAvailable) {
            this.isAvailable = false;
            this.borrowerName = borrowerName;
            this.borrowDate = new Date();
            System.out.println("Book '" + title + "' borrowed by " + borrowerName);
            return true;
        } else {
            System.out.println("Book '" + title + "' is not available");
            return false;
        }
    }
    
    public void returnBook() {
        if (!isAvailable) {
            System.out.println("Book '" + title + "' returned by " + borrowerName);
            this.isAvailable = true;
            this.borrowerName = null;
            this.borrowDate = null;
        } else {
            System.out.println("Book '" + title + "' was not borrowed");
        }
    }
    
    public void displayInfo() {
        System.out.println("ISBN: " + isbn);
        System.out.println("Title: " + title);
        System.out.println("Author: " + author);
        System.out.println("Available: " + isAvailable);
        if (!isAvailable) {
            System.out.println("Borrowed by: " + borrowerName);
            System.out.println("Borrow date: " + borrowDate);
        }
    }
    
    // Getters
    public String getIsbn() { return isbn; }
    public String getTitle() { return title; }
    public String getAuthor() { return author; }
    public boolean isAvailable() { return isAvailable; }
}

// Usage example
public class LibraryDemo {
    public static void main(String[] args) {
        // Creating book objects
        Book book1 = new Book("978-0134685991", "Effective Java", "Joshua Bloch");
        Book book2 = new Book("978-0135166307", "Java: The Complete Reference", "Herbert Schildt");
        
        // Display book information
        book1.displayInfo();
        System.out.println();
        
        // Borrow books
        book1.borrowBook("Alice Smith");
        book2.borrowBook("Bob Johnson");
        
        System.out.println();
        
        // Try to borrow already borrowed book
        book1.borrowBook("Charlie Brown");
        
        System.out.println();
        
        // Return book
        book1.returnBook();
        
        // Borrow again
        book1.borrowBook("Charlie Brown");
    }
}

Best Practices

Class Design

public class WellDesignedClass {
    // 1. Use private fields for encapsulation
    private String name;
    private int value;
    
    // 2. Provide constructors for object initialization
    public WellDesignedClass(String name, int value) {
        setName(name);  // Use setter for validation
        setValue(value);
    }
    
    // 3. Validate input in setters
    public void setName(String name) {
        if (name == null || name.trim().isEmpty()) {
            throw new IllegalArgumentException("Name cannot be null or empty");
        }
        this.name = name;
    }
    
    public void setValue(int value) {
        if (value < 0) {
            throw new IllegalArgumentException("Value cannot be negative");
        }
        this.value = value;
    }
    
    // 4. Provide meaningful getters
    public String getName() {
        return name;
    }
    
    public int getValue() {
        return value;
    }
    
    // 5. Override toString() for debugging
    @Override
    public String toString() {
        return "WellDesignedClass{name='" + name + "', value=" + value + "}";
    }
    
    // 6. Override equals() and hashCode() when needed
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        WellDesignedClass that = (WellDesignedClass) obj;
        return value == that.value && Objects.equals(name, that.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, value);
    }
}

Common Mistakes to Avoid

  1. Making all fields public

    // Bad
    public class BadClass {
        public String name;  // Direct access, no validation
        public int age;
    }
    
    // Good
    public class GoodClass {
        private String name;  // Encapsulated
        private int age;
        
        public void setAge(int age) {
            if (age >= 0) this.age = age;  // Validation
        }
    }
    
  2. Not providing constructors

    // Bad - relying only on default constructor
    Student student = new Student();
    student.setName("John");  // Multiple method calls needed
    student.setAge(20);
    
    // Good - parameterized constructor
    Student student = new Student("John", 20, "STU001");
    
  3. Not validating input

    // Bad
    public void setAge(int age) {
        this.age = age;  // No validation
    }
    
    // Good
    public void setAge(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("Invalid age: " + age);
        }
        this.age = age;
    }
    

Summary

  • Classes are blueprints that define the structure and behavior of objects
  • Objects are instances of classes that contain actual data and state
  • Use encapsulation to protect data with private fields and public methods
  • Constructors initialize objects when they are created
  • The this keyword refers to the current object instance
  • Method overloading allows multiple methods with the same name but different parameters
  • Follow best practices for class design to create maintainable and robust code

Understanding classes and objects is fundamental to Java programming and forms the foundation for all other object-oriented concepts.