JAVA • OBJECT-ORIENTED PROGRAMMING

Abstract Classes — Use abstract classes for shared behavior (intro)

Learn how abstract classes let you share common code while requiring subclasses to fill in the details.

Historical Context & Motivation

As programs grew larger in the 1960s and 1970s, developers realized that copying the same code into every class was a recipe for bugs. If you fixed a mistake in one place, you had to remember to fix it everywhere else too. The idea of inheritance — letting one class borrow behavior from another — was a huge step forward. But a new question arose: what if a parent class describes a concept so general that creating an object from it doesn't make sense? That question led directly to the invention of abstract classes.

1967
Simula 67 Introduces Classes
Norwegian researchers Ole-Johan Dahl and Kristen Nygaard created Simula, the first language with classes and inheritance. It planted the seed for object-oriented programming.
1980
Smalltalk Popularizes OOP
Smalltalk at Xerox PARC refined the idea of objects, messages, and shared behavior. The need for partially-defined parent classes became clear as hierarchies grew deeper.
1985
C++ Adds Pure Virtual Functions
Bjarne Stroustrup's C++ introduced 'pure virtual' functions, which forced subclasses to provide their own implementation — the same idea Java later called abstract methods.
1995
Java Launches with abstract Keyword
Java shipped with the abstract keyword built in, giving developers a clean, readable way to mark classes and methods that must be completed by subclasses.

The core question abstract classes answer is this: How do you share common code among related classes while still requiring each subclass to supply its own unique details? That tension between "shared" and "unique" is exactly what we will explore throughout this lesson.

Core Principles & Definitions

An abstract class is a class declared with the abstract keyword. You cannot create an object from it using new. Instead, it serves as a blueprint: it can contain fully working methods (concrete methods) alongside method signatures that have no body (abstract methods). Subclasses must override every abstract method, which guarantees they provide their own version of that behavior.

1

Cannot Be Instantiated

You cannot write new Animal() if Animal is abstract. Only concrete subclasses like Dog or Cat can be created.
2

Mix of Concrete & Abstract Methods

Concrete methods (with a body) provide shared behavior. Abstract methods (no body, just a signature) force each subclass to supply its own implementation.
3

Supports Constructors & Fields

Unlike interfaces, abstract classes can have constructors, instance variables, and any access modifier (private, protected, public). This makes them ideal for storing shared state.
4

Single Inheritance Only

A class can extend only one abstract class. This is Java's single-inheritance rule. If you need to inherit from multiple sources, you combine one abstract class with interfaces.
✦ KEY TAKEAWAY
Think of an abstract class like a recipe template at a restaurant. The template says "every sandwich must have bread" (concrete method — already defined) and "choose your own filling" (abstract method — each chef decides). You can't serve the template itself, but every real sandwich follows the same structure. That's exactly how abstract classes provide shared behavior while leaving room for customization.

Visual Explanation — The Inheritance Hierarchy

The dashed border around Shape shows it is abstract — you can't create a Shape object directly. Both Circle and Rectangle extend Shape, inherit the concrete getColor() method, and provide their own area() implementation.

In the diagram above, the abstract class Shape holds the shared behavior — storing and returning the color — so neither Circle nor Rectangle has to duplicate that code. Meanwhile, area() is declared abstract because each shape calculates its area differently. The compiler will not let you forget to implement it — if you try to create a concrete subclass without overriding area(), your code won't compile.

How Abstract Classes Work in Java

Declaring an Abstract Class

To create an abstract class, place the abstract keyword before the word class. Inside, you can write fully implemented (concrete) methods alongside abstract methods — those end with a semicolon instead of curly braces. Here is the general syntax:

