Share behaviour with inheritance, override and overload methods, hide detail with abstract classes, and define contracts with interfaces.
Why: inheritance lets one class build on another, reusing its fields and methods instead of copying them. The child uses extends; super(...) calls the parent constructor. A Dog "is an" Animal, so it gets everything an Animal has.
public class Animal {
String name;
Animal(String name) { this.name = name; }
void eat() { System.out.println(name + " is eating"); }
}
public class Dog extends Animal {
Dog(String name) { super(name); } // call Animal's constructor
void fetch() { System.out.println(name + " fetches the ball"); }
}
Dog d = new Dog("Rex");
d.eat(); // inherited from Animal
d.fetch(); // Dog's own methodWhy: a child can replace a parent method with its own version — this is overriding. The @Override annotation tells the compiler to check you really are replacing one, catching typos. At runtime Java picks the object's actual version.
public class Animal {
String speak() { return "..."; }
}
public class Dog extends Animal {
@Override
String speak() { return "Woof"; } // replaces Animal's version
}
Animal a = new Dog();
System.out.println(a.speak()); // "Woof" — the Dog version runsWhy: overloading is having several methods with the same name but different parameters. Java picks the right one by the arguments you pass. It is handy for offering convenient variations of one operation.
public class Printer {
void print(String s) { System.out.println(s); }
void print(int n) { System.out.println("number: " + n); }
void print(String s, int times) {
for (int i = 0; i < times; i++) System.out.println(s);
}
}
Printer p = new Printer();
p.print("hi"); // first version
p.print(42); // second version
p.print("ho", 2); // third versionWhy: an abstract class is a half-finished blueprint — it cannot be created with new, and it can declare abstract methods (no body) that every child MUST fill in. Use it to share common code while forcing subclasses to supply the missing pieces.
public abstract class Shape {
abstract double area(); // no body — each shape decides
void describe() { // shared code all shapes get
System.out.println("Area is " + area());
}
}
public class Square extends Shape {
double side;
Square(double side) { this.side = side; }
@Override
double area() { return side * side; } // must implement it
}Why: an interface lists methods a class promises to provide, with no implementation. A class implements it and supplies the bodies. Unlike inheritance, a class can implement many interfaces — this is how Java mixes in capabilities.
public interface Drawable {
void draw(); // the contract: any Drawable can draw
}
public class Circle implements Drawable {
@Override
public void draw() {
System.out.println("drawing a circle");
}
}
Drawable thing = new Circle(); // hold it by the interface type
thing.draw();Why: code that depends on an interface, not a concrete class, works with any implementation — present or future. Here render() accepts any Drawable, so you can pass a Circle, a Square, or anything else that promises to draw.
public interface Drawable { void draw(); }
public class Canvas {
// accepts ANY Drawable — does not care which class it really is
void render(Drawable shape) {
shape.draw();
}
}
// you can pass a Circle, Square, or any future Drawable without changing render()