By the end of this chapter, you will be able to:
Mastering these skills will help you write clean, efficient programs that solve real-world problems just like professional developers do!
Object oriented programming organises a program around objects. An object keeps its data, called attributes, together with the methods that work on that data, and it is created from a class that describes it. Java programs for a repair shop, a helpdesk or a network inventory become easier to write and change when each real thing, such as a device, a ticket or a voucher, is a class of its own. Four concepts support this way of working: inheritance lets one class build on another, encapsulation protects the data inside a class, abstraction shows what an object does and hides how, and polymorphism lets the same call behave differently for different objects. The topics on classes and objects that follow show how to write and use them in detail; this topic explains the four concepts and demonstrates each with a program.
Inheritance lets a new class, the subclass, take over the attributes and methods of an existing class, the superclass, and then add its own. The keyword extends creates the link. Inheritance suits groups of things that share common features: laptops, phones and printers in a repair shop are all devices with a name and a power switch, so those shared features can be written once in a class Device. The test for a sound inheritance link is the phrase "is a": a Laptop is a Device, a Phone is a Device.
public class InheritanceDemo {
public static void main(String[] args) {
Laptop lap = new Laptop("Lap-01", 16);
lap.powerOn();
lap.showRam();
Phone ph = new Phone("Ph-07");
ph.powerOn();
ph.makeCall("0700000000");
}
}
class Device {
protected String name;
Device(String name) {
this.name = name;
}
void powerOn() {
System.out.println(name + " is on");
}
}
class Laptop extends Device {
private int ramGb;
Laptop(String name, int ramGb) {
super(name);
this.ramGb = ramGb;
}
void showRam() {
System.out.println(name + " has " + ramGb + " GB of RAM");
}
}
class Phone extends Device {
Phone(String name) {
super(name);
}
void makeCall(String number) {
System.out.println(name + " calls " + number);
}
}
Output:
Lap-01 is on
Lap-01 has 16 GB of RAM
Ph-07 is on
Ph-07 calls 0700000000
Neither Laptop nor Phone declares name or powerOn, yet both objects use them, because they are inherited from Device. Each subclass adds only what is special to it: the RAM size for a laptop and the call feature for a phone.
Points to remember about inheritance in Java:
super(...), which runs the superclass constructor. If it is omitted, Java inserts super(), and the program does not compile when the superclass has no constructor without parameters.toString and equals.Inheritance reduces repeated code, and a correction made in the superclass reaches every subclass. The same link also means that a careless change to the superclass can break all of its subclasses, so a superclass should be small and stable.
Encapsulation means keeping the attributes of a class private and allowing other classes to reach them only through public methods. The class then controls how its data changes. A battery pack in a diagnostic tool must report a charge between 0 and 100 per cent; if any part of the program could write the attribute directly, a value such as 150 could slip in, and every calculation that follows would be wrong.
public class EncapsulationDemo {
public static void main(String[] args) {
BatteryPack pack = new BatteryPack();
pack.setCharge(80);
pack.setCharge(150);
System.out.println("Charge: " + pack.getCharge() + "%");
}
}
class BatteryPack {
private int charge;
public int getCharge() {
return charge;
}
public void setCharge(int charge) {
if (charge >= 0 && charge <= 100) {
this.charge = charge;
} else {
System.out.println("Rejected: charge must be from 0 to 100");
}
}
}
Output:
Rejected: charge must be from 0 to 100
Charge: 80%
The first call stores 80. The second is refused by the check inside setCharge, so the charge remains 80. If main tried to write pack.charge = 150;, the compiler would stop the program, because charge is private.
Benefits of encapsulation:
The usual pattern is a private attribute, a public getter named getX (isX for a boolean) and, where change is allowed, a public setter named setX.
Abstraction means showing what an object can do and hiding how it does it. A user of a storage device needs to know its type and capacity, not how the data is physically recorded. In Java, abstraction is written with abstract classes and interfaces.
An abstract class is declared with the keyword abstract and cannot be used to create objects. It can contain abstract methods, which have a header and no body, and ordinary methods, which are inherited as they are. Every concrete subclass must give a body to every abstract method.
public class AbstractionDemo {
public static void main(String[] args) {
StorageDevice[] devices = {new FlashDisk(32), new HardDisk(500)};
for (StorageDevice d : devices) {
d.describe();
}
}
}
abstract class StorageDevice {
abstract String typeName();
abstract int capacityGb();
void describe() {
System.out.println(typeName() + ": " + capacityGb() + " GB");
}
}
class FlashDisk extends StorageDevice {
private int gb;
FlashDisk(int gb) {
this.gb = gb;
}
String typeName() {
return "Flash disk";
}
int capacityGb() {
return gb;
}
}
class HardDisk extends StorageDevice {
private int gb;
HardDisk(int gb) {
this.gb = gb;
}
String typeName() {
return "Hard disk";
}
int capacityGb() {
return gb;
}
}
Output:
Flash disk: 32 GB
Hard disk: 500 GB
The method describe is written once in the abstract class and relies on two methods whose details differ in each subclass. The statement new StorageDevice() would not compile, because an abstract class is only a pattern for its subclasses.
An interface lists methods that a class promises to provide, without saying how. Its methods are public and abstract unless they are declared default or static. A class that implements an interface must write a body for every abstract method of it, unless the class is itself abstract. A class can implement several interfaces, which is how Java allows one class to have more than one type.
public class InterfaceDemo {
public static void main(String[] args) {
Printable[] items = {new Invoice(1001), new Label("WS-01")};
for (Printable p : items) {
p.print();
}
}
}
interface Printable {
void print();
}
class Invoice implements Printable {
private int number;
Invoice(int number) {
this.number = number;
}
public void print() {
System.out.println("Printing invoice " + number);
}
}
class Label implements Printable {
private String text;
Label(String text) {
this.text = text;
}
public void print() {
System.out.println("Printing label " + text);
}
}
Output:
Printing invoice 1001
Printing label WS-01
The loop in main knows only that each item is Printable. It does not know or need to know whether an item is an invoice or a label, so a new printable class can be added later without changing the loop. Choose an abstract class when related classes share attributes and ready-made methods; choose an interface when unrelated classes must offer the same ability.
Polymorphism means "many forms": one method call can produce different behaviour depending on the object that receives it. Java has two kinds. Compile-time polymorphism is method overloading, where the compiler picks a version by looking at the arguments, as explained in Demonstration of methods. Run-time polymorphism is method overriding, where a subclass replaces an inherited method with its own version and Java chooses the version while the program is running, according to the actual type of the object.
public class PolymorphismDemo {
public static void main(String[] args) {
Notification[] queue = {
new Notification(),
new EmailNotification("customer@example.com"),
new SmsNotification("0700000000")
};
for (Notification n : queue) {
n.send("Your device is ready for collection");
}
}
}
class Notification {
void send(String message) {
System.out.println("Notice: " + message);
}
}
class EmailNotification extends Notification {
private String address;
EmailNotification(String address) {
this.address = address;
}
@Override
void send(String message) {
System.out.println("Email to " + address + ": " + message);
}
}
class SmsNotification extends Notification {
private String number;
SmsNotification(String number) {
this.number = number;
}
@Override
void send(String message) {
System.out.println("SMS to " + number + ": " + message);
}
}
Output:
Notice: Your device is ready for collection
Email to customer@example.com: Your device is ready for collection
SMS to 0700000000: Your device is ready for collection
The array holds Notification references, yet each call n.send(...) runs the version that belongs to the real object: the plain notice, the email or the SMS. The loop does not test the type of each object, so adding a WhatsApp notification later means writing one new subclass and no change to the loop.
Rules for overriding:
@Override makes the compiler report a mistake, for example a misspelt method name, instead of silently creating a new method.super.send(message) inside the overriding method runs the superclass version, which is useful when the new version only adds to the old one.Create a free account to open more of this chapter.
Free: practical guides, quick cards, workplace scenarios and more.
Create a free accountType: Individual
| Tools & Equipment | Materials |
|---|---|
| Functional computer installed with Java IDE (NetBeans/Eclipse/IntelliJ) | Class attributes: bookID, title, author, isAvailable |
| Plain paper | Methods: displayDetails(), borrowBook(), returnBook() |
| Pen |
| S/N | Item | Quantity |
|---|---|---|
| 1 | Functional computer installed with Java IDE (NetBeans/Eclipse/IntelliJ) | 1 Pc per Candidate |
| 2 | Plain paper | 1 Pc per Candidate |
| 3 | Pen | 1 Pc per Candidate |
| Items to be Evaluated | Marks Available | Marks Obtained | Comments |
|---|---|---|---|
| TASK 1: Define Class and Attributes | |||
| Launched the Java IDE (Award 2 marks or 0) | 2 | ||
| Declared class named 'Book' (Award 2 marks or 0) | 2 | ||
| Declared attributes: bookID, title, author, isAvailable (Award 1 mark for each attribute declared correctly) | 4 | ||
| Sub-Total | 8 | ||
| TASK 2: Implement Methods | |||
| Declared method displayDetails() that prints all book attributes (Award 3 marks or 0) | 3 | ||
| Declared method borrowBook() that sets isAvailable to false (Award 3 marks or 0) | 3 | ||
| Declared method returnBook() that sets isAvailable to true (Award 3 marks or 0) | 3 | ||
| Sub-Total | 9 | ||
| TASK 3: Create and Manipulate Objects | |||
| Created at least two Book objects with initial values (Award 3 marks or 0) | 3 | ||
| Called displayDetails() method on each object (Award 3 marks or 0) | 3 | ||
| Called borrowBook() on one object and verified isAvailable status (Award 3 marks or 0) | 3 | ||
| Called returnBook() on same object and verified isAvailable status (Award 3 marks or 0) | 3 | ||
| Sub-Total | 12 | ||
| PRODUCT CHECKLIST | |||
| Class Book correctly defined with all attributes and methods (Award 6 marks or 0) | 6 | ||
| Objects created and methods function as expected with correct output (Award 5 marks or 0) | 5 | ||
| Sub-Total | 11 | ||
| GRAND TOTAL | 40 | ||
Type: Individual
| Tools & Equipment | Materials |
|---|---|
| Computer with Java IDE (Eclipse/NetBeans/IntelliJ) | Plain A4 Paper |
| Pen |
| S/N | Item | Quantity |
|---|---|---|
| 1 | Computer with Java IDE (Eclipse/NetBeans/IntelliJ) | 1 Pc per Candidate |
| 2 | Plain A4 Paper | 2 Pcs per Candidate |
| 3 | Pen | 1 Pc per Candidate |
| Items to be Evaluated | Marks Available | Marks Obtained | Comments |
|---|---|---|---|
| TASK 1: Class Declaration and Attributes | |||
| Launched Java IDE and created a new Java project (Award 2 marks or zero) | 2 | ||
| Declared class named 'Student' (Award 2 marks or zero) | 2 | ||
| Declared private attributes: name (String), age (int), grade (char) (Award 2 marks for each correctly declared attribute) | 6 | ||
| Sub-Total | 10 | ||
| TASK 2: Getter and Setter Methods Implementation | |||
| Declared public getter methods for all private attributes (Award 2 marks for each correctly implemented getter method) | 6 | ||
| Declared public setter methods for all private attributes (Award 2 marks for each correctly implemented setter method) | 6 | ||
| Setter methods include basic validation: age >= 0, grade is a valid char (Award full 4 marks if validation is implemented correctly, else zero) | 4 | ||
| Sub-Total | 16 | ||
| TASK 3: Testing and Display | |||
| Created a main method to instantiate the Student class object (Award 2 marks or zero) | 2 | ||
| Used setter methods to assign values to attributes (Award 1 mark for each attribute assigned correctly) | 3 | ||
| Used getter methods to display the attribute values (Award 1 mark for each attribute displayed correctly) | 3 | ||
| Sub-Total | 8 | ||
| PRODUCT CHECKLIST | |||
| Class 'Student' compiles without errors and encapsulation is correctly implemented (Award 5 marks if code compiles and encapsulation is demonstrated) | 5 | ||
| Getter and setter methods correctly restrict direct access to private attributes (Award 5 marks for proper use of access modifiers and method functionality) | 5 | ||
| Output shows correct attribute values retrieved via getter methods (Award 5 marks if output matches assigned values) | 5 | ||
| Sub-Total | 15 | ||
| GRAND TOTAL | 49 | ||
At the start of this chapter we promised you would be able to:
Tick each one you can genuinely do.
So, are you there yet?
You're competent when you can confidently do 50% or more of what this chapter promised.
Sign in to record how you're doing.