Created by: Andrew J. Pounds, Ph.D.

💻 Comparison of Object-Oriented Programming in Java, C++, and Pascal

Welcome to a comprehensive comparison of Object-Oriented Programming (OOP) across three distinct programming languages: Java, C++, and Pascal. This document explores how each language approaches the fundamental concepts of OOP and provides practical examples using the Free Pascal Compiler (fpc). By understanding these comparisons, you'll develop a deeper appreciation for language design decisions and be better equipped to choose the right tool for your projects.

❓ What is Object-Oriented Programming?

Object-Oriented Programming is a paradigm that organizes code around "objects" that contain both data (variables) and functions (methods). Think of an object like a blueprint: a Drink class defines what properties all drinks have and what operations can be performed on them.

The core principles of OOP are:

📊 Language Feature Comparison Table

Feature Java C++ Pascal
Constructor ClassName() ClassName() constructor Create()
Destructor Automatic (Garbage Collected) ~ClassName() destructor Destroy()
Inheritance extends : class(ParentClass)
Access Modifiers public, private, protected public, private, protected public, private, protected, published
Method Override @Override annotation virtual keyword virtual/override keywords
Polymorphism Virtual by default Must use virtual Must use virtual
Memory Management Automatic (Garbage Collection) Manual (new/delete) Manual (Create/Free)
Interfaces interface keyword Pure virtual classes interface keyword
Constants final keyword const keyword const keyword
Compilation javac g++ fpc

✏️ Example 1: Basic Class Definition

Let's compare how to define a basic class in all three languages:

☕ Java

public class Drink {
    final int MAXIMUM = 10;
    private String name;

    public Drink(String brandName) {
        name = brandName;
    }

    public String getName() {
        return name;
    }
}

⚙️ C++

class Drink {
private:
    static const int MAXIMUM = 10;
    string name;

public:
    Drink(string brandName) {
        name = brandName;
    }

    string getName() {
        return name;
    }
};

🔷 Pascal

type
  TDrink = class
  private
    const MAXIMUM = 10;
    FName: string;
  public
    constructor Create(brandName: string);
    function GetName(): string;
  end;

constructor TDrink.Create(brandName: string);
begin
  FName := brandName;
end;

function TDrink.GetName(): string;
begin
  Result := FName;
end;

Key Observations:

✏️ Example 2: Constructor Overloading

All three languages support multiple constructors with different parameter lists:

☕ Java

public Drink(String name) {
    this.name = name;
    this.cost = 1.75;
}

public Drink(String name, double cost) {
    this.name = name;
    this.cost = cost;
}

⚙️ C++

Drink(string name) {
    this->name = name;
    this->cost = 1.75;
}

Drink(string name, double cost) {
    this->name = name;
    this->cost = cost;
}

🔷 Pascal

constructor TDrink.Create(brandName: string);
begin
  FName := brandName;
  FCost := 1.75;
end;

constructor TDrink.Create(brandName: string; cost: Double);
begin
  FName := brandName;
  FCost := cost;
end;

Key Observations:

✏️ Example 3: Methods and Encapsulation

Here's how methods work in each language, using the vending machine example:

☕ Java

public void vend() {
    if (available > 0) {
        available -= 1;
        consumed += 1;
        moneyMade += cost;
        System.out.println(name + " Purchased.");
    }
}

public double profit() {
    return moneyMade;
}

⚙️ C++

void vend() {
    if (available > 0) {
        available -= 1;
        consumed += 1;
        moneyMade += cost;
        cout << name << " Purchased." << endl;
    }
}

double profit() {
    return moneyMade;
}

🔷 Pascal

procedure TDrink.Vend();
begin
  if FAvailable > 0 then
  begin
    FAvailable := FAvailable - 1;
    FConsumed := FConsumed + 1;
    FMoneyMade := FMoneyMade + FCost;
    WriteLn(FName + ' Purchased.');
  end;
end;

function TDrink.Profit(): Double;
begin
  Result := FMoneyMade;
end;

Key Observations:

🔧 The Complete Vending Machine Example

Now let's look at a complete, working example using the Pascal language and the Free Pascal Compiler (fpc). This program implements a simple vending machine that tracks drink sales and profits using separate unit and program files.

Important: Units and the uses Clause in Pascal

