Tutorial Java OOP Lengkap: Kuasai Object-Oriented Programming dari Dasar sampai Mahir

By | September 27, 2025

 

Tutorial Java OOP Lengkap: Kuasai Object-Oriented Programming dari Dasar sampai Mahir

Pernah nggak sih kamu bikin program Java yang akhirnya jadi “spaghetti code” – semua kode dicampur aduk, sulit dibaca, dan ribet dikembangkan? Atau penasaran gimana cara perusahaan besar seperti Google dan Amazon manage codebase yang jutaan baris? Jawabannya ada di OOP (Object-Oriented Programming) – paradigma programming yang mengorganisir code seperti dunia nyata!

Saya masih inget banget “aha moment” pertama kali memahami OOP. Dulu saya bikin program manajemen toko dengan procedural programming – semua data dicampur, functions numpuk, maintenance-nya nightmare. Setelah paham OOP, semuanya jadi terorganisir rapi kayak puzzle yang pas!

Di tutorial ini, kita akan journey bersama memahami OOP di Java dari fundamental sampai advanced concepts. Kita akan analogikan dengan dunia nyata biar gampang dicerna, plus praktik bikin sistem perpustakaan digital yang OOP banget!

Apa Itu OOP dan Kenapa Revolutionize Programming?

OOP itu seperti main LEGO. Daripada bikin rumah dari kayu utuh yang harus dipotong-potong (procedural), kita menyusun dari block-block LEGO (objects) yang sudah ada bentuk standarnya.

Analogi Dunia Nyata:

  • Class: Blueprint/mobil cetakan mobil die-cast
  • Object: Mobil fisik yang sudah dicetak
  • Attribute: Warna, merek, tahun produksi mobil
  • Method: Starter, rem, gas, klakson mobil

Keuntungan OOP:

  • Modularity: Code terpisah-pisah seperti modul LEGO
  • Reusability: Class bisa dipakai berulang di project berbeda
  • Maintainability: Ganti satu bagian, nggak perlu ubah seluruh system
  • Scalability: Mudah dikembangkan tanpa rewrite dari nol
  • Real-world modeling: Cocok untuk complex business logic

4 Pilar OOP yang Wajib Dikuasai

OOP berdiri di atas 4 pilar fundamental. Mari kita bahas satu per satu dengan analogi yang mudah dimengerti.

1. Encapsulation (Enkapsulasi)

Seperti kapsul obat – kita tau fungsinya, tapi nggak perlu tau detail isinya.

public class BankAccount {
    // Private attributes - disembunyikan dari luar
    private String accountNumber;
    private double balance;
    private String ownerName;
    
    // Public methods - interface untuk berinteraksi
    public BankAccount(String accountNumber, String ownerName) {
        this.accountNumber = accountNumber;
        this.ownerName = ownerName;
        this.balance = 0.0;
    }
    
    // Getter methods - akses terkontrol
    public double getBalance() {
        return balance;
    }
    
    public String getAccountNumber() {
        return accountNumber;
    }
    
    // Method untuk business logic
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("Deposit berhasil: " + amount);
        } else {
            System.out.println("Amount harus positif");
        }
    }
    
    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            System.out.println("Penarikan berhasil: " + amount);
        } else {
            System.out.println("Saldo tidak cukup atau amount invalid");
        }
    }
}

// Penggunaan
public class Main {
    public static void main(String[] args) {
        BankAccount account = new BankAccount("123456", "Budi Santoso");
        account.deposit(1000000);
        account.withdraw(500000);
        
        // ❌ INI ERROR: balance private, tidak bisa diakses langsung
        // account.balance = 1000000; 
        
        // ✅ INI BENAR: akses melalui method
        System.out.println("Saldo: " + account.getBalance());
    }
}

2. Inheritance (Pewarisan)

Seperti hubungan orang tua dan anak – anak mewarisi sifat orang tua, plus punya keunikan sendiri.

// Parent class (Superclass)
public class Vehicle {
    protected String brand;
    protected String model;
    protected int year;
    
    public Vehicle(String brand, String model, int year) {
        this.brand = brand;
        this.model = model;
        this.year = year;
    }
    
    public void start() {
        System.out.println(brand + " " + model + " starting...");
    }
    
    public void stop() {
        System.out.println(brand + " " + model + " stopping...");
    }
    
    public void displayInfo() {
        System.out.println("Brand: " + brand + ", Model: " + model + ", Year: " + year);
    }
}

