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
1980 • Smalltalk Popularizes OOP
1985 • C++ Adds Pure Virtual Functions
1995 • Java Launches with abstract Keyword
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.
Cannot Be Instantiated
new Animal() if Animal is abstract. Only concrete subclasses like Dog or Cat can be created.Mix of Concrete & Abstract Methods
Supports Constructors & Fields
Single Inheritance Only
KEY TAKEAWAY
SECTION 3
Visual Explanation — The Inheritance Hierarchy
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
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
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
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.
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
Step 1 — Identify shared state and behavior
All vehicles have afuelLevel(a double) and a methodrefuel(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:fuelLevelfield +refuel()concrete method - 2
Step 2 — Identify behavior that varies
The methodmove()should behave differently for aCar(drives on roads) versus aBoat(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 class
Declarepublic abstract class Vehiclewith a constructor that sets the initial fuel level, a concreterefuel()method, and an abstractmove()method ending in a semicolon.public abstract class Vehicle { ... public abstract void move(); } - 4
Step 4 — Write the concrete subclasses
CarextendsVehicleand overridesmove()to print "Driving on the road."BoatextendsVehicleand overridesmove()to print "Sailing on the water." Both callsuper(fuel)in their constructors.Both subclasses compile successfully because they implement all abstract methods. - 5
Step 5 — Use polymorphism in main()
Inmain, create an arrayVehicle[] fleet = { new Car(50), new Boat(100) };. Loop through the array and callv.move()on each element. Java automatically calls the correct version ofmove()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.
| Feature | Concrete Class | Abstract Class | Interface |
|---|---|---|---|
| Can be instantiated? | Yes | No | No |
| Has constructors? | Yes | Yes | No |
| Instance variables? | Yes | Yes | Only constants (static final) |
| Concrete methods? | All methods | Some or all | Default methods only (Java 8+) |
| Abstract methods? | No | Zero or more | All (unless default) |
| Multiple inheritance? | Single (extends 1) | Single (extends 1) | Multiple (implements many) |
| Best for… | Fully defined objects | Shared code + enforced contracts | Capability contracts across unrelated classes |
Comparison of Java class types
WHEN TO PICK ABSTRACT
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 Now | What Comes Next |
|---|---|
| Abstract class with abstract methods | Template Method pattern — the parent defines the algorithm skeleton; subclasses fill in steps |
| Single inheritance from one abstract class | Combining 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 classes | Default 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
Shape s = new Shape("blue"); if Shape is an abstract class. What would go wrong logically if Java allowed it?Show answer
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
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
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
Triangle that extends Shape but you forget to override area(). What happens, and how can you fix it? Provide two different fixes.Show answer
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
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
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
abstract keyword matters.Show answer
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.