An abstract class (declared with abstract) can mix fully-implemented methods, abstract method signatures with no body, and instance fields, but a class can only extend one abstract (or concrete) class — Java has no multiple class inheritance. An interface historically held only abstract method signatures and constants, but since Java 8 it can also declare default methods (with a body, inherited by implementers) and static methods; a class can implement any number of interfaces, which is the real reason interfaces exist as a separate mechanism.
Abstract classes: partial implementation, single inheritance
A subclass extending Shape must implement area(), but inherits describe() as-is — abstract classes are the right tool when subclasses genuinely share both state and some common behavior.
public abstract class Shape {
protected String name; // shared state, inherited by subclasses
public Shape(String name) {
this.name = name;
}
public abstract double area(); // no body — subclasses MUST implement this
public String describe() { // fully implemented — inherited as-is
return name + " has area " + area();
}
}
public class Circle extends Shape {
private double radius;
public Circle(double radius) { super("Circle"); this.radius = radius; }
public double area() { return Math.PI * radius * radius; } // required implementation
}Interfaces: multiple inheritance of behavior
A class can implement any number of interfaces — this is the capability abstract classes fundamentally cannot offer, since Java allows only one class in the extends chain.
public interface Flyable {
void fly();
}
public interface Swimmable {
void swim();
}
public class Duck implements Flyable, Swimmable { // implements BOTH
public void fly() { System.out.println("Flying"); }
public void swim() { System.out.println("Swimming"); }
}default methods: interfaces with real implementation (Java 8+)
Before Java 8, adding a new method to an interface would break every existing implementer; default provides a fallback implementation so old code keeps compiling — this is precisely why the interface change was added, to evolve APIs like the Collections framework without breaking backward compatibility.
public interface Greeter {
String name();
default String greet() { // default method: real implementation
return "Hello, " + name() + "!";
}
}
public class Person implements Greeter {
public String name() { return "Alice"; }
// no need to implement greet() — the default is inherited
}