// Child class 1 (Subclass)
public class Car extends Vehicle {
    private int numberOfDoors;
    private String fuelType;
    
    public Car(String brand, String model, int year, int doors, String fuel) {
        super(brand, model, year); // Panggil constructor parent
        this.numberOfDoors = doors;
        this.fuelType = fuel;
    }
    
    // Method khusus Car
    public void honk() {
        System.out.println("Beep beep! " + brand + " " + model + " honking!");
    }
    
    // Override method parent
    @Override
    public void displayInfo() {
        super.displayInfo(); // Panggil method parent
        System.out.println("Doors: " + numberOfDoors + ", Fuel: " + fuelType);
    }
}

// Child class 2
public class Motorcycle extends Vehicle {
    private boolean hasSidecar;
    
    public Motorcycle(String brand, String model, int year, boolean hasSidecar) {
        super(brand, model, year);
        this.hasSidecar = hasSidecar;
    }
    
    public void wheelie() {
        System.out.println(brand + " " + model + " doing wheelie!");
    }
    
    @Override
    public void displayInfo() {
        super.displayInfo();
        System.out.println("Has Sidecar: " + hasSidecar);
    }
}

// Penggunaan
public class Main {
    public static void main(String[] args) {
        Car myCar = new Car("Toyota", "Camry", 2023, 4, "Petrol");
        Motorcycle myBike = new Motorcycle("Honda", "CBR", 2023, false);
        
        myCar.start();       // Warisan dari Vehicle
        myCar.honk();        // Method khusus Car
        myCar.displayInfo(); // Override method
        
        System.out.println();
        
        myBike.start();      // Warisan dari Vehicle  
        myBike.wheelie();    // Method khusus Motorcycle
        myBike.displayInfo(); // Override method
    }
}

3. Polymorphism (Polimorfisme)

Seperti remote control universal – satu tombol power, tapi efeknya beda tergangkat device-nya.

// Interface (Kontrak)
public interface Shape {
    double calculateArea();
    double calculatePerimeter();
    void draw();
}

// Implementasi berbeda-beda
public class Circle implements Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
    
    @Override
    public double calculatePerimeter() {
        return 2 * Math.PI * radius;
    }
    
    @Override
    public void draw() {
        System.out.println("Drawing Circle with radius: " + radius);
    }
}

public class Rectangle implements Shape {
    private double width;
    private double height;
    
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    public double calculateArea() {
        return width * height;
    }
    
    @Override
    public double calculatePerimeter() {
        return 2 * (width + height);
    }
    
    @Override
    public void draw() {
        System.out.println("Drawing Rectangle " + width + "x" + height);
    }
}

// Polymorphism in action
public class ShapeCalculator {
    public void processShape(Shape shape) {
        // Satu method, banyak bentuk (polymorphism)
        shape.draw();
        System.out.println("Area: " + shape.calculateArea());
        System.out.println("Perimeter: " + shape.calculatePerimeter());
        System.out.println();
    }
}

// Penggunaan
public class Main {
    public static void main(String[] args) {
        ShapeCalculator calculator = new ShapeCalculator();
        
        Shape circle = new Circle(5.0);
        Shape rectangle = new Rectangle(4.0, 6.0);
        
        // Polymorphism: method sama, behavior berbeda
        calculator.processShape(circle);
        calculator.processShape(rectangle);
        
        // Array of shapes
        Shape[] shapes = {
            new Circle(3.0),
            new Rectangle(2.0, 4.0),
            new Circle(7.0)
        };
        
        // Process semua shapes dengan loop
        for (Shape shape : shapes) {
            calculator.processShape(shape);
        }
    }
}

4. Abstraction (Abstraksi)

Seperti menyetir mobil – kita tau cara nyetir, tapi nggak perlu tau detail mesinnya.

// Abstract class - tidak bisa diinstansiasi langsung
public abstract class Employee {
    protected String name;
    protected String id;
    protected double baseSalary;
    
    public Employee(String name, String id, double baseSalary) {
        this.name = name;
        this.id = id;
        this.baseSalary = baseSalary;
    }
    
    // Abstract method - harus diimplement child class
    public abstract double calculateSalary();
    public abstract void work();
    
    // Concrete method - sudah ada implementasi
    public void takeBreak() {
        System.out.println(name + " is taking a break");
    }
    
    public void displayInfo() {
        System.out.println("Employee: " + name + " (" + id + ")");
    }
}