When using Pascal with separate files, you organize code into units. Each unit has an interface section (public declarations) and an implementation section (actual code). Other files include units with the uses clause.

✅ The Correct Approach: Create a unit file (DrinkUnit.pas) with the class definition and a program file (Machine.pas) that uses that unit.

Why Units Are Essential for Free Pascal

The unit system in Pascal is crucial because:

🎯 How It Works: When you write uses DrinkUnit;, the compiler looks for DrinkUnit.pas (or a pre-compiled DrinkUnit.ppu), reads its interface section, and makes those declarations available to your program.

DrinkUnit.pas - The Drink Class Unit

unit DrinkUnit;

{$mode objfpc}{$H+}

interface

type
  TDrink = class
  private
    const MAXIMUM_CAPACITY = 10;
    const DEFAULT_DRINK_COST = 1.75;
  private
    FAvailable: Integer;
    FConsumed: Integer;
    FUseDefaultCost: Integer;
    FUseSuppliedCost: Integer;
    FDrinkCost: Double;
    FMoneyMade: Double;
    FName: string;
  public
    constructor Create(brandName: string);
    constructor Create(brandName: string; cost: Double);
    procedure Vend();
    procedure Refill();
    function Profit(): Double;
    function DrinksSold(): Integer;
    function GetDrinkName(): string;
    function RestockAmount(): Integer;
  end;

implementation

uses
  Math;

constructor TDrink.Create(brandName: string);
begin
  FName := brandName;
  FConsumed := 0;
  FAvailable := 0;
  FMoneyMade := 0.0;
  FDrinkCost := DEFAULT_DRINK_COST;
  FUseDefaultCost := 1;
  FUseSuppliedCost := 0;
end;

constructor TDrink.Create(brandName: string; cost: Double);
begin
  FName := brandName;
  FConsumed := 0;
  FAvailable := 0;
  FMoneyMade := 0.0;
  FDrinkCost := cost;
  FUseDefaultCost := 0;
  FUseSuppliedCost := 1;
end;

procedure TDrink.Vend();
begin
  if FAvailable > 0 then
  begin
    FAvailable := FAvailable - 1;
    FConsumed := FConsumed + 1;
    FMoneyMade := FMoneyMade + (FUseDefaultCost * DEFAULT_DRINK_COST +
                  FUseSuppliedCost * FDrinkCost);
    WriteLn(FName + ' Purchased.');
  end
  else
  begin
    WriteLn('');
    WriteLn('    SOLD OUT    ');
    WriteLn('');
  end;
end;

procedure TDrink.Refill();
begin
  FAvailable := MAXIMUM_CAPACITY;
end;

function TDrink.Profit(): Double;
begin
  Result := Round(FMoneyMade * 100.0) / 100.0;
end;

function TDrink.DrinksSold(): Integer;
begin
  Result := FConsumed;
end;

function TDrink.GetDrinkName(): string;
begin
  Result := FName;
end;

function TDrink.RestockAmount(): Integer;
begin
  Result := MAXIMUM_CAPACITY - FAvailable;
end;

end.

Machine.pas - The Driver Program

program Machine;

{$mode objfpc}{$H+}

uses
  DrinkUnit;

var
  drink1, drink2, drink3, drink4: TDrink;
  totalSold: Integer;
  totalProfit: Double;

