Tampilan
Kenapa penting
Program backend jarang hanya menghitung angka yang ditulis langsung di kode. Datanya datang dari luar — dari pengguna, file, atau request — dan sering berupa uang. Dua kesalahan klasik muncul di sini: input yang dibaca dengan cara yang salah sehingga data tertukar, dan nominal rupiah yang dihitung dengan double sehingga meleset satu sen. Selisih kecil itu bisa membuat laporan keuangan tidak seimbang atau pembayaran ditolak payment gateway. Bab ini membekalimu cara membaca input konsol dengan benar, method Math yang sering terpakai, dan BigDecimal — tipe yang dipakai hampir semua sistem pembayaran berbasis Java.
Konsep inti
Membaca input dengan Scanner
Class Scanner dari paket java.util membaca teks dari sebuah sumber, misalnya keyboard (System.in). Cara membuatnya memakai new — mekanisme object dibahas di M02, untuk sekarang anggap ini resep yang sudah jadi.
java
import java.util.Scanner;
public class ReadInput {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Nama pelanggan: ");
String name = scanner.nextLine();
System.out.print("Jumlah item: ");
int quantity = scanner.nextInt();
System.out.println();
System.out.println("Halo " + name + ", kamu memesan " + quantity + " item.");
}
}Saat dijalankan dengan input dari pipe (printf 'Budi Santoso\n3\n' | java ReadInput.java), outputnya:
text
Nama pelanggan: Jumlah item:
Halo Budi Santoso, kamu memesan 3 item.Kedua prompt tampil di satu baris karena input dari pipe tidak ikut tercetak; saat kamu mengetik langsung di terminal, ketikanmu muncul di samping prompt. Method yang paling sering dipakai:
| Method | Membaca |
|---|---|
nextLine() | satu baris utuh sampai Enter (Enter-nya dibuang) |
next() | satu "token" — teks sampai spasi berikutnya |
nextInt() | satu token lalu mengubahnya ke int |
hasNextInt() | true bila token berikutnya angka — belum membacanya |
hasNextLine() | true bila masih ada baris yang bisa dibaca (input belum habis) |
Jebakan sisa baris baru
nextInt() hanya membaca angkanya. Karakter Enter setelah angka masih tertinggal, dan nextLine() berikutnya langsung membaca sisa baris itu:
java
import java.util.Scanner;
public class NextIntTrap {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int quantity = scanner.nextInt(); // membaca "3", Enter-nya masih tertinggal
String product = scanner.nextLine(); // langsung membaca sisa baris itu: ""
System.out.println("qty=" + quantity + ", produk=[" + product + "]");
}
}Dengan input 3, Enter, Kopi Gayo, outputnya qty=3, produk=[] — nama produk tidak pernah terbaca. Cara paling aman: baca semuanya per baris, lalu ubah sendiri dengan Integer.parseInt (dari bab Tipe Data):
java
import java.util.Scanner;
public class NextLineFix {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int quantity = Integer.parseInt(scanner.nextLine().strip()); // baca satu baris utuh
String product = scanner.nextLine();
System.out.println("qty=" + quantity + ", produk=[" + product + "]");
}
}Input yang sama kini menghasilkan qty=3, produk=[Kopi Gayo].
Validasi input angka
Bila pengguna mengetik dua, nextInt() maupun Integer.parseInt menghentikan program dengan exception (cara menangkapnya dibahas di M03). Sementara itu, kamu bisa memeriksa dulu dengan hasNextInt():
java
import java.util.Scanner;
public class ValidateInput {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (!scanner.hasNextInt()) { // cek dulu, belum membaca
String wrong = scanner.next(); // buang token yang bukan angka
System.out.println("Bukan angka: " + wrong);
}
int quantity = scanner.nextInt();
System.out.println("Jumlah diterima: " + quantity);
}
}Dengan input dua, 2x, 2 (satu per baris), outputnya:
text
Bukan angka: dua
Bukan angka: 2x
Jumlah diterima: 2IO.readln: cara ringkas di Java 25
Java 25 menambahkan class IO (bab Struktur Program) yang juga bisa membaca input. IO.readln(prompt) mencetak prompt lalu mengembalikan satu baris — cocok untuk compact source file dan eksperimen:
java
void main() {
String name = IO.readln("Nama: ");
int age = Integer.parseInt(IO.readln("Umur: ").strip());
IO.println("Halo " + name + ", tahun depan umurmu " + (age + 1));
}Dengan input Sari dan 24, outputnya Nama: Umur: Halo Sari, tahun depan umurmu 25. Di aplikasi backend sungguhan input datang dari request HTTP, bukan keyboard — tetapi pola "baca teks, validasi, lalu ubah tipenya" tetap sama persis.
Class Math
Math berisi method static untuk perhitungan umum. Beberapa yang sering muncul di kode backend:
java
public class MathDemo {
public static void main(String[] args) {
System.out.println(Math.abs(-15_000)); // 15000
System.out.println(Math.max(3, 8) + " " + Math.min(3, 8));
System.out.println(Math.pow(2, 10)); // double
System.out.println(Math.sqrt(144));
System.out.println(Math.ceil(4.1) + " " + Math.floor(4.9) + " " + Math.round(4.5));
int totalItems = 41;
int pageSize = 10;
System.out.println((int) Math.ceil((double) totalItems / pageSize)); // jumlah halaman
System.out.println(-7 / 2 + " " + Math.floorDiv(-7, 2));
System.out.println(-7 % 2 + " " + Math.floorMod(-7, 2));
System.out.println(2_100_000_000 + 100_000_000); // overflow diam-diam
System.out.println(Math.addExact(2_100_000_000, 100_000_000)); // berhenti dengan exception
}
}Output:
text
15000
8 3
1024.0
12.0
5.0 4.0 5
5
-3 -4
-1 1
-2094967296
Exception in thread "main" java.lang.ArithmeticException: integer overflow
at java.base/java.lang.Math.addExact(Math.java:915)
at MathDemo.main(MathDemo.java:17)Math.powdanMath.sqrtselalu mengembalikandouble.Math.ceildipakai untuk menghitung jumlah halaman: 41 data dengan 10 per halaman butuh 5 halaman.Math.floorDiv/Math.floorModmembulatkan ke bawah (bukan ke nol), sehingga sisa bagi tidak pernah negatif untuk pembagi positif — berguna untuk indeks yang berputar atau menghitung hari dalam seminggu.Math.addExact,subtractExact, danmultiplyExactmenolak overflow alih-alih menghasilkan angka salah diam-diam (ingat overflow di bab Tipe Data). Lebih baik program berhenti daripada saldo menjadi negatif.
BigDecimal: angka desimal yang tepat
Di bab Tipe Data kamu melihat 0.1 + 0.2 menghasilkan 0.30000000000000004. Saat itu solusinya menyimpan rupiah sebagai long. Bila butuh pecahan yang tepat — sen, persen diskon 7.5, kurs mata uang — pakai java.math.BigDecimal:
java
import java.math.BigDecimal;
public class BigDecimalBasics {
public static void main(String[] args) {
System.out.println(0.1 + 0.2);
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
System.out.println(a.add(b));
System.out.println(new BigDecimal(0.1)); // dari double: membawa ketidaktepatannya
System.out.println(BigDecimal.valueOf(0.1)); // aman: lewat teks "0.1"
BigDecimal price = new BigDecimal("25000.50");
BigDecimal total = price.multiply(BigDecimal.valueOf(3));
price.add(BigDecimal.ONE); // hasilnya dibuang — price tidak berubah
System.out.println(price + " x 3 = " + total);
System.out.println(total.subtract(new BigDecimal("1.50")));
}
}Output:
text
0.30000000000000004
0.3
0.1000000000000000055511151231257827021181583404541015625
0.1
25000.50 x 3 = 75001.50
75000.00Aturan memakai BigDecimal:
- Buat dari
String(new BigDecimal("0.1")) atauBigDecimal.valueOf(...). Jangannew BigDecimal(0.1): nilainya sudah tidak tepat sejak berupadouble. - Immutable seperti
String:add,subtract,multiplymengembalikan object baru. Simpan hasilnya. - Operator
+ - * /tidak berlaku; pakai method-nya. Konstanta siap pakai:BigDecimal.ZERO,ONE,TEN.
Pembulatan, pembagian, dan perbandingan
setScale(digit, RoundingMode) menentukan jumlah angka di belakang koma sekaligus cara membulatkan:
java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class RoundingDemo {
public static void main(String[] args) {
String[] values = {"2.345", "2.355", "2.365", "-2.345"};
for (String value : values) {
BigDecimal number = new BigDecimal(value);
System.out.println(value
+ " HALF_UP=" + number.setScale(2, RoundingMode.HALF_UP)
+ " HALF_EVEN=" + number.setScale(2, RoundingMode.HALF_EVEN)
+ " DOWN=" + number.setScale(2, RoundingMode.DOWN));
}
BigDecimal bill = new BigDecimal("100000");
BigDecimal perPerson = bill.divide(BigDecimal.valueOf(3), 2, RoundingMode.HALF_UP);
System.out.println("Per orang: " + perPerson);
BigDecimal x = new BigDecimal("2.0");
BigDecimal y = new BigDecimal("2.00");
System.out.println(x.equals(y) + " " + (x.compareTo(y) == 0));
}
}Output:
text
2.345 HALF_UP=2.35 HALF_EVEN=2.34 DOWN=2.34
2.355 HALF_UP=2.36 HALF_EVEN=2.36 DOWN=2.35
2.365 HALF_UP=2.37 HALF_EVEN=2.36 DOWN=2.36
-2.345 HALF_UP=-2.35 HALF_EVEN=-2.34 DOWN=-2.34
Per orang: 33333.33
false trueHALF_UP— pembulatan "sekolah":.5ke atas (menjauhi nol). Umum dipakai untuk pajak dan tagihan.HALF_EVEN— pembulatan bankir:.5ke digit genap terdekat. Dipakai di perbankan karena tidak bias ke atas saat menjumlahkan banyak transaksi.DOWN— potong saja (ke arah nol).dividewajib diberi scale danRoundingModebila hasilnya bisa tak berujung. Tanpa itu,bill.divide(BigDecimal.valueOf(3))berhenti denganArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.- Bandingkan dengan
compareTo, bukanequals.equalsjuga membandingkan scale, sehingga2.0dan2.00dianggap berbeda.compareTomengembalikan negatif,0, atau positif —a.compareTo(b) >= 0artinyaa ≥ b.
Contoh di dunia backend
Layanan checkout menghitung invoice: total per baris, diskon persen, PPN 11%, lalu total akhir. Semua nominal memakai BigDecimal dengan dua angka desimal, dan pembulatan dilakukan di setiap langkah yang menghasilkan pecahan — sama seperti yang dicetak di struk.
java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Invoice {
static BigDecimal lineTotal(String unitPrice, int quantity) {
return new BigDecimal(unitPrice).multiply(BigDecimal.valueOf(quantity));
}
static BigDecimal percentOf(BigDecimal amount, BigDecimal percent) {
return amount.multiply(percent)
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
}
public static void main(String[] args) {
String[] names = {"Kopi Gayo 250g", "Gula Aren 500g", "Filter V60"};
String[] prices = {"89900.00", "32500.00", "45000.00"};
int[] quantities = {2, 1, 3};
BigDecimal subtotal = BigDecimal.ZERO;
for (int i = 0; i < names.length; i++) {
BigDecimal line = lineTotal(prices[i], quantities[i]);
subtotal = subtotal.add(line);
System.out.println("%-16s %d x %10s = %11s".formatted(names[i], quantities[i], prices[i], line));
}
BigDecimal discount = percentOf(subtotal, new BigDecimal("7.5"));
BigDecimal taxable = subtotal.subtract(discount);
BigDecimal tax = percentOf(taxable, new BigDecimal("11"));
BigDecimal total = taxable.add(tax);
System.out.println("Subtotal : " + subtotal);
System.out.println("Diskon 7.5%: -" + discount);
System.out.println("PPN 11% : " + tax);
System.out.println("Total : " + total);
System.out.println("Bayar 400000 cukup? " + (new BigDecimal("400000").compareTo(total) >= 0));
}
}Output:
text
Kopi Gayo 250g 2 x 89900.00 = 179800.00
Gula Aren 500g 1 x 32500.00 = 32500.00
Filter V60 3 x 45000.00 = 135000.00
Subtotal : 347300.00
Diskon 7.5%: -26047.50
PPN 11% : 35337.78
Total : 356590.28
Bayar 400000 cukup? truePPN dari 321252.50 adalah 35337.775; HALF_UP membulatkannya ke 35337.78. Harga ditulis sebagai teks ("89900.00") — persis seperti nilai yang datang dari JSON atau kolom NUMERIC database. Di aplikasi Spring Boot nanti, field harga di DTO dan entity juga bertipe BigDecimal, dan aturan pembulatan seperti ini biasanya ditetapkan sekali di satu method agar konsisten di seluruh aplikasi.
Pertanyaan interview
1. Kenapa double tidak boleh untuk uang, dan apa yang dipakai sebagai gantinya?
double menyimpan pecahan dalam biner sehingga nilai seperti 0.1 tidak tepat dan selisihnya menumpuk. Gantinya: BigDecimal (dibuat dari String, dengan scale dan RoundingMode yang eksplisit) atau long dalam satuan terkecil (sen/rupiah) bila tidak butuh pecahan.
2. Apa beda equals dan compareTo pada BigDecimal?
equals membandingkan nilai dan scale, sehingga 2.0 tidak sama dengan 2.00. compareTo hanya membandingkan nilai numerik. Untuk logika bisnis ("apakah saldo cukup?") pakai compareTo.
3. Apa beda RoundingMode.HALF_UP dan HALF_EVEN?
Keduanya membulatkan ke tetangga terdekat; bedanya saat nilai tepat di tengah. HALF_UP selalu ke atas (menjauhi nol), sedangkan HALF_EVEN ke digit genap — mengurangi bias saat menjumlahkan banyak angka, karena itu dipakai di perbankan. Pilihannya mengikuti aturan bisnis atau regulasi, dan harus konsisten.
Jebakan umum
nextInt()lalunextLine()→ nama produk terbaca kosong. Baca per baris dengannextLine()laluInteger.parseInt.new BigDecimal(0.1)membawa ketidaktepatandouble. Pakainew BigDecimal("0.1")atauBigDecimal.valueOf.- Membuang hasil operasi
BigDecimal.total.add(x);tidak mengubahtotal; tulistotal = total.add(x);. dividetanpa scale →ArithmeticExceptionuntuk pembagian seperti 1/3.- Membandingkan
BigDecimaldenganequalsatau==. PakaicompareTo. - Mengandalkan overflow diam-diam. Untuk hitungan yang tidak boleh salah, pakai
Math.addExactdan kawan-kawan.
Ringkasan
Scannermembaca input konsol; paling aman baca per baris dengannextLine()lalu ubah tipenya sendiri.hasNextInt()memeriksa input sebelum dibaca;IO.readln(Java 25) cocok untuk program kecil.Mathmenyediakanceil/floor/round,floorDiv/floorMod, danaddExactyang menolak overflow.BigDecimaluntuk angka desimal tepat: buat dariString, immutable, operasi lewat method.- Tentukan pembulatan dengan
setScale/divide+RoundingMode; bandingkan dengancompareTo.