// Concrete class 1
public class FullTimeEmployee extends Employee {
    private double bonus;
    
    public FullTimeEmployee(String name, String id, double baseSalary, double bonus) {
        super(name, id, baseSalary);
        this.bonus = bonus;
    }
    
    @Override
    public double calculateSalary() {
        return baseSalary + bonus;
    }
    
    @Override
    public void work() {
        System.out.println(name + " is working full-time");
    }
}

// Concrete class 2  
public class PartTimeEmployee extends Employee {
    private int hoursWorked;
    private double hourlyRate;
    
    public PartTimeEmployee(String name, String id, int hours, double rate) {
        super(name, id, 0); // Base salary 0 untuk part-time
        this.hoursWorked = hours;
        this.hourlyRate = rate;
    }
    
    @Override
    public double calculateSalary() {
        return hoursWorked * hourlyRate;
    }
    
    @Override
    public void work() {
        System.out.println(name + " is working part-time");
    }
}

// Penggunaan
public class Main {
    public static void main(String[] args) {
        // ❌ ERROR: Cannot instantiate abstract class
        // Employee emp = new Employee("John", "001", 5000);
        
        // ✅ BENAR: Instantiate concrete classes
        Employee fullTime = new FullTimeEmployee("Alice", "FT001", 5000, 1000);
        Employee partTime = new PartTimeEmployee("Bob", "PT001", 20, 50);
        
        // Polymorphism dengan abstraction
        Employee[] employees = {fullTime, partTime};
        
        for (Employee emp : employees) {
            emp.displayInfo();
            emp.work();
            System.out.println("Salary: " + emp.calculateSalary());
            emp.takeBreak();
            System.out.println();
        }
    }
}

Project: Sistem Perpustakaan Digital dengan OOP

Sekarang mari praktik dengan project nyata. Kita akan bangun sistem perpustakaan dengan architecture OOP yang proper.

Class Diagram Rencana:

Perpustakaan
│
├── Book (Class)
│   ├── -isbn: String
│   ├── -title: String
│   ├── -author: String
│   ├── -isAvailable: boolean
│   └── +borrowBook(), +returnBook()
│
├── Member (Abstract Class)
│   ├── -memberId: String
│   ├── -name: String
│   ├── +borrowBook(Book)
│   └── +returnBook(Book)
│   │
│   ├── StudentMember (Concrete Class)
│   └── StaffMember (Concrete Class)
│
├── Transaction (Class)
│   └── -transactionDate: Date
│
└── Library (Composition)
    └── -books: List<Book>
    └── -members: List<Member>

Implementasi Code:

import java.util.*;
import java.time.LocalDate;

// 1. Book Class (Encapsulation)
public class Book {
    private String isbn;
    private String title;
    private String author;
    private boolean isAvailable;
    
    public Book(String isbn, String title, String author) {
        this.isbn = isbn;
        this.title = title;
        this.author = author;
        this.isAvailable = true;
    }
    
    // Getters
    public String getIsbn() { return isbn; }
    public String getTitle() { return title; }
    public String getAuthor() { return author; }
    public boolean isAvailable() { return isAvailable; }
    
    // Methods
    public void borrowBook() {
        if (isAvailable) {
            isAvailable = false;
            System.out.println("Buku '" + title + "' berhasil dipinjam");
        } else {
            System.out.println("Buku '" + title + "' sedang tidak tersedia");
        }
    }
    
    public void returnBook() {
        isAvailable = true;
        System.out.println("Buku '" + title + "' berhasil dikembalikan");
    }
    
    @Override
    public String toString() {
        return title + " oleh " + author + " [" + (isAvailable ? "Tersedia" : "Dipinjam") + "]";
    }
}

// 2. Abstract Member Class (Abstraction + Inheritance)
public abstract class Member {
    protected String memberId;
    protected String name;
    protected List borrowedBooks;
    protected int maxBorrowLimit;
    
    public Member(String memberId, String name, int maxLimit) {
        this.memberId = memberId;
        this.name = name;
        this.borrowedBooks = new ArrayList<>();
        this.maxBorrowLimit = maxLimit;
    }
    
    // Abstract methods
    public abstract void borrowBook(Book book);
    public abstract void returnBook(Book book);
    
    // Concrete methods
    public void displayBorrowedBooks() {
        System.out.println("Buku yang dipinjam oleh " + name + ":");
        if (borrowedBooks.isEmpty()) {
            System.out.println("Tidak ada buku yang dipinjam");
        } else {
            for (Book book : borrowedBooks) {
                System.out.println("- " + book.getTitle());
            }
        }
    }
    
