Classes and Objects
Object-oriented programming organizes code around data structures called objects, while procedural programming focuses on step-by-step instructions. Rather than implementing every operation manually, OOP allows you to direct objects to perform specific tasks.
Relasionship Between Classes and Objects
Everything that exists in reality can be represented as an object.
Class
- A class serves as a blueprint that abstracts shared attributes and behaviors from a group of similar entities
- In Java, a class functions as a custom data type, grouping together objects with identical properties and methods
- Essentially, a class describes what a certain type of entity looks like in the real world
Class Components
- Attributes: Characteristics that describe the entity, such as a phone (brand, price, screen size)
- Behaviors: Actions the entity can perform, such as a phone (make calls, send messages)
Class vs Object Relationship
- A class is an abstract template describing a category of things
- An object is a concrete instance that occupies memory and can be manipulated
Defining a Class
Classes consist of attributes (represented by member variables) and behaviors (represented by member methods).
Definition Steps:
- Declare the class
- Define member variables within the class body
- Implement member methods within the class body
public class Person {
// Attributes: name and age
// Member variables are declared at the class level, outside methods
String name;
int age;
// Behavior: working
// Member methods lack the static keyword
public void work() {
System.out.println("Working");
}
}
Creating and Using Objects
Object Creation Syntax:
ClassName referenceName = new ClassName();
Accessing Members:
- Read or modify variables:
referenceName.memberVariable - Invoke methods:
referenceName.memberMethod();
public class PersonDemo {
public static void main(String[] args) {
// Creating an object instance
Person p = new Person();
// Accessing uninitialized member variables shows default values
System.out.println(p.name); // null
System.out.println(p.age); // 0
// Assigning values to member variables
p.name = "Alice";
p.age = 25;
System.out.println(p.name); // Alice
System.out.println(p.age); // 25
// Calling member methods
p.work();
// Printing the object reference displays class name and memory address
System.out.println(p);
}
}
Practical Example: Calculator Class
Requirement: Define a calculator class, then create and use objects in a test class.
Analysis:
- Member variables: brand, price
- Member methods: add, subtract
public class Calculator {
String brand;
int price;
public int add(int a, int b) {
return a + b;
}
public int subtract(int a, int b) {
return a - b;
}
}
public class CalculatorTest {
public static void main(String[] args) {
// Instantiate the object
Calculator calc = new Calculator();
// Initialize member variables
calc.brand = "TechPro";
calc.price = 199;
// Display values
System.out.println(calc.brand + " - $" + calc.price);
// Execute operations
System.out.println("Result: " + calc.add(10, 5));
System.out.println("Result: " + calc.subtract(10, 5));
}
}
Object Memory Behavior
Single Object Memory Layout
When creating an object, Java allocates memory in the heap area. The reference variable exists in the stack, while the actual object data resides in the heap. Member variables receive default initial values until explicitly assigned.
Multiple Objects in Memory
Each object instance maintains its own separate copy of member variables in the heap. However, member methods are shared across all instances of the same class—only one copy exists in memory, regardless of how many objects are created.
Shared References
When multiple reference variables point to the same object, they hold identical memory addresses. Modifying data through any reference affects the single underlying object, and subsequent reads from any reference will reflect those changes.
Member Variables vs Local Variables
| Aspect | Member Variable | Local Variable |
|---|---|---|
| Declaration Location | Inside class, outside methods | Inside methods or as method parameters |
| Memory Area | Heap memory | Stack memory |
| Lifecycle | Exists as long as the object exists | Exists only during method execution |
| Initial Value | Assigned default values automatically | No default value; must be initialized before use |
Encapsulation
The private Access Modifier
Overview: Private is an access modifier restricting visibility to the declaring class only.
Characteristics: Members marked private cannot be accessed directly from other classes. To work with private data, provide public accessor and mutator methods.
Implementation Pattern:
class User {
// Private data cannot be accessed from outside
private String username;
private int score;
// Public setter with validation
public void setScore(int value) {
if (value < 0) {
System.out.println("Invalid score");
} else {
score = value;
}
}
// Public getter
public int getScore() {
return score;
}
public void display() {
System.out.println(username + ": " + score);
}
}
public class UserDemo {
public static void main(String[] args) {
User u = new User();
u.username = "admin";
u.setScore(100);
u.display();
}
}
Practical Application with Private Members
Requirement: Create a User class with private fields, provide getters/setters, include a display method, and verify functionality in a test class outputting "admin: 100".
class User {
private String username;
private int score;
public void setUsername(String name) {
username = name;
}
public String getUsername() {
return username;
}
public void setScore(int points) {
score = points;
}
public int getScore() {
return score;
}
public void display() {
System.out.println(username + ": " + score);
}
}
public class UserDemo {
public static void main(String[] args) {
User u = new User();
u.setUsername("admin");
u.setScore(100);
u.display();
System.out.println(u.getUsername() + " - " + u.getScore());
}
}
The this Keyword
Purpose: The this reference points to the current object instance and resolves naming conflicts between member variables and parameters.
When a method parameter shares the same name as a member variable, using the variable alone refers to the parameter. The this keyword is required to access the member variable.
public class Product {
private String productName;
private double unitPrice;
public void setProductName(String productName) {
// this.productName refers to the member variable
// productName alone refers to the parameter
this.productName = productName;
}
public String getProductName() {
return productName;
}
public void setUnitPrice(double unitPrice) {
this.unitPrice = unitPrice;
}
public double getUnitPrice() {
return unitPrice;
}
public void printDetails() {
System.out.println(productName + " - $" + unitPrice);
}
}
this Reference Internals
The this keyword holds the memory address of whichever object invoked the current method. During method execution, this acts as an implicit parameter representing the calling object.
Principles of Encapsulation
Definition: Encapsulation is one of the three pillars of OOP (alongside inheritance and polymorphism). It models how real-world objects hide their internal complexity.
Core Principle: Hide internal state within the class and expose only controlled interfaces. Mark member variables as private and provide public getter/setter methods.
Benefits:
- Protects data integrity by enforcing validation in setters
- Promotes code reuse through well-defined method interfaces
- Allows internal implemantation changes without affecting external code
Constructor Methods
Constructor Syntax and Invocation
Syntax Requirements:
- Constructor name must exactly match the class name (including capitalization)
- No return type declaration—not even void
- Cannot use return statements to return values
Execution Timing: Constructors run automatically during object instantiation, executing once per object created. You cannot invoke a constructor manually.
class Product {
private String productName;
private int stock;
// Default constructor
public Product() {
System.out.println("Default constructor executing");
}
public void show() {
System.out.println(productName + ", " + stock);
}
}
public class ProductDemo {
public static void main(String[] args) {
Product p = new Product();
p.show();
}
}
Constructor Purpose
Constructors initialize object state during creation.
public class Vehicle {
private String model;
private int mileage;
// Java provides a default no-arg constructor if none is defined
public Vehicle() {}
// Parameterized constructor
public Vehicle(String model, int mileage) {
this.model = model;
this.mileage = mileage;
System.out.println("Parameterized constructor called");
}
public void display() {
System.out.println(model + " - " + mileage + " miles");
}
}
public class VehicleTest {
public static void main(String[] args) {
Vehicle v1 = new Vehicle("Sedan", 15000);
v1.display();
Vehicle v2 = new Vehicle();
}
}
Constructor Rules and Recommendations
Automatic Generation:
- If no constructor is defined, the compiler generates a default no-arg constructor
- If any constructor is manually defined, the compiler does not generate a default constructor
Best Practice: Always explicitly define both default and parameterized constructors to ensure flexibility and prevent compilation errors when instantiating objects with different argument sets.
Complete POJO Class Implementation
public class Employee {
private String employeeName;
private int employeeId;
// No-arg constructor
public Employee() {
}
// Parameterized constructor
public Employee(String employeeName, int employeeId) {
this.employeeName = employeeName;
this.employeeId = employeeId;
}
// Getters and setters
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}
public void output() {
System.out.println(employeeName + " - ID#" + employeeId);
}
}
public class EmployeeTest {
public static void main(String[] args) {
// Create object using no-arg constructor, then set fields via setters
Employee emp1 = new Employee();
emp1.setEmployeeName("Bob");
emp1.setEmployeeId(101);
emp1.output();
// Create object using parameterized constructor
Employee emp2 = new Employee("Carol", 102);
emp2.output();
}
}