Object Oriented Programming is one of the most common programming paradigms and is based on concepts like classes and objects.
A class represents a template for a future object (blueprint) and provides the initial state (member variables or attributes) and the initial behavior (member functions or methods)
class
public class Car{ private String color; private String model; public Car(String color, String model){ this.color = color; this.model = model; } }
An object represents the instantiation of a class, usually created by using the constructor of that specific class.
object
Car a,b,c; a = new Car("green", "polo"); b = new Car("blue", "mini"); c = new Car("red", "beetle");
OOP encourages the principle of code re-usability and of-course, writing less code.

The 4 principles of OOP programming are:
Encapsulation
Encapsulation is the mechanism through which the state of an object is hidden via private instance variables (object attributes), and the state can only be changed or accessed via publicly accessible methods (accessor methods).
In simple terms, we ask the object nicely to do what we want; we don't barge inside a house and start changing things inside, we ask to host to do it. The exception is when the house is on fire 🔥 🙂
public class EncapsulatedCar { private String description; private float speed; public EncapsulatedCar(String description) { this.description = description; speed = 0.0f; } public void accelerate(){ speed = speed + 2; } public void printSpeed(){ System.out.println(speed); } }
That would be the model of our class. And below you can see how we can only accelerate the car via methods (operations) instead of changing the value of the speed directly.
EncapsulatedCar x; x = new EncapsulatedCar("red wagon"); x.printSpeed(); x.accelerate(); x.printSpeed();
Abstraction
Abstraction specifies that only the relevant functional details of an object are presented to the user. The implementation and trivial components are hidden from the view, which allows us to work with high level entities instead of a multitude of small parts.
A very simple example is thinking about a car like a car, where we can control the direction, speed etc. but we shouldn't have to deal with how the engine works for example, at least in normal conditions;
By keeping only the important features visible, we can group objects easier and concentrate on the overall design instead of the specific components and behavior.
One of the solutions in Java for a better abstraction are abstract classes and interfaces
Abstract classes
A class can be declared as abstract and have both abstract and non abstract methods. Very important, an abstract class needs to be extended and its methods implemented, it cannot be instantiated.
They are used to define abstract concepts (as expected) and must be declared as such if they have at least one abstract method.
Key points for abstract classes:
- Must be declared using the abstract keyword
- Can have both abstract and non abstract methods
- Cannot be instantiated
- Can have constructors (unlike an interface) and static methods
- Can have final methods which will force the implementation for the sub-classes
- They are structured and can hold a state (interfaces cannot).

Below is an example of an abstract class.
public abstract class AbstractCar{ private String description; private float speed; public AbstractCar(String description, float speed){ this.description = description; this.speed = speed; } public abstract void makeSound(); }
And this is a class which extends it and implements the abstract method.
package Model; public class ElectricCar extends AbstractCar{ private int range; public ElectricCar(String description, float speed, int range){ super(description,speed); this.range = range; } @Override public void makeSound() { System.out.println(" ......... silent ........."); } }
And this is how the second class is used:
ElectricCar t; t = new ElectricCar("blue tesla", 0.0f, 300); t.makeSound(); // ......... silent .........
Interface
An interface is basically the blueprint of a class. It usually contains the declaration of multiple abstract methods which will be implemented by the desired classes.
In Java, you cannot extend multiple classes, but you can implement multiple interfaces. Also, interfaces can extend other (super) interfaces.
After version 8, Java introduced the concept of default methods inside interfaces, as a solution for the cases where you had to implement the same method in multiple interface implementations.
The difference is that a default method inside an interface is public and implemented (an abstract method is only declared).
You can see below an example:
public interface InterfaceCar { public void start(); public void accelerate(); default String makeSound(){ return "....... silent ......"; } }
And the class which implements the interface.
public class Tesla implements InterfaceCar{ private String description; private float speed; public Tesla(String description){ this.description = description; } @Override public void start() { speed = 0.0f; } @Override public void accelerate() { speed = speed + 2; } @Override public String toString(){ return String.format("%s", description); } }
And this is how it's used:
Tesla s; s = new Tesla("red model s"); s.start(); s.makeSound(); //....... silent from the interface......
Inheritance
Inheritance refers to the concept of inheriting attributes and methods from one class (superclass or parent) to another class (subclass or child). In Java, a class can only inherit one superclass.
To inherit a class, the keyword extends is used. If you want to disable the inheritance of a class, you can use the keyword final in the class definition.
In Java every class is derived from the class Object and inherits the following methods:
- toString()
- equals()
- hashCode()
- getClass()
- finalize()
- clone()
- .....
A method in a child class can be overridden if it does not have the final keyword. This can be used to adapt the behavior of an object to the desired form. One of the best examples that can be seen above are the toString() overrides where the child class has its own method for representation of an object as a string.
Usually, the visual representation of an object is a business decision and it needs specific formatting to be implemented by the developer.
@Override public void start() { speed = 0.0f; } @Override public void accelerate() { speed = speed + 2; } @Override public String toString(){ return String.format("%s", description); }
Overloading refers to the implementation of the same method by name, but with a different signature (parameter list or parameter order).
add(int, int) add(int, int, int) // add(int, int) add(int, float) // add(int, float) add(float, int)
Upcasting and downcasting refers to the operation of redefining object type via forced casting, from parent to child or vice versa. This usually happens automatically if a method specifies the type and the conversion is permitted.
Dog dog = new Dog(); Animal anim = (Animal) dog; //upcasting, moving up on the inheritance chain anim.eat();
Access specifiers
The access level refers to both attributes and methods.
- public: visible from most places
- private: visible only from inside the class
- protected: visible from the same package or subclasses
- default: package visible
UML relations

Association: The objects are associated, but A can exist without B (a Person has multiple Addresses for example).
Aggregation: A cannot exist without B, but B exists without A.
Composition: A and B cannot exist without each other (a Human and a Heart for example).
Polymorphism
Polymorphism refers to the capacity of an entity to behave differently depending on the context.
In Java, one of the best examples is when the same method (same name) behaves differently for different extended classes or with different signatures.
class Animal { public void animalSound() { System.out.println("The animal makes a sound"); } } class Pig extends Animal { public void animalSound() { System.out.println("The pig says: wee wee"); } } class Dog extends Animal { public void animalSound() { System.out.println("The dog says: bow wow"); } } class MyMainClass { public static void main(String[] args) { Animal myAnimal = new Animal(); Animal myPig = new Pig(); Animal myDog = new Dog(); myAnimal.animalSound(); myPig.animalSound(); myDog.animalSound(); } }
There are two types of polymorphism in Java: Runtime and compile time;
At compile time the overloading of methods and the operator (+) overloading is considered. Ar runtime, the methods overriding is considered.
Anonymous classes
Anonymous classes are local classes that are not named. They can be used if we have classes that are only used once. They are used to declare and instantiate a class at the same time. We cannot instantiate them in any other place because they have no name.
https://www.baeldung.com/java-anonymous-classes
new Book("Design Patterns") { @Override public String description() { return "Famous GoF book."; } }
interface Eatable{ void eat(); } class TestAnnonymousInner1{ public static void main(String args[]){ Eatable e = new Eatable(){ public void eat(){System.out.println("nice fruits");} }; e.eat(); } }