Abstract Classes and Methods in Java
The line Figure figure = new Figure(10, 10); looks completely harmless — the class has fields, it has a constructor, everything is in place. The compiler still refuses: Figure is abstract; cannot be instantiated. A single keyword in the class declaration permanently forbids creating objects of that class, and that is not a limitation but the whole point of the construct.
An abstract class in Java is a class declared with the abstract modifier: you cannot create an object of it directly, and it exists to serve as a common parent for subclasses. An abstract method is a method with no body — it has a signature, but no implementation. In plain English, "abstract" means "existing as an idea, not tied to any one concrete thing", which is exactly what these classes and methods are: they describe what subclasses can do without saying how.
Abstract method: syntax and rules
Abstract methods are methods that have no implementation. Instead of a body in curly braces after the signature, you simply put a semicolon.
General form:
abstract returnType methodName(parameterList); Example:
public abstract double calculateArea(); Rules for an abstract method:
- it has no body — only a signature followed by a semicolon; an empty body
{ }is not the same thing, that is already an ordinary method; - it must be implemented by the first concrete (non-abstract) subclass in the inheritance chain — an intermediate subclass may stay abstract and pass the obligation further down;
- it can only be declared inside an abstract class or an interface;
- it cannot be combined with
static,finalorprivate.
Abstract class and the abstract keyword
Any class that contains at least one abstract method must itself be declared abstract. To do that, put the keyword abstract before class:
public abstract class Figure {
// ...
public abstract double calculateArea();
} An abstract class cannot have objects of its own, but its type can still be used for references to objects of its subclasses:
public class Demo {
public static void main(String[] args) {
// Figure figure = new Figure(10, 10); // compile-time error:
// Figure is abstract; cannot be instantiated
Figure figure = new Rectangle(10, 20); // fine: a Figure reference
System.out.println(figure.calculateArea());
}
} Important
The rule does not work in reverse: a class can be abstract without a single abstract method. Such a class still cannot be instantiated — it is a legitimate way of saying "use my subclasses, not me". The JDK does exactly this with java.util.Calendar, for instance.
What abstraction in Java actually means
Abstraction is one of the four pillars of OOP, alongside encapsulation, inheritance and polymorphism. In practical terms, class abstraction in Java means working with an object through its general contract, without knowing which implementation is behind it: a variable, a method parameter or a collection element is declared with the common abstract type, and the concrete object is plugged in at run time.
public static double totalArea(Figure[] figures) {
double sum = 0;
for (Figure figure : figures) { // the method knows nothing about Rectangle or Triangle
sum += figure.calculateArea();
}
return sum;
} Java gives you two tools for this: the abstract class and the interface. An abstract class is the right one when subclasses are variations of a single entity and can share state and ready-made code; the comparison table below shows where the two differ.
Example: Figure, Rectangle and Triangle
Let's reuse the Figure, Triangle and Rectangle classes from the "Overriding Methods" lesson. There, Figure was a plain class whose calculateArea() returned a meaningless zero. Conceptually, Figure describes an abstract shape that has no area of its own, so it makes sense to turn calculateArea() into an abstract method — and the class itself into an abstract class.