    public boolean canBorrowMore() {
        return borrowedBooks.size() < maxBorrowLimit;
    }
    
    // Getters
    public String getMemberId() { return memberId; }
    public String getName() { return name; }
}

// 3. StudentMember Class (Inheritance)
public class StudentMember extends Member {
    private String studentId;
    
    public StudentMember(String memberId, String name, String studentId) {
        super(memberId, name, 3); // Max 3 buku untuk student
        this.studentId = studentId;
    }
    
    @Override
    public void borrowBook(Book book) {
        if (!canBorrowMore()) {
            System.out.println("Maaf " + name + ", sudah mencapai batas peminjaman");
            return;
        }
        
        if (!book.isAvailable()) {
            System.out.println("Buku '" + book.getTitle() + "' tidak tersedia");
            return;
        }
        
        book.borrowBook();
        borrowedBooks.add(book);
        System.out.println(name + " berhasil meminjam: " + book.getTitle());
    }
    
    @Override
    public void returnBook(Book book) {
        if (borrowedBooks.contains(book)) {
            book.returnBook();
            borrowedBooks.remove(book);
            System.out.println(name + " berhasil mengembalikan: " + book.getTitle());
        } else {
            System.out.println("Buku '" + book.getTitle() + "' tidak dipinjam oleh " + name);
        }
    }
}

// 4. StaffMember Class (Inheritance)
public class StaffMember extends Member {
    private String department;
    
    public StaffMember(String memberId, String name, String department) {
        super(memberId, name, 5); // Max 5 buku untuk staff
        this.department = department;
    }
    
    @Override
    public void borrowBook(Book book) {
        if (!canBorrowMore()) {
            System.out.println("Maaf " + name + ", sudah mencapai batas peminjaman");
            return;
        }
        
        if (!book.isAvailable()) {
            System.out.println("Buku '" + book.getTitle() + "' tidak tersedia");
            return;
        }
        
        book.borrowBook();
        borrowedBooks.add(book);
        System.out.println(name + " (" + department + ") berhasil meminjam: " + book.getTitle());
    }
    
    @Override
    public void returnBook(Book book) {
        if (borrowedBooks.contains(book)) {
            book.returnBook();
            borrowedBooks.remove(book);
            System.out.println(name + " berhasil mengembalikan: " + book.getTitle());
        } else {
            System.out.println("Buku '" + book.getTitle() + "' tidak dipinjam oleh " + name);
        }
    }
}

// 5. Library Class (Composition + Polymorphism)
public class Library {
    private String name;
    private List books;
    private List members;
    
    public Library(String name) {
        this.name = name;
        this.books = new ArrayList<>();
        this.members = new ArrayList<>();
    }
    
    // Method untuk manage books
    public void addBook(Book book) {
        books.add(book);
        System.out.println("Buku ditambahkan: " + book.getTitle());
    }
    
    public void removeBook(Book book) {
        books.remove(book);
        System.out.println("Buku dihapus: " + book.getTitle());
    }
    
    // Method untuk manage members (Polymorphism)
    public void addMember(Member member) {
        members.add(member);
        System.out.println("Member ditambahkan: " + member.getName());
    }
    
    // Method untuk mencari buku
    public Book findBookByTitle(String title) {
        for (Book book : books) {
            if (book.getTitle().equalsIgnoreCase(title)) {
                return book;
            }
        }
        return null;
    }
    
    public List findBooksByAuthor(String author) {
        List result = new ArrayList<>();
        for (Book book : books) {
            if (book.getAuthor().equalsIgnoreCase(author)) {
                result.add(book);
            }
        }
        return result;
    }
    
    // Display library status
    public void displayLibraryStatus() {
        System.out.println("\n=== STATUS PERPUSTAKaan " + name.toUpperCase() + " ===");
        System.out.println("Total Buku: " + books.size());
        System.out.println("Total Member: " + members.size());
        
        int availableBooks = 0;
        for (Book book : books) {
            if (book.isAvailable()) availableBooks++;
        }
        System.out.println("Buku Tersedia: " + availableBooks);
        System.out.println("Buku Dipinjam: " + (books.size() - availableBooks));
    }
    
    // Display all books
    public void displayAllBooks() {
        System.out.println("\n=== DAFTAR BUKU ===");
        for (Book book : books) {
            System.out.println(book);
        }
    }
    
