Skip to content

Java Object & Classes

Java is fundamentally an Object-Oriented Programming (OOP) language. OOP models software design based on real-world objects. Key OOP concepts supported by Java include:

  • Encapsulation: Bundling data (fields) and methods that operate on the data within a single unit (a class), and controlling access to the internal state.
  • Inheritance: Allowing a new class (subclass) to inherit properties and methods from an existing class (superclass), promoting code reuse.
  • Polymorphism: Enabling objects to take on many forms; typically, allowing a subclass object to be treated as an object of its superclass, and overriding methods for specific behavior.
  • Abstraction: Hiding complex implementation details and exposing only essential features of an object or system.

This chapter focuses on the building blocks of OOP in Java: Classes and Objects.

  • Class: A blueprint or template used to create objects. It defines the common properties (fields or attributes) and behaviors (methods) that all objects of that class will have. Example: A Car class defines properties like color, make, model and behaviors like startEngine(), accelerate(), brake().
  • Object: An instance of a class. It represents a specific entity created from the class blueprint, with its own state (specific values for the fields). Example: myBlueToyotaCamry is an object (an instance) of the Car class, with color set to “blue”, make to “Toyota”, and model to “Camry”.

Think about real-world objects: your phone, a desk, a pet. Each has:

  • State: Characteristics or attributes (e.g., a dog’s name, breed, color, age).
  • Behavior: Actions it can perform (e.g., a dog can bark, wag its tail, eat, sleep).

Software objects in Java mirror this. Their state is stored in fields (also called instance variables), and their behavior is implemented through methods.

Methods operate on an object’s internal state (its fields) and are the primary way objects interact with each other.

A class is the blueprint. It defines what all objects created from it will look like and how they will behave.

Basic Class Structure:

// Access Modifier class ClassName {
//
// // Fields (Instance Variables) - Define the state
// dataType fieldName1;
// dataType fieldName2;
// // ...
//
// // Constructors - Special methods for creating objects
// ClassName(parameters) {
// // Initialization code
// }
//
// // Methods - Define the behavior
// returnType methodName1(parameters) {
// // Method body (code)
// }
// // ...
//}

Example: A Simple Dog Class:

public class Dog {
// Fields (Instance Variables)
String breed;
int age;
String color;
String name;
// Constructor
public Dog(String dogName, String dogBreed, int dogAge, String dogColor) {
// 'this' keyword refers to the current object being created
this.name = dogName;
this.breed = dogBreed;
this.age = dogAge;
this.color = dogColor;
System.out.println("Dog object created: " + this.name);
}
// Methods (Behavior)
void bark() {
System.out.println(name + " says: Woof!");
}
void celebrateBirthday() {
this.age++; // Increment the age of this specific dog object
System.out.println("Happy Birthday, " + name + "! You are now " + age + ".");
}
}

Variable Types within a Class:

  • Instance Variables (Fields): Declared inside the class but outside any method. Each object (instance) of the class gets its own copy of these variables. They represent the object’s state (e.g., breed, age, color, name in the Dog class).
  • Local Variables: Declared inside methods, constructors, or blocks. They exist only during the execution of that block of code and are not part of the object’s state.
  • Class Variables (Static Fields): Declared with the static keyword. There is only one copy of a static variable shared among all objects of the class. Often used for constants or counters (e.g., static final int MAX_SPEED = 100; or static int dogCount = 0;).

Constructors are special methods used to initialize new objects. They have the same name as the class and no return type (not even void).

  • Every class has at least one constructor.
  • If you don’t explicitly define any constructor, the Java compiler provides a default no-argument constructor (e.g., public Dog() {}).
  • If you define any constructor (like the one in our Dog example), the default no-argument constructor is not automatically provided.
  • Constructors are invoked using the new keyword when creating an object.
  • A class can have multiple constructors with different parameter lists (constructor overloading).

Example with multiple constructors:

public class Rectangle {
double width;
double height;
// Constructor 1: No arguments (creates a 1x1 rectangle)
public Rectangle() {
this.width = 1.0;
this.height = 1.0;
}
// Constructor 2: Takes width and height
public Rectangle(double w, double h) {
this.width = w;
this.height = h;
}
// Constructor 3: Takes one value for a square
public Rectangle(double side) {
this.width = side;
this.height = side;
}
// Method to calculate area
public double getArea() {
return this.width * this.height;
}
}

Creating an object involves three steps, usually combined in one statement:

  1. Declaration: Declare a variable of the class type (Dog myDog;).
  2. Instantiation: Use the new keyword to allocate memory for the object (new).
  3. Initialization: Call a constructor to set the initial state of the object (Dog("Buddy", ...)).

Example:

// Combined declaration, instantiation, and initialization
Dog myDog = new Dog("Buddy", "Golden Retriever", 3, "Golden");
Rectangle square = new Rectangle(5.0); // Calls the third constructor

You access an object’s instance variables (fields) and methods using the dot (.) operator on the object reference variable.

// 1. Create an object
ClassName objectReference = new ClassName(parameters);
// 2. Access a field (Generally discouraged for public fields, prefer methods)
// value = objectReference.fieldName;
// objectReference.fieldName = newValue;
// 3. Call a method
// objectReference.methodName(arguments);

Example using the Dog class:

public class DogPark {
public static void main(String[] args) {
// Create two Dog objects
Dog dog1 = new Dog("Buddy", "Golden Retriever", 3, "Golden");
Dog dog2 = new Dog("Lucy", "Poodle", 5, "White");
// Access fields (Direct access shown, but usually done via methods like getters/setters)
System.out.println(dog1.name + "'s age: " + dog1.age);
// Call methods
dog1.bark(); // Output: Buddy says: Woof!
dog2.bark(); // Output: Lucy says: Woof!
dog1.celebrateBirthday(); // Output: Happy Birthday, Buddy! You are now 4.
System.out.println(dog1.name + "'s new age: " + dog1.age); // Output: Buddy's new age: 4
}
}