public abstract class Figure {
double dimension1;
double dimension2;
public Figure(double dimension1, double dimension2) {
this.dimension1 = dimension1;
this.dimension2 = dimension2;
}
public abstract double calculateArea();
} Notice that the abstract class still has fields and a constructor, even though it never has objects of its own. The constructor exists for the subclasses — they call it through super(...) to initialise the inherited part of their state.
Any subclass of an abstract class must either implement all inherited abstract methods, or be declared abstract itself:
public class Rectangle extends Figure {
public Rectangle(double dimension1, double dimension2) {
super(dimension1, dimension2);
}
@Override
public double calculateArea() {
System.out.println("Calculating the area of a rectangle.");
return dimension1 * dimension2;
}
} public class Triangle extends Figure {
public Triangle(double dimension1, double dimension2) {
super(dimension1, dimension2);
}
@Override
public double calculateArea() {
System.out.println("Calculating the area of a triangle.");
return dimension1 * dimension2 / 2;
}
} Tip
Always put @Override above the implementation of an abstract method. If you misspell the name or get the parameter list wrong, the compiler tells you immediately; without the annotation you would just declare a brand-new method, the abstract one would stay unimplemented, and you would get a confusing error somewhere else in the file instead.
An array of an abstract type and polymorphism
You cannot have objects of an abstract class, but you can absolutely create an array (or a List) of the abstract type: it simply holds references to objects of its subclasses.
public class FindAreasExample {
public static void main(String[] args) {
Figure[] figures = new Figure[3];
// figures[0] = new Figure(10, 10); // will not compile: Figure is abstract
figures[0] = new Rectangle(10, 10);
figures[1] = new Rectangle(20, 10);
figures[2] = new Triangle(10, 10);
for (Figure figure : figures) {
double area = figure.calculateArea();
System.out.println(area);
}
}
} Program output:
Calculating the area of a rectangle.
100.0
Calculating the area of a rectangle.
200.0
Calculating the area of a triangle.
50.0 Compile and run the example like this:
javac Figure.java Rectangle.java Triangle.java FindAreasExample.java
java FindAreasExample At compile time the only type visible is Figure, and the call is allowed because the method is declared in the abstract class. Which implementation actually runs is decided by the JVM at run time, from the real type of the object — this is dynamic dispatch (late binding).
What is allowed and not allowed in an abstract class
| Element | Allowed? | Why |
|---|---|---|
| Abstract methods | Yes | Zero, one or many — there is no limit |
| Regular methods with a body | Yes | Ready-made behaviour shared by all subclasses |
Fields, including non-final ones | Yes | Ordinary mutable state; an interface cannot do this |
| Constructor | Yes | Invoked from a subclass via super(...) |
| Static methods, static fields, static blocks | Yes | But a static method itself cannot be abstract |
main method | Yes | It is static, so it runs without an instance of the class |
Creating an object with new | No | Figure is abstract; cannot be instantiated. The new Figure(...) { ... } syntax is not an exception — it creates an anonymous subclass |
abstract + final | No | A contradiction: final blocks inheritance, and an abstract class is useless without subclasses |
abstract + static on a method | No | Static methods are not overridden, so there is no way to plug an implementation in |
abstract + private on a method | No | A private method is invisible to subclasses, so it cannot be implemented there |
| Abstract constructor | No | Constructors are never inherited or overridden |
Modern Java
Since Java 17 an abstract class can also be sealed: public sealed abstract class Figure permits Rectangle, Triangle { }. The class still cannot be instantiated, but now the list of subclasses is fixed and known to the compiler — which lets switch pattern matching check that you have covered every case.
Abstract classes in the JDK: AbstractList example
The standard library is full of abstract classes whose names start with Abstract: AbstractList, AbstractMap, AbstractSet, InputStream, Number. They are skeletal implementations: the class writes out all the repetitive code for you and leaves only a couple of abstract methods to fill in.
java.util.AbstractList is the clearest example. Implement just get(int) and size(), and you get a working read-only List with iterator(), indexOf(), contains(), equals(), hashCode() and toString() already provided:
import java.util.AbstractList;
public class SquaresList extends AbstractList<Integer> {
private final int size;
public SquaresList(int size) {
this.size = size;
}
@Override
public Integer get(int index) {
return index * index;
}
@Override
public int size() {
return size;
}
}
// new SquaresList(5) behaves like a List: [0, 1, 4, 9, 16] Writing the same class from scratch as implements List<Integer> would mean implementing about 25 methods by hand. That is the practical value of an abstract class: shared code lives in the parent, and the subclass only supplies what is genuinely unique to it.
Abstract class vs interface
Since default methods arrived in Java 8 and private interface methods in Java 9, the line between an interface and an abstract class has become thinner — but it has not disappeared.
| Criterion | Abstract class | Interface |
|---|---|---|
| How many you can extend / implement | Only one | As many as you like |
| Mutable state (fields) | Yes, any fields | No, only public static final constants |
| Constructor | Has one | Does not have one |
| Ready-made code | Any methods with a body | default, static and (since Java 9) private methods |
| Access modifiers on members | Any, including protected and private | public by default; private allowed for helper methods only |
| When to choose it | Subclasses are variations of one entity and share state and code | You need a contract or a role for otherwise unrelated classes |
The JDK routinely uses both at once: List is the interface (the contract), AbstractList is the abstract class (the shared implementation), and ArrayList is the concrete class you actually instantiate.
Where developers get tripped up
- Trying to instantiate an abstract class.
new Figure(10, 10)will not compile, even though the class has a perfectly good constructor. - Forgetting to implement a method in a subclass. The compiler then demands either an implementation or that the subclass itself be marked
abstract. - Narrowing visibility in the implementation. A method declared
public abstractcannot be implemented asprotected— when overriding you may widen access, never restrict it. - Confusing "abstract" with "empty". A method with a body like
{ }orreturn 0;is an ordinary method: nobody is forced to override it, and the bug surfaces at run time instead of at compile time. - Making a class abstract when nothing extends it. An abstraction with no implementation behind it is just code that can never run.
- Calling an abstract method from the constructor. The subclass overrides it, but at that moment the subclass fields are not initialised yet — you get
0ornullinstead of real values.
Practice
Write an abstract class Employee with fields name and baseSalary, a constructor and an abstract method double calculateSalary(). Add two subclasses: Manager (base salary plus a 20% bonus) and Developer (base salary plus a fixed 500 for overtime). Put the objects into an Employee[] array and print the total payroll in a loop.
Self-check: try adding a third subclass without implementing calculateSalary() and read the compiler message carefully — it is the same one you will meet in interviews.
Official reference: Abstract Methods and Classes — The Java Tutorials.
Frequently asked questions
Can you create an object of an abstract class?
Not directly: new Figure(10, 10) is a compile-time error. What you can do is create an object of an anonymous subclass — new Figure(10, 10) { public double calculateArea() { return 0; } } compiles fine, because it does not create a Figure, it creates an unnamed subclass that implements the abstract method.
Why does an abstract class need a constructor if it never has objects?
The constructor of an abstract class initialises the inherited fields and is invoked from a subclass constructor via super(...). If a subclass does not call it explicitly, the compiler inserts a call to super() automatically — so if the abstract class has no no-argument constructor, the subclass will not compile at all.
What happens if a subclass does not implement an abstract method?
The compiler reports an error along the lines of "is not abstract and does not override abstract method". There are two ways out: implement the method, or declare the subclass abstract as well and pass the obligation to the next class in the inheritance chain.
Can an abstract class extend a concrete class?
Yes. An abstract class can extend any non-abstract class and add abstract methods on top of it — this is a common way to make an existing class non-instantiable and force subclasses to fill in a missing step. The reverse works too: a concrete class can extend an abstract one as long as it implements every inherited abstract method.
What is the difference between abstraction and encapsulation?
Abstraction is about design: it decides what a type exposes and hides the implementation behind a general contract such as Figure or List. Encapsulation is about access control: it hides the internal data of an object behind private fields and accessor methods so that the state cannot be corrupted from outside. Abstraction hides complexity, encapsulation hides data — and normally you use both at the same time.
Comments