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.
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.
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
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.
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 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.
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.
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.
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.
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.fuelLevel field + refuel() concrete methodmove() 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.move() — each subclass provides its own bodypublic 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(); }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.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.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 |
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 |
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
Shape s = new Shape("blue"); if Shape is an abstract class. What would go wrong logically if Java allowed it?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());Triangle that extends Shape but you forget to override area(). What happens, and how can you fix it? Provide two different fixes.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).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.