Tampilan
Kenapa penting
Setiap data di backend — ID pengguna, stok barang, harga, status aktif — harus disimpan dalam tipe yang tepat. Salah pilih tipe bisa berakibat fatal tapi diam-diam: total transaksi yang tiba-tiba negatif karena overflow, harga yang selisih satu rupiah karena double, atau ID yang terpotong karena casting. Bab ini membangun kebiasaan memilih tipe data dengan sadar sejak awal.
Konsep inti
Variabel dan tipe
Variabel adalah nama untuk sebuah nilai. Java adalah bahasa statically typed: tipe setiap variabel ditentukan saat kompilasi dan tidak bisa berubah. Kalau kamu menaruh nilai yang tidak cocok, javac menolak sebelum program sempat berjalan.
Tipe di Java terbagi dua kelompok besar:
- Tipe primitif — menyimpan nilainya langsung (angka, karakter, boolean). Ada tepat 8 tipe.
- Tipe referensi — menyimpan referensi (alamat) ke sebuah object, misalnya
String. Semua tipe selain 8 primitif adalah tipe referensi.
Delapan tipe primitif
| Tipe | Ukuran | Rentang / isi | Contoh pemakaian |
|---|---|---|---|
byte | 8 bit | −128 s.d. 127 | data biner mentah |
short | 16 bit | −32.768 s.d. 32.767 | jarang dipakai |
int | 32 bit | ±2,1 miliar | hitungan, stok, umur — default |
long | 64 bit | ±9,2 × 10¹⁸ | ID database, timestamp, uang |
float | 32 bit | pecahan, ±7 digit presisi | jarang dipakai di backend |
double | 64 bit | pecahan, ±15 digit presisi | rasio, statistik, koordinat |
char | 16 bit | satu karakter Unicode (UTF-16) | inisial, kode huruf |
boolean | — | true atau false | flag: aktif, sudah bayar |
Untuk bilangan bulat, pakai int kecuali nilainya bisa melewati 2,1 miliar — saat itu pakai long. Untuk pecahan, pakai double.
java
public class PrimitiveTypes {
public static void main(String[] args) {
byte age = 27;
short year = 2026;
int stock = 1_500;
long population = 8_100_000_000L;
float rating = 4.5f;
double pi = 3.141592653589793;
char grade = 'A';
boolean active = true;
System.out.println(age + " " + year + " " + stock + " " + population);
System.out.println(rating + " " + pi + " " + grade + " " + active);
System.out.println(Integer.MAX_VALUE);
System.out.println(Long.MAX_VALUE);
}
}Output:
text
27 2026 1500 8100000000
4.5 3.141592653589793 A true
2147483647
9223372036854775807Literal
Literal adalah nilai yang ditulis langsung di kode. Beberapa aturannya:
- Angka bulat tanpa akhiran bertipe
int. Untuklongyang melewati batasint, tambahkanL:8_100_000_000L. TanpaL,javacmenolak dengan pesaninteger number too large. - Angka desimal tanpa akhiran bertipe
double. Untukfloatwajib akhiranf(4.5f). Akhiranduntukdoubleboleh tapi jarang ditulis. - Garis bawah
_boleh dipakai sebagai pemisah ribuan agar mudah dibaca:1_000_000. charmemakai kutip tunggal ('A'),Stringmemakai kutip ganda ("A").
Pakai huruf besar L, bukan l — huruf l kecil mudah tertukar dengan angka 1.
Tipe referensi: String
String adalah class untuk teks. Variabel String tidak menyimpan teksnya langsung, melainkan referensi ke object String. Karena berupa object, String punya method yang bisa dipanggil dengan titik. Tipe referensi juga bisa bernilai null — artinya "tidak menunjuk ke object apa pun".
java
public class ReferenceTypes {
public static void main(String[] args) {
String name = "Budi";
String greeting = name;
System.out.println(greeting.length());
System.out.println(greeting.toUpperCase());
String nothing = null;
System.out.println(nothing);
}
}Output: 4, BUDI, lalu null. Memanggil method pada variabel bernilai null (misalnya nothing.length()) akan menghentikan program dengan NullPointerException. API String dibahas lengkap di bab String & Array.
Wrapper class dan mengubah teks menjadi angka
Setiap tipe primitif punya pasangan class yang disebut wrapper class: int ↔ Integer, long ↔ Long, double ↔ Double, boolean ↔ Boolean, char ↔ Character, dan seterusnya. Di backend kamu akan memakainya setiap hari karena dua alasan:
- Data dari luar selalu berupa teks. Parameter URL
?page=3, isi form, header, dan file CSV datang sebagaiString. Untuk menghitung, ubah dulu denganInteger.parseInt,Long.parseLong, atauDouble.parseDouble. Arah sebaliknya memakaiString.valueOf(...). - Wrapper boleh bernilai
null, primitif tidak. Ini berguna untuk menyatakan "tidak diisi", misalnya kolom database yang boleh kosong. Nanti kamu juga butuh wrapper untuk menyimpan angka di collection (M03).
java
public class WrapperDemo {
public static void main(String[] args) {
String pageParam = "3"; // dari URL ?page=3 — selalu berupa teks
int page = Integer.parseInt(pageParam);
long orderId = Long.parseLong("9007199254740993");
double rating = Double.parseDouble("4.75");
boolean active = Boolean.parseBoolean("true");
System.out.println(page + 1);
System.out.println(orderId + " " + rating + " " + active);
String label = String.valueOf(page); // angka → teks
System.out.println(label + "1");
Integer boxed = page; // autoboxing: int → Integer
int unboxed = boxed; // unboxing: Integer → int
Integer discount = null; // wrapper boleh null, primitif tidak
System.out.println(boxed + unboxed + " " + discount);
Integer small1 = 127, small2 = 127;
Integer big1 = 1000, big2 = 1000;
System.out.println(small1 == small2);
System.out.println(big1 == big2);
System.out.println(big1.equals(big2));
}
}Output:
text
4
9007199254740993 4.75 true
31
6 null
true
false
truePerhatikan tiga hal. Pertama, label + "1" menghasilkan "31" karena label sudah berupa teks. Kedua, compiler otomatis mengubah int ↔ Integer (autoboxing dan unboxing), jadi keduanya terasa seperti tipe yang sama — padahal bukan. Ketiga, == pada wrapper membandingkan referensi object, sama seperti pada String. Hasil true untuk 127 hanya kebetulan: JVM menyimpan cache object Integer untuk −128 sampai 127. Untuk nilai lain, == bisa false walau angkanya sama. Bandingkan nilai wrapper dengan equals, atau ubah dulu ke primitif.
Dua kesalahan lain menghentikan program saat berjalan: unboxing null (int x = discount;) melempar NullPointerException, dan Integer.parseInt("12a") melempar NumberFormatException. Cara menangani error seperti ini dibahas di modul M03; untuk sekarang, ingat bahwa input dari luar perlu divalidasi sebelum di-parse.
var: inferensi tipe lokal
Sejak Java 10, variabel lokal boleh dideklarasikan dengan var. Compiler menebak tipenya dari nilai awal — tipenya tetap statis, hanya tidak ditulis.
java
public class VarDemo {
public static void main(String[] args) {
var count = 10; // int
var total = 10L; // long
var price = 9.99; // double
var product = "Kopi"; // String
System.out.println(count + total);
System.out.println(price);
System.out.println(product.length());
}
}Aturan var: hanya untuk variabel lokal di dalam method, wajib diberi nilai awal, dan tidak boleh diinisialisasi dengan null. Pakai var saat tipenya jelas dari sisi kanan; tulis tipe eksplisit saat tipenya tidak langsung terbaca (misalnya long vs int).
Casting: widening dan narrowing
Mengubah nilai dari satu tipe ke tipe lain disebut casting.
- Widening (ke tipe yang lebih lebar, mis.
int→long→double) terjadi otomatis karena tidak ada data yang hilang. - Narrowing (ke tipe yang lebih sempit, mis.
double→int) wajib ditulis eksplisit dengan(tipe), karena data bisa hilang. Tanpa cast,javacmenolak dengan pesanpossible lossy conversion.
java
public class Casting {
public static void main(String[] args) {
int items = 42;
long bigItems = items; // widening: otomatis
double ratio = bigItems; // widening: otomatis
System.out.println(ratio);
double price = 19.99;
int truncated = (int) price; // narrowing: wajib cast, desimal dibuang
System.out.println(truncated);
int large = 200;
byte small = (byte) large; // di luar rentang byte
System.out.println(small);
char letter = 'A';
int code = letter; // char → int: kode Unicode
System.out.println(code);
}
}Output: 42.0, 19, -56, 65. Perhatikan dua hal: cast double → int memotong desimal (bukan membulatkan), dan cast ke tipe yang terlalu kecil menghasilkan angka "aneh" karena hanya bit terbawah yang disimpan.
Overflow
Kalau hasil operasi melewati batas tipenya, nilai berputar tanpa error — inilah overflow.
java
public class Overflow {
public static void main(String[] args) {
int max = Integer.MAX_VALUE;
System.out.println(max + 1);
int millisPerMonthWrong = 24 * 60 * 60 * 1000 * 30;
long millisPerMonth = 24L * 60 * 60 * 1000 * 30;
System.out.println(millisPerMonthWrong);
System.out.println(millisPerMonth);
}
}Output: -2147483648, -1702967296, 2592000000. Pada baris kedua, semua operand bertipe int, sehingga perkaliannya overflow sebelum hasilnya disimpan. Menulis 24L di awal membuat seluruh perkalian dihitung dalam long.
Pecahan, uang, dan Math.round
float dan double menyimpan pecahan dalam bentuk biner, sehingga banyak angka desimal tidak bisa disimpan persis.
java
public class MoneyPrecision {
public static void main(String[] args) {
System.out.println(0.1 + 0.2);
System.out.println(Math.round(2.5));
System.out.println(Math.round(-2.5));
System.out.println(Math.round(2.4f));
long rounded = Math.round(1234.56);
int asInt = (int) Math.round(1234.56);
System.out.println(rounded + " " + asInt);
}
}Output:
text
0.30000000000000004
3
-2
2
1235 1235Math.round(double) membulatkan ke bilangan bulat terdekat (nilai tepat .5 dibulatkan ke atas, ke arah positif) dan mengembalikan long — jadi perlu cast (int) bila kamu butuh int. Untuk float, hasilnya int.
Karena ketidaktepatan ini, uang tidak disimpan sebagai double. Pilihan yang umum di backend: simpan sebagai long dalam satuan terkecil (rupiah utuh, atau sen untuk mata uang lain), atau pakai class java.math.BigDecimal bila butuh pecahan desimal yang tepat. BigDecimal akan kamu temui lagi saat membangun aplikasi Spring Boot.
Contoh di dunia backend
Menghitung total pesanan: harga disimpan sebagai long rupiah, PPN dihitung dengan double lalu dibulatkan kembali ke long.
java
public class OrderTotal {
public static void main(String[] args) {
long unitPriceRupiah = 125_000L; // harga dalam rupiah, tanpa pecahan
int quantity = 3;
long subtotal = unitPriceRupiah * quantity;
double taxRate = 0.11; // PPN 11%
long tax = Math.round(subtotal * taxRate);
long total = subtotal + tax;
var orderId = "ORD-2026-0001";
boolean paid = false;
System.out.println(orderId + " subtotal=" + subtotal + " tax=" + tax + " total=" + total + " paid=" + paid);
}
}Output: ORD-2026-0001 subtotal=375000 tax=41250 total=416250 paid=false. Pola ini — nilai uang tetap bilangan bulat, pecahan hanya di langkah perhitungan lalu segera dibulatkan — jauh lebih aman daripada menyimpan total sebagai double. Di aplikasi nyata, ID entitas dari database juga biasanya bertipe long.
Pertanyaan interview
1. Apa beda tipe primitif dan wrapper class? Kapan memakai masing-masing?
Primitif menyimpan nilai langsung, tidak bisa null, dan efisien — pakai untuk perhitungan. Wrapper seperti Integer adalah object: bisa null (berguna untuk nilai opsional, misalnya kolom database yang boleh kosong) dan dibutuhkan collection. Risikonya: == membandingkan referensi dan unboxing null melempar NullPointerException.
2. Kenapa double tidak boleh dipakai untuk uang? Apa alternatifnya?
double menyimpan pecahan dalam bentuk biner sehingga banyak nilai desimal tidak tepat (0.1 + 0.2 ≠ 0.3) dan selisihnya menumpuk. Simpan uang sebagai long dalam satuan terkecil (rupiah, sen) atau pakai BigDecimal bila butuh pecahan desimal yang tepat.
3. Apa itu integer overflow dan bagaimana mencegahnya?
Hasil operasi yang melewati batas tipe akan berputar ke sisi lain tanpa error, misalnya Integer.MAX_VALUE + 1 menjadi negatif. Cegah dengan memilih long untuk nilai besar (ID, uang, milidetik), memastikan operand pertama sudah long, atau memakai Math.addExact/Math.multiplyExact yang melempar exception saat overflow.
Jebakan umum
- Overflow diam-diam.
inthanya sampai ±2,1 miliar. Total transaksi, ID, dan milidetik bisa melewatinya — pakailong, dan pastikan operand pertama sudahlong(24L * ...). - Cast memotong, bukan membulatkan.
(int) 9.99hasilnya9. PakaiMath.roundbila maksudnya pembulatan. doubleuntuk uang.0.1 + 0.2bukan0.3. Simpan uang sebagailongatauBigDecimal.- Lupa akhiran
Latauf.long big = 3_000_000_000;gagal dikompilasi;float f = 4.5;juga gagal karena4.5bertipedouble. ==padaInteger/Long. Lolos saat dites dengan angka kecil, gagal untuk angka di atas 127. Pakaiequals.- Unboxing
null.int total = discount;saatdiscountbernilainullmelemparNullPointerException. vartanpa nilai awal yang jelas.var x;danvar x = null;tidak boleh.var id = 1;menghasilkanint, bukanlong.
Ringkasan
- Java punya 8 tipe primitif; sisanya (termasuk
String) tipe referensi yang bisa bernilainull. - Default praktis:
intuntuk bilangan bulat,longuntuk ID/waktu/uang,doubleuntuk pecahan,booleanuntuk flag. varmenebak tipe dari nilai awal, tapi tipenya tetap statis.- Teks → angka dengan
Integer.parseInt/Long.parseLong; wrapper bolehnulldan dibandingkan denganequals. - Widening otomatis; narrowing wajib cast dan memotong data. Overflow terjadi tanpa error.
Math.round(double)mengembalikanlong; uang disimpan sebagailongatauBigDecimal, bukandouble.