Java OOP Vocabulary Flashcards (Lectures 3.1–3.7)

3.1 Object Oriented Programming

  • Java is an object-oriented programming language: one of the primary ways to organize programs is around objects.
  • Objects are a kind of value that combines data and the code that operates on that data into a single unit.
  • Objects are defined by classes, which provide a blueprint for creating objects of a certain kind, describing the data and code that all instances of that class have.
  • Real-world modeling: classes can represent real-world things (e.g., a Student class to model students in a school).
  • Not all classes need to map to real-world entities; some are purely organizational constructs within code.
  • In Java, all programs are built out of classes; every Java program starts with public class (at least one class must be defined).
  • This is why you see the pattern: public class … in Java programs.

3.2 Introducing Methods

  • Terminology note: in Java (and many other languages), the term "methods" is used for code that belongs to a class. In other languages you might hear "functions".
  • Prerequisites (from notes): basic Java syntax (if statements), understanding of variables and types, familiarity with the Design Recipe approach.
  • Think of methods like recipes: write once, reuse many times.
  • Objectives of this section:
    • Recognize classes and objects.
    • Describe the relationship between classes and objects.
    • Describe the difference between objects and data of primitive type.
    • Describe the difference between static and instance elements of a class.
    • Design a simple class in Java.
    • List the basic principles of object-oriented programming.

3.2.1 Why Do We Need Methods?

  • Repeated Logic Example (game health/gold):
    • Code snippet example shows health and gold increasing after wins.
    • Repeated code; if you need to change the reward amount, you’d have to edit every copy.
  • Key Insight: when you copy-paste code, it’s a sign you need a method.

3.2.1.1 Quick Check

  • Questions to answer:
    • Why is copy-pasting code problematic?
    • What happens if the reward amount changes?
    • How does this relate to the Design Recipe’s "repeated logic" warning?

