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.

Java programming emblem with a coffee cup, code braces, and terminal
Inside this lesson 10 sections 5 questions 7 figures

SECTION 1

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.

SECTION 2

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.

SECTION 3

Visual Explanation — The Inheritance Hierarchy

ABSTRACT CLASSShape+ color: String+ area(): double «abstract»+ getColor(): StringCONCRETE CLASSCircle+ radius: double+ area(): double ✓ → returns π × r²CONCRETE CLASSRectangle+ width, height: double+ area(): double ✓ → returns w × hgetColor() is inherited by both — no need to rewrite it!
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.

SECTION 4

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

publicabstractclassClassName//Fields(instancevariables)//Constructor(s)//Concretemethod(hasabody)publicreturnTypemethodA()...//Abstractmethod(nobody)publicabstractreturnTypemethodB();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

publicclassSubClassNameextendsClassName@OverridepublicreturnTypemethodB()//Providetheactualimplementationherepublic 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

Shapes=newCircle("red",5.0);System.out.println(s.area());//callsCircle′sarea()System.out.println(s.getColor());//callsShape′sgetColor()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.

SECTION 5

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.

// Abstract parent classpublic abstract class Shape { private String color; public Shape(String color) { this.color = color; } // Concrete method — shared by ALL subclasses public String getColor() { return color; } // Abstract method — each subclass MUST implement public abstract double area();}// Concrete subclass #1public class Circle extends Shape { private double radius; public Circle(String c, double r) { super(c); radius = r; } @Override public double area() { return Math.PI * radius * radius; }}// Concrete subclass #2public class Rectangle extends Shape { private double width, height; public Rectangle(String c, double w, double h) { super(c); width = w; height = h; } @Override public double area() { return width * height; }
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.

SECTION 6

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. 1

    Step 1 — Identify shared state and behavior

    All 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. 2

    Step 2 — Identify behavior that varies

    The 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. 3

    Step 3 — Write the abstract class

    Declare 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. 4

    Step 4 — Write the concrete subclasses

    Car 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. 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."

SECTION 7

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.

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

Comparison of Java class types

💡

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.

SECTION 8

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.

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

From abstract classes to advanced design

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.

SECTION 9

Practice Problems

Question 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?
Show answer
An abstract class may contain abstract methods — methods with no body. If Java let you create a Shape object and you called s.area(), there would be no code to execute because area() has no body in Shape. The abstract keyword is Java's way of saying "this class is incomplete — only finished subclasses should become objects." This prevents runtime crashes from calling unimplemented methods.

Question 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());
Show answer
The first line prints green because getColor() is a concrete method inherited from Shape and it simply returns the color field. The second line prints approximately 28.274333882308138 because Circle.area() calculates π × 3² = π × 9 ≈ 28.27. Even though the variable s is declared as type Shape, Java uses polymorphism to call Circle's version of area() at runtime.

Question 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.
Show answer
The code will not compile. Java will report an error like "Triangle is not abstract and does not override abstract method area() in Shape." There are two fixes. First, you can implement area() in Triangle — for example, returning 0.5 × base × height. Second, you can declare Triangle itself as abstract (using public abstract class Triangle extends Shape), which pushes the requirement down to whatever subclass eventually extends Triangle. The first fix is usually the right choice unless Triangle is itself a general category.

Question 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).
Show answer
Create public abstract class Media with a private String title field, a constructor that takes a title, a concrete getTitle() method, and public abstract void displayInfo();. Then public class Song extends Media adds a field like artist, calls super(title) in its constructor, and overrides displayInfo() to print the title and artist. The shared getTitle() is written once in Media and inherited by all three subclasses — no code duplication needed.

Question 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.
Show answer
The classmate's approach has two major problems. First, if a developer forgets to override the empty method in a subclass, the program compiles and runs — but it silently returns wrong values (like 0 or null) instead of crashing at compile time. The abstract keyword turns this invisible bug into a compiler error, catching the mistake before the program ever runs. Second, without abstract, someone could accidentally create an object of the parent class itself (for example, new Vehicle(50)), which represents nothing meaningful in the real world. Marking the class abstract makes the code self-documenting: it tells every developer on the team that this class is a template, not a finished product. In short, abstract is about safety and clarity — the compiler enforces rules that comments and good intentions cannot.

SUMMARY

Lesson Summary

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)