Chapter 2: Classes, Objects, and Store Modeling
Chapter 1 used loose variables and helper methods to build a tiny store workflow. Chapter 2 turns that workflow into real models. The learner starts creating Product, Customer, CartItem, and Cart style objects so data and behavior stop floating around as unrelated variables. The project stays simple, but the code becomes more structured and easier to grow.
Why Loose Variables Eventually Need Real Objects
Chapter 1 was intentionally small. A few variables and a few methods were enough to build a checkout estimate. But that style hits a limit quickly. As soon as the program must carry product details, customer information, and cart line behavior together, loose variables become harder to trust. The code still works, but it stops telling a clean story.
That is where classes enter. A class groups related state and behavior under one name. Instead of passing five separate values everywhere, you can create one `Product` object. Instead of recalculating cart line details from scattered data, you can create one `CartItem`. The real gain is not fewer variables. The gain is a clearer mental model of the program.
This chapter is still beginner-friendly. It is not about design patterns or architecture. It is about the first honest move from procedural code into object-oriented Java. Once that move is understood, the rest of the project can grow in a far more controlled way.
- Loose variables work for tiny scripts but scale poorly.
- Classes group related facts and behavior under one concept.
- Objects make the code read closer to the domain story.
- This chapter is the bridge between syntax practice and real modeling.
String productName = "Keyboard";
double unitPrice = 49.99;
int stock = 12;
// This works for a tiny script, but a Product object will soon be clearer.
Classes as Blueprints and Objects as Runtime Instances
A class describes what a kind of thing knows and what it can do. An object is one actual runtime instance of that class. In e-commerce terms, `Product` is the blueprint. One keyboard, one headset, and one mouse can each be object instances with their own values.
This distinction matters because beginners often mix the class itself with the objects created from it. A class is not a row of data. It is the type that defines the shape of that data and the operations that make sense for it. When you call `new Product(…)`, you are creating one object that matches that blueprint.
Once you see classes and objects clearly, the code becomes easier to explain. Instead of saying 'I have some variables for a product,' you can say 'I have a Product object with state and behavior.' That is the language of real Java modeling.
- A class defines the shape and behavior of a concept.
- An object is one runtime instance created from that class.
- Multiple objects can share the same class but hold different state.
- Thinking in objects makes the domain easier to describe and extend.
public class Product {
String name;
double unitPrice;
int stock;
}
Product keyboard = new Product();
keyboard.name = "Keyboard";
keyboard.unitPrice = 49.99;
keyboard.stock = 12;
Fields and State: What a Store Object Must Remember
Fields store the state of an object. In a `Product`, that state might include a name, a SKU, a unit price, and a stock count. In a `Customer`, it might include an id, email, and loyalty flag. The key beginner skill here is not only declaring fields. It is choosing fields that truly belong to the object.
Good object state reflects the real role of the type. If a value always travels with the product, it probably belongs in `Product`. If a value only matters for one cart line, it probably belongs in `CartItem`. This is how modeling begins: by deciding where data naturally lives.
A class with the wrong fields can still compile, but it will feel awkward to use. That awkwardness is useful. It tells you the model does not match the domain yet.
- Fields are the stored state of an object.
- Choose fields that naturally belong to the object’s role.
- A good model makes data placement feel obvious.
- Awkward field choices usually signal a weak domain model.
public class CartItem {
String productName;
double unitPrice;
int quantity;
}
Constructors and Creating Valid Objects From the Start
Constructors create objects and establish their first valid state. In beginner code, they are often introduced as syntax. In real modeling, they are more important than that. They are the moment where the object becomes usable.
A `Product` constructor can require a name, a price, and a stock count. A `CartItem` constructor can require a product reference and a quantity. This makes object creation explicit and reduces the chance of half-initialized data drifting through the program.
Constructors also teach a broader lesson: object setup should be intentional. If creating an object requires certain values, the code should make those values visible at the creation site instead of hiding them behind later field assignment.
- Constructors establish the initial state of an object.
- Required data should be visible when the object is created.
- Constructors help prevent half-built objects.
- Clear creation code makes the model easier to reason about.
public class Product {
String name;
double unitPrice;
int stock;
public Product(String name, double unitPrice, int stock) {
this.name = name;
this.unitPrice = unitPrice;
this.stock = stock;
}
}
Instance Methods and Behavior That Belongs Close to the Data
An object becomes more than a data bag when it owns behavior that naturally belongs to its state. A `CartItem` can calculate its own line total. A `Product` can answer whether it is in stock. A `Cart` can count how many items it currently holds.
This does not mean every method must live on the object. It means you should first ask whether the behavior depends on that object’s state. If it does, keeping the behavior near the data often makes the program easier to read.
This is one of the first places where object-oriented design starts paying off. Instead of writing `calculateLineTotal(unitPrice, quantity)` everywhere, you can call `item.lineTotal()` and let the object explain itself.
- Instance methods operate on one object’s state.
- Behavior often becomes clearer when it lives near the relevant fields.
- Objects become easier to use when they can answer domain questions directly.
- Good instance methods make outside code simpler.
public class CartItem {
String productName;
double unitPrice;
int quantity;
public double lineTotal() {
return unitPrice * quantity;
}
}
The `this` Keyword and Referring to the Current Object Clearly
The `this` keyword refers to the current object. Beginners meet it first in constructors because parameter names often match field names. In that situation, `this.unitPrice` refers to the field while `unitPrice` refers to the constructor parameter.
The deeper idea is clarity. `this` makes it obvious that the code is talking about object state. That becomes useful beyond constructors whenever you want to emphasize that a method is reading or changing the current object.
You do not need to overuse `this`, but you should understand what it means. It is part of reading object-oriented Java confidently.
- `this` points to the current object instance.
- It is commonly used to distinguish fields from parameters.
- It can also improve clarity when reading or updating object state.
- Understanding `this` is part of reading constructor-heavy code well.
public Product(String name, double unitPrice, int stock) {
this.name = name;
this.unitPrice = unitPrice;
this.stock = stock;
}
Object References and How Store Objects Work Together
Objects become more useful when they can point to one another. A `CartItem` does not need to copy every product detail as loose data. It can hold a reference to a `Product` object. A `Cart` can hold many `CartItem` objects. A `Customer` can place an order that points back to customer information.
This is where object-oriented thinking starts to feel real. The program is no longer a list of independent values. It is a small graph of connected objects. Understanding those references makes later chapters on encapsulation, collections, and object responsibility much easier.
The important beginner lesson is that an object reference is not the whole other object copied inline. It is a link to that object. That affects how updates and shared state behave.
- Objects can hold references to other objects.
- References let the model express relationships directly.
- A reference is a link to another object, not a full copy of it.
- Connected objects form the basis of richer domain models.
public class CartItem {
Product product;
int quantity;
public CartItem(Product product, int quantity) {
this.product = product;
this.quantity = quantity;
}
}
Mini Project Refactor: From Checkout Script to Product, Customer, and CartItem
The Chapter 2 milestone is a refactor, not a framework build. The learner should take the Chapter 1 checkout logic and move core state into real objects. Product data belongs in `Product`. Customer data belongs in `Customer`. Repeated product-plus-quantity data belongs in `CartItem`.
This refactor matters because it changes how the learner thinks about the code. The program is no longer just values being pushed through helper methods. It becomes a small domain model with named types and clearer responsibilities.
That is enough for Chapter 2. The next chapter exists because these objects still expose too much raw state. Once the learner feels that tension, encapsulation becomes a natural next step instead of an abstract OOP slogan.
- The Chapter 2 milestone is a refactor into objects, not a new framework layer.
- The code should read more like a domain model than a math script.
- Types such as Product, Customer, and CartItem prepare the project for cleaner rules.
- Chapter 3 will tighten these models by protecting their state more carefully.
Product keyboard = new Product("Keyboard", 49.99, 12);
Customer customer = new Customer("nima@example.com", true);
CartItem item = new CartItem(keyboard, 2);
double subtotal = item.lineTotal();
System.out.println("Subtotal for " + keyboard.name + ": " + subtotal);
Chapter takeaway
A class gives shape to related state and behavior. Once the learner can model a product, cart item, or customer as an object with fields, constructors, and instance methods, the project is ready to move from procedural scripts toward maintainable backend design.