Java adalah bahasa yang sepenuhnya berorientasi objek — setiap baris kode berada dalam sebuah class. Berbeda dengan C++ yang menambahkan OOP di atas bahasa C, dan C# yang mengembangkannya lebih modern, Java membangun OOP dari nol dengan filosofi yang sangat konsisten. Di artikel ini kita mempelajari dasar membuat class di Java, dengan beberapa perbedaan penting dibanding C# maupun C++.
Aturan Penting Java: Nama File = Nama Class
// File: Mahasiswa.java — WAJIB sama dengan nama public class
public class Mahasiswa {
// Isi class
}
Jika class bernama Mahasiswa, file harus bernama Mahasiswa.java. Ini aturan wajib Java yang berbeda dari C# (lebih fleksibel) dan C++ (file bisa bernama apa saja).
Membuat Class Pertama
public class Mahasiswa {
// Instance variables (fields)
private String nama;
private int nim;
private double ipk;
// Constructor
public Mahasiswa(String nama, int nim, double ipk) {
this.nama = nama; // this.nama = field, nama = parameter
this.nim = nim;
this.ipk = ipk;
}
// Getter methods
public String getNama() { return nama; }
public int getNim() { return nim; }
public double getIpk() { return ipk; }
// Setter dengan validasi
public void setIpk(double ipk) {
if (ipk < 0.0 || ipk > 4.0) {
System.out.println("IPK harus antara 0.0 dan 4.0");
return;
}
this.ipk = ipk;
}
// toString() — konvensi Java untuk representasi string objek
@Override
public String toString() {
return String.format("[%d] %s — IPK: %.2f", nim, nama, ipk);
}
}
// File: Main.java
public class Main {
public static void main(String[] args) {
Mahasiswa m = new Mahasiswa("Ali Akbar", 2024001, 3.75);
System.out.println(m);
// Output: [2024001] Ali Akbar — IPK: 3.75
m.setIpk(3.90);
System.out.println(m.getIpk());
// Output: 3.9
}
}
Access Modifiers Java (4 Level)
Java memiliki 4 level akses — lebih banyak dari C++ dan C#:
public class ContohAkses {
public String publik = "Akses dari mana saja";
protected String terlindungi = "Akses dari package yang sama + subclass";
String defaultAkses = "Akses dari package yang sama (tanpa keyword)";
private String privat = "Hanya dari dalam class ini";
}
| Modifier | Class sendiri | Package sama | Subclass | Semua |
|---|---|---|---|---|
public | ✅ | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ✅ | ❌ |
| (default) | ✅ | ✅ | ❌ | ❌ |
private | ✅ | ❌ | ❌ | ❌ |
Constructor Overloading
public class Produk {
private String nama;
private double harga;
private int stok;
// Constructor lengkap
public Produk(String nama, double harga, int stok) {
this.nama = nama;
this.harga = harga;
this.stok = stok;
}
// Constructor dengan stok default = 0
public Produk(String nama, double harga) {
this(nama, harga, 0); // Memanggil constructor lain dengan this()
}
// Constructor copy
public Produk(Produk lain) {
this(lain.nama, lain.harga, lain.stok);
}
public boolean tersedia() { return stok > 0; }
@Override
public String toString() {
return String.format("%s: Rp %.0f (stok: %d)", nama, harga, stok);
}
}
// Penggunaan
Produk laptop = new Produk("ThinkPad", 15_000_000, 5);
Produk mouse = new Produk("Wireless Mouse", 250_000); // Stok 0
Produk laptop_copy = new Produk(laptop); // Copy constructor
System.out.println(laptop); // Output: ThinkPad: Rp 15000000 (stok: 5)
System.out.println(mouse); // Output: Wireless Mouse: Rp 250000 (stok: 0)
System.out.println(mouse.tersedia()); // Output: false
Static Members
Static members milik class, bukan instance — mirip dengan C# dan C++:
public class IDGenerator {
private static int counter = 0; // Dibagi oleh semua instance
private int id;
private String nama;
public IDGenerator(String nama) {
counter++;
this.id = counter;
this.nama = nama;
}
public static int getCounter() { return counter; }
public int getId() { return id; }
@Override
public String toString() {
return String.format("ID-%04d: %s", id, nama);
}
}
IDGenerator u1 = new IDGenerator("Ali");
IDGenerator u2 = new IDGenerator("Budi");
IDGenerator u3 = new IDGenerator("Candra");
System.out.println(IDGenerator.getCounter()); // Output: 3
System.out.println(u1); // Output: ID-0001: Ali
System.out.println(u3); // Output: ID-0003: Candra
Package dan Import
Java menggunakan sistem package untuk mengorganisir class:
// File: src/com/kamusngoding/model/Pengguna.java
package com.kamusngoding.model; // Deklarasi package
public class Pengguna {
private String username;
private String email;
public Pengguna(String username, String email) {
this.username = username;
this.email = email;
}
// ...
}
// File: src/com/kamusngoding/Main.java
package com.kamusngoding;
import com.kamusngoding.model.Pengguna; // Import class dari package lain
public class Main {
public static void main(String[] args) {
Pengguna user = new Pengguna("ali123", "ali@email.com");
}
}
Konvensi penamaan package menggunakan domain terbalik: com.namaPerusahaan.namaProyek.
Contoh Nyata: Sistem Parkir Sederhana
public class KendaraanParkir {
private static int totalKendaraan = 0;
private String platNomor;
private String jenisKendaraan;
private long waktuMasuk; // timestamp
public KendaraanParkir(String plat, String jenis) {
this.platNomor = plat.toUpperCase();
this.jenisKendaraan = jenis;
this.waktuMasuk = System.currentTimeMillis();
totalKendaraan++;
System.out.printf("✅ %s masuk parkir (total: %d)%n", platNomor, totalKendaraan);
}
public void keluarParkir() {
long durasi = (System.currentTimeMillis() - waktuMasuk) / 1000;
int tarif = jenisKendaraan.equals("Motor") ? 2_000 : 5_000;
System.out.printf("🚗 %s keluar | Durasi: %ds | Biaya: Rp %,d%n",
platNomor, durasi, tarif);
totalKendaraan--;
}
public static int getTotalKendaraan() { return totalKendaraan; }
}
Pertanyaan yang Sering Diajukan
Mengapa Java tidak punya destructor seperti C++?
Java menggunakan Garbage Collector (GC) yang otomatis membersihkan objek yang tidak lagi digunakan. GC berjalan non-deterministik — kamu tidak bisa tahu kapan tepatnya objek dihapus. Itulah mengapa Java tidak punya destructor. Untuk resource yang harus dilepas segera (file, koneksi), gunakan try-with-resources dengan AutoCloseable — akan dipelajari di artikel Exception Handling.
Mengapa getter/setter di Java sangat verbose dibanding C# properties?
Java memang tidak memiliki native property syntax seperti C#. Getter/setter di Java adalah method biasa dengan konvensi nama (getX()/setX()). Framework seperti Lombok bisa auto-generate getter/setter dengan annotation @Getter/@Setter, membuat kode lebih ringkas. Di Java modern (record, JDK 16+), ada record untuk immutable data class yang lebih ringkas.
Apa perbedaan this.nama = nama dan penggunaan this() di constructor?
this.nama mengacu pada field (instance variable) objek saat ini — berguna saat nama parameter sama dengan nama field. this() adalah pemanggilan constructor lain dalam class yang sama — berguna untuk constructor overloading tanpa duplikasi kode. Keduanya menggunakan kata kunci this tapi dalam konteks yang berbeda.
Bagaimana cara terbaik men-override toString() di Java?
toString() harus mengembalikan representasi string yang bermakna dari objek, termasuk nama class dan nilai field penting. Gunakan String.format() untuk output yang terformat. Gunakan @Override annotation selalu — ini menandai bahwa kamu me-override method dari superclass dan compiler akan error jika signature tidak cocok.
Kesimpulan
| Konsep | Java | C# | C++ |
|---|---|---|---|
| File naming | Wajib = nama public class | Bebas | Bebas |
| Properties | Getter/setter method | Native get/set | Manual getter/setter |
| Default access | Package-private | private | private |
| Destructor | Tidak ada (GC) | Finalizer | ~Class() deterministic |
| Data class | record (Java 16+) | record (C# 9+) | Manual |
Artikel sebelumnya: Koleksi Data di Java — ArrayList, HashMap, dan koleksi lainnya.
Langkah selanjutnya: Inheritance dan Polymorphism di Java — cara membuat hierarki class dan polymorphism di Java.