ABSTRACT CLASS SYNTAX
public abstract class ClassName { // Fields (instance variables) // Constructor(s) // Concrete method (has a body) public returnType methodA() { ... } // Abstract method (no body) public abstract returnType methodB(); }
The abstract keyword appears on both the class declaration and on each method that has no body. Concrete methods inside the class work like normal methods.

Extending an Abstract Class

A subclass uses the extends keyword just like with any other parent class. The critical rule is that the subclass must override every abstract method unless the subclass is also declared abstract. When overriding, you provide the method body with curly braces. The @Override annotation is optional but strongly recommended — it tells the compiler to check that you are actually overriding a method from the parent.

SUBCLASS SYNTAX
public class SubClassName extends ClassName { @Override public returnType methodB() { // Provide the actual implementation here } }
The subclass inherits all concrete methods and fields from the abstract parent. Only the abstract methods require new code.

Using Polymorphism with Abstract Types

Even though you can't instantiate an abstract class, you can use it as a reference type. For example, you can declare a variable of type Shape and have it point to a Circle object. When you call area(), Java uses polymorphism to run the Circle version of the method at runtime. This lets you write flexible code that works with any subclass of the abstract class.

POLYMORPHIC REFERENCE
Shape s = new Circle("red", 5.0); System.out.println(s.area()); // calls Circle's area() System.out.println(s.getColor()); // calls Shape's getColor()
The variable s is declared as type Shape (the abstract class) but holds a Circle object. Java figures out the correct method at runtime.

Full Code Example — Shape Hierarchy

Let's see a complete, compilable example. We'll build the Shape hierarchy from the diagram, then demonstrate polymorphism in a main method. Pay attention to which parts are shared and which parts each subclass defines on its own.

Complete Java code for the Shape abstract class and its two concrete subclasses. Notice how getColor() is written once in the parent, while area() is implemented differently in each child.

Notice three important details. First, Shape has a constructor even though you can't create a Shape directly — the constructor is called via super(c) in each subclass. Second, getColor() is written exactly once and both subclasses inherit it for free. Third, trying to compile a subclass without an area() method will cause a compiler error — Java enforces the contract.

Worked Example — Building a Vehicle Hierarchy

Let's walk through designing an abstract class from scratch for a different scenario. Imagine you are building a program that models different vehicles. All vehicles share some behaviors (like having a fuel level), but the way each vehicle moves is unique.

Designing an Abstract Vehicle Class
1
Step 1 — Identify shared state and behaviorAll vehicles have a fuelLevel (a double) and a method refuel(double amount) that increases it. These are the same for every vehicle, so they belong in the abstract parent as concrete methods and fields.
Shared: fuelLevel field + refuel() concrete method
2
Step 2 — Identify behavior that variesThe method move() should behave differently for a Car (drives on roads) versus a Boat (sails on water). Since the parent can't know how each vehicle moves, move() should be declared abstract.
Abstract: move() — each subclass provides its own body
3
Step 3 — Write the abstract classDeclare public abstract class Vehicle with a constructor that sets the initial fuel level, a concrete refuel() method, and an abstract move() method ending in a semicolon.
public abstract class Vehicle { ... public abstract void move(); }
4
Step 4 — Write the concrete subclassesCar extends Vehicle and overrides move() to print "Driving on the road." Boat extends Vehicle and overrides move() to print "Sailing on the water." Both call super(fuel) in their constructors.
Both subclasses compile successfully because they implement all abstract methods.
5
Step 5 — Use polymorphism in main()In main, create an array Vehicle[] fleet = { new Car(50), new Boat(100) };. Loop through the array and call v.move() on each element. Java automatically calls the correct version of move() for each object at runtime.
Output: "Driving on the road." then "Sailing on the water."

Abstract Classes vs. Interfaces vs. Concrete Classes

Java gives you three ways to define types: concrete classes, abstract classes, and interfaces. Choosing the right one depends on how much shared implementation you have and how many parent types a class needs. The table below summarizes the key differences.

Comparison of Java class types
FeatureConcrete ClassAbstract ClassInterface
Can be instantiated?YesNoNo
Has constructors?YesYesNo
Instance variables?YesYesOnly constants (static final)
Concrete methods?All methodsSome or allDefault methods only (Java 8+)
Abstract methods?NoZero or moreAll (unless default)
Multiple inheritance?Single (extends 1)Single (extends 1)Multiple (implements many)
Best for…Fully defined objectsShared code + enforced contractsCapability contracts across unrelated classes
💡 WHEN TO PICK ABSTRACT
Use an abstract class when your subclasses share a significant amount of code (fields, helper methods) but differ in at least one behavior. Think of it like a smartphone manufacturer: the factory builds the same screen and battery for every model (shared code), but each model gets its own camera module (abstract method). If there's no shared code at all and you just want a list of required methods, an interface is the better tool.

Connection to Advanced OOP Concepts

Abstract classes are a stepping stone to several powerful techniques you'll encounter as you advance in Java. Understanding them now makes it much easier to grasp design patterns, frameworks, and large-scale software architecture later on.

From abstract classes to advanced design
What You Know NowWhat Comes Next
Abstract class with abstract methodsTemplate Method pattern — the parent defines the algorithm skeleton; subclasses fill in steps
Single inheritance from one abstract classCombining abstract class + interfaces — extending one parent while implementing multiple interfaces for flexibility
Polymorphic references (Shape s = new Circle(…))Factory Method pattern — a method returns objects of different subclasses through a parent type
Concrete methods in abstract classesDefault methods in interfaces (Java 8+) — interfaces can also provide default implementations

As you explore AP Computer Science A or college-level Java, you'll see that abstract classes form the backbone of many libraries. For example, Java's AbstractList class provides most of the code for a custom list — you only need to override get() and size(). This pattern of "do most of the work in the parent, let the child fill in the gaps" is everywhere in professional Java development.

Practice Problems

PROBLEM 1 — CONCEPTUAL
Explain in your own words why you cannot write Shape s = new Shape("blue"); if Shape is an abstract class. What would go wrong logically if Java allowed it?
PROBLEM 2 — BASIC CALCULATION
Given the Shape hierarchy from this lesson, what is the output of the following code? Shape s = new Circle("green", 3.0); System.out.println(s.getColor()); System.out.println(s.area());
PROBLEM 3 — INTERMEDIATE
You add a new subclass Triangle that extends Shape but you forget to override area(). What happens, and how can you fix it? Provide two different fixes.
PROBLEM 4 — APPLIED
You are designing a music player app. You have three types of media: Song, Podcast, and Audiobook. All three share a title field and a getTitle() method, but each displays its information differently via a displayInfo() method. Design the abstract class and one subclass (write the class signatures and key methods, not full code).
PROBLEM 5 — CRITICAL THINKING
A classmate argues: "Abstract classes are pointless. You could just use a regular parent class and leave method bodies empty (returning 0 or null). The code would still compile." Write a paragraph explaining at least two reasons why this argument is flawed and why the abstract keyword matters.

Lesson Summary

An abstract class in Java is declared with the abstract keyword and cannot be instantiated. It can contain concrete methods (with bodies) that provide shared behavior to all subclasses, along with abstract methods (no bodies) that each subclass must override. Subclasses use extends to inherit from the abstract class and call super() to invoke the parent's constructor.

The key advantage is code reuse without duplication: common logic lives in one place, while polymorphism lets you write flexible code using the abstract type as a reference. The compiler enforces the contract — if a concrete subclass doesn't implement every abstract method, the program won't compile. Abstract classes sit between concrete classes (fully implemented) and interfaces (pure contracts), giving you the best of both worlds when related classes share significant code.

Varsity Tutors • Java • Abstract Classes — Use abstract classes for shared behavior (intro)