It’s standard practice in Java to make fields private to enforce encapsulation. This means they can only be accessed or modified through methods (like getters and setters) defined within the class. This prevents external code from putting the object into an invalid state.

Example with private fields and public methods (Getters/Setters):

public class SecureBankAccount {
private String accountNumber;
private double balance;
public SecureBankAccount(String accNumber, double initialBalance) {
this.accountNumber = accNumber;
// Add validation for balance
if (initialBalance >= 0) {
this.balance = initialBalance;
}
else {
this.balance = 0;
System.err.println("Initial balance cannot be negative. Set to 0.");
}
}
// Getter for balance (allows reading)
public double getBalance() {
return balance;
}
// Getter for account number
public String getAccountNumber() {
return accountNumber;
}
// Method to deposit (allows controlled modification)
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
} else {
System.err.println("Deposit amount must be positive.");
}
}
// Method to withdraw (allows controlled modification)
public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true; // Success
} else {
System.err.println("Withdrawal failed: Invalid amount or insufficient funds.");
return false; // Failure
}
}
}

Access Modifiers (public, private, protected, default) control the visibility of classes, fields, and methods. They are crucial for encapsulation and API design.

Keep these rules in mind when organizing your Java code:

  • Each source file (.java file) can contain only one public class.
  • The name of the source file must exactly match the name of the public class it contains (e.g., the Dog class must be in a file named Dog.java).
  • A source file can contain multiple non-public (package-private) classes.
  • If the class is part of a package, the package declaration must be the very first statement in the file.
  • import statements, if present, must appear between the package statement (if any) and the class declaration(s).
  • package and import statements apply to all classes within the source file.

Packages are used to organize related classes and interfaces into namespaces, preventing naming conflicts and improving code structure (e.g., java.util, com.mycompany.myapp.ui).

Import statements allow you to use classes from other packages without specifying their full qualified name (package + class name) every time. Example: import java.util.ArrayList; lets you use ArrayList instead of java.util.ArrayList.

Let’s create two classes: Employee (the blueprint) and EmployeeTest (to create and use Employee objects).

1. Employee.java:

package com.example.hr; // Example package declaration
import java.time.LocalDate;
import java.time.Period;
public class Employee {
// Using private fields for encapsulation
private String name;
private String designation;
private double salary;
private LocalDate hireDate;
// Constructor
public Employee(String name, String designation, double salary, LocalDate hireDate) {
this.name = name;
this.designation = designation;
this.salary = salary; // Consider adding validation
this.hireDate = hireDate;
}
// Getter methods
public String getName() {
return name;
}
public String getDesignation() {
return designation;
}
public double getSalary() {
return salary;
}
public LocalDate getHireDate() {
return hireDate;
}
// Setter methods (optional, only if modification is allowed)
public void setDesignation(String designation) {
this.designation = designation;
}
public void setSalary(double salary) {
if (salary >= 0) { // Basic validation
this.salary = salary;
} else {
System.err.println("Salary cannot be negative.");
}
}
// Method to calculate years of service
public int getYearsOfService() {
return Period.between(hireDate, LocalDate.now()).getYears();
}
// Method to print employee details
public void printEmployeeDetails() {
System.out.println("---------------------------");
System.out.println("Name: " + name);
System.out.println("Designation: " + designation);
System.out.printf("Salary: $%.2f%n", salary); // Formatted output
System.out.println("Hire Date: " + hireDate);
System.out.println("Years of Service: " + getYearsOfService());
System.out.println("---------------------------");
}
}

2. EmployeeTest.java (in the same package or import the Employee class):

package com.example.hr; // Same package as Employee
// Or if in a different package:
// import com.example.hr.Employee;
import java.time.LocalDate;
import java.time.Month;
public class EmployeeTest {
public static void main(String[] args) {
// Create Employee objects using the constructor
Employee empOne = new Employee("James Smith", "Senior Software Engineer", 95000.00, LocalDate.of(2019, Month.MARCH, 15));
Employee empTwo = new Employee("Mary Anne", "Software Engineer", 72000.00, LocalDate.of(2021, Month.JULY, 1));
// Use methods to interact with the objects
System.out.println("Initial Employee Details:");
empOne.printEmployeeDetails();
empTwo.printEmployeeDetails();
// Modify an object using a setter method
System.out.println("\nPromoting " + empTwo.getName() + "...");
empTwo.setDesignation("Senior Software Engineer");
empTwo.setSalary(90000.00);
System.out.println("\nUpdated Employee Details:");
empOne.printEmployeeDetails(); // Unchanged
empTwo.printEmployeeDetails(); // Shows promotion
}
}

To compile and run (assuming files are in the correct package structure):

# Navigate to the base directory containing the 'com' folder
cd path/to/your/project/src # Or similar base path
# Compile both Java files (use -d to specify output directory)
mkdir ../bin # Create a directory for compiled classes (optional but good practice)
javac -d ../bin com/example/hr/Employee.java com/example/hr/EmployeeTest.java
# Run the EmployeeTest class (specify classpath)
java -cp ../bin com.example.hr.EmployeeTest

Expected output will show the details of the two employees, followed by the updated details for Mary Anne after her promotion.

Understanding classes and objects is fundamental. The next chapters will delve deeper into related concepts like inheritance, polymorphism, and basic data types in Java.