3.2.2 Creating Your First Method

  • Analogy: a method is like a vending machine: you put in parameters, it does work, and you get back a return value.
  • Basic recipe:
    • public static returnType methodName(parameters) { // method body return something; }
  • Breakdown of parts:
    • public: method is accessible to other code (like a public library).
    • static: the method belongs to the class itself, not to any specific instance.
    • returnType: type of value returned by the method; use void if it returns nothing.
    • methodName: descriptive name (camelCase, starts with a lowercase letter).
    • parameters: inputs to the method; each parameter has a type and a name; zero or more parameters; separated by commas.
  • Example explained: public static int addTwoNumbers(int a, int b) { int sum = a + b; return sum; }
    • It’s public and static, returns int, named addTwoNumbers, takes two int parameters a and b, computes sum, returns it.
  • Simple formula inside: ext{sum} = a + b

Compiler reminder

  • If you mismatched parameter types, the compiler flags an error, acting as an early alert system for type mismatches; e.g., if a method signature is public static int addTwoNumbers(String a, int b) and you call addTwoNumbers(3, 5), you’ll get a type error because the expected types are (String, int).

3.2.3 Trying a void Method—Why Doesn’t It Work?

  • Example: public static void awardVictory(int health, int gold) { /* health and gold are copies */ health += 5; gold += 10; }
  • In main(): playerHealth = 10; playerGold = 50;
  • Inside the method, health and gold are copies; changes do not affect the originals when the method ends.
  • Options to update originals:
    • Use void and hope Java changes originals? No.
    • Return both values somehow? Not directly—methods return one thing.
    • Return the new value and store it back in main? Yes.
  • Conceptual takeaway: primitives are passed by value; Java makes copies of primitive parameters.

3.2.5 Many Fields → Multiple Parameters Fiasco

  • Problem: as the number of related variables grows, passing them individually becomes unwieldy (health, gold, xp, mana, stamina, luck, etc.).
  • Risks:
    • Too many parameters to track.
    • Difficulty in maintaining correct order; swapping leads to bugs.
  • The Design Recipe perspective warns about handling too many pieces individually.

3.2.6 What We’ve Learned (Big Ideas)

  • Methods help avoid copying code.
  • Java makes copies of numbers we pass to methods (primitive types).
  • When methods need to update values, they should return them.
  • When there are many parameters, it’s a sign to move toward using classes to bundle related data.

3.2.7 Your Turn

  • Practice exercise to apply what you’ve learned about methods and design recipes.

3.2.8 Common Pitfalls to Watch Out For

  • Forgetting to return: if your method declares a return type, every code path must return a value.
  • Return type mismatch: returning a double from a method declared to return int, etc.
  • Parameter type confusion: passing a type that doesn’t match the parameter type.

3.3 Method Parameters

  • Recall: methods helped reduce code duplication by passing data in parameters.
  • Method Syntax recap:
    • public static returnType methodName(type1 param1, type2 param2) { // body return value; }
  • Key points:
    • Methods reduce code duplication.
    • Parameters are passed by value (copies).
    • Return type must match the declaration.
    • void methods don’t return anything.
    • static methods belong to the class (not to an instance).
  • Design Recipe Steps:
    1. Formulate data definitions
    2. Write method signature
    3. Test with examples
    4. Implement and verify

3.3.1 The Problem: Too Many Separate Pieces

  • Example: a game with many player stats carried as separate variables (health, gold, xp, mana, stamina, etc.).
  • Issue: calling multiple methods to update each stat becomes tedious and error-prone.

3.3.2 Why This Is A Problem

  • If you need a method that changes multiple pieces, you’d end up with:
    • A method with many parameters (e.g., awardVictory(int health, int gold, int xp, int mana, …))
  • Risk: swap errors, misordered parameters, and difficulty extending to multiple players.

3.3.3 The Solution: Grouping Related Things Together

  • Introduce a class to bundle related data (e.g., a Player class with health, gold, xp, etc.).
  • Example: Player p1 = new Player(100, 50, 0, 75, 90); p1.increaseHealth(5);
  • Concept: a class acts as a blueprint for creating objects that:
    • Keep related data together
    • Include methods that work with that data
    • Protect their data from accidental misuse

3.4 Classes and Objects

  • Already saw the issue of scattered data and line-by-line updates; classes help group data and behavior.
  • Idea: treat a player as a single entity (an object) with its own data and operations.

3.4.1 Step 1: An Empty "Container" Called Player

  • Minimal class skeleton:
    • public class Player { // We'll fill details in soon! }

3.4.2 Step 2: Putting Variables Inside That Box

  • Move data fields inside the class so each Player has its own data:
    • public class Player { int health; int gold; }
  • Each Player object created with new Player() gets its own storage for health and gold.

3.4.3 Step 3: Setting Initial Values

  • Start values aren’t left at 0 by default; use a constructor to set initial values.
  • Constructor basics (special method):
    • Name matches the class, has no return type, and is public with parameters.
    • Example:
    • public class Player { int health; int gold; public Player(int startHealth, int startGold) { health = startHealth; gold = startGold; } }
  • When you write: Player p = new Player(10, 50); what happens:
    1. new creates space in memory
    2. defaults are filled (0s)
    3. constructor runs to initialize fields
    4. a reference to the new object is returned
  • This pattern is standard in Java (e.g., String name = new String("Alice");).

3.4.4 Step 3: Setting Initial Values (continued)

  • The constructor example is shown and discussed; constructors set up the object’s initial state.

3.4.5 Step 4: Adding "Operations" for That Player

  • Compare static vs instance approaches:
    • Static approach (parameters passed in):
    • public static void healPlayer(int currentHealth, int healAmount) { currentHealth += healAmount; }
    • Instance method approach (data is inside the object):
    • public void heal(int amount) { this.health += amount; }
  • Instance methods are often clearer and safer because they operate on the object’s own data.
  • Example within a Player class:
    • public class Player { int health; int gold; public Player(int startHealth, int startGold) { health = startHealth; gold = startGold; } public void heal(int amount) { health += amount; } public void addGold(int amount) { gold += amount; } }
  • Creating and using a Player:
    • Player p = new Player(10, 50); p.heal(5); // health becomes 15

3.4.5.1 The ’this’ Keyword: Who Am I?

  • When parameter names clash with field names, use this to refer to the field:
    • Example (problem):
    • public void heal(int health) { health = health; // which health is which? }
    • Corrected with this:
    • public void heal(int health) { this.health = health; }
  • How it works in practice: calling p.heal(5) passes the object as this to the method.
  • Practical notes:
    • this is explicit but often optional when names don’t clash.
    • It’s useful when passing the current object to another method or to avoid ambiguity.

3.5 Encapsulation & Abstraction

  • Two broad approaches to organizing code:
    • Way 1: Keep data and rules separate (traditional, global state, static helpers).
    • Way 2: Package data with their rules inside classes (Object-Oriented Programming, OOP).
  • OOP goals: encapsulation, abstraction.
    • Encapsulation: protect data from invalid changes.
    • Abstraction: hide complex details behind simple commands.
  • Example: two ways to implement a BankAccount-like structure.
    • Encapsulation example: private fields and public methods to operate on them.
    • Abstraction example: a clear interface (deposit, withdraw, getBalance) that hides internal workings.

3.5.1 Two Ways: Keep or Package Pieces (Recap)

  • Traditional: static state and static methods with scattered data.
  • OOP: organize into classes; data is guarded; methods operate on the data.

3.5.2 Beyond Simple Classes

  • Problem: external code can break rules by directly setting fields (e.g., hero.health = -999).
  • Solution: private fields to prevent direct access.
  • Example: private int health; private fields protect data.

3.5.3 Understanding Encapsulation

  • Bundling data and code together; protecting data from invalid changes.
  • Interface: clear, simple methods a user calls (e.g., heal, takeDamage).
  • Invariants: rules that must always be true (e.g., health within 0..maxHealth).
  • Example: Player with private health and maxHealth with a setter enforcing bounds.

3.5.3.1 Bundling: Keeping Related Things Together

  • Example: BankAccount with private balance, accountNum, interestRate and a method addInterest that uses these values.

3.5.3.2 Protection: Making Sure Data Changes Safely

  • Example: Player with private health and a method takeDamage that ensures health doesn’t go below 0.

3.5.3.3 Interface: Making Your Class Easy to Use

  • Example: BankAccount with deposit, withdraw, getBalance, and internal private balance.
  • The point: public methods provide a simple, safe interface for users.

3.5.3.4 Invariants: Rules That Must Be True

  • Example: Player with health and maxHealth; setHealth enforces bounds.

3.5.4 Different Ways to Enforce Rules

  • Constructor checks: prevent invalid object creation.
  • Method guards: validate inputs inside methods (e.g., if amount < 0, clamp to 0).
  • Range enforcement: keep values in bounds (e.g., using Math.max/Math.min).
  • Complete rejection: return a success/failure boolean to indicate operation result.
  • Choose the approach that makes sense for the situation.

3.5.5 Abstraction: What Users Need vs. What Code Does

  • Analogy: a restaurant where you order a cheeseburger; the kitchen handles complex steps (grill temp, cooking time) and you only see a simple interface (orderCheeseburger).
  • Code example demonstrates hiding internal steps and exposing a simple method.
  • Abstraction and encapsulation together create reliable, easy-to-use code.

3.5.6 Looking Ahead: More OOP Features

  • Acknowledgment that there are more features (e.g., inheritance, polymorphism) to explore beyond encapsulation and abstraction.

3.5.7 Encapsulation & Abstraction in Practice (BankAccount Example)

  • Step-by-step BankAccount with encap., constructor checks, deposit/withdraw validations, getBalance, and private addInterest.
  • Highlights: input validation, balance updates, transaction logging, interest calculation, and user-facing methods.

3.5.8 From Design Recipe to OOP

  • Design Recipe steps align with OOP: data definitions, constraints, and methods that work with data.
  • OOP enforces constraints through methods and data encapsulation.

3.6 Primitive & Reference Types

  • Core memory/datum distinction in Java:
    • Primitive types (int, double, boolean, etc.): stored directly by value; copies are independent; type names start with lowercase.
    • Reference types (Objects): stored as a reference/address to the actual data; copying the reference shares the same object; type names start with uppercase.
  • Real-world analogies:
    • Primitives: sticky notes with the value; copying creates independent notes.
    • Objects: lockers (references); sharing a locker means changes are visible to all who hold the same locker number.

3.6.1 Two Ways Java Stores Data

  • Primitives: stored directly; copies are independent.
  • References: store addresses; copies share the same object in memory.

3.6.2 How Primitives Work: Independent Copies

  • Example: int score = 100; int backupScore = score; backupScore = 50; score remains 100.
  • Demonstrates independent copies for primitives across assignments.

Common primitive examples

  • double price = 9.99; double salePrize = price; salePrize = 7.99; // salePrize changes do not affect price
  • boolean gameOver = false; boolean lastState = gameOver; lastState = true; // independent copies
  • A common mistake: int lives = 3; int extraLives = lives; extraLives--; // lives remains 3

3.6.3 How Objects Work: Sharing Access Through References

  • Example: class Player with health field.
  • Object example:
    • Player hero = new Player(100);
    • Player sidekick = hero; // both references point to the same object
    • sidekick.damage(25); // affects the shared Player object
    • hero.getHealth() shows the updated health (75)
  • Memory view: both references point to the same Player instance in memory.

3.6.4 Methods with References: The Right and Wrong Ways

  • Area damage scenario: damages to multiple players.
  • WRONG WAY: passing primitive health values, which only modify copies and not the actual objects.
  • RIGHT WAY: pass references to Player objects and call their methods to modify real objects.
  • Example: rightAreaDamage(Player p1, Player p2, int damage) { p1.damage(damage); p2.damage(damage); }
  • Memory diagrams show the difference between modifying copies vs. modifying the shared objects.

Common Mistakes when using references

  • Mistake 1: Passing a number (health) instead of a Player object to a method that should modify the object (healPlayer(hero, 5) vs healPlayer(hero.getHealth(), 5)).
  • Mistake 2: Thinking new variables mean new objects (Player backup = hero; backup.damage(10); affects hero).
  • Mistake 3: Pass-by-value misunderstanding: primitives vs objects; objects pass a copy of the reference, not a copy of the object.

3.6.5 What Really Happens Inside Methods

  • Wrong Way: passing health value (a primitive) leads to a method’s copy and no change to the original object.
  • Right Way: passing the Player reference allows the method to modify the actual object through its methods.
  • Visual memory comparison shows how copying references vs copying primitive values affects the outcome.

3.6.6 Sharing Multiple Lockers: Group Methods

  • Boss fight example: bossFight(Player hero, Player boss, int damage) {
    hero.damage(damage); // modifies hero
    boss.damage(damage*2); // modifies boss
    }
  • Create two players and pass them to the method to modify both.
  • Key point: a method can receive multiple references and modify all the corresponding objects.

3.6.7 Summary: What You Need to Remember

3.6.7.1 Key Concepts

  • Primitives are like sticky notes: each variable has its own copy.
  • Objects are like lockers: multiple references can point to the same object; changes via one reference affect all references to that object.

3.6.7.2 Common Mistakes to Avoid

  • Wrong: Passing the number instead of the locker (object). Example: void healPlayer(int health, int amount) { health += amount; } // Changes thrown away
  • Right: Passing a Player reference: void healPlayer(Player player, int amount) { player.heal(amount); }
  • Wrong: Assuming new variables mean new objects; e.g., Player backup = hero; // backup is the same object as hero
  • Right: To create a separate object, instantiate a new Player with a value, e.g., Player clone = new Player(hero.getHealth());
  • Misunderstanding Pass-by-Value: Java passes copies of references for objects, but the references point to the same underlying objects.

3.6.8 Looking Ahead: Objects in Memory

  • This section foreshadows deeper topics on how objects are stored and how method calls interact with objects in memory.

Quick Reference Highlights

  • Method formula example: ext{sum} = a + b inside a method returning an int.
  • Key method concepts:
    • public static: class-level access
    • returnType: type of value returned (use void if none)
    • parameters: zero or more typed inputs
  • Encapsulation essentials:
    • Private fields to protect data
    • Public methods to manipulate data safely
    • Invariants to enforce rules (e.g., health bounds)
  • Abstraction vs. Encapsulation:
    • Encapsulation protects data
    • Abstraction hides internal complexity behind simple interfaces
  • Primitives vs. References:
    • Primitives: independent copies on assignment
    • References: multiple references can share the same object; changes propagate through all references to that object

Notes:

  • This study guide mirrors the progression from objects and classes to methods, parameter handling, and encapsulation/abstraction, culminating in the primitive vs. reference distinction and its memory implications. Use these bullets as a structured review to map to code practice and future topics (inheritance, polymorphism, etc.).