begin
  // Create drinks using different constructors
  drink1 := TDrink.Create('Coke');
  drink2 := TDrink.Create('Sprite');
  drink3 := TDrink.Create('Doctor Pepper');
  drink4 := TDrink.Create('V8 Splash', 4.25);

  // Refill all drinks
  drink1.Refill();
  drink2.Refill();
  drink3.Refill();
  drink4.Refill();

  // Purchase 8 Cokes
  drink1.Vend();
  drink1.Vend();
  drink1.Vend();
  drink1.Vend();
  drink1.Vend();
  drink1.Vend();
  drink1.Vend();
  drink1.Vend();

  // Purchase 6 Sprites
  drink2.Vend();
  drink2.Vend();
  drink2.Vend();
  drink2.Vend();
  drink2.Vend();
  drink2.Vend();

  // Purchase 2 Doctor Peppers
  drink3.Vend();
  drink3.Vend();

  // Purchase 11 V8 Splash (will exceed capacity)
  drink4.Vend();
  drink4.Vend();
  drink4.Vend();
  drink4.Vend();
  drink4.Vend();
  drink4.Vend();
  drink4.Vend();
  drink4.Vend();
  drink4.Vend();
  drink4.Vend();
  drink4.Vend();

  // Calculate totals
  totalSold := drink1.DrinksSold() + drink2.DrinksSold() +
               drink3.DrinksSold() + drink4.DrinksSold();
  totalProfit := drink1.Profit() + drink2.Profit() +
                 drink3.Profit() + drink4.Profit();

  // Display results
  WriteLn('A Total of ', totalSold, ' drinks were sold for a profit of ', totalProfit:0:2);
  WriteLn();
  WriteLn(drink1.RestockAmount(), ' ', drink1.GetDrinkName(), ' needed to restock machine');
  WriteLn(drink2.RestockAmount(), ' ', drink2.GetDrinkName(), ' needed to restock machine');
  WriteLn(drink3.RestockAmount(), ' ', drink3.GetDrinkName(), ' needed to restock machine');
  WriteLn(drink4.RestockAmount(), ' ', drink4.GetDrinkName(), ' needed to restock machine');

  // Free memory
  drink1.Free;
  drink2.Free;
  drink3.Free;
  drink4.Free;
end.

⚡ Compilation and Execution Instructions

Step-by-Step: Two-File Compilation with fpc (Correct Method)

  1. Create DrinkUnit.pas with the TDrink class code (shown above)
  2. Create Machine.pas with the main program code (shown above)
  3. Save both files in the same directory
  4. Open a terminal and navigate to that directory
  5. Compile the program (fpc automatically finds and compiles the unit):
    fpc Machine.pas
  6. Run the program:
    ./Machine
✅ The Key to Success: Use uses DrinkUnit; in Machine.pas. The Free Pascal compiler will automatically locate and compile DrinkUnit.pas when it processes the uses clause.

The Difference Between Program and Unit Files in Pascal

File Type Declaration Purpose Contains
Unit unit UnitName; Reusable module with classes/functions interface and implementation sections
Program program ProgramName; Executable entry point begin...end. block with main code

Sample Output:

Coke Purchased.
Coke Purchased.
Coke Purchased.
Coke Purchased.
Coke Purchased.
Coke Purchased.
Coke Purchased.
Coke Purchased.
Sprite Purchased.
Sprite Purchased.
Sprite Purchased.
Sprite Purchased.
Sprite Purchased.
Sprite Purchased.
Doctor Pepper Purchased.
Doctor Pepper Purchased.
V8 Splash Purchased.
V8 Splash Purchased.
V8 Splash Purchased.
V8 Splash Purchased.
V8 Splash Purchased.
V8 Splash Purchased.
V8 Splash Purchased.
V8 Splash Purchased.
V8 Splash Purchased.
V8 Splash Purchased.

    SOLD OUT    

A Total of 26 drinks were sold for a profit of $67.75

8 Coke needed to restock machine
4 Sprite needed to restock machine
8 Doctor Pepper needed to restock machine
0 V8 Splash needed to restock machine

🎯 Understanding the Code

Unit Structure and the uses Clause

In Pascal, when you split code into multiple files, you use units with interface and implementation sections:

File Section Purpose
DrinkUnit.pas unit DrinkUnit; Declares that this file is the DrinkUnit module
DrinkUnit.pas interface Public declarations visible to other files
DrinkUnit.pas implementation Actual method code (private to the unit)
Machine.pas program Machine; Declares this as the main executable
Machine.pas uses DrinkUnit; Imports the DrinkUnit to access TDrink class

Why Separate Interface and Implementation?

The separation is essential because:

Class Variables vs. Instance Variables

Aspect Class Constants Instance Variables
Declaration const MAXIMUM_CAPACITY = 10; FAvailable: Integer;
Scope Shared by all instances Unique to each instance
Memory One copy for the whole class One copy per object
Example All drinks have max capacity of 10 Each drink tracks its own inventory

Encapsulation in Action

Notice that in the TDrink class, all data members are declared in a private section. This means they cannot be accessed directly from outside the class. Instead, users must use the public methods:

