Microlink IT and Business College Department of CS OOP Using Java Preparing For The Ethiopian National Exit Examination
Microlink IT and Business College Department of CS OOP Using Java Preparing For The Ethiopian National Exit Examination
Department of CS
OOP using java
🇪🇹
Preparing for the Ethiopian National Exit
Examination
Course contents
1. Introduction to OOP 6. Abstraction (Abstract Classes &
Interfaces)
2. Classes and Objects
7. OOP Relationships (Association,
Aggregation, Composition)
3. Encapsulation &
8. Exception Handling in OOP
Information Hiding
9. SOLID Design Principles
4. Inheritance
10. Real-World Applications (Case
Studies)
5. Polymorphism
11. GUI Development with JavaFX/Swing
12. Advanced Topics (Generics,
Collections)
13. Exam Preparation & Practic
Learning Objectives
Chapter Objectives:
Explain core OOP concepts (encapsulation,
inheritance, polymorphism, abstraction).
Design Java classes using OOP principles.
Implement real-world systems (e.g., banking, e-
commerce) with Java.
Develop GUI applications using JavaFX/Swing.
Apply SOLID principles to write maintainable
code.
Solve exam-style problems on OOP and Java.
Introduction to OOP
What is OOP?
Example:
Definition: class Student { // Class
"A programming paradigm that String name; // Field (data)
organizes software design
around objects (data + behavior)
void study() { // Method (behavior)
rather than functions." System.out.println(name + " is
Key Terms: studying.");
Class: Blueprint (e.g., class Car {}) }
Object: Instance (e.g., Car tesla = }
new Car();)
Procedural vs. OOP: comparison table
Example:
// Procedural
void printStudentName(String
name) { ... }
// OOP
class Student {
String name;
void printName() { ... }
}
Why Use OOP?
Advantages:
Analogy:
Modularity: Isolate code into
Car Manufacturing:
objects (e.g., BankAccount, Different teams (objects)
User). work on engine, wheels,
Reusability: Inherit features etc.
(e.g., ElectricCar extends Car).
Security: Encapsulation
protects data (e.g., private
balance).
Key OOP Concepts (Preview)
The 4 Pillars:
Encapsulation
Inheritance
Polymorphism
Abstraction
Objects in Real Life & Code
Real-World Object:
Java Equivalent:
Car: Attributes (color, class Car {
model), Behaviors (start,
String color; // Attribute
stop).
void start() { // Behavior
System.out.println("Car
started.");
}
}
Objects: software perspective
is a software
construct/module that
bundles together state
(data) and behavior
(functions) which, taken
together, represent an
abstraction of a real-world
object.
Class vs. Object
[Class: Car] → [Objects:
A class is an abstraction
Tesla, Toyota, BMW] describing the common
features of all objects in a
group of similar objects.
For example: “Student”
Class vs. Object...
Example:
Car tesla = new Car(); //
Object 1
tesla.color = "Red";
args) {
Analogy:
Student student1 = new
Student(); // Object 1
Class = Cookie cutter 🍪
student1.name = "Alice";
Object = Actual cookies
student1.introduce(); // Output:
"Hello, I'm Alice"
}
}
Constructors
Purpose: Initialize objects
Example:
at creation. class Car {
String model;
Types:
// Parameterized constructor
Default Constructor: Auto- Car(String m) {
generated (no args). this.model = m;
Parameterized }
Constructor: Custom }
initialization.
// Usage:
Car myCar = new Car("Tesla");
The this Keyword
Why? Resolves ambiguity
Example:
between instance variables class Employee {
and parameters.
String name;
Employee(String name) {
this.name = name; //
"this" refers to the current
object
}
}
Common Mistake Alert:
Employee(String name) {
name = name; // Bug!
Doesn't assign to the field.
}
Memory Allocation (Stack vs. Heap)
Diagram:
Key Points:
[Stack] [Heap]
Stack: Stores references
(e.g., Car myCar).
myCar (reference) → Car
Heap: Stores object data
{ model: "Tesla" }
(e.g., new Car()).
Object Lifecycle
Creation: new keyword
Example:
allocates memory. Car car = new Car(); //
Usage: Call Creation
methods/access fields. car.drive(); // Usage
Destruction: Garbage car = null; // Eligible for
GC
Collector (GC) reclaims
unused memory.
Real-World Example: Bank Account
Class Design:
Usage:
class BankAccount {
BankAccount account =
private double balance;
new BankAccount();
void deposit(double amount) {
balance += amount;
account.deposit(1000);
}
System.out.println(account.
double getBalance() {
getBalance()); // 1000.0
return balance;
}
}
Common Pitfalls
NullPointerException:
Uninitialized Objects:
Car car = null;
Student s; // No 'new' →
car.drive(); // Crash! Compiler error
Summary & Exam Tips
Key Takeaways:
Classes define structure; objects are instances.
Use constructors for initialization.
this avoids field/parameter conflicts.
Exercise:
Write a Rectangle class with fields (width, height) and methods
(calculateArea()).
Hands-On Exercise
Task:
Starter Code
Create a MobilePhone class
class MobilePhone {
with:
// Your code here
Fields: brand, price
}
Constructor: Parameterized
Method: printDetails()
🔒
Chapter 3 - Encapsulation & Information
Hiding
🔒 Encapsulation & Information Hiding
Definition:
Example:
"Bundling data (fields) and class BankAccount {
methods that operate on that
data into a single unit (class), private double balance; //
while restricting direct access to Hidden data
some components." public double getBalance()
Key Principles: { // Controlled access
Data Hiding: Mark fields as return balance;
private.
}
Controlled Access: Provide
public getters/setters. }
Why Encapsulation?
Benefits:
Real-World Analogy:
Security: Prevent invalid
Medical Records: Only
data (e.g., negative doctors (methods) can
balance). access/update your records
Flexibility: Change internal (data).
logic without breaking
external code.
Maintainability: Easier
debugging (validation in
one place).
Access Modifiers in Java
Golden Rule:
Fields: private (usually)
Methods: public (if part of
API)
Getters & Setters (Standard Pattern)
Purpose: Example:
class Student {
Getter: Retrieve field value
private String name;
// Getter
(getFieldName()).
public String getName() {
Setter: Validate + modify
return name;
}
field value (setFieldName()).
// Setter with validation
public void setName(String newName) {
if (newName != null && !
newName.isEmpty()) {
this.name = newName;
}
}
}
Real-World Example: Temperature Control
Problem: Prevent invalid Solution:
class Thermostat {
temperature values.
private double temperature;
public void setTemperature(double temp) {
if (temp >= -50 && temp <= 50) { //
Validation
this.temperature = temp;
} else {
System.out.println("Invalid
temperature!");
}
}
}
Information Hiding vs. Encapsulation
Subtle Difference:
Analogy:
Information Hiding:
Information Hiding: "Don’t
Design principle (hide show how the engine
implementation details). works."
Encapsulation:
Encapsulation: "Put the
Implementation technique engine under the hood and
(using private + provide a pedal."
getters/setters).
Common Mistakes
Exposing Data:
Weak Validation:
public double balance; //
public void setAge(int age) {
Danger! No control.
this.age = age; // No
check for negative age!
}
Summary & Exam Tips
Key Takeaways:
Exam Focus:
Use private fields + public
Identify encapsulation
getters/setters. violations in given code.
Validate data in setters.
Write a Person class with
encapsulated age (validate:
0-120).
Hands-On Exercise
Task:
Starter Code:
Create a Book class with:
class Book {
private fields: title, author,
// Your code here
price
}
Validate price (> 0) in setter
Solution:
class Book {
private String title, author;
private double price;
public void setPrice(double price) {
if (price > 0) {
this.price = price;
} else {
System.out.println("Price must be
positive!");
}
}
// ... (other getters/setters)
}
Chapter 4 - Inheritance
Definition:
Syntax:
"A mechanism where a
class Animal { } //
child class inherits fields Superclass
and methods from a parent
class Dog extends Animal
class, enabling code reuse."
{ } // Subclass
Key Terms:
Superclass (Parent): Base
class being inherited from
Subclass (Child): Class that
inherits
Why Use Inheritance?
Benefits:
Real-World Example:
Code Reuse: Avoid
Animal
duplicating common logic
/ \
Polymorphism: Enable
method overriding (Chapter
Dog Cat
5)
Logical Hierarchy: Model
"is-a" relationships
Types of Inheritance in Java
Supported Types:
Unsupported:
Single: One parent → one
Multiple inheritance (use
child interfaces instead)
Multilevel: Grandparent →
parent → child
Hierarchical: One parent →
multiple children
The extends Keyword
How It Works:
Example:
class Vehicle { // Parent
Subclass gains all non-
void start() {
private members of
superclass
System.out.println("Vehicle
started");
}
}
class Car extends Vehicle { // Child
// Inherits start()
}
Method Overriding
Concept: Example:
class Bird {
Child class provides its own
void sing() {
implementation of an
System.out.println("Bird song");
inherited method
}
}
Rules:
class Duck extends Bird {
Same method signature as
@Override
parent
void sing() {
System.out.println("Quack quack");
Use @Override annotation
}
}
The super Keyword
Three Uses: Example:
class Employee {
Access parent class fields:
String name = "Employee";
super.field
}
class Manager extends Employee {
Call parent class methods:
String name = "Manager";
super.method()
void printNames() {
Invoke parent constructor:
System.out.println(super.name); //
super() "Employee"
System.out.println(this.name); //
"Manager"
}
}
Constructor Chaining
Key Points: Example:
class Person {
Child constructors must call
Person() {
}
Default super() is added
class Student extends Person {
automatically if missing
Student() {
// super(); // Added automatically
System.out.println("Student constructor");
}
}
Output when creating
Student:
"Person constructor"
"Student constructor"
Real-World Example: Banking System
Class Hierarchy:
Usage:
class BankAccount {
double balance;
SavingsAccount acct = new
void deposit(double amount) { balance +=
amount; }
SavingsAccount();
}
acct.deposit(1000); //
class SavingsAccount extends BankAccount {
Inherited method
double interestRate;
void addInterest() {
acct.addInterest(); //
balance += balance * interestRate; Specialized method
}
}
Common Pitfalls
Accidental Overriding:
Same method name but
different signature → method
hiding
Incorrect super Usage:
super() must be first line in
constructor
Overuse of Inheritance:
Favor composition over
inheritance when possible
Summary & Exam Tips
Key Takeaways:
Exam Focus:
Use extends to create
Identify inheritance
parent-child relationships relationships in code
Override methods for snippets
specialized behavior
Write a Shape superclass
Use super to access parent with Circle and Rectangle
members subclasses
Hands-On Exercise
Task:
Starter Code:
Create:
class Person {
Person superclass with
// Your code here
name and introduce()
method
}
Student subclass that adds
studentId and overrides
introduce()
Solution
class Person {
class Student extends Person {
String name;
String studentId;
@Override
void introduce() {
void introduce() {
System.out.println("I'm
super.introduce();
" + name);
System.out.println("My
} student ID: " + studentId);
}
}
}
Chapter 5: Polymorphism
Objective:
Understand polymorphism
and its types
Master method overriding
and dynamic binding
Apply polymorphism in
real-world Java programs
What is Polymorphism?
Definition:
Real-World Analogy:
Greek for "many forms"
A button performs different
Ability of an object to take actions:
different forms
Power button → Turns
Same method behaves device on/off
differently based on the
Elevator button → Moves to
object a floor
Types of Polymorphism
1. Compile-Time
Example:
Polymorphism (Static)
class Calculator {
Achieved via method
// Method 1
overloading
int add(int a, int b) { return a +
b; }
Methods must have:
Same name
// Method 2 (Overloaded)
Different parameters
(type/count/order)
double add(double a, double b)
{ return a + b; }
}
Runtime Polymorphism (Dynamic)
Achieved via method
overriding
Requires inheritance +
same method signature
Method Overriding (Runtime Polymorphism)
Rules: Example:
class Animal {
Same method name and
void makeSound() {
parameters as parent
System.out.println("Animal sound");
Child class method cannot
}
}
have a more restrictive
@Override
Use @Override annotation
void makeSound() {
(best practice)
System.out.println("Bark!");
}
}
Usage:
Animal myPet = new
Dog(); // Upcasting
myPet.makeSound(); //
Output: "Bark!" (Not
"Animal sound")
Dynamic Method Dispatch
How it Works: Example:
Animal[] pets = new Animal[3];
JVM (not compiler) decides
pets[0] = new Dog(); // Actual object:
which method to call at Dog
runtime
pets[1] = new Cat(); // Actual object:
Based on the actual object Cat
abstraction (100%
void draw(); // Abstract method
}
polymorphic)
class Circle implements Drawable {
@Override
public void draw() {
System.out.println("Drawing circle");
}
}
class Square implements Drawable {
@Override
public void draw() {
System.out.println("Drawing square");
}
}
Usage:
Drawable[] shapes = {new
Circle(), new Square()};
for (Drawable shape :
shapes) {
shape.draw(); //
Polymorphic call
}
Limitations of Polymorphism
Cannot override:
Example of Invalid Override:
static methods (class-level,
class Parent {
not object-level)
public void show() { /* ... */ }
final methods
}
private methods
class Child extends Parent {
Cannot reduce visibility:
@Override
If parent method is public,
private void show() { /* ...
*/ } // Compile error!
child cannot make it private
}
super Keyword in Polymorphism
Purpose: Call parent class’s Example:
class Vehicle {
overridden method
void start() {
System.out.println("Vehicle started");
}
}
class Car extends Vehicle {
@Override
void start() {
super.start(); // Calls Vehicle's start()
System.out.println("Car started");
}
}
Real-World Example (Banking System)
Scenario: Different account
class Account {
types calculate interest
double balance;
differently
double
calculateInterest() {
return balance *
0.01; // Default 1%
}
}
class SavingsAccount
class FixedDeposit extends
extends Account { Account {
@Override
@Override
double calculateInterest() {
double calculateInterest() {
return balance * 0.05; //
return balance * 0.08; //
5% for savings 8% for FD
}
}
}
}
Usage:
Account[] accounts = {
new SavingsAccount(),
new FixedDeposit()
};
for (Account acc : accounts) {
System.out.println(acc.calculateInt
erest());
}
Common Mistakes & Debugging
Error 1: Forgetting
class Parent {
@Override
protected void demo() { /* ... */
}
Risk: Accidentally
}
overloading instead of
overriding
class Child extends Parent {
@Override
Error 2: Incorrect access
void demo() { /* ... */ } // Error:
modifiers Default (package-private) <
protected
}
Error 3: Assuming
polymorphism works with
fields
Field access is compile-time
(based on reference type)
Exam-Style Questions
Q1: What’s the output?
Answer: (Runtime
class A { void m1() polymorphism)
{ System.out.println("A"); } }
class B extends A { void m1()
{ System.out.println("B"); } }
A obj = new B();
obj.m1();
Q2: Fix the code:
Issue: Print() ≠ print() (Case-
class X { void print() { /* ... sensitive = no override)
*/ } }
class Y extends X { void
Print() { /* ... */ } }
Summary
Hands-On Exercise
Problem:
Starter Code:
Create a Shape hierarchy
abstract class Shape {
with Circle, Rectangle
abstract double area();
Override area() method
polymorphically
}
// Implement Circle and
Rectangle
Chapter 6: Abstraction (Abstract Classes &
Interfaces)
Objective:
Understand abstraction as
an OOP concept
Differentiate abstract
classes and interfaces
Apply abstraction to real-
world problems
What is Abstraction?
Definition:
Why Use Abstraction?
Hiding complex
Reduces complexity
implementation details,
Enforces structure
exposing only essential
features.
Supports modular design
Analogy: Car dashboard
(shows speed, hides engine
mechanics).
Abstract Classes
Key Features: abstract class Animal {
// Abstract method (no implementation)
Declared with abstract
abstract void makeSound();
// Concrete method
keyword
void breathe() {
Can have both abstract (no
System.out.println("Breathing...");
}
methods
class Dog extends Animal {
Cannot be instantiated
@Override
void makeSound() {
directly
System.out.println("Bark!");
}
}
Usage:
Animal myDog = new
Dog();
myDog.makeSound(); //
Output: "Bark!"
myDog.breathe(); //
Output: "Breathing..."
Interfaces
Key Features (Pre-Java 8+):
interface Drawable {
void draw(); // Abstract method
100% abstract (until Java 8)
}
Uses interface keyword
class Circle implements Drawable {
All methods are public
@Override
abstract by default
public void draw() {
Fields are public static final
System.out.println("Drawing a
circle");
}
}
Usage:
Drawable d = new Circle();
d.draw(); // Output:
"Drawing a circle"
Java 8+ Interface Enhancements
Default Methods:
interface Vehicle {
Provide implementation in
default void start() {
interfaces
System.out.println("Vehicle
started");
Avoid breaking existing
code
}
}
class Car implements Vehicle {
// No need to override start()
}
Static Methods:
interface MathUtils {
static int square(int x)
{ return x * x; }
}
// Usage:
int result =
MathUtils.square(5);
When to Use Abstract Classes vs. Interfaces
Use Abstract Classes When:
Use Interfaces When:
You want to share code
among related classes
You need multiple
You need non-final fields inheritance of type
You require a constructor
You want to define a
contract (API)
You’re working with
lambda expressions
Real-World Example (Payment System)
Abstract Class Approach:
class CreditCardPayment
extends Payment {
abstract class Payment {
@Override
double amount;
void processPayment() {
abstract void
processPayment(); System.out.println("Processin
} g credit card: $" + amount);
}
}
Interface Approach:
interface Refundable {
class PayPalPayment extends
Payment implements Refundable {
void processRefund();
@Override
}
void processPayment() { /* ... */ }
@Override
public void processRefund() {
/* ... */ }
}
Common Pitfalls
Instantiation Attempt:
Missing
Animal a = new Animal(); // Implementations:
Error: Animal is abstract
class BadDog extends
Animal { } // Error: Must
implement makeSound()
Diamond Problem (Solved with Default
Methods):
interface A { default void show()
{ /* ... */ } }
interface B { default void show()
{ /* ... */ } }
class C implements A, B {
@Override // Must override to
resolve conflict
public void show()
{ A.super.show(); }
}
Summary Cheat Sheet