Day 6: Classes and Objects
What You'll Learn Today
- Classes and objects fundamentals
- Fields and methods
- Constructors
- Access modifiers
- Static members
- Records (Java 16+)
Classes and Objects
A class is a blueprint, and an object is an instance created from that blueprint.
// Defining a class (blueprint)
public class Dog {
// Fields (attributes)
String name;
int age;
// Method (behavior)
void bark() {
System.out.println(name + ": Woof!");
}
}
// Creating an object (instantiation)
Dog dog1 = new Dog();
dog1.name = "Buddy";
dog1.age = 3;
dog1.bark(); // Buddy: Woof!
flowchart LR
subgraph Class["Class (Blueprint)"]
CD["Dog<br>name, age<br>bark()"]
end
subgraph Objects["Objects (Instances)"]
O1["dog1<br>Buddy, 3 yrs"]
O2["dog2<br>Max, 5 yrs"]
end
Class -->|"new"| O1
Class -->|"new"| O2
style CD fill:#3b82f6,color:#fff
style O1 fill:#22c55e,color:#fff
style O2 fill:#22c55e,color:#fff
Constructors
A constructor is a special method called when an object is created to initialize its state.
public class Dog {
String name;
int age;
// Constructor
Dog(String name, int age) {
this.name = name; // this = the current object
this.age = age;
}
void introduce() {
System.out.printf("%s (%d years old)%n", name, age);
}
}
// Usage
Dog dog = new Dog("Buddy", 3);
dog.introduce(); // Buddy (3 years old)
Constructor Overloading
public class Dog {
String name;
int age;
String breed;
// All-arguments constructor
Dog(String name, int age, String breed) {
this.name = name;
this.age = age;
this.breed = breed;
}
// Name and age only
Dog(String name, int age) {
this(name, age, "Unknown"); // Call another constructor
}
// Name only
Dog(String name) {
this(name, 0, "Unknown");
}
}
Key point: You can use
this()to call another constructor in the same class. This is known as constructor chaining.
Access Modifiers
public class BankAccount {
private String owner;
private double balance;
public BankAccount(String owner, double initialBalance) {
this.owner = owner;
this.balance = initialBalance;
}
// getter
public double getBalance() {
return balance;
}
// deposit
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
// withdraw
public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
}
| Modifier | Same Class | Same Package | Subclass | Anywhere |
|---|---|---|---|---|
private |
Yes | No | No | No |
| (default) | Yes | Yes | No | No |
protected |
Yes | Yes | Yes | No |
public |
Yes | Yes | Yes | Yes |
flowchart TB
subgraph Access["Access Modifier Scope"]
Private["private<br>Same class only"]
Default["(default)<br>Same package"]
Protected["protected<br>+ Subclasses"]
Public["public<br>Anywhere"]
end
Private --> Default --> Protected --> Public
style Private fill:#ef4444,color:#fff
style Default fill:#f59e0b,color:#fff
style Protected fill:#3b82f6,color:#fff
style Public fill:#22c55e,color:#fff
Best practice: Keep fields
privateand providegetter/settermethods as needed. This principle is called encapsulation.
Getters and Setters
public class Student {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
setScore(score);
}
// getters
public String getName() { return name; }
public int getScore() { return score; }
// setter (with validation)
public void setScore(int score) {
if (score >= 0 && score <= 100) {
this.score = score;
} else {
throw new IllegalArgumentException("Score must be between 0 and 100");
}
}
}
Static Members
A static member belongs to the class itself, not to any particular object.
public class Counter {
private static int count = 0; // shared across all objects
private String name;
Counter(String name) {
this.name = name;
count++;
}
// static method
static int getCount() {
return count;
}
void show() {
System.out.printf("%s (total: %d)%n", name, count);
}
}
// Usage
Counter a = new Counter("A");
Counter b = new Counter("B");
Counter c = new Counter("C");
System.out.println(Counter.getCount()); // 3
| Type | Belongs To | Access Pattern |
|---|---|---|
| Instance member | Object | obj.method() |
| Static member | Class | ClassName.method() |
Note: Static methods cannot access
thisor non-static members.
The toString Method
Customize the string representation of an object.
public class Dog {
private String name;
private int age;
Dog(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return String.format("Dog{name='%s', age=%d}", name, age);
}
}
Dog dog = new Dog("Buddy", 3);
System.out.println(dog); // Dog{name='Buddy', age=3}
Records (Java 16+)
Records provide a concise way to define classes whose sole purpose is to hold data.
// Traditional class (lots of boilerplate)
public class Point {
private final int x;
private final int y;
Point(int x, int y) { this.x = x; this.y = y; }
int x() { return x; }
int y() { return y; }
// + equals, hashCode, toString...
}
// Record (equivalent functionality in one line)
record Point(int x, int y) {}
Records automatically generate:
- A constructor
- Accessor methods (
x(),y()) equals(),hashCode(),toString()
record Student(String name, int score) {
// You can add custom methods
String grade() {
if (score >= 90) return "A";
if (score >= 80) return "B";
if (score >= 70) return "C";
return "D";
}
}
var s = new Student("Alice", 85);
System.out.println(s.name()); // Alice
System.out.println(s.score()); // 85
System.out.println(s.grade()); // B
System.out.println(s); // Student[name=Alice, score=85]
Recommendation: Use records for classes that simply hold data.
Hands-On: Bank Account System
public class BankAccount {
private static int nextId = 1;
private final int id;
private String owner;
private double balance;
public BankAccount(String owner, double initialBalance) {
this.id = nextId++;
this.owner = owner;
this.balance = initialBalance;
}
public BankAccount(String owner) {
this(owner, 0);
}
public void deposit(double amount) {
if (amount <= 0) {
System.out.println("Deposit amount must be positive.");
return;
}
balance += amount;
System.out.printf("[Account %d] Deposited $%,.2f -> Balance: $%,.2f%n", id, amount, balance);
}
public boolean withdraw(double amount) {
if (amount <= 0 || amount > balance) {
System.out.println("Withdrawal failed.");
return false;
}
balance -= amount;
System.out.printf("[Account %d] Withdrew $%,.2f -> Balance: $%,.2f%n", id, amount, balance);
return true;
}
public void transfer(BankAccount to, double amount) {
if (this.withdraw(amount)) {
to.deposit(amount);
System.out.printf("-> Transferred $%,.2f from %s to %s%n", amount, this.owner, to.owner);
}
}
@Override
public String toString() {
return String.format("Account %d: %s (Balance: $%,.2f)", id, owner, balance);
}
public static void main(String[] args) {
var alice = new BankAccount("Alice", 1000.00);
var bob = new BankAccount("Bob", 500.00);
System.out.println(alice);
System.out.println(bob);
System.out.println();
alice.deposit(300.00);
bob.withdraw(100.00);
alice.transfer(bob, 250.00);
System.out.println();
System.out.println(alice);
System.out.println(bob);
}
}
Summary
| Concept | Description |
|---|---|
| Class | A blueprint for creating objects |
| Object | An instance created from a class |
| Constructor | Initializes an object upon creation |
this |
A reference to the current object |
| Access modifiers | private / protected / public |
| Encapsulation | Protecting fields by making them private |
static |
A member that belongs to the class |
| Record | A concise data-holding class (Java 16+) |
Key Takeaways
- Keep fields
private(encapsulation) - Use constructors to properly initialize objects
staticmembers are shared across the entire class- Use records for data-only classes
Exercises
Exercise 1: Basic
Create a Book class (with title, author, and page count), instantiate several books, and display their information.
Exercise 2: Applied
Create a ShoppingCart class that supports adding items, removing items, and calculating the total price.
Challenge
Using record, define a Money record (amount and currency) and implement addition and subtraction methods for the same currency. Throw an error if the currencies differ.
References
Next up: In Day 7, you'll learn about Inheritance and Polymorphism -- how to extend and reuse classes effectively.