✅ This is encapsulation: The internal details (how money is tracked, how inventory works) are hidden, and users interact through well-defined methods.

❌ Common Mistakes and Solutions

Mistake 1: Forgetting the Mode Directive

❌ Wrong (DrinkUnit.pas):
unit DrinkUnit;

interface

type
  TDrink = class  // ERROR: Class not recognized without ObjFPC mode
✅ Correct (DrinkUnit.pas):
unit DrinkUnit;

{$mode objfpc}{$H+}  // Enable Object Pascal mode and long strings

interface

type
  TDrink = class  // Works correctly

Mistake 2: Forgetting to Free Objects

❌ Wrong (Machine.pas):
begin
  drink1 := TDrink.Create('Coke');
  drink1.Vend();
  // Program ends without freeing memory - MEMORY LEAK!
end.
✅ Correct (Machine.pas):
begin
  drink1 := TDrink.Create('Coke');
  drink1.Vend();
  drink1.Free;  // Always free objects when done
end.

Mistake 3: Using Wrong Assignment Operator

❌ Wrong:
FName = brandName;  // ERROR: This is comparison, not assignment
✅ Correct:
FName := brandName;  // Pascal uses := for assignment

Mistake 4: Forgetting begin/end Blocks

❌ Wrong:
if FAvailable > 0 then
  FAvailable := FAvailable - 1;
  FConsumed := FConsumed + 1;  // This line always executes!
✅ Correct:
if FAvailable > 0 then
begin
  FAvailable := FAvailable - 1;
  FConsumed := FConsumed + 1;  // Now properly inside the if block
end;

Mistake 5: Unit Name Not Matching File Name

❌ Wrong (file: DrinkUnit.pas):
unit Beverage;  // ERROR! Unit name should match filename
✅ Correct (file: DrinkUnit.pas):
unit DrinkUnit;  // Unit name matches filename

Mistake 6: Forgetting the uses Clause

❌ Wrong (Machine.pas):
program Machine;

{$mode objfpc}{$H+}

var
  drink1: TDrink;  // ERROR: TDrink is not defined

begin
  drink1 := TDrink.Create('Coke');
end.
✅ Correct (Machine.pas):
program Machine;

{$mode objfpc}{$H+}

uses
  DrinkUnit;  // Import the unit containing TDrink

var
  drink1: TDrink;  // Now TDrink is recognized

begin
  drink1 := TDrink.Create('Coke');
  drink1.Free;
end.

Mistake 7: Using return Instead of Result

❌ Wrong:
function TDrink.Profit(): Double;
begin
  return FMoneyMade;  // ERROR: Pascal doesn't use 'return'
end;
✅ Correct:
function TDrink.Profit(): Double;
begin
  Result := FMoneyMade;  // Pascal uses Result for return values
end;

✏️ Your Programming Exercise

Objective: Modify the vending machine program to replace the V8 Splash drink with a new beverage of your choice.

Assignment:

The current program includes V8 Splash at $4.25 as the fourth drink. Your task is to:

  1. Replace V8 Splash: Change the drink name to something different (e.g., "Orange Juice", "Apple Cider", "Iced Tea", "Lemonade", etc.) and choose a new price (different from $4.25)
  2. Modify the vending operations: Change how many times each drink is purchased by modifying the number of Vend() method calls for each drink
  3. Update the output display: Ensure all the drink names and restock amounts are printed correctly
  4. Test your program: Compile with fpc and run it to ensure it works properly

Specific Changes to Make:

Testing Your Solution:

  1. Make your changes to Machine.pas (or DrinkUnit.pas if needed)
  2. Save both files
  3. Compile:
    fpc Machine.pas
  4. Run:
    ./Machine
  5. Verify the output shows your new drink name and the correct profit calculations
🎯 Challenge: Before running your program, calculate what the total profit should be. Does your output match your prediction?

⭐ Key Takeaways About Object-Oriented Programming

🏆 Language Strengths Summary

Language Strengths Best For
Java Write once, run anywhere; Strong ecosystem; Great for large projects Enterprise applications, web backends, Android apps
C++ High performance; Maximum control; Close to hardware System software, games, performance-critical apps
Pascal Clear readable syntax; Strong typing; Excellent for learning Education, rapid application development, cross-platform GUI apps