// Organize things into classes
// We define our own data types
public class Player {
private int health = 100;
public void heal(int amount) {
health += amount; // This Player's health
}
}
// Organize things into classes
// We have a separate class for running the program
class GameState {
public static void main(String[] args) {
Player hero = new Player(20);
Player villan = new Player(10);
// Clear which player we're modifying
hero.heal(20);
villan.heal(5);
}
}
The second way is called Object-Oriented Programming (OOP). It bundles data with the code that works on that data. Java helps us do this through:
Subsubsection3.5.3.1Bundling: Keeping Related Things Together
public class BankAccount {
// Things that every account needs:
private double balance; // How much money you have
private String accountNum; // Like your account number at a real bank
private double interestRate; // How much interest you earn
// The code that works with these values:
public void addInterest() {
// Calculate interest using the account's own values
double earned = balance * interestRate;
balance = balance + earned;
}
}
Notice how the interest calculation uses both balance and interestRate? They belong together!
Subsubsection3.5.3.2Protection: Making Sure Data Changes Safely
public class Player {
private int health; // Only Player methods can change this
// Safe way to take damage - health can't go below 0
public void takeDamage(int amount) {
if (amount > health) {
health = 0; // Player is defeated
} else {
health = health - amount;
}
}
}
Now players can’t have negative health, which might break game rules!
Subsubsection3.5.3.3Interface: Making Your Class Easy to Use
BankAccount myAccount = new BankAccount("12345");
myAccount.deposit(100); // Put money in
myAccount.withdraw(50); // Take money out
double money = myAccount.getBalance(); // Check how much you have
Subsubsection3.5.3.4Invariants: Rules That Must Always Be True
public class Player {
private int health;
private int maxHealth = 100;
public void setHealth(int newHealth) {
// Check all our rules:
if (newHealth < 0) {
health = 0;
} else if (newHealth > maxHealth) {
health = maxHealth;
} else {
health = newHealth;
}
}
}
These rules are always enforced—no accidental breakage!
public boolean withdraw(int amount) {
if (amount > balance) {
return false; // Can't withdraw more than you have
}
balance -= amount;
return true; // success
}
public class Restaurant {
// Store kitchen details inside the class (encapsulation)
private double grillTemp; // Current temperature of grill
private int burgerCount; // How many burgers we can make
private int grillSpace; // How many burgers fit on grill
// Simple interface for customers:
public boolean orderCheeseburger() {
// First check if we can make the burger
if (burgerCount <= 0) {
return false; // Can't make burger - out of ingredients
}
// Complex steps hidden from customer:
setGrillTemp(375); // Heat the grill
boolean success = cookPatty(); // Try to cook the burger
if (success) {
burgerCount = burgerCount - 1; // Use up ingredients
return true; // Burger ready!
}
return false; // Something went wrong
}
// Helper method - customers don't need to see this
private boolean cookPatty() {
if (grillSpace > 0) { // If there's room on grill
grillSpace = grillSpace - 1; // Take up one spot
return true; // Cooking successful
}
return false; // Grill is full
}
}
// Customer just needs to know:
Restaurant store = new Restaurant();
boolean gotBurger = store.orderCheeseburger(); // Don't care about the details!
public class Player {
private int health; // Encapsulation
private double healBonus; // (protect the data)
public void heal(int amount) { // Abstraction
// Complex healing logic hidden from users:
applyHealingBuffs();
checkStatusEffects();
updateHealth(amount); // Users don't see these steps
}
}
class Player {
private int health;
public void takeDamage(int amount) { ... }
}
class Monster {
private int health;
public void takeDamage(int amount) { ... }
}
class Building {
private int health;
public void takeDamage(int amount) { ... }
}
Writing the same health/damage code for every destructible thing seems wasteful! OOP has two more powerful features:
// Design Recipe says: "health must be ≥ 0"
public class Player {
private int health; // Data definition + protection
public void heal(int amt) { // Methods enforce constraints
if (amt < 0) return; // No negative healing
health = Math.min(100, // Can't exceed max
health + amt);
}
}
Encapsulation protects data by bundling it with methods and restricting direct access, while abstraction hides complex implementation details behind a simpler interface.
Exactly. Encapsulation is about bundling and protection, whereas abstraction is about hiding complexity.
Encapsulation means we cannot write constructors; abstraction means we cannot have any private fields.
Incorrect. Constructors and private fields are independent of these definitions.
Encapsulation forces all data to be global, while abstraction forbids using methods longer than 5 lines.
No. Encapsulation encourages hiding data, not making it global, and there’s no rule about method line limits.
There is no real difference. Both terms refer to exactly the same concept in Java.
In the section, we saw that a Player class can prevent negative health by restricting access to its health field. In 1–2 sentences, explain how you would enforce the “health ≥ 0” rule if someone tries p1.setHealth(-999) or p1.takeDamage(1000).
Sample Solution: I’d make health private and provide methods like setHealth or takeDamage that check if the new health would be negative, then set it to 0 instead. This ensures outsiders can’t directly write p1.health = -999, preserving the invariant “health ≥ 0.”
Checkpoint3.5.4.Parsons: Encapsulate the Player’s Data.
Below is a short snippet of “scattered” code for a game. Rearrange the following lines to create a single, valid Player class with private fields and methods heal and takeDamage enforcing “health ≥ 0.”
Checkpoint3.5.5.Multiple Choice: Ways to Enforce Constraints.
In “Different Ways to Enforce Rules,” we saw strategies like constructor checks, method guards, and range enforcement. Which approach forcibly rejects or denies an invalid operation, often returning success/failure as a boolean?
Checkpoint3.5.6.Reflection: OOP vs. Non-OOP Approaches.
Think back to the “Two Ways: Keep or Package Pieces” example. In 2–4 sentences, explain how making a Player class with private data and methods (like heal) helps prevent the mistakes we saw in the scattered code (where direct global variables were easy to misuse).
Sample Solution: A Player class keeps all relevant data and logic together, so we can’t just accidentally do playerHealth = -999 or pass the wrong variable order. private fields force changes through safe methods that enforce “health ≥ 0.” This cuts down on guesswork and makes the code clearer by grouping data and actions in one place.