    // Display all members
    public void displayAllMembers() {
        System.out.println("\n=== DAFTAR MEMBER ===");
        for (Member member : members) {
            System.out.println(member.getName() + " (" + member.getMemberId() + ")");
            member.displayBorrowedBooks();
            System.out.println();
        }
    }
}

// 6. Main Class untuk Testing
public class PerpustakaanApp {
    public static void main(String[] args) {
        // Create library
        Library library = new Library("Perpustakaan Digital");
        
        // Create books
        Book book1 = new Book("001", "Pemrograman Java", "Budi Raharjo");
        Book book2 = new Book("002", "Struktur Data", "Ahmad Santoso");
        Book book3 = new Book("003", "Algoritma", "Siti Mahmud");
        Book book4 = new Book("004", "Database Systems", "John Doe");
        
        // Add books to library
        library.addBook(book1);
        library.addBook(book2);
        library.addBook(book3);
        library.addBook(book4);
        
        // Create members (Polymorphism)
        Member student1 = new StudentMember("S001", "Alice", "2024001");
        Member student2 = new StudentMember("S002", "Bob", "2024002");
        Member staff1 = new StaffMember("T001", "Mr. Smith", "IT Department");
        
        // Add members to library
        library.addMember(student1);
        library.addMember(student2);
        library.addMember(staff1);
        
        // Test borrowing books
        System.out.println("\n=== TEST PEMINJAMAN BUKU ===");
        student1.borrowBook(book1);
        student1.borrowBook(book2);
        student1.borrowBook(book3);
        student1.borrowBook(book4); // Should fail (limit reached)
        
        staff1.borrowBook(book4); // Staff should succeed
        
        // Display status
        library.displayLibraryStatus();
        library.displayAllMembers();
        
        // Test returning books
        System.out.println("\n=== TEST PENGEMBALIAN BUKU ===");
        student1.returnBook(book2);
        staff1.returnBook(book4);
        
        // Final status
        library.displayLibraryStatus();
        library.displayAllBooks();
    }
}

Advanced OOP Concepts

1. Interfaces vs Abstract Classes

// Interface - untuk multiple inheritance-like behavior
public interface Loanable {
    void borrowItem();
    void returnItem();
    boolean isAvailable();
}

public interface Reservable {
    void reserveItem();
    void cancelReservation();
}

// Class bisa implement multiple interfaces
public class ResearchBook extends Book implements Loanable, Reservable {
    // Harus implement semua method dari interfaces
}

2. Composition over Inheritance

// Daripada inheritance, gunakan composition
public class Engine {
    public void start() {
        System.out.println("Engine starting...");
    }
}

public class Car {
    private Engine engine; // Composition
    
    public Car() {
        this.engine = new Engine();
    }
    
    public void startCar() {
        engine.start();
        System.out.println("Car is ready to go!");
    }
}

Best Practices Java OOP

  1. Gunakan meaningful names: Class noun, method verb
  2. Single Responsibility Principle: Satu class, satu tanggung jawab
  3. Favor composition over inheritance
  4. Program to interface, not implementation
  5. Keep fields private dengan public getters/setters
  6. Use final untuk immutable fields
  7. Override equals(), hashCode(), dan toString()

Common Mistakes Pemula dalam OOP

  • God classes: Class yang melakukan terlalu banyak hal
  • Overusing inheritance: Terlalu deep inheritance hierarchy
  • Ignoring encapsulation: Public fields everywhere
  • Stringly typed code: Menggunakan string untuk type checking
  • Premature optimization: Over-engineering sebelum perlu

Kesimpulan: OOP adalah Mindset, Bukan Sekadar Syntax

Menguasai OOP itu seperti belajar bermain catur – kamu perlu pahami pergerakan dasar setiap buah (syntax), tapi yang lebih penting adalah strategy dan pola pikirnya (mindset).

Dengan OOP, kamu bukan cuma menulis code, tapi mendesain architecture. Ini skill yang membedakan programmer biasa dengan software engineer yang bisa membangun system scalable dan maintainable.

Practice yang disarankan:

  • Redesign program procedural lama ke OOP
  • Buat class diagrams sebelum coding
  • Study open-source Java projects
  • Terapkan design patterns sederhana

Ingat: OOP mastery datang dengan experience. Start small, think objects, dan terus practice! Selamat ber-OOP ria! 🚀